about summary refs log tree commit diff
diff options
context:
space:
mode:
authorgiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2019-06-28 23:45:11 +0000
committergiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2019-06-28 23:45:11 +0000
commitcdf6e0151411d887fef61245cb303ef190b29335 (patch)
tree678c2212e125e66e0a868773e2b4ec460794da4e
parentde1311e820dc892f1a3c5c9ae70dbc56868030d8 (diff)
downloadnetpbm-mirror-cdf6e0151411d887fef61245cb303ef190b29335.tar.gz
netpbm-mirror-cdf6e0151411d887fef61245cb303ef190b29335.tar.xz
netpbm-mirror-cdf6e0151411d887fef61245cb303ef190b29335.zip
Promote Advanced to Stable
git-svn-id: http://svn.code.sf.net/p/netpbm/code/stable@3641 9d0c8265-081b-0410-96cb-a4ca84ce46f8
-rw-r--r--GNUmakefile132
-rw-r--r--analyzer/Makefile32
-rw-r--r--analyzer/pamfile.c248
-rw-r--r--analyzer/pamfind.c222
-rw-r--r--analyzer/pamgetcolor.c573
-rw-r--r--analyzer/pamsumm.c121
-rw-r--r--analyzer/pamtable.c200
-rw-r--r--analyzer/pamtilt.c2
-rw-r--r--analyzer/pgmtexture.c66
-rw-r--r--analyzer/pnmpsnr.c260
-rw-r--r--analyzer/ppmhist.c228
-rw-r--r--buildtools/Makefile12
-rwxr-xr-xbuildtools/configure.pl115
-rw-r--r--buildtools/debian/README17
-rwxr-xr-xbuildtools/debian/mkdeb17
-rw-r--r--buildtools/genfontc.c199
-rwxr-xr-xbuildtools/installnetpbm.pl533
-rw-r--r--buildtools/libopt.c10
-rwxr-xr-xbuildtools/makeman67
-rw-r--r--buildtools/manpage.mk607
-rwxr-xr-xbuildtools/stamp-date19
-rw-r--r--common.mk74
-rw-r--r--config.mk.in10
-rw-r--r--converter/bmp.h133
-rw-r--r--converter/other/Makefile38
-rwxr-xr-xconverter/other/anytopnm14
-rw-r--r--converter/other/bmptopnm.c827
-rw-r--r--converter/other/cameratopam/camera.c48
-rw-r--r--converter/other/cameratopam/cameratopam.c1
-rw-r--r--converter/other/cameratopam/foveon.c2
-rw-r--r--converter/other/cameratopam/identify.c1
-rw-r--r--converter/other/cameratopam/ljpeg.c1
-rw-r--r--converter/other/fiasco/binerror.c5
-rw-r--r--converter/other/fiasco/binerror.h4
-rw-r--r--converter/other/fiasco/buttons.c4
-rw-r--r--converter/other/fiasco/buttons.h4
-rw-r--r--converter/other/fiasco/codec/approx.c1025
-rw-r--r--converter/other/fiasco/codec/approx.h4
-rw-r--r--converter/other/fiasco/codec/bintree.c4
-rw-r--r--converter/other/fiasco/codec/bintree.h4
-rw-r--r--converter/other/fiasco/codec/coder.c5
-rw-r--r--converter/other/fiasco/codec/coder.h4
-rw-r--r--converter/other/fiasco/codec/coeff.c4
-rw-r--r--converter/other/fiasco/codec/coeff.h4
-rw-r--r--converter/other/fiasco/codec/control.c4
-rw-r--r--converter/other/fiasco/codec/control.h4
-rw-r--r--converter/other/fiasco/codec/cwfa.h4
-rw-r--r--converter/other/fiasco/codec/decoder.c316
-rw-r--r--converter/other/fiasco/codec/decoder.h4
-rw-r--r--converter/other/fiasco/codec/dfiasco.c4
-rw-r--r--converter/other/fiasco/codec/dfiasco.h4
-rw-r--r--converter/other/fiasco/codec/domain-pool.c4
-rw-r--r--converter/other/fiasco/codec/domain-pool.h4
-rw-r--r--converter/other/fiasco/codec/ip.c4
-rw-r--r--converter/other/fiasco/codec/ip.h4
-rw-r--r--converter/other/fiasco/codec/motion.c4
-rw-r--r--converter/other/fiasco/codec/motion.h4
-rw-r--r--converter/other/fiasco/codec/mwfa.c4
-rw-r--r--converter/other/fiasco/codec/mwfa.h4
-rw-r--r--converter/other/fiasco/codec/options.c5
-rw-r--r--converter/other/fiasco/codec/options.h4
-rw-r--r--converter/other/fiasco/codec/prediction.c4
-rw-r--r--converter/other/fiasco/codec/prediction.h4
-rw-r--r--converter/other/fiasco/codec/subdivide.c4
-rw-r--r--converter/other/fiasco/codec/subdivide.h4
-rw-r--r--converter/other/fiasco/codec/tiling.c4
-rw-r--r--converter/other/fiasco/codec/tiling.h4
-rw-r--r--converter/other/fiasco/codec/wfa.h4
-rw-r--r--converter/other/fiasco/codec/wfalib.c5
-rw-r--r--converter/other/fiasco/codec/wfalib.h4
-rw-r--r--converter/other/fiasco/display.c4
-rw-r--r--converter/other/fiasco/display.h4
-rw-r--r--converter/other/fiasco/doc/README.LIB2
-rw-r--r--converter/other/fiasco/doc/fiasco_c_options_new.32
-rw-r--r--converter/other/fiasco/doc/fiasco_coder.32
-rw-r--r--converter/other/fiasco/doc/fiasco_d_options_new.32
-rw-r--r--converter/other/fiasco/doc/fiasco_decoder_new.32
-rw-r--r--converter/other/fiasco/doc/fiasco_get_error_message.32
-rw-r--r--converter/other/fiasco/doc/fiasco_image_new.32
-rw-r--r--converter/other/fiasco/doc/fiasco_options_new.32
-rw-r--r--converter/other/fiasco/doc/fiasco_renderer_new.32
-rw-r--r--converter/other/fiasco/doc/fiasco_set_verbosity.32
-rw-r--r--converter/other/fiasco/fiasco.h4
-rw-r--r--converter/other/fiasco/fiascotopnm.c7
-rw-r--r--converter/other/fiasco/input/basis.c4
-rw-r--r--converter/other/fiasco/input/basis.h4
-rw-r--r--converter/other/fiasco/input/matrices.c4
-rw-r--r--converter/other/fiasco/input/matrices.h4
-rw-r--r--converter/other/fiasco/input/mc.c4
-rw-r--r--converter/other/fiasco/input/mc.h4
-rw-r--r--converter/other/fiasco/input/nd.c4
-rw-r--r--converter/other/fiasco/input/nd.h4
-rw-r--r--converter/other/fiasco/input/read.c5
-rw-r--r--converter/other/fiasco/input/read.h4
-rw-r--r--converter/other/fiasco/input/tree.c4
-rw-r--r--converter/other/fiasco/input/tree.h4
-rw-r--r--converter/other/fiasco/input/weights.c4
-rw-r--r--converter/other/fiasco/input/weights.h4
-rw-r--r--converter/other/fiasco/lib/arith.c4
-rw-r--r--converter/other/fiasco/lib/arith.h4
-rw-r--r--converter/other/fiasco/lib/bit-io.c5
-rw-r--r--converter/other/fiasco/lib/bit-io.h4
-rw-r--r--converter/other/fiasco/lib/dither.c16
-rw-r--r--converter/other/fiasco/lib/dither.h4
-rw-r--r--converter/other/fiasco/lib/error.c403
-rw-r--r--converter/other/fiasco/lib/error.h4
-rw-r--r--converter/other/fiasco/lib/image.c4
-rw-r--r--converter/other/fiasco/lib/image.h4
-rw-r--r--converter/other/fiasco/lib/list.c4
-rw-r--r--converter/other/fiasco/lib/list.h4
-rw-r--r--converter/other/fiasco/lib/macros.h4
-rw-r--r--converter/other/fiasco/lib/misc.c4
-rw-r--r--converter/other/fiasco/lib/misc.h4
-rw-r--r--converter/other/fiasco/lib/rpf.c296
-rw-r--r--converter/other/fiasco/lib/rpf.h4
-rw-r--r--converter/other/fiasco/lib/types.h4
-rw-r--r--converter/other/fiasco/output/matrices.c4
-rw-r--r--converter/other/fiasco/output/matrices.h4
-rw-r--r--converter/other/fiasco/output/mc.c4
-rw-r--r--converter/other/fiasco/output/mc.h4
-rw-r--r--converter/other/fiasco/output/nd.c4
-rw-r--r--converter/other/fiasco/output/nd.h4
-rw-r--r--converter/other/fiasco/output/tree.c4
-rw-r--r--converter/other/fiasco/output/tree.h4
-rw-r--r--converter/other/fiasco/output/weights.c4
-rw-r--r--converter/other/fiasco/output/weights.h4
-rw-r--r--converter/other/fiasco/output/write.c4
-rw-r--r--converter/other/fiasco/output/write.h4
-rw-r--r--converter/other/fiasco/params.c18
-rw-r--r--converter/other/fiasco/params.h4
-rw-r--r--converter/other/fiasco/pnmtofiasco.c4
-rw-r--r--converter/other/fiasco/system.fiascorc4
-rw-r--r--converter/other/gemtopnm.c200
-rw-r--r--converter/other/giftopnm.c575
-rw-r--r--converter/other/ipdb.c5
-rw-r--r--converter/other/jbig/jbigtopnm.c394
-rw-r--r--converter/other/jbig/libjbig/jbig.c198
-rw-r--r--converter/other/jpeg2000/jpeg2ktopam.c108
-rw-r--r--converter/other/jpeg2000/libjasper/jp2/jp2_dec.c1032
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_dec.c15
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_enc.c494
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_t1enc.c1557
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_util.c26
-rw-r--r--converter/other/jpeg2000/pamtojpeg2k.c89
-rw-r--r--converter/other/jpegtopnm.c1
-rw-r--r--converter/other/pamtogif.c627
-rw-r--r--converter/other/pamtopdbimg.c152
-rw-r--r--converter/other/pamtopng.c396
-rw-r--r--converter/other/pamtopnm.c2
-rw-r--r--converter/other/pamtosvg/pxl-outline.c2
-rw-r--r--converter/other/pamtosvg/vector.c2
-rw-r--r--converter/other/pamtotga.c350
-rw-r--r--converter/other/pamtotiff.c210
-rw-r--r--converter/other/pamtouil.c1
-rw-r--r--converter/other/pamtowinicon.c18
-rw-r--r--converter/other/pclxl.h14
-rw-r--r--converter/other/pdbimgtopam.c33
-rw-r--r--converter/other/pgmtoppm.c1
-rw-r--r--converter/other/pngtopam.c98
-rw-r--r--converter/other/pngx.c72
-rw-r--r--converter/other/pngx.h17
-rw-r--r--converter/other/pnmtoddif.c3
-rw-r--r--converter/other/pnmtojpeg.c1
-rw-r--r--converter/other/pnmtopalm/palm.h30
-rw-r--r--converter/other/pnmtopalm/palmcolormap.c134
-rw-r--r--converter/other/pnmtopalm/palmcolormap.h25
-rw-r--r--converter/other/pnmtopalm/palmtopnm.c402
-rw-r--r--converter/other/pnmtopalm/pnmtopalm.c567
-rw-r--r--converter/other/pnmtopclxl.c126
-rwxr-xr-xconverter/other/pnmtoplainpnm2
-rw-r--r--converter/other/pnmtopng.c390
-rw-r--r--converter/other/pnmtops.c7
-rw-r--r--converter/other/pnmtosir.c153
-rw-r--r--converter/other/pstopnm.c285
-rw-r--r--converter/other/rletopnm.c13
-rw-r--r--converter/other/svgtopam.c254
-rw-r--r--converter/other/tiff.c1
-rw-r--r--converter/other/tifftopnm.c558
-rw-r--r--converter/other/winicontopam.c6
-rw-r--r--converter/other/xwdtopnm.c1
-rw-r--r--converter/pbm/Makefile19
-rw-r--r--converter/pbm/cmuwmtopbm.c7
-rw-r--r--converter/pbm/g3.h136
-rw-r--r--converter/pbm/g3prefab.c286
-rw-r--r--converter/pbm/g3prefab.h14
-rw-r--r--converter/pbm/g3topbm.c172
-rw-r--r--converter/pbm/g3ttable.c116
-rw-r--r--converter/pbm/g3ttable.h13
-rw-r--r--converter/pbm/pbmtoascii.c49
-rw-r--r--converter/pbm/pbmtoepson.c1
-rw-r--r--converter/pbm/pbmtog3.c729
-rw-r--r--converter/pbm/pbmtolj.c41
-rw-r--r--converter/pbm/pbmtoln03.c211
-rw-r--r--converter/pbm/pbmtomacp.c6
-rw-r--r--converter/pbm/pbmtomgr.c9
-rw-r--r--converter/pbm/pbmtonokia.c9
-rw-r--r--converter/pbm/pbmtopk.c1
-rw-r--r--converter/pbm/pbmtoppa/pbm.c273
-rw-r--r--converter/pbm/pbmtoppa/pbmtoppa.c1
-rw-r--r--converter/pbm/pbmtox10bm37
-rw-r--r--converter/pbm/pbmtoxbm.c1
-rw-r--r--converter/pgm/sbigtopgm.c1
-rw-r--r--converter/ppm/hpcdtoppm/Makefile4
-rwxr-xr-xconverter/ppm/hpcdtoppm/hpcdtoppm22
-rw-r--r--converter/ppm/ilbmtoppm.c809
-rw-r--r--converter/ppm/picttoppm.c369
-rw-r--r--converter/ppm/pjtoppm.c215
-rw-r--r--converter/ppm/ppmtoarbtxt.c52
-rw-r--r--converter/ppm/ppmtobmp.c447
-rw-r--r--converter/ppm/ppmtogif.c1
-rw-r--r--converter/ppm/ppmtoicr.c32
-rw-r--r--converter/ppm/ppmtomitsu.c16
-rw-r--r--converter/ppm/ppmtompeg/HISTORY2
-rw-r--r--converter/ppm/ppmtompeg/frame.c18
-rw-r--r--converter/ppm/ppmtompeg/frametype.c3
-rw-r--r--converter/ppm/ppmtompeg/gethostname.c1
-rw-r--r--converter/ppm/ppmtompeg/jrevdct.c44
-rw-r--r--converter/ppm/ppmtompeg/mfwddct.c31
-rw-r--r--converter/ppm/ppmtompeg/mpeg.c1
-rw-r--r--converter/ppm/ppmtompeg/parallel.c2
-rw-r--r--converter/ppm/ppmtompeg/param.c2
-rw-r--r--converter/ppm/ppmtompeg/ppmtompeg.c16
-rw-r--r--converter/ppm/ppmtompeg/rate.c46
-rw-r--r--converter/ppm/ppmtompeg/readframe.c1
-rw-r--r--converter/ppm/ppmtompeg/specifics.c242
-rw-r--r--converter/ppm/ppmtoxpm.c1
-rw-r--r--converter/ppm/sldtoppm.c220
-rw-r--r--converter/ppm/tgatoppm.c292
-rw-r--r--converter/ppm/winicontoppm.c1
-rw-r--r--converter/ppm/ximtoppm.c1
-rw-r--r--converter/ppm/xpmtoppm.c113
-rw-r--r--converter/ppm/xvminitoppm.c64
-rw-r--r--converter/ppm/yuvtoppm.c1
-rw-r--r--converter/tga.h13
-rw-r--r--doc/HISTORY584
-rw-r--r--doc/INSTALL70
-rw-r--r--doc/TESTS99
-rw-r--r--doc/USERDOC108
-rw-r--r--editor/Makefile20
-rw-r--r--editor/pamaddnoise.c2
-rw-r--r--editor/pamaltsat.c535
-rw-r--r--editor/pambrighten.c271
-rw-r--r--editor/pamcomp.c141
-rw-r--r--editor/pamcut.c480
-rw-r--r--editor/pamditherbw.c210
-rw-r--r--editor/pamenlarge.c717
-rw-r--r--editor/pamflip/flip.h2
-rw-r--r--editor/pamflip/pamflip.c223
-rw-r--r--editor/pamflip/pamflip_sse.c44
-rw-r--r--editor/pamflip/pamflip_sse.h2
-rw-r--r--editor/pamhue.c209
-rw-r--r--editor/pamlevels.c515
-rw-r--r--editor/pammixmulti.c543
-rw-r--r--editor/pamperspective.c1
-rw-r--r--editor/pamrecolor.c2
-rw-r--r--editor/pamrubber.c2
-rw-r--r--editor/pamscale.c450
-rwxr-xr-xeditor/pamstretch-gen165
-rw-r--r--editor/pamstretch.c364
-rw-r--r--editor/pamundice.c3
-rw-r--r--editor/pbmclean.c55
-rw-r--r--editor/pbmmask.c394
-rw-r--r--editor/pbmreduce.c514
-rw-r--r--editor/pgmmedian.c3
-rw-r--r--editor/pnmconvol.c178
-rw-r--r--editor/pnmcrop.c787
-rw-r--r--editor/pnmhisteq.c34
-rw-r--r--editor/pnminvert.c4
-rwxr-xr-xeditor/pnmmargin8
-rw-r--r--editor/pnmmontage.c1
-rw-r--r--editor/pnmnorm.c116
-rw-r--r--editor/pnmpad.c3
-rw-r--r--editor/pnmpaste.c32
-rwxr-xr-xeditor/pnmquant39
-rwxr-xr-xeditor/pnmquantall19
-rw-r--r--editor/pnmremap.c254
-rw-r--r--editor/pnmrotate.c2
-rw-r--r--editor/pnmshear.c2
-rw-r--r--editor/pnmstitch.c1
-rw-r--r--editor/ppmbrighten.c274
-rw-r--r--editor/ppmchange.c94
-rw-r--r--editor/ppmcolormask.c188
-rw-r--r--editor/ppmdraw.c69
-rwxr-xr-xeditor/ppmfade16
-rw-r--r--editor/ppmlabel.c2
-rwxr-xr-xeditor/ppmquant13
-rwxr-xr-xeditor/ppmshadow134
-rw-r--r--editor/ppmshadow.doc4
-rw-r--r--editor/specialty/Makefile8
-rw-r--r--editor/specialty/pammixinterlace.c62
-rw-r--r--editor/specialty/pampaintspill.c2
-rw-r--r--editor/specialty/pgmabel.c5
-rw-r--r--editor/specialty/pnmindex.c1
-rw-r--r--editor/specialty/pnmmercator.c2
-rw-r--r--editor/specialty/ppmglobe.c2
-rw-r--r--editor/specialty/ppmntsc.c4
-rw-r--r--generator/Makefile9
-rw-r--r--generator/pamcrater.c2
-rw-r--r--generator/pamgauss.c264
-rw-r--r--generator/pamgradient.c29
-rw-r--r--generator/pamshadedrelief.c2
-rw-r--r--generator/pamtris/Makefile27
-rw-r--r--generator/pamtris/boundaries.c262
-rw-r--r--generator/pamtris/boundaries.h72
-rw-r--r--generator/pamtris/framebuffer.c339
-rw-r--r--generator/pamtris/framebuffer.h75
-rw-r--r--generator/pamtris/input.c695
-rw-r--r--generator/pamtris/input.h27
-rw-r--r--generator/pamtris/limits_pamtris.h11
-rw-r--r--generator/pamtris/pamtris.c171
-rw-r--r--generator/pamtris/triangle.c327
-rw-r--r--generator/pamtris/triangle.h25
-rw-r--r--generator/pamtris/utils.c266
-rw-r--r--generator/pamtris/utils.h58
-rw-r--r--generator/pamtris/varying.h12
-rw-r--r--generator/pbmmake.c16
-rw-r--r--generator/pbmtext.c1596
-rw-r--r--generator/pbmtextps.c736
-rwxr-xr-xgenerator/pgmcrater20
-rw-r--r--generator/pgmkernel.c4
-rw-r--r--generator/pgmmake.c70
-rw-r--r--generator/pgmnoise.c21
-rw-r--r--generator/pgmramp.c31
-rw-r--r--generator/ppmcie.c38
-rw-r--r--generator/ppmforge.c2
-rw-r--r--generator/ppmpat.c582
-rwxr-xr-xgenerator/ppmrainbow45
-rw-r--r--generator/ppmwheel.c309
-rw-r--r--lib/Makefile21
-rw-r--r--lib/colorname.c113
-rw-r--r--lib/colorname.h14
-rw-r--r--lib/libpam.c191
-rw-r--r--lib/libpam.h6
-rw-r--r--lib/libpamcolor.c643
-rw-r--r--lib/libpamn.c215
-rw-r--r--lib/libpbm2.c24
-rw-r--r--lib/libpbm3.c100
-rw-r--r--lib/libpbmfont.c1603
-rw-r--r--lib/libpbmfont0.c335
-rw-r--r--lib/libpbmfont1.c359
-rw-r--r--lib/libpbmfont2.c1051
-rw-r--r--lib/libpbmfontdump.c96
-rw-r--r--lib/libpgm.h6
-rw-r--r--lib/libpm.c115
-rw-r--r--lib/libpnm3.c293
-rw-r--r--lib/libppm1.c108
-rw-r--r--lib/libppmcolor.c486
-rw-r--r--lib/libppmd.c7
-rw-r--r--lib/libppmfuzzy.c2
-rw-r--r--lib/libsystem.c276
-rw-r--r--lib/pam.h165
-rw-r--r--lib/path.c157
-rw-r--r--lib/pbmfont.h288
-rw-r--r--lib/pbmfontdata.h7
-rw-r--r--lib/pbmfontdata0.c9
-rw-r--r--lib/pbmfontdata1.c269
-rw-r--r--lib/pbmfontdata2.c463
-rw-r--r--lib/pgm.h38
-rw-r--r--lib/pm.h71
-rw-r--r--lib/pm_gamma.h24
-rw-r--r--lib/pm_system.h47
-rw-r--r--lib/pmfileio.c187
-rw-r--r--lib/pnm.h53
-rw-r--r--lib/ppm.h93
-rw-r--r--lib/ppmdraw.h65
-rw-r--r--lib/util/mallocvar.c8
-rw-r--r--lib/util/mallocvar.h18
-rw-r--r--lib/util/nstring.c105
-rw-r--r--lib/util/nstring.h33
-rw-r--r--lib/util/pm_c_util.h13
-rw-r--r--lib/util/runlength.c103
-rw-r--r--lib/util/runlength.h3
-rw-r--r--lib/util/shhopt.c58
-rw-r--r--lib/util/shhopt.h66
-rw-r--r--lib/util/wordaccess.h75
-rw-r--r--lib/util/wordaccess_64_le.h28
-rw-r--r--lib/util/wordaccess_be_aligned.h35
-rw-r--r--lib/util/wordaccess_be_unaligned.h20
-rw-r--r--lib/util/wordaccess_generic.h29
-rw-r--r--lib/util/wordintclz.h93
-rw-r--r--netpbm.c46
-rw-r--r--other/Makefile10
-rw-r--r--other/pamexec.c1
-rw-r--r--other/pamfix.c12
-rw-r--r--other/pamlookup.c16
-rw-r--r--other/pampick.c12
-rw-r--r--other/pamsplit.c1
-rw-r--r--other/pamx/Makefile10
-rw-r--r--other/pamx/pamx.c1
-rw-r--r--other/pamx/send.c119
-rw-r--r--other/pamx/window.c1
-rw-r--r--other/pnmcolormap.c173
-rw-r--r--pm_config.in.h16
-rwxr-xr-xtest/Available-Testprog75
-rwxr-xr-xtest/Execute-Tests48
-rw-r--r--test/Makefile10
-rw-r--r--test/Test-Order42
-rw-r--r--test/all-in-place.ok29
-rwxr-xr-xtest/all-in-place.test126
-rw-r--r--test/bmp-quant-roundtrip.ok6
-rwxr-xr-xtest/bmp-quant-roundtrip.test36
-rw-r--r--test/bmp-roundtrip.ok20
-rwxr-xr-xtest/bmp-roundtrip.test41
-rw-r--r--test/cut-cat-roundtrip.ok92
-rwxr-xr-xtest/cut-cat-roundtrip.test128
-rw-r--r--test/enlarge-reduce-roundtrip.ok17
-rwxr-xr-xtest/enlarge-reduce-roundtrip.test34
-rw-r--r--test/fiasco-roundtrip.ok1
-rwxr-xr-xtest/fiasco-roundtrip.test23
-rw-r--r--test/g3-roundtrip.ok16
-rwxr-xr-xtest/g3-roundtrip.test51
-rw-r--r--test/gif-quant-roundtrip.ok2
-rwxr-xr-xtest/gif-quant-roundtrip.test2
-rw-r--r--test/gif-roundtrip.ok13
-rwxr-xr-xtest/gif-roundtrip.test105
-rwxr-xr-xtest/jbig-roundtrip.test1
-rw-r--r--test/jpeg-roundtrip.ok3
-rwxr-xr-xtest/jpeg-roundtrip.test19
-rw-r--r--test/jpeg2k-roundtrip.ok1
-rwxr-xr-xtest/jpeg2k-roundtrip.test7
-rw-r--r--test/legacy-names.ok14
-rwxr-xr-xtest/legacy-names.test80
-rw-r--r--test/palm-roundtrip.ok11
-rwxr-xr-xtest/palm-roundtrip.test39
-rw-r--r--test/palm-roundtrip2.ok5
-rwxr-xr-xtest/palm-roundtrip2.test31
-rw-r--r--test/pamcut.ok15
-rwxr-xr-xtest/pamcut.test35
-rw-r--r--test/pamenlarge-pamscale-point.ok9
-rwxr-xr-xtest/pamenlarge-pamscale-point.test36
-rw-r--r--test/pamenlarge-pbm.ok86
-rwxr-xr-xtest/pamenlarge-pbm.test61
-rw-r--r--test/pamfile.ok7
-rwxr-xr-xtest/pamfile.test7
-rw-r--r--test/pamfind.ok83
-rwxr-xr-xtest/pamfind.test32
-rw-r--r--test/pamgauss.ok (renamed from test/ppmgauss.ok)72
-rwxr-xr-xtest/pamgauss.test (renamed from test/ppmgauss.test)2
-rw-r--r--test/pammixmulti-identity.ok22
-rwxr-xr-xtest/pammixmulti-identity.test77
-rw-r--r--test/pamscale-filters1.ok15
-rwxr-xr-xtest/pamscale-filters1.test64
-rw-r--r--test/pamscale-filters2.ok24
-rwxr-xr-xtest/pamscale-filters2.test97
-rw-r--r--test/pamscale-filters3.ok7
-rwxr-xr-xtest/pamscale-filters3.test32
-rw-r--r--test/pamscale-reportonly.ok18
-rwxr-xr-xtest/pamscale-reportonly.test35
-rw-r--r--test/pamstretch.ok20
-rwxr-xr-xtest/pamstretch.test48
-rw-r--r--test/pamtable.ok66
-rwxr-xr-xtest/pamtable.test20
-rw-r--r--test/pamtopdbimg.ok20
-rwxr-xr-xtest/pamtopdbimg.test95
-rw-r--r--test/pbmtext-bdf.ok21
-rwxr-xr-xtest/pbmtext-bdf.test169
-rw-r--r--test/pbmtext-iso88591.ok4
-rwxr-xr-xtest/pbmtext-iso88591.test46
-rw-r--r--test/pbmtext-utf8.ok8
-rwxr-xr-xtest/pbmtext-utf8.test95
-rw-r--r--test/pbmtext.ok37
-rwxr-xr-xtest/pbmtext.test92
-rw-r--r--test/pbmtog3.ok1
-rwxr-xr-xtest/pbmtog3.test10
-rw-r--r--test/pdb-roundtrip.ok18
-rwxr-xr-xtest/pdb-roundtrip.test55
-rw-r--r--test/pgmnoise.ok1
-rw-r--r--test/pgmnoise.rand-ok3
-rwxr-xr-xtest/pgmnoise.test22
-rw-r--r--test/pnmpaste-pbm.ok22
-rwxr-xr-xtest/pnmpaste-pbm.test47
-rw-r--r--test/pnmpsnr.ok9
-rwxr-xr-xtest/pnmpsnr.test11
-rw-r--r--test/pnmquantall.ok5
-rwxr-xr-xtest/pnmquantall.test27
-rw-r--r--test/pnmtile.ok2
-rwxr-xr-xtest/pnmtile.test2
-rw-r--r--test/ppmbrighten.ok6
-rwxr-xr-xtest/ppmbrighten.test4
-rw-r--r--test/ppmchange.ok9
-rwxr-xr-xtest/ppmchange.test41
-rw-r--r--test/ppmforge.ok1
-rw-r--r--test/ppmforge.rand-ok3
-rwxr-xr-xtest/ppmforge.test17
-rw-r--r--test/ppmhist.ok14
-rwxr-xr-xtest/ppmhist.test21
-rw-r--r--test/ppmpat-random.rand-ok7
-rwxr-xr-xtest/ppmpat-random.test22
-rw-r--r--test/ppmpat.ok6
-rwxr-xr-xtest/ppmpat.test38
-rw-r--r--test/ppmrough.ok1
-rw-r--r--test/ppmrough.rand-ok3
-rwxr-xr-xtest/ppmrough.test15
-rw-r--r--test/ps-flate-roundtrip.ok3
-rwxr-xr-xtest/ps-flate-roundtrip.test48
-rw-r--r--test/ps-roundtrip.ok1
-rwxr-xr-xtest/ps-roundtrip.test14
-rw-r--r--test/rgb3-roundtrip.ok2
-rwxr-xr-xtest/rgb3-roundtrip.test2
-rw-r--r--test/symmetry.ok6
-rwxr-xr-xtest/symmetry.test8
-rw-r--r--test/targa-roundtrip.ok40
-rwxr-xr-xtest/targa-roundtrip.test82
-rw-r--r--test/testrandom.c30
-rw-r--r--test/tiff-flate-lzw-roundtrip.ok7
-rwxr-xr-xtest/tiff-flate-lzw-roundtrip.test34
-rw-r--r--test/tiff-roundtrip.ok34
-rwxr-xr-xtest/tiff-roundtrip.test104
-rw-r--r--test/tiffcmyk-roundtrip.ok5
-rwxr-xr-xtest/tiffcmyk-roundtrip.test42
-rwxr-xr-xtest/utahrle-roundtrip.test1
-rw-r--r--version.mk4
512 files changed, 33320 insertions, 16760 deletions
diff --git a/GNUmakefile b/GNUmakefile
index ca2181b3..1beebb4c 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -11,6 +11,7 @@
 #   deb:          Make a .deb file in the current dir.
 #
 #   check-tree:     Conduct tests on Netpbm files in the source dir. 
+#                   with "target=pamtotga" tests only pamtotga.
 #   check-package:  Conduct tests on packaged Netpbm files.
 #   check-install:  Conduct tests on installed Netpbm files.
 #   check:          Default check.  Synonym for check-package.
@@ -101,18 +102,27 @@ all: nonmerge
 .PHONY: nonmerge
 nonmerge: $(PRODUCT_SUBDIRS:%=%/all)
 
-# Parallel make (make --jobs) is not smart enough to coordinate builds
-# between submakes, so a naive parallel make would cause certain
-# targets to get built multiple times simultaneously.  That is usually
-# unacceptable.  So we introduce extra dependencies here just to make
-# sure such targets are already up to date before the submake starts,
-# for the benefit of parallel make.  Note that we ensure that parallel
-# make works for 'make all' in the top directory, but it may still fail
-# for the aforementioned reason for other invocations.
+# Completely parallel make (make --jobs) does not work because there are
+# multiple targets somewhere in the Netpbm build that depend upon pm_config.h
+# and similar targets, and the threads building those multiple targets might
+# simultaneously find that pm_config.h needs to be built and proceed to build
+# it.  After one thread has built pm_config.h, it will proceed to use
+# pm_config.h, but the other thread is still building pm_config.h, which is
+# not valid while it is in the middle of being built.
+#
+# But many submakes don't have any such shared dependencies, so build their
+# targets in parallel just fine.  So we declare this make file ineligible for
+# parallel make and have special dependencies to get pm_config.h and like
+# targets built before any submakes begin.  The submakes will thus never find
+# that pm_config.h needs to be built, so we leave them eligible for parallel
+# make.
+
+.NOTPARALLEL:
 
 $(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
+netpbm: lib/all
 
 .PHONY: lib/util/all
 lib/util/all:
@@ -139,7 +149,7 @@ $(TYPEGEN) $(ENDIANGEN): $(BUILDDIR)/buildtools
 	$(MAKE) -C $(dir $@) -f $(SRCDIR)/buildtools/Makefile \
 	    SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@) 
 
-inttypes_netpbm.h: $(TYPEGEN)
+$(BUILDDIR)/inttypes_netpbm.h: $(TYPEGEN)
 	$(TYPEGEN) >$@
 
 
@@ -155,7 +165,7 @@ $(TESTRANDOM): $(BUILDDIR)/test
 # that or to override the results, because it doesn't work if he's
 # cross compiling.
 
-pm_config.h: \
+$(BUILDDIR)/pm_config.h: \
   $(SRCDIR)/pm_config.in.h config.mk inttypes_netpbm.h \
   $(ENDIANGEN)
 # Note that this rule depends on the effect of the .DELETE_ON_ERROR
@@ -197,7 +207,7 @@ endif
 MAJOR := $(NETPBM_MAJOR_RELEASE)
 MINOR := $(NETPBM_MINOR_RELEASE)
 POINT := $(NETPBM_POINT_RELEASE)
-version.h: $(SRCDIR)/version.mk
+$(BUILDDIR)/version.h: $(SRCDIR)/version.mk
 	@rm -f $@
 	@echo "/* Generated by make file rule */" >>$@
 	@echo "#define NETPBM_VERSION" \
@@ -276,11 +286,9 @@ install-run: install-nonmerge
 endif
 
 .PHONY: install-merge install-nonmerge
-install-merge: install.merge install.lib install.data \
-	install.manwebmain install.manweb install.man
+install-merge: install.merge install.lib install.data
 
-install-nonmerge: install.bin install.lib install.data \
-	install.manwebmain install.manweb install.man
+install-nonmerge: install.bin install.lib install.data
 
 .PHONY: merge
 merge: lib/all netpbm
@@ -375,35 +383,12 @@ netpbm:%:%.o $(OBJECT_DEP) $(NETPBMLIB) $(URTLIBDEP) $(LIBOPT)
 
 netpbm.o: mergetrylist
 
-install.merge: local.install.merge
-.PHONY: local.install.merge
-local.install.merge:
-	cd $(PKGDIR)/bin; $(SYMLINKEXE) netpbm pnmnoraw
-	cd $(PKGDIR)/bin; $(SYMLINKEXE) netpbm gemtopbm
-	cd $(PKGDIR)/bin; $(SYMLINKEXE) netpbm pnminterp
-	cd $(PKGDIR)/bin; $(SYMLINKEXE) netpbm pgmoil
-	cd $(PKGDIR)/bin; $(SYMLINKEXE) netpbm ppmtojpeg
-	cd $(PKGDIR)/bin; $(SYMLINKEXE) netpbm bmptoppm
-	cd $(PKGDIR)/bin; $(SYMLINKEXE) netpbm pgmnorm
-	cd $(PKGDIR)/bin; $(SYMLINKEXE) netpbm pnmfile
-
 ifneq ($(NETPBMLIBTYPE),unixstatic)
 install.lib: lib/install.lib
 else
 install.lib:
 endif
 
-.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)" > $@
-	chmod $(INSTALL_PERM_MAN) $@
-
-$(PKGDIR)/bin/doc.url: $(PKGDIR)/bin
-	echo "$(NETPBM_DOCURL)" > $@
-	chmod $(INSTALL_PERM_MAN) $@
-
 .PHONY: install-dev
 # Note that you might install the development package and NOT the runtime
 # package.  If you have a special system for building stuff, maybe for 
@@ -455,10 +440,27 @@ deb:
 .PHONY: check-package
 .PHONY: check-install
 
+# Variables from the make env we pass down to the test scripts.
+CHECK_VARS = \
+	BUILDDIR=$(BUILDDIR) \
+	RGBDEF=$(RGBDEF) \
+	PALMMAPDIR=$(PALMMAPDIR) \
+	BUILD_FIASCO=$(BUILD_FIASCO) \
+	JASPERLIB="$(JASPERLIB)" \
+	JBIGLIB="$(JBIGLIB)" \
+	JPEGLIB="$(JPEGLIB)" \
+	PNGLIB="$(PNGLIB)" \
+	TIFFLIB="$(TIFFLIB)" \
+	URTLIB="$(URTLIB)" \
+	X11LIB="$(X11LIB)" \
+	XML2_LIBS="$(XML2_LIBS)" \
+	LEX="$(LEX)" \
+	ZLIB="$(ZLIB)" \
+
 # Test files in source tree.
-# This does not work when Netpbm is compiled in a separate build dir.
+# BUILDBINDIRS is a list of directories which contain target binaries
 
-check-tree : SRCBINDIRS :=./analyzer \
+check-tree : BUILDBINDIRS :=./analyzer \
 ./converter/other \
 ./converter/other/cameratopam \
 ./converter/other/fiasco \
@@ -470,15 +472,22 @@ check-tree : SRCBINDIRS :=./analyzer \
 ./converter/pbm/pbmtoppa \
 ./converter/pgm \
 ./converter/ppm \
-./converter/ppm/hpcdtoppm \
 ./converter/ppm/ppmtompeg \
 ./converter/ppm \
 ./editor \
 ./editor/pamflip \
 ./editor/specialty \
 ./generator \
+./generator/pamtris \
+./other \
+./other/pamx
+
+check-tree : SRCBINDIRS :=./converter/ \
+./converter/other/ \
+./converter/ppm/hpcdtoppm/ \
+./editor \
+./generator \
 ./other \
-./other/pamx \
 ./
 
 # Create colon-separated PATH list from the above.
@@ -486,10 +495,13 @@ check-tree : SRCBINDIRS :=./analyzer \
 
 # Kludge to test whether realpath is available:
 ifeq ($(realpath $(CURDIR)/.),$(CURDIR))
-  check-tree : RBINDIRS :=\
-    $(foreach dir,$(SRCBINDIRS),$(realpath $(BUILDDIR)/$(dir)))
+  check-tree : RBINDIRS :=$(sort \
+     $(foreach dir,$(BUILDBINDIRS),$(realpath $(BUILDDIR)/$(dir))) \
+     $(foreach dir,$(SRCBINDIRS),$(realpath $(SRCDIR)/$(dir))))
 else
-  check-tree : RBINDIRS :=$(foreach dir,$(SRCBINDIRS),$(BUILDDIR)/$(dir))  
+  check-tree : RBINDIRS :=$(sort \
+     $(foreach dir,$(BUILDBINDIRS),$(BUILDDIR)/$(dir)) \
+     $(foreach dir,$(SRCBINDIRS),$(SRCDIR)/$(dir)))
 endif
 
 # Kludge to express characters given special meanings by GNU Make.
@@ -498,15 +510,16 @@ empty :=
 space := $(empty) $(empty)
 colon :=:
 
-check-tree : PBM_TEST_PATH := $(subst $(space),$(colon),$(RBINDIRS))
+check-tree : PBM_TEST_PATH := $(subst $(space),$(colon),$(strip $(RBINDIRS)))
 check-tree : PBM_LIBRARY_PATH ?= $(BUILDDIR)/lib
 check-tree : RGBDEF ?= $(SRCDIR)/lib/rgb.txt
+check-tree : PALMMAPDIR ?= $(SRCDIR)/converter/other/pnmtopalm
 
 
 # Create RESULTDIR.
-# If it already exists, rename and covert to an archive directory.
+# If it already exists, rename and convert to an archive directory.
 # Use numbered backup.
-# TODO: Renaming fails with old versions of mv which do not support -T.  
+# Note: Renaming fails with old versions of mv which do not have -T.  
 
 resultdir-backup: FORCE
 	if [ -d $(RESULTDIR) ]; \
@@ -514,13 +527,17 @@ resultdir-backup: FORCE
 	fi; \
 	mkdir -p $(RESULTDIR); \
 
+.PHONY: testdir test
+
+testdir:
+	$(MAKE) -C test
 
-check-tree: $(TESTRANDOM) resultdir-backup
+check-tree: testdir resultdir-backup
 	cd $(RESULTDIR); \
+	  $(CHECK_VARS) \
 	  CHECK_TYPE=tree \
-	  PBM_TEST_PATH=$(PBM_TEST_PATH) BUILDDIR=$(BUILDDIR) \
+	  PBM_TEST_PATH=$(PBM_TEST_PATH) \
 	  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.
@@ -533,23 +550,24 @@ check-tree: $(TESTRANDOM) resultdir-backup
 check-package : PBM_TEST_PATH := $(PKGDIR)/bin
 check-package : PBM_LIBRARY_PATH := $(PKGDIR)/lib
 check-package : RGBDEF ?= $(PKGDIR)/misc/rgb.txt
+check-package : PALMMAPDIR ?= $(PKGDIR)/misc/
 check: check-package
 
-check-package: $(TESTRANDOM) resultdir-backup
+check-package: testdir resultdir-backup
 	cd $(RESULTDIR); \
+	  $(CHECK_VARS) \
 	  CHECK_TYPE=package \
-	  PBM_TEST_PATH=$(PBM_TEST_PATH) BUILDDIR=$(BUILDDIR) \
+	  PBM_TEST_PATH=$(PBM_TEST_PATH) \
 	  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
+check-install: PALMMAPDIR ?= ""
+check-install: testdir resultdir-backup
 	cd $(RESULTDIR); \
+	  $(CHECK_VARS) \
 	  CHECK_TYPE=install \
-	  BUILDDIR=$(BUILDDIR) \
-	  RGBDEF=$(RGBDEF) \
 	  $(SRCDIR)/test/Execute-Tests 2>&1
 
 
diff --git a/analyzer/Makefile b/analyzer/Makefile
index c96a6d0a..11466662 100644
--- a/analyzer/Makefile
+++ b/analyzer/Makefile
@@ -14,9 +14,26 @@ include $(BUILDDIR)/config.mk
 # This package is so big, it's useful even when some parts won't 
 # build.
 
-PORTBINARIES = pamfile pammosaicknit pamslice pamsumm pamtilt \
-               pbmminkowski pgmhist pnmhistmap ppmhist pgmminkowski
-MATHBINARIES = pamsharpmap pamsharpness pgmtexture pnmpsnr 
+PORTBINARIES = \
+  pamfind \
+  pamgetcolor \
+  pamfile \
+  pammosaicknit \
+  pamslice \
+  pamsumm \
+  pamtable \
+  pamtilt \
+  pbmminkowski \
+  pgmhist \
+  pnmhistmap \
+  ppmhist \
+  pgmminkowski \
+
+MATHBINARIES = \
+  pamsharpmap \
+  pamsharpness \
+  pgmtexture \
+  pnmpsnr \
 
 BINARIES = $(PORTBINARIES) $(MATHBINARIES)
 SCRIPT =
@@ -30,12 +47,14 @@ OBJECTS = $(BINARIES:%=%.o)
 MERGEBINARIES = $(BINARIES)
 MERGE_OBJECTS = $(MERGEBINARIES:%=%.o2)
 
+HAVE_MERGE_COMPAT=YES
+
 .PHONY: all
 all: $(BINARIES)
 
 include $(SRCDIR)/common.mk
 
-install.bin: install.bin.local
+install.bin install.merge: install.bin.local
 .PHONY: install.bin.local
 install.bin.local: $(PKGDIR)/bin
 # Remember that $(SYMLINK) might just be a copy command.
@@ -44,5 +63,10 @@ install.bin.local: $(PKGDIR)/bin
 	cd $(PKGDIR)/bin ; \
 	$(SYMLINK) pamfile$(EXE) pnmfile$(EXE)
 
+mergecomptrylist:
+	cat /dev/null >$@
+	echo "TRY(\"pgmslice\", main_pamslice);" >>$@
+	echo "TRY(\"pnmfile\",  main_pamfile);"  >>$@
+
 FORCE:
 
diff --git a/analyzer/pamfile.c b/analyzer/pamfile.c
index a7c3c5ac..7b61f6fd 100644
--- a/analyzer/pamfile.c
+++ b/analyzer/pamfile.c
@@ -10,21 +10,24 @@
 ** implied warranty.
 */
 
+#include <stdbool.h>
 #include "pm_c_util.h"
 #include "mallocvar.h"
 #include "nstring.h"
 #include "shhopt.h"
 #include "pam.h"
 
+enum ReportFormat {RF_HUMAN, RF_COUNT, RF_MACHINE, RF_SIZE};
+
 struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
     int inputFileCount;  /* Number of input files */
     const char ** inputFilespec;  /* Filespecs of input files */
-    unsigned int allimages;  /* -allimages or -count */
-    unsigned int count;      /* -count */
-    unsigned int comments;   /* -comments */
+    unsigned int allimages;
+    unsigned int comments;
+    enum ReportFormat reportFormat;
 };
 
 
@@ -42,35 +45,49 @@ parseCommandLine(int argc, const char ** argv,
     optStruct3 opt;
 
     unsigned int option_def_index;
+    unsigned int countSpec, machineSpec, sizeSpec;
 
     MALLOCARRAY_NOFAIL(option_def, 100);
-    
+
     option_def_index = 0;   /* incremented by OPTENT3 */
     OPTENT3(0,   "allimages", OPT_FLAG,  NULL, &cmdlineP->allimages,   0);
-    OPTENT3(0,   "count",     OPT_FLAG,  NULL, &cmdlineP->count,       0);
+    OPTENT3(0,   "count",     OPT_FLAG,  NULL, &countSpec,             0);
     OPTENT3(0,   "comments",  OPT_FLAG,  NULL, &cmdlineP->comments,    0);
+    OPTENT3(0,   "machine",   OPT_FLAG,  NULL, &machineSpec,           0);
+    OPTENT3(0,   "size",      OPT_FLAG,  NULL, &sizeSpec,              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 */
-    
+    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->inputFilespec = (const char **)&argv[1];
     cmdlineP->inputFileCount = argc - 1;
 
+    if (machineSpec + sizeSpec + countSpec > 1)
+        pm_error("You can specify only one of -machine, -size, and -count");
+    else if (machineSpec)
+        cmdlineP->reportFormat = RF_MACHINE;
+    else if (sizeSpec)
+        cmdlineP->reportFormat = RF_SIZE;
+    else if (countSpec)
+        cmdlineP->reportFormat = RF_COUNT;
+    else
+        cmdlineP->reportFormat = RF_HUMAN;
+
     free(option_def);
 }
 
 
 
 static void
-dumpHeader(struct pam const pam) {
+dumpHeaderHuman(struct pam const pam) {
 
     switch (pam.format) {
     case PAM_FORMAT:
-        printf("PAM, %d by %d by %d maxval %ld\n", 
+        printf("PAM, %d by %d by %d maxval %ld\n",
                pam.width, pam.height, pam.depth, pam.maxval);
         printf("    Tuple type: %s\n", pam.tuple_type);
         break;
@@ -84,22 +101,22 @@ dumpHeader(struct pam const pam) {
         break;
 
 	case PGM_FORMAT:
-        printf("PGM plain, %d by %d  maxval %ld\n", 
+        printf("PGM plain, %d by %d  maxval %ld\n",
                pam.width, pam.height, pam.maxval);
         break;
 
 	case RPGM_FORMAT:
-        printf("PGM raw, %d by %d  maxval %ld\n", 
+        printf("PGM raw, %d by %d  maxval %ld\n",
                pam.width, pam.height, pam.maxval);
         break;
 
 	case PPM_FORMAT:
-        printf("PPM plain, %d by %d  maxval %ld\n", 
+        printf("PPM plain, %d by %d  maxval %ld\n",
                pam.width, pam.height, pam.maxval);
         break;
 
 	case RPPM_FORMAT:
-        printf("PPM raw, %d by %d  maxval %ld\n", 
+        printf("PPM raw, %d by %d  maxval %ld\n",
                pam.width, pam.height, pam.maxval);
         break;
     }
@@ -108,23 +125,79 @@ dumpHeader(struct pam const pam) {
 
 
 static void
+dumpHeaderMachine(struct pam const pam) {
+
+    const char * formatString;
+    bool plain;
+
+    switch (pam.format) {
+    case PAM_FORMAT:
+        formatString = "PAM";
+        plain = false;
+        break;
+
+	case PBM_FORMAT:
+        formatString = "PBM";
+        plain = TRUE;
+        break;
+
+	case RPBM_FORMAT:
+        formatString = "PBM";
+        plain = false;
+        break;
+
+	case PGM_FORMAT:
+        formatString = "PGM";
+        plain = TRUE;
+        break;
+
+	case RPGM_FORMAT:
+        formatString = "PGM";
+        plain = false;
+        break;
+
+	case PPM_FORMAT:
+        formatString = "PPM";
+        plain = TRUE;
+        break;
+
+	case RPPM_FORMAT:
+        formatString = "PPM";
+        plain = false;        break;
+    }
+
+    printf("%s %s %d %d %d %ld %s\n", formatString,
+           plain ? "PLAIN" : "RAW", pam.width, pam.height,
+           pam.depth, pam.maxval, pam.tuple_type);
+
+}
+
+
+static void
+dumpHeaderSize(struct pam const pam) {
+
+    printf("%d %d\n", pam.width, pam.height);
+}
+
+
+static void
 dumpComments(const char * const comments) {
 
     const char * p;
     bool startOfLine;
-    
+
     printf("Comments:\n");
 
     for (p = &comments[0], startOfLine = TRUE; *p; ++p) {
         if (startOfLine)
             printf("  #");
-        
+
         fputc(*p, stdout);
-        
+
         if (*p == '\n')
             startOfLine = TRUE;
         else
-            startOfLine = FALSE;
+            startOfLine = false;
     }
     if (!startOfLine)
         fputc('\n', stdout);
@@ -133,14 +206,36 @@ dumpComments(const char * const comments) {
 
 
 static void
-doOneImage(const char * const name,
-           unsigned int const imageDoneCount,
-           FILE *       const fileP,
-           bool         const allimages,
-           bool         const justCount,
-           bool         const wantComments,
-           bool *       const eofP) {
-                    
+readToNextImage(const struct pam * const pamP,
+                bool *             const eofP) {
+
+    tuple * tuplerow;
+    unsigned int row;
+
+    tuplerow = pnm_allocpamrow(pamP);
+
+    for (row = 0; row < pamP->height; ++row)
+        pnm_readpamrow(pamP, tuplerow);
+
+    pnm_freepamrow(tuplerow);
+
+    {
+        int eof;
+        pnm_nextimage(pamP->file, &eof);
+        *eofP = eof;
+    }
+}
+
+
+static void
+doOneImage(const char *      const name,
+           unsigned int      const imageDoneCt,
+           FILE *            const fileP,
+           enum ReportFormat const reportFormat,
+           bool              const allimages,
+           bool              const wantComments,
+           bool *            const eofP) {
+
     struct pam pam;
     const char * comments;
     enum pm_check_code checkRetval;
@@ -148,76 +243,71 @@ doOneImage(const char * const name,
     pam.comment_p = &comments;
 
     pnm_readpaminit(fileP, &pam, PAM_STRUCT_SIZE(comment_p));
-        
-    if (!justCount) {
+
+    switch (reportFormat) {
+    case RF_COUNT:
+        break;
+    case RF_SIZE:
+        dumpHeaderSize(pam);
+        break;
+    case RF_MACHINE:
+        printf("%s: ", name);
+        dumpHeaderMachine(pam);
+        break;
+    case RF_HUMAN:
         if (allimages)
-            printf("%s:\tImage %d:\t", name, imageDoneCount);
-        else 
+            printf("%s:\tImage %d:\t", name, imageDoneCt);
+        else
             printf("%s:\t", name);
-            
-        dumpHeader(pam);
+
+        dumpHeaderHuman(pam);
+
         if (wantComments)
             dumpComments(comments);
     }
     pm_strfree(comments);
 
     pnm_checkpam(&pam, PM_CHECK_BASIC, &checkRetval);
-    if (allimages) {
-        tuple * tuplerow;
-        unsigned int row;
-        
-        tuplerow = pnm_allocpamrow(&pam);
-        
-        for (row = 0; row < pam.height; ++row) 
-            pnm_readpamrow(&pam, tuplerow);
-        
-        pnm_freepamrow(tuplerow);
-        
-        {
-            int eof;
-            pnm_nextimage(fileP, &eof);
-            *eofP = eof;
-        }
-    }
+
+    if (allimages)
+        readToNextImage(&pam, eofP);
 }
 
 
 
 static void
-describeOneFile(const char * const name,
-                FILE *       const fileP,
-                bool         const allimages,
-                bool         const justCount,
-                bool         const wantComments) {
+describeOneFile(const char *      const name,
+                FILE *            const fileP,
+                enum ReportFormat const reportFormat,
+                bool              const allimages,
+                bool              const wantComments) {
 /*----------------------------------------------------------------------------
    Describe one image stream (file).  Its name, for purposes of display,
    is 'name'.  The file is open as *fileP and positioned to the beginning.
 
+   'reportFormat' tells which of the various sets of information we provide.
+
    'allimages' means report on every image in the stream and read all of
    every image from it, as opposed to reading just the header of the first
    image and reporting just on that.
 
-   'justCount' means don't tell anything about the stream except how
-   many images are in it.  Pretty useless without 'allimages'.
-
    'wantComments' means to show the comments from the image header.
-   Meaningless with 'justCount'.
+   Meaningful only when 'reportFormat' is RF_HUMAN.
 -----------------------------------------------------------------------------*/
-    unsigned int imageDoneCount;
+    unsigned int imageDoneCt;
         /* Number of images we've processed so far */
     bool eof;
-    
-    eof = FALSE;
-    imageDoneCount = 0;
 
-    while (!eof && (imageDoneCount < 1 || allimages)) {
-        doOneImage(name, imageDoneCount, fileP,
-                   allimages, justCount, wantComments,
+    for (eof = false, imageDoneCt = 0;
+         !eof && (imageDoneCt < 1 || allimages);
+        ++imageDoneCt
+        ) {
+        doOneImage(name, imageDoneCt, fileP,
+                   reportFormat, allimages, wantComments,
                    &eof);
-        ++imageDoneCount;
     }
-    if (justCount)
-        printf("%s:\t%u images\n", name, imageDoneCount);
+    if (reportFormat == RF_COUNT)
+        printf("%s:\t%u images\n", name, imageDoneCt);
 }
 
 
@@ -232,19 +322,29 @@ main(int argc, const char *argv[]) {
     parseCommandLine(argc, argv, &cmdline);
 
     if (cmdline.inputFileCount == 0)
-        describeOneFile("stdin", stdin, cmdline.allimages || cmdline.count,
-                        cmdline.count, cmdline.comments);
+        describeOneFile("stdin", stdin, cmdline.reportFormat,
+                        cmdline.allimages ||
+                            cmdline.reportFormat == RF_COUNT,
+                        cmdline.comments);
     else {
         unsigned int i;
         for (i = 0; i < cmdline.inputFileCount; ++i) {
             FILE * ifP;
+
             ifP = pm_openr(cmdline.inputFilespec[i]);
-            describeOneFile(cmdline.inputFilespec[i], ifP, 
-                            cmdline.allimages || cmdline.count,
-                            cmdline.count, cmdline.comments);
+
+            describeOneFile(cmdline.inputFilespec[i], ifP,
+                            cmdline.reportFormat,
+                            cmdline.allimages ||
+                                cmdline.reportFormat == RF_COUNT,
+                            cmdline.comments);
+
             pm_close(ifP);
 	    }
 	}
-    
+
     return 0;
 }
+
+
+
diff --git a/analyzer/pamfind.c b/analyzer/pamfind.c
new file mode 100644
index 00000000..860c01ff
--- /dev/null
+++ b/analyzer/pamfind.c
@@ -0,0 +1,222 @@
+#include <nstring.h>
+#include <pam.h>
+
+#include "pm_c_util.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+
+
+typedef struct {
+    unsigned int * target;
+    unsigned int   targetDepth;
+    const char *   color;  /* NULL means not specified */
+    const char *   inputFileName;
+} CmdLineInfo;
+
+
+static CmdLineInfo
+parsedCommandLine(int                 argc,
+                  const char ** const argv) {
+
+    optEntry * option_def;
+        /* Instructions to OptParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    CmdLineInfo cmdLine;
+
+    unsigned int targetSpec, colorSpec;
+    const char ** target;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0,   "target",  OPT_STRINGLIST, &target,         &targetSpec, 0);
+    OPTENT3(0,   "color",   OPT_STRING,     &cmdLine.color,  &colorSpec,  0);
+    OPTENT3(0,  0,          OPT_END,        NULL,            NULL,        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);
+
+    if (targetSpec) {
+        if (colorSpec)
+            pm_error("You cannot specify both -target and -color");
+        else {
+            unsigned int i;
+
+            cmdLine.color = NULL;
+
+            cmdLine.target = NULL;  /* initial value */
+
+            for (i = 0, cmdLine.targetDepth = 0; target[i]; ++i) {
+                unsigned int sampleVal;
+                const char * error;
+
+                pm_string_to_uint(target[i], &sampleVal, &error);
+                if (error) {
+                    pm_error("Invalid sample value in -target option: '%s'.  "
+                             "%s", target[i], error);
+                }
+
+                REALLOCARRAY(cmdLine.target, i+1);
+
+                cmdLine.target[cmdLine.targetDepth++] = sampleVal;
+            }
+
+            free(target);
+        }
+    } else if (!colorSpec)
+        pm_error("You must specify either -target or -color");
+
+    if (argc-1 < 1)
+        cmdLine.inputFileName = "-";
+    else {
+        cmdLine.inputFileName = argv[1];
+
+        if (argc-1 > 1)
+            pm_error("Too many arguments: %u.  "
+                     "The only possible argument is the input file name",
+                     argc-1);
+    }
+    free(option_def);
+
+    return cmdLine;
+}
+
+
+
+static void
+freeCmdLine(CmdLineInfo const cmdLine) {
+
+    if (!cmdLine.color)
+        pnm_freepamtuple(cmdLine.target);
+}
+
+
+
+static tuple
+targetValue(CmdLineInfo  const cmdLine,
+            struct pam * const inpamP) {
+/*----------------------------------------------------------------------------
+   The tuple value user wants us to find in the image, per 'cmdLine'.
+
+   The return value is to be interpreted per *inpamP.
+-----------------------------------------------------------------------------*/
+    tuple retval;
+
+    if (cmdLine.color) {
+        if (inpamP->depth != 3)
+            pm_error("You specified -color, but the input image has "
+                     "depth %u, not 3", inpamP->depth);
+        else
+            retval = pnm_parsecolor(cmdLine.color, inpamP->maxval);
+    } else {
+        if (cmdLine.targetDepth != inpamP->depth)
+            pm_error("You specified a %u-tuple for -target, "
+                     "but the input image of of depth %u",
+                     cmdLine.targetDepth, inpamP->depth);
+        else {
+            unsigned int i;
+
+            retval = pnm_allocpamtuple(inpamP);
+
+            for (i = 0; i < inpamP->depth; ++i)
+                retval[i] = cmdLine.target[i];
+        }
+    }
+
+    return retval;
+}
+
+
+
+static void
+printHeader(FILE *       const ofP,
+            struct pam * const inpamP,
+            tuple        const target) {
+
+    unsigned int plane;
+
+    fprintf(ofP, "Locations containing tuple ");
+
+    fprintf(ofP, "(");
+
+    for (plane = 0; plane < inpamP->depth; ++plane) {
+        fprintf(ofP, "%u", (unsigned)target[plane]);
+        if (plane + 1 < inpamP->depth)
+            fprintf(ofP, "/");
+    }
+
+    fprintf(ofP, ")");
+
+    fprintf(ofP, "/%u", (unsigned)inpamP->maxval);
+
+    fprintf(ofP, ":\n");
+}
+
+
+
+static void
+pamfind(FILE *       const ifP,
+        struct pam * const inpamP,
+        CmdLineInfo  const cmdLine,
+        FILE *       const ofP) {
+
+    pnm_readpaminit(ifP, inpamP, PAM_STRUCT_SIZE(tuple_type));
+
+    {
+        tuple * const inputRow = pnm_allocpamrow(inpamP);
+        tuple   const target   = targetValue(cmdLine, inpamP);
+
+        unsigned int row;
+
+        printHeader(ofP, inpamP, target);
+
+        for (row = 0; row < inpamP->height; ++row) {
+            unsigned int col;
+
+            pnm_readpamrow(inpamP, inputRow);
+
+            for (col = 0; col < inpamP->width; ++col) {
+
+                if (pnm_tupleequal(inpamP, target, inputRow[col])) {
+                    fprintf(ofP, "(%u, %u)\n", row, col);
+                }
+            }
+        }
+        pnm_freepamtuple(target);
+        pnm_freepamrow(inputRow);
+    }
+}
+
+
+
+int
+main(int argc, const char *argv[]) {
+
+    FILE * ifP;
+    CmdLineInfo cmdLine;
+    struct pam  inpam;
+
+    pm_proginit(&argc, argv);
+
+    cmdLine = parsedCommandLine(argc, argv);
+
+    ifP = pm_openr(cmdLine.inputFileName);
+
+    pamfind(ifP, &inpam, cmdLine, stdout);
+
+    freeCmdLine(cmdLine);
+
+    pm_close(inpam.file);
+
+    return 0;
+}
+
+
+
diff --git a/analyzer/pamgetcolor.c b/analyzer/pamgetcolor.c
new file mode 100644
index 00000000..430f3b07
--- /dev/null
+++ b/analyzer/pamgetcolor.c
@@ -0,0 +1,573 @@
+#include <string.h>
+#include <nstring.h>
+#include <pm_gamma.h>
+#include <pam.h>
+
+#include "pm_c_util.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+
+typedef unsigned int  uint;
+
+typedef struct {
+/*----------------------------------------------------------------------------
+  Specification of a circular "region" over which to measure the average color
+-----------------------------------------------------------------------------*/
+    uint         x;        /* coordinates of the center                      */
+    uint         y;        /* of the region;                                 */
+    const char * label;    /* optional label supplied on the command line    */
+} RegSpec;
+
+typedef struct {
+/*----------------------------------------------------------------------------
+  Represents a single color measurement over a "region"
+-----------------------------------------------------------------------------*/
+    uint         area;     /* area in pixels over which to average the color */
+    /* cumulative normalized intensity-proportiunal value of the region:     */
+    double       color[3];
+} RegData;
+
+typedef struct {
+/*----------------------------------------------------------------------------
+  All the information the user supplied in the command line, in a form easy
+  for the program to use.
+-----------------------------------------------------------------------------*/
+    uint         linear;
+    uint         radius;
+    uint         regN;      /* number of regions                             */
+    uint         maxLbLen;  /* maximum label length                          */
+    RegSpec *    regSpecs;
+        /* list of points to sample, dymamically allocated*/
+    const char * formatStr; /* output color format as string                 */
+    uint         formatId;  /* the Id of the selected color format           */
+    uint         formatArg; /* the argument to the color formatting function */
+    const char * infile;
+} CmdLineInfo;
+
+/* Generic pointer to a color-formatting function. Returns the textual
+   representation of the color <tuple> in terms of the image pointed-to
+   by <pamP>. <param> is a generic integer parameter that depends on the
+   specific funcion and may denote precison or maxval.
+*/
+typedef const char *
+(*FormatColor)(struct pam * const pamP,
+               tuple        const color,
+               uint         const param);
+
+typedef struct ColorFormat {
+/*----------------------------------------------------------------------------
+  The color format specification
+-----------------------------------------------------------------------------*/
+    char        const * id;
+        /* format id (compared against the -format command-line argument) */
+    FormatColor const   formatColor;
+        /* function that returns converts a color into this format */
+    char        const * argName;
+        /* meaning of the <param> argument of <formatColor>() */
+    uint        const   defParam;
+        /* default value of that argument        */
+    uint        const   maxParam;
+        /* maximum value of that argument        */
+} ColorFormat;
+
+
+
+static const char *
+fcInt(struct pam * const pamP,
+      tuple        const color,
+      uint         const param) {
+/*----------------------------------------------------------------------------
+  Format 'color' as an integer tuple with maxval 'param'
+-----------------------------------------------------------------------------*/
+    return pnm_colorspec_rgb_integer(pamP, color, param);
+}
+
+
+
+static const char *
+fcNorm(struct pam * const pamP,
+       tuple        const color,
+       uint         const param) {
+/*----------------------------------------------------------------------------
+  Format 'color' as normalized tuple with precision 'param'
+-----------------------------------------------------------------------------*/
+    return pnm_colorspec_rgb_norm(pamP, color, param);
+}
+
+
+
+static const char *
+fcX11(struct pam * const pamP,
+      tuple        const color,
+      uint         const param) {
+/*----------------------------------------------------------------------------
+  Format 'color' as hexadecimal tuple with 'param' digits
+-----------------------------------------------------------------------------*/
+    return pnm_colorspec_rgb_x11(pamP, color, param);
+}
+
+
+
+static int const defaultFormat = 0;
+
+/* Table with the full information about color formats */
+ColorFormat const formats[ 3 ] = {
+    /*   Id     Function  Argument name  Default  Max   */
+    {   "int",  &fcInt,   "maxval",      255,     65535  },
+    {   "norm", &fcNorm,  "digit count",   3,         6  },
+    {   "x11",  &fcX11,   "digit count",   2,         4  }
+};
+
+
+
+static inline uint
+sqri(int const v) {
+
+    return v * v;
+}
+
+
+
+static RegSpec
+parsedRegSpec(const char * const s) {
+/*----------------------------------------------------------------------------
+  The region specification represented by command line argument 's'.
+
+  's' is of the format x,y[:label].
+-----------------------------------------------------------------------------*/
+    char * end;
+    char * start;
+    RegSpec res;
+
+    start = (char *)s;
+
+    res.x = strtol(start, &end, 10);
+    do {
+        if (start == end)
+            break; /* x not parsed */
+        start = end;
+        if (*end != ',')
+            break;  /* no comma after x */
+        start = end + 1;
+
+        res.y = strtol(start, &end, 10);
+        if (start == end)
+            break; /* y not parsed */
+
+        /* these multiple returns to avoid goto and deep nesting: */
+        if (*end == '\0') { /* no label specified */
+            res.label = (char *)s;
+            return res;
+        }
+        if (*end == ':') { /* a label specified */
+            res.label = end + 1;
+            if (*res.label == '\0')
+                break; /* empty label */
+            return res;
+        }
+    } while (false);
+
+    pm_error("Wrong region specification: %s", s);
+
+    return res; /* to avoid the false warning that nothing is returned */
+}
+
+
+
+static void
+parseColorFmt(const char * const formatStr,
+              uint *       const formatIdP,
+              uint *       const formatArgP) {
+/*----------------------------------------------------------------------------
+  Parse the color format specification string 'formatStr' as
+  *formatIdP and *formatArgP.
+
+  A format specification string is of format format[:arg].
+-----------------------------------------------------------------------------*/
+    const char *  const errSpec = "Wrong color format specification: ";
+
+    const char *  colonLoc; /* location of the colon in the specification */
+    uint          n, f;
+    const ColorFormat * formatP;
+    uint formatId;
+    bool found;
+
+    colonLoc  = strchr(formatStr, ':');
+    if (colonLoc != NULL) n = colonLoc - formatStr;
+    else                  n = strlen(formatStr);
+
+    for (f = 0, found = false; f < ARRAY_SIZE(formats) && !found; ++f) {
+        if (strncmp(formatStr, formats[f].id, n) == 0) {
+            found = true;
+            formatId = f;
+        }
+    }
+    if (!found)
+        pm_error("Color format not recognized.");
+
+    *formatIdP = formatId;
+
+    formatP = &formats[formatId];
+
+    if (colonLoc) {
+        long int arg;
+        const char * argStart;
+        char * argEnd;
+
+        argStart = colonLoc + 1;
+
+        if (*argStart == '\0')
+            pm_error("%sthe colon should be followed by %s.",
+                errSpec, formatP->argName);
+
+        arg = strtol(argStart, &argEnd, 10);
+
+        if (*argEnd != '\0')
+            pm_error("%sfailed to parse the %s: %s.",
+                errSpec, formatP->argName, argStart);
+
+        if (arg < 1)
+            pm_error("%s%s must be greater than zero.",
+                errSpec, formatP->argName);
+
+        if (arg > formatP->maxParam)
+            pm_error("%s%s cannot exceed %i.",
+                errSpec, formatP->argName, formatP->maxParam);
+
+        *formatArgP = arg;
+    } else
+        *formatArgP = formatP->defParam;
+}
+
+
+
+static CmdLineInfo
+parsedCommandLine(int                 argc,
+                  const char ** const argv) {
+
+    optEntry * option_def;
+        /* Instructions to OptParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    CmdLineInfo cmdLine;
+
+    uint infileSpec, radiusSpec, formatSpec, linearSpec;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "infile",    OPT_STRING, &cmdLine.infile,    &infileSpec, 0);
+    OPTENT3(0, "radius",    OPT_INT,    &cmdLine.radius,    &radiusSpec, 0);
+    OPTENT3(0, "format",    OPT_STRING, &cmdLine.formatStr, &formatSpec, 0);
+    OPTENT3(0, "linear",    OPT_FLAG,   &cmdLine.linear,    &linearSpec, 0);
+    OPTENT3(0,  0,          OPT_END,    NULL,               NULL,        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);
+
+    if (!infileSpec)
+        cmdLine.infile = "-";
+
+    if (!radiusSpec)
+        cmdLine.radius = 0;
+
+    if (formatSpec) {
+        parseColorFmt(cmdLine.formatStr,
+                      &cmdLine.formatId, &cmdLine.formatArg);
+    } else {
+        cmdLine.formatId  = defaultFormat;
+        cmdLine.formatArg = formats[defaultFormat].defParam;
+    }
+
+    if (!linearSpec)
+        cmdLine.radius = 0;
+
+    if (argc-1 < 1)
+        pm_error("No regions specified.");
+
+    cmdLine.regN = argc - 1;
+
+    MALLOCARRAY(cmdLine.regSpecs, cmdLine.regN);
+
+    if (!cmdLine.regSpecs)
+        pm_error("Could not get memory for %u region specifications",
+                 cmdLine.regN);
+
+    {
+        uint r;
+        uint maxLbLen;
+
+        for (r = 0, maxLbLen = 0; r < argc - 1; ++r) {
+            size_t lbLen;
+            cmdLine.regSpecs[r] = parsedRegSpec(argv[r+1]);
+            lbLen = strlen(cmdLine.regSpecs[r].label);
+            maxLbLen = MAX(maxLbLen, lbLen);
+        }
+        cmdLine.maxLbLen = maxLbLen;
+    }
+
+    free(option_def);
+
+    return cmdLine;
+}
+
+
+
+static void
+freeCommandLine(CmdLineInfo const cmdLine) {
+
+    free(cmdLine.regSpecs);
+}
+
+
+
+static RegData *
+allocRegSamples(uint n) {
+/*----------------------------------------------------------------------------
+  Allocate an array of <n> initialized region samples.  The array should be
+  freed after use.
+-----------------------------------------------------------------------------*/
+    uint r;
+    RegData * regSamples;
+    regSamples = calloc(n, sizeof(RegData));
+
+    for (r = 0; r < n; ++r) {
+        uint l;
+
+        regSamples[r].area = 0;
+
+        for (l = 0; l < 3; ++l)
+            regSamples[r].color[l] = 0.0;
+    }
+    return regSamples;
+}
+
+
+
+static uint
+getYmax(struct pam * const pamP,
+        CmdLineInfo  const cmdLine) {
+/*----------------------------------------------------------------------------
+  Find the maximum row in the image that contains a pixel from a region.
+-----------------------------------------------------------------------------*/
+    uint ymax, r, ycmax;
+    ycmax = 0;
+
+    for (r = 0; r < cmdLine.regN; ++r) {
+        RegSpec spec = cmdLine.regSpecs[r];
+        if (spec.y >= pamP->height || spec.x >= pamP->width)
+            pm_error("Region at %i,%i is outside the image boundaries.",
+                     spec.x, spec.y);
+
+        if (spec.y > ycmax)
+            ycmax = spec.y;
+    }
+    ymax = MIN(pamP->height - 1, ycmax + cmdLine.radius);
+
+    return ymax;
+}
+
+
+
+static void
+readChord(RegData *    const dataP,
+          uint         const linear,
+          struct pam * const pamP,
+          tuple *      const row,
+          uint         const x0,
+          uint         const x1) {
+/*----------------------------------------------------------------------------
+  Update region sample *dataP with the data from horizontal chord lying in row
+  'row' and going from 'x0' to 'x1'. 'linear' means tuples in 'row' are the
+  intensity-linear values as opposed to normal libnetpbm gamma-adjusted
+  values.
+-----------------------------------------------------------------------------*/
+    uint x;
+
+    for (x = x0; x <= x1; ++x) {
+        uint l;
+
+        for (l = 0; l < 3; ++l) {
+            double val;
+
+            val = (double)row[x][l] / pamP->maxval;
+            /* convert to intensity because brightness is not additive: */
+            if (!linear)
+                val = pm_ungamma709(val);
+            dataP->color[l] += val;
+        }
+        ++dataP->area;
+    }
+}
+
+
+
+static void
+processRow(tuple *             const row,
+           uint                const y,
+           struct pam *        const pamP,
+           const CmdLineInfo * const cmdLineP,
+           RegData *           const regSamples) {
+/*----------------------------------------------------------------------------
+  Read a row from image described by *pamP into 'row', and update region
+  samples regSamples[] from it.  'y' is the position of the row.
+-----------------------------------------------------------------------------*/
+    uint r;
+
+    pnm_readpamrow(pamP, row);
+
+    for (r = 0; r < cmdLineP->regN; ++r) {
+        RegSpec   const spec = cmdLineP->regSpecs[r];
+        RegData * const dataP = &regSamples[r];
+        int       const yd = (int)spec.y - (int)y;
+
+        if (abs(yd) > cmdLineP->radius) {
+            /* Row is entirely above or below the region; Avoid the slow root
+               operation
+            */
+        } else {
+            uint const xd2 = sqri(cmdLineP->radius) - sqri(yd);
+            uint const xd  = ROUNDU(sqrt((double)xd2));
+
+            int x0, x1;
+
+            x0 = spec.x - xd;  /* initial value */
+            x1 = spec.x + xd;  /* initial value */
+
+            /* clip horizontal chord to image boundaries: */
+            if (x0 < 0)
+                x0 = 0;
+            if (x1 >= pamP->width)
+                x1 = pamP->width - 1;
+
+            readChord(dataP, cmdLineP->linear, pamP, row, x0, x1);
+        }
+    }
+}
+
+
+
+static RegData *
+colorsFmImage(struct pam * const pamP,
+              CmdLineInfo  const cmdLine) {
+/*----------------------------------------------------------------------------
+  Color data for the regions requested by 'cmdLine' in the image described by
+  *pamP.
+-----------------------------------------------------------------------------*/
+    uint      y, ymax;
+    RegData * samplesP;
+    tuple *   row;
+    FILE *    ifP;
+
+    ifP = pm_openr(cmdLine.infile);
+
+    pnm_readpaminit(ifP, pamP, PAM_STRUCT_SIZE(tuple_type));
+
+    ymax = getYmax(pamP, cmdLine);
+
+    samplesP = allocRegSamples(cmdLine.regN);
+    row      = pnm_allocpamrow(pamP);
+
+    for (y = 0; y <= ymax; ++y)
+        processRow(row, y, pamP, &cmdLine, samplesP);
+
+    pnm_freepamrow(row);
+    pm_close(ifP);
+
+    return samplesP;
+}
+
+
+
+static const char *
+outputColorSpec(RegData      const data,
+                CmdLineInfo  const cmdLine,
+                struct pam * const pamP,
+                tuple        const tup) {
+/*----------------------------------------------------------------------------
+  Color of region sample 'data' formatted for output as requested by
+  'cmdLine'.
+
+  *pamP tells how to interpret 'data'.
+
+  'tup' is working space for internal use.
+-----------------------------------------------------------------------------*/
+    uint l;
+
+    for (l = 0; l < 3; ++l)
+        tup[l] = pm_gamma709(data.color[l]/data.area) * pamP->maxval;
+
+    return formats[cmdLine.formatId].
+        formatColor(pamP, tup, cmdLine.formatArg);
+}
+
+
+
+static void
+printColors(struct pam *    const pamP,
+            CmdLineInfo     const cmdLine,
+            FILE *          const ofP,
+            const RegData * const regSamples) {
+/*----------------------------------------------------------------------------
+  Print the colors regSamples[] to *ofP in the format
+  requested by 'cmdLine'.
+
+  *pamP tells how to interpret regSamples[]
+-----------------------------------------------------------------------------*/
+    char  fmt[20];
+    uint  r;
+    tuple tup;
+
+    tup = pnm_allocpamtuple(pamP);
+
+    pm_snprintf(fmt, sizeof(fmt), "%%%is: %%s\n", cmdLine.maxLbLen);
+
+    for (r = 0; r < cmdLine.regN; ++r) {
+        RegSpec      spec;
+        RegData      data;
+        const char * color;
+
+        spec  = cmdLine.regSpecs[r];
+
+        data  = regSamples[r];
+
+        color = outputColorSpec(data, cmdLine, pamP, tup);
+
+        fprintf(ofP, fmt, spec.label, color);
+
+        pm_strfree(color);
+    }
+    pnm_freepamtuple(tup);
+}
+
+
+
+int
+main(int argc, const char *argv[]) {
+
+    RegData *   regSamples;
+    CmdLineInfo cmdLine;
+    struct pam  pam;
+
+    pm_proginit(&argc, argv);
+
+    cmdLine = parsedCommandLine(argc, argv);
+
+    regSamples = colorsFmImage(&pam, cmdLine);
+
+    printColors(&pam, cmdLine, stdout, regSamples);
+
+    freeCommandLine(cmdLine);
+    free(regSamples);
+
+    return 0;
+}
+
+
+
diff --git a/analyzer/pamsumm.c b/analyzer/pamsumm.c
index c427fa7d..9b74e789 100644
--- a/analyzer/pamsumm.c
+++ b/analyzer/pamsumm.c
@@ -1,43 +1,38 @@
-/******************************************************************************
+/*=============================================================================
                                pamsumm
-*******************************************************************************
+===============================================================================
   Summarize all the samples of a PAM image with various functions.
 
   By Bryan Henderson, San Jose CA 2004.02.07.
 
   Contributed to the public domain
-
-
-******************************************************************************/
-
+=============================================================================*/
 #include "pm_c_util.h"
 #include "pam.h"
 #include "shhopt.h"
 #include "mallocvar.h"
 
-enum function {FN_ADD, FN_MEAN, FN_MIN, FN_MAX};
+enum Function {FN_ADD, FN_MEAN, FN_MIN, FN_MAX};
 
-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;
-    unsigned int normalize;
-    unsigned int brief;
-    unsigned int verbose;
+    const char *  inputFileName;  /* Name of input file */
+    enum Function function;
+    unsigned int  normalize;
+    unsigned int  brief;
+    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 = malloc(100*sizeof(optEntry));
-        /* Instructions to OptParseOptions2 on how to parse our options.
+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;
 
@@ -45,7 +40,9 @@ parseCommandLine(int argc, char ** const argv,
 
     unsigned int sumSpec, meanSpec, minSpec, maxSpec;
 
-    option_def_index = 0;   /* incremented by OPTENTRY */
+    MALLOCARRAY(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
     OPTENT3(0,   "sum",       OPT_FLAG,  NULL, &sumSpec,             0);
     OPTENT3(0,   "mean",      OPT_FLAG,  NULL, &meanSpec,            0);
     OPTENT3(0,   "min",       OPT_FLAG,  NULL, &minSpec,             0);
@@ -58,7 +55,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 */
 
-    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (sumSpec + minSpec + maxSpec > 1)
@@ -72,22 +69,24 @@ parseCommandLine(int argc, char ** const argv,
         cmdlineP->function = FN_MIN;
     } else if (maxSpec) {
         cmdlineP->function = FN_MAX;
-    } else 
+    } else
         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.",
+        pm_error("Too many arguments (%d).  File name is the only argument.",
                  argc-1);
 
     if (argc-1 < 1)
-        cmdlineP->inputFilespec = "-";
-    else 
-        cmdlineP->inputFilespec = argv[1];
-    
+        cmdlineP->inputFileName = "-";
+    else
+        cmdlineP->inputFileName = argv[1];
+
+    free(option_def);
 }
 
 
-struct accum {
+
+struct Accum {
     union {
         double sum;
         unsigned int min;
@@ -98,8 +97,8 @@ struct accum {
 
 
 static void
-initAccumulator(struct accum * const accumulatorP,
-                enum function  const function) {
+initAccumulator(struct Accum * const accumulatorP,
+                enum Function  const function) {
 
     switch(function) {
     case FN_ADD:  accumulatorP->u.sum = 0.0;      break;
@@ -114,8 +113,8 @@ initAccumulator(struct accum * const accumulatorP,
 static void
 aggregate(struct pam *   const inpamP,
           tuple *        const tupleRow,
-          enum function  const function,
-          struct accum * const accumulatorP) {
+          enum Function  const function,
+          struct Accum * const accumulatorP) {
 
     unsigned int col;
 
@@ -123,11 +122,11 @@ aggregate(struct pam *   const inpamP,
         unsigned int plane;
         for (plane = 0; plane < inpamP->depth; ++plane) {
             switch(function) {
-            case FN_ADD:  
-            case FN_MEAN: 
+            case FN_ADD:
+            case FN_MEAN:
                 accumulatorP->u.sum += tupleRow[col][plane];
             break;
-            case FN_MIN:  
+            case FN_MIN:
                 if (tupleRow[col][plane] < accumulatorP->u.min)
                     accumulatorP->u.min = tupleRow[col][plane];
                 break;
@@ -135,7 +134,7 @@ aggregate(struct pam *   const inpamP,
                 if (tupleRow[col][plane] > accumulatorP->u.min)
                     accumulatorP->u.min = tupleRow[col][plane];
                 break;
-            } 
+            }
         }
     }
 }
@@ -143,18 +142,18 @@ aggregate(struct pam *   const inpamP,
 
 
 static void
-printSummary(struct accum  const accumulator,
+printSummary(struct Accum  const accumulator,
              unsigned int  const scale,
              unsigned int  const count,
-             enum function const function,
-             bool          const normalize,
+             enum Function const function,
+             bool          const mustNormalize,
              bool          const brief) {
 
-    switch(function) {
-    case FN_ADD: {  
+    switch (function) {
+    case FN_ADD: {
         const char * const intro = brief ? "" : "the sum of all samples is ";
 
-        if (normalize)
+        if (mustNormalize)
             printf("%s%f\n", intro, accumulator.u.sum/scale);
         else
             printf("%s%u\n", intro, (unsigned int)accumulator.u.sum);
@@ -163,27 +162,27 @@ printSummary(struct accum  const accumulator,
     case FN_MEAN: {
         const char * const intro = brief ? "" : "the mean of all samples is ";
 
-        if (normalize)
+        if (mustNormalize)
             printf("%s%f\n", intro, accumulator.u.sum/count/scale);
         else
             printf("%s%f\n", intro, accumulator.u.sum/count);
     }
     break;
     case FN_MIN: {
-        const char * const intro = 
+        const char * const intro =
             brief ? "" : "the minimum of all samples is ";
 
-        if (normalize)
+        if (mustNormalize)
             printf("%s%f\n", intro, (double)accumulator.u.min/scale);
         else
             printf("%s%u\n", intro, accumulator.u.min);
     }
     break;
     case FN_MAX: {
-        const char * const intro = 
+        const char * const intro =
             brief ? "" : "the maximum of all samples is ";
 
-        if (normalize)
+        if (mustNormalize)
             printf("%s%f\n", intro, (double)accumulator.u.max/scale);
         else
             printf("%s%u\n", intro, accumulator.u.max);
@@ -195,20 +194,20 @@ printSummary(struct accum  const accumulator,
 
 
 int
-main(int argc, char *argv[]) {
+main(int argc, const char *argv[]) {
 
-    FILE* ifP;
-    tuple* inputRow;   /* Row from input image */
-    int row;
-    struct cmdlineInfo cmdline;
+    FILE * ifP;
+    tuple * inputRow;   /* Row from input image */
+    unsigned int row;
+    struct CmdlineInfo cmdline;
     struct pam inpam;   /* Input PAM image */
-    struct accum accumulator;
+    struct Accum accumulator;
 
-    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));
 
@@ -216,17 +215,17 @@ main(int argc, char *argv[]) {
 
     initAccumulator(&accumulator, cmdline.function);
 
-    for (row = 0; row < inpam.height; row++) {
+    for (row = 0; row < inpam.height; ++row) {
         pnm_readpamrow(&inpam, inputRow);
 
         aggregate(&inpam, inputRow, cmdline.function, &accumulator);
     }
     printSummary(accumulator, (unsigned)inpam.maxval,
-                 inpam.height * inpam.width * inpam.depth, 
+                 inpam.height * inpam.width * inpam.depth,
                  cmdline.function, cmdline.normalize, cmdline.brief);
 
     pnm_freepamrow(inputRow);
     pm_close(inpam.file);
-    
+
     return 0;
 }
diff --git a/analyzer/pamtable.c b/analyzer/pamtable.c
new file mode 100644
index 00000000..2835469a
--- /dev/null
+++ b/analyzer/pamtable.c
@@ -0,0 +1,200 @@
+/*=============================================================================
+                               pamtable
+===============================================================================
+  Print the raster as a table of numbers.
+
+  By Bryan Henderson, San Jose CA 2017.04.15.
+
+  Contributed to the public domain
+
+=============================================================================*/
+#include <math.h>
+#include "pm_c_util.h"
+#include "pam.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+#include "nstring.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;  /* Name of input file */
+    unsigned int  verbose;
+};
+
+
+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;
+
+    MALLOCARRAY(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+
+    OPTENT3(0,   "verbose",   OPT_FLAG,  NULL, &cmdlineP->verbose,   0);
+        /* For future expansion */
+
+    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("Too many arguments (%d).  File name is the only argument.",
+                 argc-1);
+
+    if (argc-1 < 1)
+        cmdlineP->inputFileName = "-";
+    else
+        cmdlineP->inputFileName = argv[1];
+
+    free(option_def);
+}
+
+
+
+typedef struct {
+
+    const char * sampleFmt;
+       /* Printf format of a sample, e.g. %3u */
+
+    const char * interSampleGutter;
+       /* What we print between samples within a tuple */
+
+    const char * interTupleGutter;
+       /* What we print between tuples within a row */
+
+} Format;
+
+
+
+static const char *
+sampleFormat(const struct pam * const pamP) {
+/*----------------------------------------------------------------------------
+   The printf format string for a single sample in the output table.
+
+   E.g "%03u".
+
+   This format does not include any spacing between samples.
+-----------------------------------------------------------------------------*/
+    unsigned int const decimalWidth = ROUNDU(ceil(log10(pamP->maxval + 1)));
+
+    const char * retval;
+
+    pm_asprintf(&retval, "%%%uu", decimalWidth);
+
+    return retval;
+}
+
+
+
+static void
+makeFormat(const struct pam * const pamP,
+           Format *           const formatP) {
+
+    formatP->sampleFmt = sampleFormat(pamP);
+
+    formatP->interSampleGutter = " ";
+
+    formatP->interTupleGutter = pamP->depth > 1 ? "|" : " ";
+}
+
+
+
+static void
+unmakeFormat(Format * const formatP) {
+
+    pm_strfree(formatP->sampleFmt);
+}
+
+
+
+static void
+printRow(const struct pam * const pamP,
+         tuple *            const tupleRow,
+         Format             const format,
+         FILE *             const ofP) {
+
+    unsigned int col;
+
+    for (col = 0; col < pamP->width; ++col) {
+        unsigned int plane;
+
+        if (col > 0)
+            fputs(format.interTupleGutter, ofP);
+
+        for (plane = 0; plane < pamP->depth; ++plane) {
+
+            if (plane > 0)
+                fputs(format.interSampleGutter, ofP);
+
+            fprintf(ofP, format.sampleFmt, tupleRow[col][plane]);
+        }
+    }
+
+    fputs("\n", ofP);
+}
+
+
+
+static void
+printRaster(FILE *             const ifP,
+            const struct pam * const pamP,
+            FILE *             const ofP) {
+
+    Format format;
+
+    tuple * inputRow;   /* Row from input image */
+    unsigned int row;
+
+    makeFormat(pamP, &format);
+
+    inputRow = pnm_allocpamrow(pamP);
+
+    for (row = 0; row < pamP->height; ++row) {
+        pnm_readpamrow(pamP, inputRow);
+
+        printRow(pamP, inputRow, format, ofP);
+    }
+
+    pnm_freepamrow(inputRow);
+
+    unmakeFormat(&format);
+}
+
+
+
+int
+main(int argc, const char *argv[]) {
+
+    FILE * ifP;
+    struct CmdlineInfo cmdline;
+    struct pam inpam;   /* Input PAM image */
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
+
+    printRaster(ifP, &inpam, stdout);
+
+    pm_close(inpam.file);
+
+    return 0;
+}
+
+
+
diff --git a/analyzer/pamtilt.c b/analyzer/pamtilt.c
index 302c6247..70338545 100644
--- a/analyzer/pamtilt.c
+++ b/analyzer/pamtilt.c
@@ -10,7 +10,7 @@
   All work has been contributed to the public domain by its authors.
 =============================================================================*/
 
-#define _XOPEN_SOURCE   /* get M_PI in math.h */
+#define _XOPEN_SOURCE 500  /* get M_PI in math.h */
 
 #include <assert.h>
 #include <math.h>
diff --git a/analyzer/pgmtexture.c b/analyzer/pgmtexture.c
index 07317336..58833277 100644
--- a/analyzer/pgmtexture.c
+++ b/analyzer/pgmtexture.c
@@ -34,9 +34,9 @@
 ** 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.
-** 
+**
 ** Modification History:
-** 24 Jun 91 - J. Michael Carstensen <jmc@imsor.dth.dk> supplied fix for 
+** 24 Jun 91 - J. Michael Carstensen <jmc@imsor.dth.dk> supplied fix for
 **             correlation function.
 **
 ** 05 Oct 05 - Marc Breithecker <Marc.Breithecker@informatik.uni-erlangen.de>
@@ -151,7 +151,7 @@ matrix (unsigned int const nrl,
 
 
 
-static void 
+static void
 results (const char *  const name,
          const float * const a) {
 
@@ -167,7 +167,7 @@ results (const char *  const name,
 
 
 
-static void 
+static void
 simplesrt (unsigned int  const n,
            float *       const arr) {
 
@@ -190,7 +190,7 @@ simplesrt (unsigned int  const n,
 
 
 
-static void 
+static void
 mkbalanced (float **     const a,
             unsigned int const n) {
 
@@ -240,7 +240,7 @@ mkbalanced (float **     const a,
 
 
 
-static void 
+static void
 reduction (float **     const a,
            unsigned int const n) {
 
@@ -262,7 +262,7 @@ reduction (float **     const a,
             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]); 
+                SWAP(a[j][i], a[j][m]);
             a[j][i] = a[j][i];
         }
         if (x != 0.0) {
@@ -302,7 +302,7 @@ norm(float **     const a,
 
 
 
-static void 
+static void
 hessenberg(float **     const a,
            unsigned int const n,
            float *      const wr,
@@ -344,7 +344,7 @@ hessenberg(float **     const a,
                     float const z = sqrt(fabs(q));
                     x += t;
                     if (q >= 0.0) {
-                        float const z2 = p + sign(z, p); 
+                        float const z2 = p + sign(z, p);
                         wr[nn - 1] = wr[nn] = x + z2;
                         if (z2)
                             wr[nn] = x - w / z2;
@@ -386,7 +386,7 @@ hessenberg(float **     const a,
                         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) + 
+                        v = fabs(p) * (fabs(a[m - 1][m - 1]) + fabs(z) +
                                        fabs(a[m + 1][m + 1]));
                         if (u + v == v)
                             break;
@@ -454,7 +454,7 @@ hessenberg(float **     const a,
 
 
 
-static float 
+static float
 f1_a2m(float **     const p,
        unsigned int const ng) {
 /*----------------------------------------------------------------------------
@@ -478,12 +478,12 @@ f1_a2m(float **     const p,
 
 
 
-static float 
+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.
@@ -508,7 +508,7 @@ f2_contrast(float **     const p,
 
 
 
-static float 
+static float
 f3_corr(float **     const p,
         unsigned int const ng) {
 /*----------------------------------------------------------------------------
@@ -518,11 +518,12 @@ f3_corr(float **     const p,
    the image.
 -----------------------------------------------------------------------------*/
     unsigned int i;
-    float sumSqrx, sumSqry, tmp;
+    float sumSqrx;
+    float tmp;
     float * px;
     float meanx, meany, stddevx, stddevy;
 
-    sumSqrx = 0.0; sumSqry = 0.0;
+    sumSqrx = 0.0;
     meanx = 0.0; meany = 0.0;
 
     px = vector(0, ng);
@@ -545,7 +546,6 @@ f3_corr(float **     const p,
     }
 
     meany = meanx;
-    sumSqry = sumSqrx;
     stddevx = sqrt(sumSqrx - (SQR(meanx)));
     stddevy = stddevx;
 
@@ -560,7 +560,7 @@ f3_corr(float **     const p,
 
 
 
-static float 
+static float
 f4_var (float **     const p,
         unsigned int const ng) {
 /*----------------------------------------------------------------------------
@@ -584,7 +584,7 @@ f4_var (float **     const p,
 
 
 
-static float 
+static float
 f5_idm (float **     const p,
         unsigned int const ng) {
 /*----------------------------------------------------------------------------
@@ -603,7 +603,7 @@ f5_idm (float **     const p,
 
 
 
-static float 
+static float
 f6_savg (float **     const p,
          unsigned int const ng) {
 /*----------------------------------------------------------------------------
@@ -631,7 +631,7 @@ f6_savg (float **     const p,
 
 
 
-static float 
+static float
 f7_svar (float **     const p,
          unsigned int const ng,
          float        const s) {
@@ -660,7 +660,7 @@ f7_svar (float **     const p,
 
 
 
-static float 
+static float
 f8_sentropy (float **     const p,
              unsigned int const ng) {
 /*----------------------------------------------------------------------------
@@ -688,7 +688,7 @@ f8_sentropy (float **     const p,
 
 
 
-static float 
+static float
 f9_entropy (float **     const p,
             unsigned int const ng) {
 /*----------------------------------------------------------------------------
@@ -707,9 +707,9 @@ f9_entropy (float **     const p,
 
 
 
-static float 
-f10_dvar (float **     const p,
-          unsigned int const ng) {
+static float
+f10_dvar(float **     const p,
+         unsigned int const ng) {
 /*----------------------------------------------------------------------------
    Difference Variance
 -----------------------------------------------------------------------------*/
@@ -728,7 +728,7 @@ f10_dvar (float **     const p,
     for (i = 0; i < ng; ++i) {
         unsigned int j;
         for (j = 0; j < ng; ++j)
-            pxpy[abs(i - j)] += p[i][j];
+            pxpy[abs((int)i - (int)j)] += p[i][j];
     }
     /* Now calculate the variance of Pxpy (Px-y) */
     for (i = 0, sum = 0.0, sumSqr = 0.0; i < ng; ++i) {
@@ -743,7 +743,7 @@ f10_dvar (float **     const p,
 
 
 
-static float 
+static float
 f11_dentropy (float **     const p,
               unsigned int const ng) {
 /*----------------------------------------------------------------------------
@@ -761,7 +761,7 @@ f11_dentropy (float **     const p,
     for (i = 0; i < ng; ++i) {
         unsigned int j;
         for (j = 0; j < ng; ++j)
-            pxpy[abs(i - j)] += p[i][j];
+            pxpy[abs((int)i - (int)j)] += p[i][j];
     }
     for (i = 0, sum = 0.0; i < ng; ++i)
         sum += pxpy[i] * log10(pxpy[i] + EPSILON);
@@ -771,7 +771,7 @@ f11_dentropy (float **     const p,
 
 
 
-static float 
+static float
 f12_icorr (float **     const p,
            unsigned int const ng) {
 /*----------------------------------------------------------------------------
@@ -817,8 +817,8 @@ f12_icorr (float **     const p,
 
 
 
-static float 
-f13_icorr (float **     const p, 
+static float
+f13_icorr (float **     const p,
            unsigned int const ng) {
 /*----------------------------------------------------------------------------
   Information Measures of Correlation
@@ -863,7 +863,7 @@ f13_icorr (float **     const p,
 
 
 
-static float 
+static float
 f14_maxcorr (float **     const p,
              unsigned int const ng) {
 /*----------------------------------------------------------------------------
diff --git a/analyzer/pnmpsnr.c b/analyzer/pnmpsnr.c
index af74e8c8..2363e8c3 100644
--- a/analyzer/pnmpsnr.c
+++ b/analyzer/pnmpsnr.c
@@ -2,7 +2,7 @@
  *  pnmpsnr.c: Compute error (RMSE, PSNR) between images
  *
  *
- *  Derived from pnmpnsmr by Ulrich Hafner, part of his fiasco package,
+ *  Derived from pnmpnsmr by Ullrich Hafner, part of his fiasco package,
  *  On 2001.03.04.
 
  *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
@@ -21,6 +21,33 @@
 
 
 
+struct TargetSet {
+    unsigned int targetSpec;
+    float        target;
+    unsigned int target1Spec;
+    float        target1;
+    unsigned int target2Spec;
+    float        target2;
+    unsigned int target3Spec;
+    float        target3;
+};
+
+
+
+static bool
+targetSet_compTargetSpec(struct TargetSet const targetSet) {
+/*----------------------------------------------------------------------------
+   The target set specifies individual color component targets
+   (some may be "don't care", though).
+-----------------------------------------------------------------------------*/
+    return
+        targetSet.target1Spec ||
+        targetSet.target2Spec ||
+        targetSet.target3Spec;
+}
+
+
+
 struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
@@ -28,11 +55,39 @@ struct CmdlineInfo {
     const char * inputFile1Name;  /* Name of first input file */
     const char * inputFile2Name;  /* Name of second input file */
     unsigned int rgb;
+    unsigned int machine;
+    unsigned int maxSpec;
+    float        max;
+    bool         targetMode;
+    struct TargetSet target;
 };
 
 
 
 static void
+interpretTargetSet(struct TargetSet const targetSet,
+                   bool *           const targetModeP) {
+
+    if (targetSet.targetSpec && targetSet.target <= 0.0)
+        pm_error("Nonpositive -target does not make sense");
+
+    if (targetSet.target1Spec && targetSet.target1 <= 0.0)
+        pm_error("Nonpositive -target1 does not make sense");
+
+    if (targetSet.target2Spec && targetSet.target2 <= 0.0)
+        pm_error("Nonpositive -target2 does not make sense");
+
+    if (targetSet.target3Spec && targetSet.target3 <= 0.0)
+        pm_error("Nonpositive -target3 does not make sense");
+
+    *targetModeP =
+        targetSet.targetSpec || targetSet.target1Spec ||
+        targetSet.target2Spec || targetSet.target3Spec;
+}
+
+
+
+static void
 parseCommandLine(int argc, const char ** argv,
                  struct CmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
@@ -47,18 +102,31 @@ parseCommandLine(int argc, const char ** argv,
     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);
+    OPTENT3(0,   "rgb",      OPT_FLAG,  NULL,
+            &cmdlineP->rgb,       0);
+    OPTENT3(0,   "machine",  OPT_FLAG,  NULL,
+            &cmdlineP->machine,   0);
+    OPTENT3(0,   "max",      OPT_FLOAT, &cmdlineP->max,
+            &cmdlineP->maxSpec,   0);
+    OPTENT3(0,   "target",   OPT_FLOAT, &cmdlineP->target.target,
+            &cmdlineP->target.targetSpec,   0);
+    OPTENT3(0,   "target1",  OPT_FLOAT, &cmdlineP->target.target1,
+            &cmdlineP->target.target1Spec,   0);
+    OPTENT3(0,   "target2",  OPT_FLOAT, &cmdlineP->target.target2,
+            &cmdlineP->target.target2Spec,   0);
+    OPTENT3(0,   "target3",  OPT_FLOAT, &cmdlineP->target.target3,
+            &cmdlineP->target.target3Spec,   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) 
+    if (argc-1 < 2)
         pm_error("Takes two arguments:  names of the two files to compare");
     else {
         cmdlineP->inputFile1Name = argv[1];
@@ -70,6 +138,11 @@ parseCommandLine(int argc, const char ** argv,
     }
 
     free(option_def);
+
+    interpretTargetSet(cmdlineP->target, &cmdlineP->targetMode);
+
+    if (cmdlineP->targetMode && cmdlineP->maxSpec)
+        pm_error("-max is meaningless with -targetX");
 }
 
 
@@ -213,10 +286,10 @@ sqDiffYCbCr(tuple    const tuple1,
     struct SqDiff retval;
 
     double y1, y2, cb1, cb2, cr1, cr2;
-    
+
     pnm_YCbCrtuple(tuple1, &y1, &cb1, &cr1);
     pnm_YCbCrtuple(tuple2, &y2, &cb2, &cr2);
-    
+
     retval.sqDiff[Y_INDEX]  = square(y1  - y2);
     retval.sqDiff[CB_INDEX] = square(cb1 - cb2);
     retval.sqDiff[CR_INDEX] = square(cr1 - cr2);
@@ -308,12 +381,12 @@ sumSqDiffFromRaster(struct pam * const pam1P,
 
     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);
 
@@ -345,32 +418,121 @@ sumSqDiffFromRaster(struct pam * const pam1P,
 
 
 
-static void
-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.
-        */
+struct Psnr {
+/*----------------------------------------------------------------------------
+   The PSNR of an image, in some unspecified color space.
+-----------------------------------------------------------------------------*/
+    double psnr[3];
+};
 
-    unsigned int i;
 
 
+static struct Psnr
+psnrFromSumSqDiff(struct SqDiff const sumSqDiff,
+                  double        const maxSumSqDiff,
+                  unsigned int  const componentCt) {
+/*----------------------------------------------------------------------------
+   Compute the PSNR from the sums of the squares of the differences in the
+   pixels 'sumSqDiff' (separated by colorpspace component, where there are
+   'componentCt' components).
+
+   'maxSumSqDiff' is the 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.
+
+   Where there is no difference between the images, return infinity.
+-----------------------------------------------------------------------------*/
+
+    struct Psnr retval;
+    unsigned int i;
+
     /* The PSNR is the ratio of the maximum possible 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.
     */
 
+    for (i = 0; i < componentCt; ++i) {
+        if (sumSqDiff.sqDiff[i] > 0)
+            retval.psnr[i] = 10 * log10(maxSumSqDiff/sumSqDiff.sqDiff[i]);
+        else
+            retval.psnr[i] = 1.0/0.0;
+    }
+    return retval;
+}
+
+
+
+static bool
+psnrIsFinite(double const psnr) {
+
+    /* We would just use C standard isfinite(), but that is not standard
+       before C99.  Neither is INFINITY.
+
+       A finite PSNR, in this program, cannot be anywhere near 1,000,000,
+       because of limits of the program, so we just compare to that.
+    */
+
+    return psnr < 1000000.0;
+}
+
+
+
+static void
+reportTarget(struct Psnr      const psnr,
+             ColorSpace       const colorSpace,
+             struct TargetSet const target) {
+
+    bool hitsTarget;
+
+    if (colorSpace.componentCt == 1) {
+        if (!target.targetSpec)
+            pm_error("Image is monochrome and you specified "
+                     "-target1, -target2, or -target3 but not -target");
+
+        hitsTarget = psnr.psnr[0] >= target.target;
+    } else {
+        float compTarget[3];
+
+        unsigned int i;
+
+        assert(colorSpace.componentCt == 3);
+
+        if (targetSet_compTargetSpec(target)) {
+            compTarget[0] = target.target1Spec ? target.target1 : -1;
+            compTarget[1] = target.target2Spec ? target.target2 : -1;
+            compTarget[2] = target.target3Spec ? target.target3 : -1;
+        } else {
+            assert(target.targetSpec);
+            compTarget[0] = target.target;
+            compTarget[1] = target.target;
+            compTarget[2] = target.target;
+        }
+        for (i = 0, hitsTarget = true;
+             i < colorSpace.componentCt && hitsTarget;
+             ++i) {
+
+            if (psnr.psnr[i] < compTarget[i])
+                hitsTarget = false;
+        }
+    }
+    fprintf(stdout, "%s\n", hitsTarget ? "match" : "nomatch");
+}
+
+
+
+static void
+reportPsnrHuman(struct Psnr   const psnr,
+                ColorSpace    const colorSpace,
+                const char *  const fileName1,
+                const char *  const fileName2) {
+
+    unsigned int i;
+
     pm_message("PSNR between '%s' and '%s':", fileName1, fileName2);
 
     for (i = 0; i < colorSpace.componentCt; ++i) {
@@ -378,10 +540,8 @@ reportPsnr(struct pam    const pam,
 
         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]));
+        if (psnrIsFinite(psnr.psnr[i]))
+            pm_message("  %-6.6s %.2f dB", label, psnr.psnr[i]);
         else
             pm_message("  %-6.6s no difference", label);
 
@@ -391,13 +551,34 @@ reportPsnr(struct pam    const pam,
 
 
 
+static void
+reportPsnrMachine(struct Psnr  const psnr,
+                  unsigned int const componentCt,
+                  bool         const maxSpec,
+                  float        const max) {
+
+    unsigned int i;
+
+    for (i = 0; i < componentCt; ++i) {
+        double const clipped = maxSpec ? MIN(max, psnr.psnr[i]) : psnr.psnr[i];
+
+        if (i > 0)
+            fprintf(stdout, " ");
+
+        fprintf(stdout, "%.2f", clipped);
+    }
+    fprintf(stdout, "\n");
+}
+
+
+
 int
 main (int argc, const char **argv) {
     FILE * if1P;
     FILE * if2P;
     struct pam pam1, pam2;
     ColorSpace colorSpace;
-    
+
     struct CmdlineInfo cmdline;
 
     pm_proginit(&argc, argv);
@@ -424,8 +605,27 @@ main (int argc, const char **argv) {
         struct SqDiff const sumSqDiff =
             sumSqDiffFromRaster(&pam1, &pam2, colorSpace);
 
-        reportPsnr(pam1, sumSqDiff, colorSpace,
-                   cmdline.inputFile1Name, cmdline.inputFile2Name);
+        double const maxSumSqDiff =
+            square(pam1.maxval) * pam1.width * pam1.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.
+            */
+
+        struct Psnr const psnr =
+            psnrFromSumSqDiff(
+                sumSqDiff, maxSumSqDiff, colorSpace.componentCt);
+
+        if (cmdline.targetMode)
+            reportTarget(psnr, colorSpace, cmdline.target);
+        else if (cmdline.machine)
+            reportPsnrMachine(psnr, colorSpace.componentCt,
+                              cmdline.maxSpec, cmdline.max);
+        else
+            reportPsnrHuman(psnr, colorSpace,
+                            cmdline.inputFile1Name, cmdline.inputFile2Name);
+
+
     }
     pm_close(if2P);
     pm_close(if1P);
diff --git a/analyzer/ppmhist.c b/analyzer/ppmhist.c
index 9b526606..c4ab3581 100644
--- a/analyzer/ppmhist.c
+++ b/analyzer/ppmhist.c
@@ -22,7 +22,7 @@ enum sort {SORT_BY_FREQUENCY, SORT_BY_RGB};
 
 enum ColorFmt {FMT_DECIMAL, FMT_HEX, FMT_FLOAT, FMT_PPMPLAIN};
 
-struct cmdline_info {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
@@ -38,7 +38,7 @@ struct cmdline_info {
 
 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.
@@ -172,7 +172,7 @@ 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'.
+  states its 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
@@ -230,21 +230,21 @@ separateInvalidItems(colorhist_vector const chv,
                      unsigned int  *  const validColorCtP) {
 /*----------------------------------------------------------------------------
   Move invalid color entries from chv to chvInvalid.
-  Count how many color entries are valid. 
+  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
@@ -261,7 +261,7 @@ sortHistogramForensic(enum sort        const sortFn,
 
     {
         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;
@@ -269,7 +269,7 @@ sortHistogramForensic(enum sort        const sortFn,
 
         qsort((void*) chv, validColorCt,
               sizeof(struct colorhist_item), compare_function);
-        
+
         qsort((void*) chvInvalid, colorCt - validColorCt,
               sizeof(struct colorhist_item), compare_function);
     }
@@ -295,20 +295,104 @@ sortHistogramNormal(enum sort        const sortFn,
 }
 
 
+typedef struct {
+    unsigned int nTotal;
+        /* Number of colors; sum of all the following */
+    unsigned int nBlack;
+        /* 1 if black is present; 0 otherwise */
+    unsigned int nWhite;
+        /* 1 if white is present; 0 otherwise */
+    unsigned int nGray;
+        /* Number of gray shades, not including black and white */
+    unsigned int nColor;
+        /* number of colors other than black, white, and gray */
+
+} ColorSummary;
+
+
+
+static ColorSummary
+colorSummary(colorhist_vector const chv,
+             unsigned int     const colorCt,
+             pixval           const maxval) {
+
+    ColorSummary retval;
+
+    unsigned int i;
+
+    retval.nTotal = colorCt;
+
+    for (i = 0,
+             retval.nBlack = 0,
+             retval.nWhite = 0,
+             retval.nGray = 0,
+             retval.nColor = 0;
+         i < colorCt;
+         ++i) {
+
+        pixel const color = chv[i].color;
+        pixval const r = PPM_GETR(color);
+        pixval const g = PPM_GETG(color);
+        pixval const b = PPM_GETB(color);
+
+        if (r == 0 && g == 0 && b == 0)
+            ++retval.nBlack;
+        else if (r == maxval && g == maxval && b == maxval)
+            ++retval.nWhite;
+        else if (r == g && r ==b)
+            ++retval.nGray;
+        else
+            ++retval.nColor;
+    }
+    assert(retval.nBlack + retval.nWhite + retval.nGray + retval.nColor ==
+           retval.nTotal);
+
+    return retval;
+}
+
+
+static void
+printColorSummary(ColorSummary const colorSummary,
+                  const char * const prefix) {
+
+    printf("%sSummary: %u colors: %u black, %u white, %u gray, %u color\n",
+           prefix,
+           colorSummary.nTotal,
+           colorSummary.nBlack,
+           colorSummary.nWhite,
+           colorSummary.nGray,
+           colorSummary.nColor);
+
+    printf("\n");
+}
+
+
+
+typedef struct {
+/*----------------------------------------------------------------------------
+   A map of color name to color.
+
+   The actual information is outside of this structure; we just point to it.
+-----------------------------------------------------------------------------*/
+    unsigned int  n;
+
+    pixel *       color;
+
+    const char ** name;
+} ColorDict;
+
+
 
 static const char *
-colornameLabel(pixel        const color,
-               pixval       const maxval,
-               unsigned int const nDictColor,
-               pixel        const dictColors[],
-               const char * const dictColornames[]) {
+colornameLabel(pixel     const color,
+               pixval    const maxval,
+               ColorDict const colorDict) {
 /*----------------------------------------------------------------------------
    Return the name of the color 'color' or the closest color in the
    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
-   dictionary.
+   'colorDict' is the color dictionary.
 
    Return the name in static storage within this subroutine.
 -----------------------------------------------------------------------------*/
@@ -322,16 +406,16 @@ colornameLabel(pixel        const color,
 
     PPM_DEPTH(color255, color, maxval, 255);
 
-    colorIndex = ppm_findclosestcolor(dictColors, nDictColor, &color255);
+    colorIndex = ppm_findclosestcolor(colorDict.color, colorDict.n, &color255);
 
-    assert(colorIndex >= 0 && colorIndex < nDictColor);
+    assert(colorIndex >= 0 && colorIndex < colorDict.n);
 
-    if (PPM_EQUAL(dictColors[colorIndex], color))
+    if (PPM_EQUAL(colorDict.color[colorIndex], color))
         STRSCPY(retval, " ");
     else
         STRSCPY(retval, "*");
 
-    STRSCAT(retval, dictColornames[colorIndex]);
+    STRSCAT(retval, colorDict.name[colorIndex]);
 
     return retval;
 }
@@ -343,13 +427,22 @@ printColors(colorhist_vector const chv,
             int              const colorCt,
             pixval           const maxval,
             enum ColorFmt    const colorFmt,
-            unsigned int     const nKnown,
-            pixel            const knownColors[],
-            const char *     const colornames[]) {
+            bool             const withColorName,
+            ColorDict        const colorDict) {
+/*----------------------------------------------------------------------------
+   Print to Standard Output the list of colors, one per line in 'chv',
+   of which there are 'colorCt'.
+
+   Print the color in format 'colorFmt'.
 
-    int i;
+   If 'withColorName' is true, we add the name of each color to the line.
+   'oclorDict' is a list of known names of colors. If the color is not in the
+   list, we add the name of the color closest to it whose name we know,
+   prefixed by "*".
+-----------------------------------------------------------------------------*/
+    unsigned int i;
 
-    for (i = 0; i < colorCt; 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);
@@ -360,9 +453,8 @@ printColors(colorhist_vector const chv,
 
         const char * colornameValue;
 
-        if (colornames)
-            colornameValue = colornameLabel(chv[i].color, maxval,
-                                            nKnown, knownColors, colornames);
+        if (withColorName)
+            colornameValue = colornameLabel(chv[i].color, maxval, colorDict);
         else
             colornameValue = "";
 
@@ -393,6 +485,47 @@ printColors(colorhist_vector const chv,
 
 
 static void
+printHistogram(colorhist_vector const chv,
+               unsigned int     const colorCt,
+               pixval           const maxval,
+               enum ColorFmt    const colorFmt,
+               bool             const wantHeader,
+               bool             const wantColorName) {
+
+    ColorDict colorDict;
+
+    if (colorFmt == FMT_PPMPLAIN)
+        printf("P3\n# color map\n%d 1\n%d\n", colorCt, maxval);
+
+    if (wantHeader) {
+        const char * commentDelim = colorFmt == FMT_PPMPLAIN ? "#" : " ";
+
+        printColorSummary(colorSummary(chv, colorCt, maxval), commentDelim);
+
+        printf("%s  r     g     b   \t lum \t count  %s\n",
+               commentDelim, wantColorName ? "name" : "");
+        printf("%s----- ----- ----- \t-----\t------- %s\n",
+               commentDelim, wantColorName ? "----" : "");
+    }
+    if (wantColorName) {
+        bool const mustOpenTrue = TRUE;
+        ppm_readcolordict(NULL, mustOpenTrue,
+                          &colorDict.n, &colorDict.name, &colorDict.color,
+                          NULL);
+    }
+
+    printColors(chv, colorCt, maxval,
+                colorFmt, wantColorName, colorDict);
+
+    if (wantColorName) {
+        free(colorDict.color);
+        free(colorDict.name);
+    }
+}
+
+
+
+static void
 summarizeInvalidPixels(unsigned long int const validPixelCt,
                        unsigned long int const invalidPixelCt,
                        pixval            const maxval) {
@@ -430,7 +563,7 @@ printInvalidSamples(colorhist_vector const chv,
     unsigned long int invalidPixelCt;
 
     for (i = 0, validPixelCt = 0; i < validColorCt; ++i)
-        validPixelCt += chv[i].value; 
+        validPixelCt += chv[i].value;
 
     for (i = 0, invalidPixelCt = 0; i < invalidColorCt; ++i) {
         pixval       const r     = PPM_GETR(chvInvalid[i].color);
@@ -438,7 +571,7 @@ printInvalidSamples(colorhist_vector const chv,
         pixval       const b     = PPM_GETB(chvInvalid[i].color);
         unsigned int const count = chvInvalid[i].value;
 
-        invalidPixelCt += chvInvalid[i].value; 
+        invalidPixelCt += chvInvalid[i].value;
 
         switch(colorFmt) {
         case FMT_FLOAT:
@@ -470,7 +603,7 @@ printInvalidSamples(colorhist_vector const chv,
 int
 main(int argc, const char *argv[]) {
 
-    struct cmdline_info cmdline;
+    struct CmdlineInfo cmdline;
     FILE * ifP;
     colorhist_vector chv;
     colorhist_vector chvInvalid;
@@ -479,9 +612,6 @@ main(int argc, const char *argv[]) {
     pixval mmaxval;
     int format;
     int colorCt;
-    unsigned int dictColorCt;
-    const char ** dictColornames;
-    pixel * dictColors;
     unsigned int validColorCt;
 
     pm_proginit(&argc, argv);
@@ -512,38 +642,13 @@ main(int argc, const char *argv[]) {
         validColorCt = colorCt;
     }
 
-    /* And print the histogram. */
-    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 ? '#' : ' ';
-        printf("%c  r     g     b   \t lum \t count  %s\n",
-               commentDelim, cmdline.colorname ? "name" : "");
-        printf("%c----- ----- ----- \t-----\t------- %s\n",
-               commentDelim, cmdline.colorname ? "----" : "");
-    }
-    if (cmdline.colorname) {
-        bool const mustOpenTrue = TRUE;
-        ppm_readcolordict(NULL, mustOpenTrue,
-                          &dictColorCt, &dictColornames, &dictColors, NULL);
-    } else {
-        dictColors = NULL;
-        dictColornames = NULL;
-    }
-
-    printColors(chv, validColorCt, maxval,
-                cmdline.colorFmt, dictColorCt, dictColors, dictColornames);
+    printHistogram(chv, validColorCt, maxval,
+                   cmdline.colorFmt, !cmdline.noheader, cmdline.colorname);
 
     if (colorCt > validColorCt)
         printInvalidSamples(chv, chvInvalid, colorCt, validColorCt,
                             maxval, cmdline.colorFmt);
 
-    if (dictColors)
-        free(dictColors);
-    if (dictColornames)
-        free(dictColornames);
-
     ppm_freecolorhist(chv);
 
     if (chvInvalid)
@@ -553,4 +658,3 @@ main(int argc, const char *argv[]) {
 }
 
 
-
diff --git a/buildtools/Makefile b/buildtools/Makefile
index 8671c066..0490865b 100644
--- a/buildtools/Makefile
+++ b/buildtools/Makefile
@@ -33,10 +33,20 @@ libopt.o: libopt.c
 typegen.o endiangen.o:%.o:%.c
 	$(CC_FOR_BUILD) -c -o $@ $(CFLAGS_FOR_BUILD) $<
 
+# genfontc is strange because it isn't really a build tool; it's a development
+# tool.  And it uses Netpbm.  So we don't even build it by default and the
+# developer who builds it explicitly may have to be careful.
+genfontc.o:%.o:%.c importinc
+	$(CC_FOR_BUILD) -c -o $@ $(CFLAGS_FOR_BUILD) \
+	  $(NETPBM_INCLUDES) \
+	  $<
+genfontc:%:%.o $(NETPBMLIB)
+	$(LD_FOR_BUILD) -o $@ $(LDFLAGS_FOR_BUILD)  $< $(NETPBMLIB)
+
 $(BUILDPROGS):%:%.o
 	$(LD_FOR_BUILD) -o $@ $(LDFLAGS_FOR_BUILD) $<
 
 distclean clean: cleanlocal
 .PHONY: cleanlocal
 cleanlocal:
-	rm -f $(BUILDPROGS)
+	rm -f $(BUILDPROGS) genfontc
diff --git a/buildtools/configure.pl b/buildtools/configure.pl
index 9cafd019..4e6ff21a 100755
--- a/buildtools/configure.pl
+++ b/buildtools/configure.pl
@@ -442,6 +442,18 @@ sub askAboutDjgpp() {
 
 
 
+sub askAboutMingw() {
+
+    print("Are you building for the MinGW environment?\n");
+    print("\n");
+    
+    my $retval = promptYesNo("n");
+
+    return $retval;
+}
+
+
+
 sub computePlatformDefault($) {
 
     my ($defaultP) = @_;
@@ -556,6 +568,29 @@ sub getPlatform() {
 
 
 
+sub warnMingwXopenSource($) {
+    my ($subplatform) = @_;
+
+    if ($subplatform ne 'cygwin' && $subplatform ne 'djgpp') {
+        my $mingw = askAboutMingw();
+
+        if ($mingw) {
+            print << 'EOF';
+
+MinGW does not implement enough of the standard on which Netpbm relies to
+build out-of-the-box, but there is a trivial way to add the needed capability
+to MinGW.  See file doc/INSTALL for details.
+
+Press ENTER to continue.
+
+EOF
+            <STDIN>;
+        }
+    }
+}
+
+
+
 sub getGccChoiceFromUser($) {
     my ($platform) = @_;
 
@@ -1164,10 +1199,20 @@ sub getPngLibrary($@) {
 
     my ($pnglib, $pnghdr_dir);
 
-    if (commandExists('libpng-config')) {
-        # We don't need to ask where Libpng is; there's a 'libpng-config'
-        # That tells exactly how to access it, and the make files will use
-        # that.
+    if (system('pkg-config libpng --exists') == 0) {
+        # We don't need to ask where Libpng is; the Pkg-config database knows
+        # and the make files will use that.
+        #
+        # To limit the confusion when someone tries to use our result in
+        # spite of the fact that 'libpng-config' exists, we assign suggestive
+        # dummy values (just for use in human debugging).
+        $pnglib     = 'USE_PKG_CONFIG.a';
+        $pnghdr_dir = 'USE_PKG_CONFIG.a';
+    } elsif (commandExists('libpng-config')) {
+        # As with Pkg-config above, we can find out how to access the
+        # library by invoking a 'libpng-config' command.
+        $pnglib     = 'USE_LIBPNG-CONFIG.a';
+        $pnghdr_dir = 'USE_LIBPNG-CONFIG.a';
     } else {
         {
             my $default = "libpng" . libSuffix($platform);
@@ -1252,6 +1297,12 @@ sub getX11Library($@) {
     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.
+        #
+        # To limit the confusion when someone tries to use our result in
+        # spite of the fact that 'libpng-config' exists, we assign suggestive
+        # dummy values (just for use in human debugging).
+        $x11lib     = 'USE_PKGCONFIG.a';
+        $x11hdr_dir = 'USE_PKGCONFIG.a';
     } else {
         {
             my $default;
@@ -1456,14 +1507,52 @@ sub gnuOptimizeOpt($) {
 
 
 
+sub wnostrictoverflowWorks($) {
+    my ($gccCommandName) = @_;
+
+    my ($cFile, $cFileName) = tempFile(".c");
+
+    print $cFile "int x;";
+    
+    my $compileCommand =
+        "$gccCommandName -c -o /dev/null -Wno-strict-overflow $cFileName";
+    print("Doing test compile to see if -Wno-strict-overflow works: "
+          . "$compileCommand\n");
+    my $rc = system($compileCommand);
+    
+    unlink($cFileName);
+    close($cFile);
+
+    return ($rc == 0);
+}
+
+
+
 sub gnuCflags($) {
     my ($gccCommandName) = @_;
 
-    return("CFLAGS = " . gnuOptimizeOpt($gccCommandName) . " -ffast-math " .
-           " -pedantic -fno-common " . 
-           "-Wall -Wno-uninitialized -Wmissing-declarations -Wimplicit " .
-           "-Wwrite-strings -Wmissing-prototypes -Wundef " .
-           "-Wno-unknown-pragmas\n");
+    my $flags;
+
+    $flags = gnuOptimizeOpt($gccCommandName) . " -ffast-math " .
+        " -pedantic -fno-common " . 
+        "-Wall -Wno-uninitialized -Wmissing-declarations -Wimplicit " .
+        "-Wwrite-strings -Wmissing-prototypes -Wundef " .
+        "-Wno-unknown-pragmas ";
+
+    if (wnostrictoverflowWorks($gccCommandName)) {
+        # The compiler generates some optimizations based on the assumption
+        # that you wouldn't code something that can arithmetically overflow,
+        # so adding a positive value to something can only make it bigger.
+        # E.g. if (x + y > x), where y is unsigned, is a no-op.  The compiler
+        # optionally warns when it makes that assumption.  Sometimes, the
+        # compiler is able to do that optimization because of inlining, so the
+        # code per se is not ridiculous, it just becomes superfluous in
+        # context.  That means you can't code around the warning.  Ergo, we
+        # must disable the warning.
+
+        $flags .= '-Wno-strict-overflow';
+    }
+    return("CFLAGS = $flags\n");
 }
 
 
@@ -2030,6 +2119,10 @@ if ($platform eq "NONE") {
     exit;
 }
 
+if ($platform eq 'WINDOWS') {
+    warnMingwXopenSource($subplatform);
+}
+
 getCompiler($platform, $subplatform, \my $compiler);
 
 getLinker($platform, $compiler, \my $baseLinker, \my $linkViaCompiler);
@@ -2142,8 +2235,8 @@ my ($linuxsvgalib, $linuxsvgahdr_dir) = getLinuxsvgaLibrary($platform);
 
 print("\n");
 
-# We should add the JBIG and URT libraries here too.  They're a little
-# more complicated because there are versions shipped with Netpbm.
+# We should add the URT, JBIG, and Jasper libraries here too.  They're a
+# little more complicated because there are versions shipped with Netpbm.
 
 
 #******************************************************************************
diff --git a/buildtools/debian/README b/buildtools/debian/README
index 02fae4b5..7cefb249 100644
--- a/buildtools/debian/README
+++ b/buildtools/debian/README
@@ -37,11 +37,11 @@ To install Netpbm as a Debian package:
 PREREQUSISITES
 --------------
 
-The following information was taken from the Wheezy version (Version 7) of
-Debian, in January 2014.
+The following information was taken from the Jessie version (Version 8) of
+Debian, in March 2017.
 
 You don't actually need the current version of any of these.  For example,
-while we list package libjpeg8-dev, the package libjpeg62-dev works fine.
+while we list package libjpeg62-dev, the package libjpeg8-dev works fine.
 
 
 Building
@@ -54,12 +54,11 @@ indicate you don't have them, and the build will simply omit some parts.
 For example, if you don't install libx11-dev, the Netpbm build process
 will not build the 'pamx' program.
 
-  libjpeg8-dev
-  libpng12-0-dev
-  libsvga1-dev
+  libjpeg-dev
+  libpng-dev
   libtiff5-dev
   libx11-dev
-  libxml2a-dev
+  libxml2-dev
   zlib1g-dev
 
 
@@ -80,9 +79,9 @@ The following Debian packages are the known prerequisites for running Netpbm
 (and the package created by 'mkdeb' knows this).
 
     libc6
-    libjpeg8
+    libjpeg62 or libjpeg8
     libpng12-0
-    libsvga1
+    libsvga1 (available only on older systems)
     libtiff5
     libx11-6
     zlib1g
diff --git a/buildtools/debian/mkdeb b/buildtools/debian/mkdeb
index 42a986eb..684ca0fb 100755
--- a/buildtools/debian/mkdeb
+++ b/buildtools/debian/mkdeb
@@ -10,6 +10,10 @@
 #  package (what Debian contains is derived from Sourceforge Netpbm ca.
 #  2002).
 #
+#  The dependencies this package declares are those that can be satisfied by
+#  Debian 8 (Jessie) and Debian 9 (Stretch).  Netpbm works fine on other
+#  versions of Debian, but you may have to change the dependencies in this
+#  program or ignore dependencies at install time.
 ###############################################################################
 
 use strict;
@@ -111,8 +115,8 @@ sub control($$) {
 
 # The Debian packaging system doesn't provide a way to express Netpbm's actual
 # prerequisites.  For example, Netpbm needs Version 6.2 or better of Libjpeg,
-# but there is no way to state that here.  Instead, we state Libjpeg 8.
-# This makes the Netpbm package less useful.
+# but there is no way to state that here.  Instead, we state Libjpeg 6.2
+# exactly.  This makes the Netpbm package less useful.
 
     my %control;
 
@@ -134,9 +138,8 @@ sub control($$) {
     $control{'Depends'} =
         'libc6, ' .
         'libjpeg62, ' .
-        'libpng12-0, ' .
-        'libsvga1, ' .
-        'libtiff4, ' .
+        'libpng12-0 | libpng16-16, ' .
+        'libtiff5, ' .
         'libx11-6, ' .
         'libxml2, ' .
         'zlib1g, ' .
@@ -425,7 +428,9 @@ sub buildDpkgBuildTree($$$$$) {
     makePkgConfig("$dpkgDirName/usr/lib/pkgconfig/netpbm.pc", $templateSubsR,
                   $netpbmPkgDir);
 
-    makeManweb($dpkgDirName, $netpbmPkgDir);
+    # Beginning in Netpbm 10.78 (March 2017_, 'make package' doesn't package
+    # the manweb stuff, so we no longer put it in the Debian package.
+    #makeManweb($dpkgDirName, $netpbmPkgDir);
 }
 
 
diff --git a/buildtools/genfontc.c b/buildtools/genfontc.c
new file mode 100644
index 00000000..ce6b8c9c
--- /dev/null
+++ b/buildtools/genfontc.c
@@ -0,0 +1,199 @@
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "shhopt.h"
+#include "pm.h"
+#include "pbmfont.h"
+
+
+
+struct CmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * font;    
+    const char * builtin; 
+    const char * header; 
+    const char * varname; 
+    unsigned int verbose;
+};
+
+
+
+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 OptParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int fontSpec, builtinSpec, headerSpec, varnameSpec;
+
+    unsigned int option_def_index;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0, "font",      OPT_STRING, &cmdlineP->font, &fontSpec,        0);
+    OPTENT3(0, "builtin",   OPT_STRING, &cmdlineP->builtin, &builtinSpec,  0);
+    OPTENT3(0, "header",    OPT_STRING, &cmdlineP->header, &headerSpec,    0);
+    OPTENT3(0, "varname",   OPT_STRING, &cmdlineP->varname, &varnameSpec,  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 (!fontSpec)
+        cmdlineP->font = NULL;
+
+    if (!builtinSpec)
+        cmdlineP->builtin = NULL;
+
+    if (!headerSpec)
+        cmdlineP->header = NULL;
+
+    if (!varnameSpec)
+        cmdlineP->varname = NULL;
+}
+
+
+
+static void
+reportFont(struct font * const fontP) {
+
+    unsigned int n;
+    unsigned int c;
+
+    pm_message("FONT:");
+    pm_message("  character dimensions: %uw x %uh",
+               fontP->maxwidth, fontP->maxheight);
+    pm_message("  Additional vert white space: %d pixels", fontP->y);
+
+    for (c = 0, n = 0; c < ARRAY_SIZE(fontP->glyph); ++c) {
+        if (fontP->glyph[c])
+            ++n;
+    }
+
+    pm_message("  # characters: %u", n);
+}
+
+
+
+static void
+computeFont(const char *   const fontName,
+            const char *   const builtinName,
+            struct font ** const fontPP) {
+
+    struct font * fontP;
+
+    if (fontName)
+        fontP = pbm_loadfont(fontName);
+    else {
+        if (builtinName)
+            fontP = pbm_defaultfont(builtinName);
+        else
+            fontP = pbm_defaultfont("bdf");
+    }
+
+    *fontPP = fontP;
+}
+
+
+
+
+static void
+dumpfont(struct font * const fontP,
+         const char *  const header,
+         const char *  const varname,
+         FILE *        const ofP) {
+/*----------------------------------------------------------------------------
+  Dump out font as C source code.
+-----------------------------------------------------------------------------*/
+    unsigned int i;
+    unsigned int ng;
+
+    for (i = 0, ng = 0; i < 256; ++i) {
+        if (fontP->glyph[i])
+            ++ng;
+    }
+
+    if (header != NULL)
+        printf("#include \"%s\"\n\n", header);    
+
+    printf("static struct glyph _g[%d] = {\n", ng);
+
+    for (i = 0; i < 256; ++i) {
+        struct glyph * const glyphP = fontP->glyph[i];
+
+        if (glyphP) {
+            unsigned int j;
+
+            printf(" { %d, %d, %d, %d, %d, \"",
+                   glyphP->width, glyphP->height,
+                   glyphP->x, glyphP->y, glyphP->xadd);
+            
+            for (j = 0; j < glyphP->width * glyphP->height; ++j) {
+                if (glyphP->bmap[j])
+                    printf("\\1");
+                else
+                    printf("\\0");
+            }    
+            --ng;
+            printf("\" }%s\n", ng ? "," : "");
+        }
+    }
+    printf("};\n");
+
+    printf("struct font %s = { %d, %d, %d, %d, {\n",
+           (varname == NULL) ? "XXX_font" : varname,
+           fontP->maxwidth, fontP->maxheight, fontP->x, fontP->y);
+
+    {
+        unsigned int i;
+
+        for (i = 0; i < 256; ++i) {
+            if (fontP->glyph[i])
+                printf(" _g + %d", ng++);
+            else
+                printf(" NULL");
+        
+            if (i != 255)
+                printf(",");
+
+            printf("\n");
+        }
+    }
+
+    printf(" }\n};\n");
+}
+
+
+
+int
+main(int argc, const char *argv[]) {
+
+    struct CmdlineInfo cmdline;
+    struct font * fontP;
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+    
+    computeFont(cmdline.font, cmdline.builtin, &fontP);
+
+    if (cmdline.verbose)
+        reportFont(fontP);
+
+    dumpfont(fontP, cmdline.header, cmdline.varname, stdout);
+}
+
+
+
diff --git a/buildtools/installnetpbm.pl b/buildtools/installnetpbm.pl
index 61900335..d29fda1a 100755
--- a/buildtools/installnetpbm.pl
+++ b/buildtools/installnetpbm.pl
@@ -8,6 +8,7 @@ use strict;
 use English;
 use Fcntl;
 use File::Basename;
+use Cwd qw(getcwd);
 
 my ($TRUE, $FALSE) = (1,0);
 
@@ -46,6 +47,37 @@ sub prompt($$) {
 
 
 
+sub fsObjPrompt($$) {
+    my ($prompt, $default) = @_;
+#-----------------------------------------------------------------------------
+#  Prompt for a filesystem object name and accept glob pattern such as
+#  ~/mydir and /usr/lib/net* .
+#
+#  If there are zero or multiple filesystem object names that match the
+#  pattern the user gave, ask again.  If there is only one possible name
+#  consistent with the user's response, return that even if no filesystem
+#  object by that name exists.
+#-----------------------------------------------------------------------------
+    my $globbedResponse;
+
+    while (!$globbedResponse) {
+        my $response = prompt($prompt, $default);
+
+        my @matchList = glob($response);
+
+        if (@matchList == 0) {
+            print("No filesystem object matches that pattern\n");
+        } elsif (@matchList > 1) {
+            print("Multiple filesystem objects match that pattern\n");
+        } else {
+            $globbedResponse = $matchList[0];
+        }
+    }
+    return $globbedResponse;
+}
+
+
+
 sub getPkgdir() {
 #-----------------------------------------------------------------------------
 #  Find out from the user where the Netpbm package is (i.e. where
@@ -53,6 +85,17 @@ sub getPkgdir() {
 #-----------------------------------------------------------------------------
     my $pkgdir;
 
+    # We allow the user to respond with a shell filename pattern.  This seems
+    # like a lot of complexity for a barely useful feature, but we actually
+    # saw a problem where a user typed ~/mypackage without realizing that ~ is
+    # a globbing thing and was stumped when we said no such file exists, while
+    # shell commands said it does.
+
+    # Note that glob() of something that has no wildcard/substitution
+    # characters just returns its argument, whether a filesystem object by
+    # that name exists or not.  But for a wildcard pattern that doesn't match
+    # any existing files, glob() returns an empty list.
+
     while (!$pkgdir) {
     
         print("Where is the install package you created with " .
@@ -61,13 +104,32 @@ sub getPkgdir() {
         
         my $response = prompt("package directory", $default);
 
-        if (!-f("$response/pkginfo")) {
-            print("This does not appear to be a Netpbm install package. \n");
-            print("A file named $response/pkginfo does not exist.\n");
-            print("\n");
+        my @matchList = glob($response);
+
+        if (@matchList == 0) {
+            print("No filesystem object matches that pattern\n");
+        } elsif (@matchList > 1) {
+            print("Multiple filesystem objects match that pattern\n");
         } else {
-            $pkgdir = $response;
+            my $fsObjNm = $matchList[0];
+            
+            if (!-e($fsObjNm)) {
+                print("No filesystem object named '$fsObjNm' exists.\n");
+            } else {
+                if (!-d($fsObjNm)) {
+                    print("'$fsObjNm' is not a directory\n");
+                } else {
+                    if (!-f("$fsObjNm/pkginfo")) {
+                        print("Directory '$fsObjNm' does not appear to be " .
+                              "a Netpbm install package. \n");
+                        print("It does not contain a file named 'pkginfo'.\n");
+                    } else {
+                        $pkgdir = $fsObjNm;
+                    }
+                }
+            }
         }
+        print("\n");
     }
     print("\n");
     return $pkgdir;
@@ -80,8 +142,8 @@ sub makePrefixDirectory($) {
     my ($prefixDir) = @_;
 
     if ($prefixDir ne "" and !-d($prefixDir)) {
-        print("No directory named '$prefixDir' exists.  Do you want " .
-              "to create it?\n");
+        print("No directory named '$prefixDir' exists.  " .
+              "Do you want to create it?\n");
 
         my $done;
         while (!$done) {
@@ -97,6 +159,7 @@ sub makePrefixDirectory($) {
                 $done = $TRUE;
             } 
         }
+        print("\n");
     }
 }
 
@@ -123,7 +186,7 @@ sub getPrefix() {
         $default = "/usr/local/netpbm";
     }
 
-    my $response = prompt("install prefix", $default);
+    my $response = fsObjPrompt("install prefix", $default);
 
     my $prefix;
 
@@ -185,7 +248,7 @@ sub getBinDir($) {
     while (!$binDir) {
         my $default = "$prefix/bin";
 
-        my $response = prompt("program directory", $default);
+        my $response = fsObjPrompt("program directory", $default);
         
         if (-d($response)) {
             $binDir = $response;
@@ -243,7 +306,7 @@ sub getLibDir($) {
     while (!$libDir) {
         my $default = "$prefix/lib";
 
-        my $response = prompt("shared library directory", $default);
+        my $response = fsObjPrompt("shared library directory", $default);
         
         if (-d($response)) {
             $libDir = $response;
@@ -559,9 +622,194 @@ sub installSharedLib($$$) {
 
 
 
-sub getLinkDir($) {
+sub getSharedLinkDir($) {
+#-----------------------------------------------------------------------------
+#  Find out from the user where he wants the shared library stubs installed
+#  and return that.
+#-----------------------------------------------------------------------------
+    my ($prefix) = @_;
+
+    print("Where do you want the shared library stub (used to link-edit\n" .
+          "programs to use the shared lirary) installed?\n");
+    print("\n");
+
+    my $linkDir;
+
+    while (!$linkDir) {
+        my $default = "$prefix/lib";
+
+        my $response = fsObjPrompt("shared library stub directory", $default);
+        
+        if (-d($response)) {
+            $linkDir = $response;
+        } else {
+            my $succeeded = mkdir($response, 0777);
+            
+            if (!$succeeded) {
+                print("Unable to create directory '$response'.  " .
+                      "Error=$ERRNO\n");
+            } else {
+                $linkDir = $response;
+            }
+        }
+    }
+    print("\n");
+
+    return $linkDir;
+}
+
+
+
+sub removeDotDirs($) {
+
+    my ($readDirResultR) = @_;
+
+    my @dirContents;
+
+    foreach (@{$readDirResultR}) {
+        if ($_ ne '.' && $_ ne '..') {
+            push(@dirContents, $_);
+        }
+    }
+
+    return \@dirContents;
+}
+
+
+
+sub readDirContents($$$) {
+    my ($dirName, $contentsRR, $errorR) = @_;
+#-----------------------------------------------------------------------------
+#  Return the contents of the directory named $dirName, excluding the
+#  fake . and .. entries.
+#-----------------------------------------------------------------------------
+    my $dirContentsR;
+    my $error;
+
+    my $success = opendir(DIR, $dirName);
+
+    if (!$success) {
+        $error = "Unable to open directory '$dirName' with opendir()";
+    } else {
+        my @readDirResult = readdir(DIR);
+
+        $dirContentsR = removeDotDirs(\@readDirResult);
+
+        closedir(DIR);
+    }
+
+    $$contentsRR = $dirContentsR;
+
+    if ($errorR) {
+        $$errorR = $error;
+    }
+}
+
+
+
+sub dirContents($) {
+    my ($dirName) = @_;
+#-----------------------------------------------------------------------------
+#  Return the contents of the directory named $dirName, excluding the
+#  fake . and .. entries.
+#-----------------------------------------------------------------------------
+
+    readDirContents($dirName, \my $contentsR, \my $error);
+
+    if ($error) {
+        die($error);
+    }
+    return @{$contentsR};
+}
+
+
+
+sub fixSharedStubSymlink($$) {
+#-----------------------------------------------------------------------------
+#  This is a hack to install a shared library link on a GNU system.
+#
+# On systems that use the GNU dynamic linker, the shared library stub (the
+# file one uses at link-edit time to tell the linker what it needs to know
+# about the shared library that the code will use at run time) is just a
+# symbolic link to a copy of the actual shared library.  In the Netpbm
+# package, this is a relative symbolic link to the shared library in the
+# package.
+
+# Assuming Caller just copied the contents of the 'sharedlink' directory
+# straight from the package to the install target system, that symbolic link
+# isn't necessarily correct, and even if it is, it's probably messy.  (In the
+# normal case, the link value is ../lib/libnetpbm.so.<MAJ>).
+
+# So what we do is just detect and patch up that case.  If the stub is a
+# symbolic link to something in the shared library directory of the package,
+# we replace it with a symbolic link to the same thing in the shared library
+# directory of the install target system.
+# -----------------------------------------------------------------------------
+    my ($linkDir, $shlibDir) = @_;
+
+    my $oldCwd = getcwd();
+    chdir($linkDir);
+
+    foreach my $fsObjNm (dirContents('.')) {
+        if (-l("$fsObjNm")) {
+            if (readlink($fsObjNm) =~ m{^\.\./lib/(.*)$}) {
+                my $shlibNm = $1;
+
+                unlink($fsObjNm) or
+                    die("Failed to delete symlink copied from package " .
+                        "in order to replace it with a proper symlink " .
+                        "for this installation");
+
+                if ($linkDir eq $shlibDir) {
+                    symlink($shlibNm, $fsObjNm) or
+                        die("Failed to create symlink as shared library stub");
+                } else {
+                    symlink("$shlibDir/$shlibNm", $fsObjNm) or
+                        die("Failed to create symlink as shared library stub");
+                }
+                    
+                print("Linked $shlibDir/$shlibNm from $linkDir/$fsObjNm");
+            }
+        }
+    }
+    chdir($oldCwd);
+}
+
+
+
+sub installSharedStub($$$$) {
+
+    my ($pkgdir, $prefix, $shlibDir, $linkdirR) = @_;
+
+    if (-d("$pkgdir/sharedlink")) {
+        my $linkDir = getSharedLinkDir($prefix);
+
+        print("Installing shared library stubs.\n");
+
+        my $rc = system("$cpCommand $pkgdir/sharedlink/* $linkDir/");
+
+        if ($rc != 0) {
+            print("Copy of files from $pkgdir/sharedlink " .
+                  "to $linkDir failed.\n");
+            print("cp return code is $rc\n");
+        } else {
+            fixSharedStubSymlink($linkDir, $shlibDir);
+
+            print("done.\n");
+        }
+        $$linkdirR = $linkDir;
+    } else {
+        print("You did not build a shared library, so I will not " .
+              "install a stub \n");
+        $$linkdirR = undef;
+    }
+}
+
+
+
+sub getStaticLinkDir($) {
 #-----------------------------------------------------------------------------
-#  Find out from the user where he wants the link-edit libraries installed and
+#  Find out from the user where he wants the static  libraries installed and
 #  return that.
 #-----------------------------------------------------------------------------
     my ($prefix) = @_;
@@ -574,7 +822,7 @@ sub getLinkDir($) {
     while (!$linkDir) {
         my $default = "$prefix/lib";
 
-        my $response = prompt("static library directory", $default);
+        my $response = fsObjPrompt("static library directory", $default);
         
         if (-d($response)) {
             $linkDir = $response;
@@ -600,15 +848,16 @@ sub installStaticLib($$$) {
 
     my ($pkgdir, $prefix, $linkdirR) = @_;
 
-    if (-d("$pkgdir/link")) {
-        my $linkDir = getLinkDir($prefix);
+    if (-d("$pkgdir/staticlink")) {
+        my $linkDir = getStaticLinkDir($prefix);
 
-        print("Installing link libraries.\n");
+        print("Installing static link libraries.\n");
 
-        my $rc = system("$cpCommand $pkgdir/link/* $linkDir/");
+        my $rc = system("$cpCommand $pkgdir/staticlink/* $linkDir/");
 
         if ($rc != 0) {
-            print("Copy of files from $pkgdir/link to $linkDir failed.\n");
+            print("Copy of files from $pkgdir/staticlink " .
+                  "to $linkDir failed.\n");
             print("cp return code is $rc\n");
         } else {
             print("done.\n");
@@ -617,6 +866,7 @@ sub installStaticLib($$$) {
     } else {
         print("You did not build a static library, so I will not " .
               "install one \n");
+        $$linkdirR = undef;
     }
 }
 
@@ -637,7 +887,7 @@ sub getDataDir($) {
     while (!$dataDir) {
         my $default = "$prefix/lib";
 
-        my $response = prompt("data file directory", $default);
+        my $response = fsObjPrompt("data file directory", $default);
         
         if (-d($response)) {
             $dataDir = $response;
@@ -674,7 +924,7 @@ sub getHdrDir($) {
     while (!$hdrDir) {
         my $default = "$prefix/include";
 
-        my $response = prompt("header directory", $default);
+        my $response = fsObjPrompt("header directory", $default);
         
         if (-d($response)) {
             $hdrDir = $response;
@@ -752,228 +1002,6 @@ 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");
-
-    print("\n");
-
-    my $manDir;
-
-    while (!$manDir) {
-        my $default = "$prefix/man";
-
-        my $response = prompt("man page directory", $default);
-
-        if (-d($response)) {
-            $manDir = $response;
-        } else {
-            my $succeeded = mkdir($response, 0777);
-            
-            if (!$succeeded) {
-                print("Unable to create directory '$response'.  " .
-                      "Error=$ERRNO\n");
-            } else {
-                $manDir = $response;
-            }
-        }
-    }
-    print("\n");
-
-    return $manDir;
-}
-
-
-
-sub removeObsoleteManPage($) {
-
-    my ($mandir) = @_;
-
-    unlink("$mandir/man1/pgmoil");
-    unlink("$mandir/man1/pgmnorm");
-    unlink("$mandir/man1/ppmtojpeg");
-    unlink("$mandir/man1/bmptoppm");
-    unlink("$mandir/man1/ppmtonorm");
-    unlink("$mandir/man1/ppmtouil");
-    unlink("$mandir/man1/pnmnoraw");
-    unlink("$mandir/man1/gemtopbm");
-    unlink("$mandir/man1/pnminterp");
-}
-
-
-
-sub tryToCreateManwebConf($) {
-
-    my ($manweb_conf_filename) = $@;
-
-    print("You don't have a /etc/manweb.conf, which is the " .
-          "configuration\n");
-    print("file for the 'manweb' program, which is a quick way to " .
-          "get to Netpbm\n");
-    print("documentation.  Would you like to create one now?\n");
-        
-    my $done;
-    
-    while (!$done) {
-        my $response = prompt("create /etc/manweb.conf", "Y");
-        
-        if (uc($response) eq "Y") {
-            my $successful = open(MANWEB_CONF, ">/etc/manweb.conf");
-            if (!$successful) {
-                print("Unable to create file /etc/manweb.conf.  " .
-                          "error = $ERRNO\n");
-            } else {
-                print(MANWEB_CONF "#Configuration file for Manweb\n");
-                print(MANWEB_CONF "webdir=/usr/man/web\n");
-                close(MANWEB_CONF);
-                $done = $TRUE;
-            }
-        } else {
-            $done = $TRUE;
-        }
-    }
-}
-
-
-
-sub getWebdir($) {
-    my ($manweb_conf_filename) = @_;
-#-----------------------------------------------------------------------------
-#  Return the value of the Manweb "web directory," as indicated by the
-#  Manweb configuration file $manweb_conf_filename.
-#
-#  If that file doesn't exist, or doesn't have a 'webdir' value, or
-#  the 'webdir' value is a chain of directories instead of just one,
-#  we return an undefined value.
-#-----------------------------------------------------------------------------
-    my $webdir;
-
-    my $success = open(MANWEB_CONF, "<$manweb_conf_filename");
-    if (!$success) {
-        print("Unable to open file '$manweb_conf_filename' for reading.  " .
-              "error is $ERRNO\n");
-    } else {
-        while (<MANWEB_CONF>) {
-            chomp();
-            if (/^\s*#/) {
-                #It's comment - ignore
-            } elsif (/^\s*$/) {
-                #It's a blank line - ignore
-            } elsif (/\s*(\S+)\s*=\s*(\S+)/) {
-                #It looks like "keyword=value"
-                my ($keyword, $value) = ($1, $2);
-                if ($keyword eq "webdir") {
-                    # We can't handle a multi-directory path; we're looking
-                    # only for a webdir statement naming a sole directory.
-                    if ($value !~ m{:}) {
-                        $webdir = $value;
-                    }
-                }
-            }
-        }
-        close(MANWEB_CONF);
-    }              
-
-    return $webdir
-}
-
-
-
-sub userWantsManwebSymlink($$) {
-
-    my ($webdir, $netpbmWebdir) = @_;
-
-    print("Your manweb.conf file says top level documentation " .
-          "is in $webdir, \n");
-    print("but you installed netpbm.url in $netpbmWebdir.\n");
-    print("Do you want to create a symlink in $webdir now?\n");
-
-    my $wants;
-    my $done;
-    
-    while (!$done) {
-        my $response = prompt("create symlink (Y/N)", "Y");
-        
-        if (uc($response) eq "Y") {
-            $wants = $TRUE;
-            $done = $TRUE;
-        } elsif (uc($response) eq "N") {
-            $wants = $FALSE;
-            $done = $TRUE;
-        }
-    }
-    return $wants;
-}
-
-
-
-sub makeInManwebPath($) {
-
-    my ($netpbmWebdir) = @_;
-
-    # Now we need /etc/manweb.conf to point to the directory in which we
-    # just installed netpbm.url.
-
-    if (!-f("/etc/manweb.conf")) {
-        tryToCreateManwebConf("/etc/manweb.conf");
-    }
-    if (-f("/etc/manweb.conf")) {
-        my $webdir = getWebdir("/etc/manweb.conf");
-        if (defined($webdir)) {
-            if ($webdir ne $netpbmWebdir) {
-                if (userWantsManwebSymlink($webdir, $netpbmWebdir)) {
-                    my $old = "$netpbmWebdir/netpbm.url";
-                    my $new = "$webdir/netpbm.url";
-                    mkdir($webdir, 0777);
-                    my $success = symlink($old, $new);
-                    if (!$success) {
-                        print("Failed to create symbolic link from $new to " .
-                              "$old.  Error is $ERRNO\n");
-                    }
-                }
-            }
-        }
-    }
-}
-
-
-
-sub installManPage($$$) {
-
-
-# Note: This installs the pointer man pages and the netpbm.url file for Manweb.
-
-    my ($pkgdir, $prefix, $mandirR) = @_;
-
-    my $manDir = getManDir($prefix);
-
-    print("Installing man pages...\n");
-
-    my $rc = system("$cpCommand $pkgdir/man/* $manDir/");
-
-    if ($rc != 0) {
-        print("copy of man pages from $pkgdir/man to $manDir failed.\n");
-        print("cp exit code is $rc\n");
-    } else {
-        print("done.\n");
-    }
-
-    print("\n");
-
-    removeObsoleteManPage($manDir);
-
-    makeInManwebPath("$manDir/web");
-    
-    $$mandirR = $manDir;
-}
-
-
-
 sub netpbmVersion($) {
     my ($pkgdir) = @_;
 
@@ -1030,9 +1058,6 @@ processTemplate($$$) {
             if (defined($infoR->{INCLUDEDIR})) {
                 s/\@INCLUDEDIR@/$infoR->{INCLUDEDIR}/;
             }
-            if (defined($infoR->{MANDIR})) {
-                s/\@MANDIR@/$infoR->{MANDIR}/;
-            }
             push(@output, $_);
         }
     }
@@ -1102,7 +1127,7 @@ sub getPkgconfigDir($) {
     while (!$pkgconfigDir) {
         my $default = "$prefix/lib/pkgconfig";
 
-        my $response = prompt("Pkg-config directory", $default);
+        my $response = fsObjPrompt("Pkg-config directory", $default);
         
         if (-d($response)) {
             $pkgconfigDir = $response;
@@ -1184,8 +1209,14 @@ print("\n");
 
 my $pkgdir = getPkgdir();
 
+print("Installing from package directory '$pkgdir'\n");
+print("\n");
+
 my $prefix = getPrefix();
 
+print("Using prefix '$prefix'\n");
+print("\n");
+
 $cpCommand = getCpCommand();
 
 installProgram($pkgdir, $prefix, \my $bindir);
@@ -1194,7 +1225,10 @@ print("\n");
 installSharedLib($pkgdir, $prefix, \my $libdir);
 print("\n");
 
-installStaticLib($pkgdir, $prefix, \my $linkdir);
+installSharedStub($pkgdir, $prefix, $libdir, \my $sharedlinkdir);
+print("\n");
+
+installStaticLib($pkgdir, $prefix, \my $staticlinkdir);
 print("\n");
 
 installDataFile($pkgdir, $prefix, \my $datadir);
@@ -1203,8 +1237,7 @@ print("\n");
 installHeader($pkgdir, $prefix, \my $includedir);
 print("\n");
 
-installManPage($pkgdir, $prefix, \my $mandir);
-print("\n");
+my $linkdir = defined($sharedlinkdir) ? $sharedlinkdir : $staticlinkdir;
 
 my $templateSubsR =
     {VERSION    => netpbmVersion($pkgdir),
@@ -1213,7 +1246,7 @@ my $templateSubsR =
      LINKDIR    => $linkdir,
      DATADIR    => $datadir,
      INCLUDEDIR => $includedir,
-     MANDIR     => $mandir};
+    };
 
 installConfig($bindir, $templateSubsR);
 
diff --git a/buildtools/libopt.c b/buildtools/libopt.c
index a0bf1cda..f62194e5 100644
--- a/buildtools/libopt.c
+++ b/buildtools/libopt.c
@@ -65,6 +65,7 @@
   handle explicit file names.
 
 -----------------------------------------------------------------------------*/
+#define _DEFAULT_SOURCE 1  /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE 1      /* Make sure strdup() is in stdio.h */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
@@ -497,21 +498,20 @@ main(int argc, char **argv) {
 
     bool error;
     bool runtime;  /* -runtime option has been seen */
-    bool quiet;    /* -quiet option has been seen */
     int retval;
     unsigned int arg;  /* Index into argv[] of argument we're processing */
     char outputLine[1024];
 
     strcpy(outputLine, "");  /* initial value */
     runtime = FALSE;  /* initial value */
-    quiet = FALSE;   /* initial value */
     error = FALSE;  /* no error yet */
+
     for (arg = 1; arg < argc && !error; arg++) {
         if (strcmp(argv[arg], "-runtime") == 0)
             runtime = TRUE;
-        else if (strcmp(argv[arg], "-quiet") == 0)
-            quiet = TRUE;
-        else {
+        else if (strcmp(argv[arg], "-quiet") == 0) {
+            /* Doesn't do anything today */
+        } else {
             const char * options;
             processOneLibrary(argv[arg], runtime, explicit, 
                               &options, &error);
diff --git a/buildtools/makeman b/buildtools/makeman
index 94ee2172..dc8e45ce 100755
--- a/buildtools/makeman
+++ b/buildtools/makeman
@@ -24,8 +24,15 @@
 #
 # By Eric S. Raymond <esr@thyrsus.com>
 # Version 1.0, July 26 2004
+#
+# Modified by Akira F. Urushibata <afu@wta.att.ne.jp>
+# Version 1.1, February 11 2016
+#
+#   Added ability to process &mdash; &minus;
+#   Added footer message to clarify original source. 
+#
 
-import os, sys, exceptions, re
+import os, sys, re
 
 source = "netpbm documentation"
 section = 1
@@ -37,7 +44,13 @@ warning = r'''\
 .\" against that, and send it to the Netpbm maintainer.
 '''
 
-class LiftException(exceptions.Exception):
+footerprefix = '''.SH DOCUMENT SOURCE
+This manual page was generated by the Netpbm tool 'makeman' from HTML
+source.  The master documentation is at
+.IP
+.B http://netpbm.sourceforge.net/doc/'''
+
+class LiftException(Exception):
     def __init__(self, message, retval=1):
         self.message = message
         self.retval = retval
@@ -56,6 +69,7 @@ def makeman(name, file, indoc):
     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',"")
     indoc = indoc.replace('<html xmlns="http://www.w3.org/1999/xhtml">', "")
+    indoc = indoc.replace('<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">', "")
     indoc = indoc.replace("<HEAD>", "").replace("</HEAD>", "")
     indoc = indoc.replace("<head>", "").replace("</head>", "")
     indoc = re.sub('(?i)<A HREF="#index">Table Of Contents</A>', "", indoc)
@@ -92,6 +106,7 @@ def makeman(name, file, indoc):
         title = None
     indoc = titlematch.sub("", indoc)
     indoc = re.sub("(?i)\n*<BR>\n+", "\n", indoc)
+    indoc = re.sub("(?i)<BR>", "\n", indoc)
     indoc = ('.TH "%s" %d "%s" "%s"\n' % (title,section,date,source)) + indoc
     # Literal layout
     indoc = re.sub("(?i)\n *<PRE>", "\n.nf", indoc)
@@ -145,7 +160,7 @@ def makeman(name, file, indoc):
         if xrefsection == 0:
             return "\n.I " + xrefto
         else:
-            return "\n.BR %s (%d)\n" % (xrefto, xrefsection)
+            return '\n.BR "%s" (%d)\\c\n\\&' % (xrefto, xrefsection)
     indoc = re.sub(r'(?i)\n* *(?:\\fB)?<A[ \n]+HREF="?([^>]+.html)"?>([^<]+)</A>(?:\\fP)?',
                    xrefmatch, indoc)
     # Format URLs
@@ -203,31 +218,39 @@ def makeman(name, file, indoc):
     indoc = re.sub(r"<\?makeman (.*) \?>", r'\1', indoc)
     # Comments
     indoc = re.sub("<!--([^\n])*-->", r'.\"\1', indoc)
+    # Acronyms
+    indoc = re.sub('<acronym [a-zA-Z0-9:= \n"]*>', "", indoc)
+    indoc = re.sub("</acronym>", "", indoc)
     # Image tags
     indoc = re.sub(' *<img src="([^"]*)" alt="([^"]*)"( *[a-z]*="?[0-9]*"?)*>', ".B \\2\n.IMG -C \\1", indoc)
     # Special characters
     indoc = indoc.replace("&quot;", "'")
     indoc = indoc.replace("&nbsp;", "\\ ")
+    indoc = indoc.replace("&minus;", "-")
+    indoc = indoc.replace("&mdash;", "-")
+    indoc = indoc.replace("&mu;", "mu")
+    indoc = indoc.replace("&sigma;", "sigma")
     # Tables
-    indoc = re.sub(' *<table[^>]*>.*', ".TS", indoc)
-    indoc = re.sub(" *</table>.*", ".TE", indoc)
+    # This will not handle rowspan
+    indoc = re.sub('(?i) *<table[^>]*>.*', ".TS", indoc)
+    indoc = re.sub("(?i) *</table>.*", ".TE", indoc)
     # First the single-line case
-    indoc = re.sub("</td> *<td>", "\t", indoc)
-    indoc = re.sub("<tr> *<td>", "", indoc)
-    indoc = re.sub("</td> *</tr>", "", indoc)
+    indoc = re.sub("(?i)</td> *<td>", "\t", indoc)
+    indoc = re.sub("(?i)<tr> *<td>", "", indoc)
+    indoc = re.sub("(?i)</td> *</tr>", "", indoc)
     # Then the multiline case
-    indoc = re.sub(r'\s*<t[hd][^>]*>([^<\n]*)</t[dh]>\s*', '\t\\1', indoc)
-    indoc = re.sub(r'\s*<t[hd][^>]*>([^<]*)</t[dh]>\s*', '\tT{\n\\1T}', indoc)
+    indoc = re.sub(r'(?i)\s*<t[hd][^>]*>([^<\n]*)</t[dh]>\s*', '\t\\1', indoc)
+    indoc = re.sub(r'(?i)\s*<t[hd][^>]*>([^<]*)</t[dh]>\s*', '\tT{\n\\1T}', indoc)
     indoc = indoc.replace("\n\\&T}", "\nT}")
-    indoc = re.sub(" *</tr>", "", indoc)
-    indoc = re.sub(" *<tr[^>]*>\t*", "", indoc)
-    indoc = re.sub(r"\.TS\s+<caption>([^<]*)</caption>\s*", ".B \\1\n.TS\n", indoc)
+    indoc = re.sub("(?i) *</tr>", "", indoc)
+    indoc = re.sub("(?i) *<tr[^>]*>\t*", "", indoc)
+    indoc = re.sub(r"\.TS\s+<[Cc][Aa][Pp][Tt][Ii][Oo][Nn]>([^<]*)</[Cc][Aa][Pp][Tt][Ii][Oo][Nn]>\s*", ".B \\1\n.TS\n", indoc)
     # Debugging
     #sys.stderr.write("Name: %s, Title: %s, Date: %s\n" % (name, title, date))
     # Time for error checking now
     badlines = []
     for line in indoc.split("\n"):
-        if "<" in line or ">" in line.replace(" >", "") or re.search("&.*;", line):
+        if "<" in line or ">" in line.replace(" >", "") or re.search(r'(?<!^\\)&.*;', line):
             badlines.append(line)
     if badlines:
         sys.stderr.write(("Bad lines from %s:\n-----------------\n" % file) + "\n".join(badlines) + "\n-----------------\n")
@@ -241,6 +264,7 @@ def makeman(name, file, indoc):
     indoc = indoc.replace("\n@%@%@", "\n\\&.")
     # Mark these generated pages so people won't hand-hack them.
     indoc = warning + indoc
+    indoc = indoc + footerprefix + os.path.basename(file) +"\n.PP"
     return indoc
 
 def main(args, mainout=sys.stdout, mainerr=sys.stderr):
@@ -308,9 +332,7 @@ def main(args, mainout=sys.stdout, mainerr=sys.stderr):
                 outdoc = makeman(name, file, indoc)
             except:
                 os.remove(tempfile)
-                # Pass the exception upwards
-                (exc_type, exc_value, exc_traceback) = sys.exc_info()
-                raise exc_type, exc_value, exc_traceback
+                raise
             if outdoc == indoc:
                 os.remove(tempfile)
             if outdoc is None:
@@ -319,11 +341,11 @@ def main(args, mainout=sys.stdout, mainerr=sys.stderr):
                 outfp.write(outdoc)
                 outfp.close()	# under Windows you can't rename an open file
                 stem = file[:file.find(".")]
-                os.rename(tempfile, stem + "." + `sectmap[file]`)
-    except LiftException, e:
+                os.rename(tempfile, stem + "." + repr(sectmap[file]))
+    except LiftException as e:
         mainerr.write("makeman: " + e.message + "\n")
         return e.retval
-    except IOError, e:
+    except IOError as e:
         mainerr.write("makeman: file I/O error: %s\n" % e)
         return 3
     except KeyboardInterrupt:
@@ -331,15 +353,14 @@ def main(args, mainout=sys.stdout, mainerr=sys.stderr):
         return 4
     except:
         if verbosity:
-            (exc_type, exc_value, exc_traceback) = sys.exc_info()
-            raise exc_type, exc_value, exc_traceback
+            raise
         else:
             mainerr.write("makeman: internal error!\n")
             return 5
 
 if __name__ == "__main__":
     # Run the main sequence
-    raise SystemExit, main(sys.argv[1:])
+    raise SystemExit(main(sys.argv[1:]))
 
 # The following sets edit modes for GNU EMACS
 # Local Variables:
diff --git a/buildtools/manpage.mk b/buildtools/manpage.mk
index 76116ffe..ef1a1039 100644
--- a/buildtools/manpage.mk
+++ b/buildtools/manpage.mk
@@ -1,414 +1,203 @@
 # Make Unix man pages from Netpbm HTML user manual
-
-MAKEMAN = makeman
-
-MANDIR = /usr/share/man/
-
-# These can convert to man pages cleanly
-MAN1 = \
-	411toppm.1 \
-	anytopnm.1 \
-	asciitopgm.1 \
-	atktopbm.1 \
-	bioradtopgm.1 \
-	bmptopnm.1 \
-	bmptoppm.1 \
-	brushtopbm.1 \
-	cameratopam.1 \
-	cmuwmtopbm.1 \
-	ddbugtopbm.1 \
-	escp2topbm.1 \
-	eyuvtoppm.1 \
-	fiascotopnm.1 \
-	fitstopnm.1 \
-	fstopgm.1 \
-	g3topbm.1 \
-	gemtopbm.1 \
-	gemtopnm.1 \
-	giftopnm.1 \
-	gouldtoppm.1 \
-	hdifftopam.1 \
-	hipstopgm.1 \
-	hpcdtoppm.1 \
-	icontopbm.1 \
-	ilbmtoppm.1 \
-	imgtoppm.1 \
-	infotopam.1 \
-	jbigtopnm.1 \
-	jpeg2ktopam.1 \
-	jpegtopnm.1 \
-	leaftoppm.1 \
-	lispmtopgm.1 \
-	macptopbm.1 \
-	manweb.1 \
-	mdatopbm.1 \
-	mgrtopbm.1 \
-	mrf.1 \
-	mrftopbm.1 \
-	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 \
-	pbmmask.1 \
-	pbmpage.1 \
-	pbmpscale.1 \
-	pbmreduce.1 \
-	pbmtext.1 \
-	pbmtextps.1 \
-	pbmto10x.1 \
-	pbmto4425.1 \
-	pbmtoascii.1 \
-	pbmtoatk.1 \
-	pbmtobbnbg.1 \
-	pbmtocmuwm.1 \
-	pbmtodjvurle.1 \
-	pbmtoepsi.1 \
-	pbmtoepson.1 \
-	pbmtoescp2.1 \
-	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 \
-	pbmtonokia.1 \
-	pbmtopgm.1 \
-	pbmtopi3.1 \
-	pbmtopk.1 \
-	pbmtoplot.1 \
-	pbmtoppa.1 \
-	pbmtopsg3.1 \
-	pbmtoptx.1 \
-	pbmtowbmp.1 \
-	pbmtox10bm.1 \
-	pbmtoxbm.1 \
-	pbmtoybm.1 \
-	pbmtozinc.1 \
-	pbmupc.1 \
-	pc1toppm.1 \
-	pcdovtoppm.1 \
-	pcxtoppm.1 \
-	pfmtopam.1 \
-	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 \
-	pgmnorm.1 \
-	pgmoil.1 \
-	pgmramp.1 \
-	pgmslice.1 \
-	pgmtexture.1 \
-	pgmtofs.1 \
-	pgmtolispm.1 \
-	pgmtopbm.1 \
-	pgmtopgm.1 \
-	pgmtoppm.1 \
-	pi1toppm.1 \
-	pi3topbm.1 \
-	picttoppm.1 \
-	pjtoppm.1 \
-	pktopbm.1 \
-	pngtopnm.1 \
-	pnmalias.1 \
-	pnmarith.1 \
-	pnmcat.1 \
-	pnmcolormap.1 \
-	pnmcomp.1 \
-	pnmconvol.1 \
-	pnmcrop.1 \
-	pnmcut.1 \
-	pnmdepth.1 \
-	pnmenlarge.1 \
-	pnmfile.1 \
-	pnmgamma.1 \
-	pnmhisteq.1 \
-	pnmhistmap.1 \
-	pnmindex.1 \
-	pnminterp.1 \
-	pnminvert.1 \
-	pnmmargin.1 \
-	pnmmontage.1 \
-	pnmnlfilt.1 \
-	pnmnoraw.1 \
-	pnmnorm.1 \
-	pnmpad.1 \
-	pnmpaste.1 \
-	pnmpsnr.1 \
-	pnmquant.1 \
-	pnmremap.1 \
-	pnmrotate.1 \
-	pnmscale.1 \
-	pnmscalefixed.1 \
-	pnmshear.1 \
-	pnmsmooth.1 \
-	pnmsplit.1 \
-	pnmstitch.1 \
-	pnmtile.1 \
-	pnmtoddif.1 \
-	pnmtofiasco.1 \
-	pnmtofits.1 \
-	pnmtojbig.1 \
-	pnmtojpeg.1 \
-	pnmtopalm.1 \
-	pnmtopclxl.1 \
-	pnmtoplainpnm.1 \
-	pnmtopng.1 \
-	pnmtopnm.1 \
-	pnmtops.1 \
-	pnmtorast.1 \
-	pnmtorle.1 \
-	pnmtosgi.1 \
-	pnmtosir.1 \
-	pnmtotiff.1 \
-	pnmtotiffcmyk.1 \
-	pnmtoxwd.1 \
-	ppm3d.1 \
-	ppmbrighten.1 \
-	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 \
-	ppmglobe.1 \
-	ppmhist.1 \
-	ppmlabel.1 \
-	ppmmake.1 \
-	ppmmix.1 \
-	ppmnorm.1 \
-	ppmntsc.1 \
-	ppmpat.1 \
-	ppmquant.1 \
-	ppmquantall.1 \
-	ppmrainbow.1 \
-	ppmrelief.1 \
-	ppmrough.1 \
-	ppmshadow.1 \
-	ppmshift.1 \
-	ppmspread.1 \
-	ppmsvgalib.1 \
-	ppmtoacad.1 \
-	ppmtoarbtxt.1 \
-	ppmtobmp.1 \
-	ppmtoeyuv.1 \
-	ppmtoicr.1 \
-	ppmtoilbm.1 \
-	ppmtojpeg.1 \
-	ppmtoleaf.1 \
-	ppmtolj.1 \
-	ppmtomap.1 \
-	ppmtomitsu.1 \
-	ppmtompeg.1 \
-	ppmtoneo.1 \
-	ppmtopcx.1 \
-	ppmtopgm.1 \
-	ppmtopi1.1 \
-	ppmtopict.1 \
-	ppmtopj.1 \
-	ppmtopjxl.1 \
-	ppmtoppm.1 \
-	ppmtopuzz.1 \
-	ppmtorgb3.1 \
-	ppmtosixel.1 \
-	ppmtoterm.1 \
-	ppmtotga.1 \
-	ppmtouil.1 \
-	ppmtowinicon.1 \
-	ppmtoxpm.1 \
-	ppmtoyuv.1 \
-	ppmtoyuvsplit.1 \
-	ppmtv.1 \
-	ppmwheel.1 \
-	psidtopgm.1 \
-	pstopnm.1 \
-	qrttoppm.1 \
-	rasttopnm.1 \
-	rawtopgm.1 \
-	rawtoppm.1 \
-	rgb3toppm.1 \
-	rlatopam.1 \
-	rletopnm.1 \
-	sbigtopgm.1 \
-	sgitopnm.1 \
-	sirtopnm.1 \
-	sldtoppm.1 \
-	spctoppm.1 \
-	spottopgm.1 \
-	sputoppm.1 \
-	tgatoppm.1 \
-	thinkjettopbm.1 \
-	tifftopnm.1 \
-	vidtoppm.1 \
-	wbmptopbm.1 \
-	winicontoppm.1 \
-	xbmtopbm.1 \
-	ximtoppm.1 \
-	xpmtoppm.1 \
-	xvminitoppm.1 \
-	xwdtopnm.1 \
-	ybmtopbm.1 \
-	yuvsplittoppm.1 \
-	yuvtoppm.1 \
-	zeisstopnm.1 \
-
-MAN3 = \
-	libnetpbm_image.3 \
-	libnetpbm_ug.3 \
-	libnetpbm.3 \
-	libpbm.3 \
-	libpgm.3 \
-	libpm.3 \
-	libpnm.3 \
-	libppm.3 \
-	libsystem.3 \
-	libtmpfile.3 \
-
-MAN5 = \
-	extendedopacity.5 \
-	pam.5 \
-	pbm.5 \
-	pfm.5 \
-	pgm.5 \
-	pnm.5 \
-	ppm.5 \
-
-# These things do get converted to man pages and installed.
-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.
-EXCEPTIONS = directory.html libnetpbm_dir.html libnetpbm_draw.html error.html
-STUBS = pcdindex.1 ppmcolors.1 pnmflip.1 ppmtogif.1
-
-# This works if you've done a full SVN checkout.
-USERGUIDE= ../../userguide
-
-XML = $(HTMLMANUALS:.html=.xml) netpbm.xml
-
-# List everything in the userguide directory that is not categorized above.
-# Use this to check that 'make manpages' converts as much as possible
-# of the HTML documentation.
-uncategorized:
-	@echo $(HTMLMANUALS) $(EXCEPTIONS) $(STUBS) | tr " " "\n" | sort >LIST1
-	@(cd $(USERGUIDE); ls | sort) >LIST2
-	@comm -3 LIST1 LIST2
-	@rm LIST1 LIST2
-
-# Make man pages -- reports bad lines to standard error.
-manpages:
-	@python $(MAKEMAN) -d $(USERGUIDE) index.html $(HTMLMANUALS) 
+# GNU make version 3.81 or newer recommended.
+# Tested with GNU make version 3.80.
+
+# CAVEAT: USERGUIDE must be a valid directory: even for "make clean"!
+
+# MAKEFILE_DIR is the directory with this file: manpage.mk.
+# Should be buildtools.
+# Use $(realpath) and $(lastword) if available.
+# (both appear in GNU make v. 3.81)
+
+ifeq ($(realpath $(CURDIR)/.),$(CURDIR))
+  MAKEFILE_DIR := $(dir $(realpath $(lastword $(MAKEFILE_LIST))))
+else
+  MAKEFILE_DIR := $(dir $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)))
+endif
+
+# Program 'makeman' should be in the same directory.
+MAKEMAN ?= $(MAKEFILE_DIR)makeman
+
+# Install location of manpages.
+# Subdirectories man{1,3,5} must exist.
+MANDIR ?= /usr/share/man/
+
+# Directory with the HTML input files.  It must be explicitly set and
+# must be a valid directory.
+
+ifeq ($(USERGUIDE),)
+  $(error error: Variable USERGUIDE must be explicitly set)
+else
+  ifeq ($(wildcard $(USERGUIDE)/*html),)
+    $(error error: No HTML files found in $(USERGUIDE))
+  endif
+endif
+
+# In the past, the following default value was used.  It works if you've done
+# a Subversion checkout for source code and userguide in the same directory,
+# and you are working in a subdirectory of netpbm, say ./buildtools .
+# USERGUIDE = ../../userguide
+
+# The files that don't get converted to manual pages.
+# Override at the command line if necessary.
+
+# error.html: appears when problem occurs when fetching HTML files with wget.
+# directory.html: list of Netpbm programs.
+# libnetpbm_dir.html: directory to pages describing libnetpbm functions
+# hpcdtoppm:  Not distributed via Sourceforge for copyright restrictions.
+# ppmsvgalib: Not used in systems with X Window System.
+# vidtoppm:   Does not compile due to missing header files.
+
+EXCEPTIONS = \
+	directory.html \
+	error.html \
+	hpcdtoppm.html \
+	liberror.html \
+	libnetpbm_dir.html \
+	ppmsvgalib.html \
+	vidtoppm.html
+
+# File lists
+
+# We do not provide a list of troff manpages to be generated.
+# Instead the list is generated afresh from HTML file names.  Reasons:
+# 1. Any list would have to be updated every time an HTML file is added.
+# 2. The suffix (man section) depends on content (a "META" tag) of the
+#    HTML file.  (The makeman script is clever.)
+# 3. In one instance the file stem name changes: index.html => netpbm.1
+
+# HTML files in USERGUIDE
+HTML_ALL := $(sort $(notdir $(wildcard $(USERGUIDE)/*.html)))
+HTMLMANUALS := $(filter-out $(EXCEPTIONS),$(HTML_ALL))
+HTML_REJECT := $(filter $(EXCEPTIONS),$(HTML_ALL))
+
+# Subsets of HTMLMANUALS, by target man section
+HTML3 := $(shell cd $(USERGUIDE) && \
+                fgrep -l -i '<META NAME="manual_section" CONTENT="3">' \
+                      $(HTMLMANUALS))
+HTML5 := $(shell cd $(USERGUIDE) && \
+                fgrep -l -i '<META NAME="manual_section" CONTENT="5">' \
+                      $(HTMLMANUALS))
+HTML1 := $(filter-out $(HTML3) $(HTML5),$(HTMLMANUALS))
+
+# Troff man pages, by section
+MAN1 := $(patsubst index.1,netpbm.1,$(HTML1:.html=.1))
+MAN3 := $(HTML3:.html=.3)
+MAN5 := $(HTML5:.html=.5)
+MANPAGES := $(MAN1) $(MAN3) $(MAN5)
+
+# XML
+XML1 := $(MAN1:.1=.xml)
+XML3 := $(MAN3:.3=.xml)
+XML5 := $(MAN5:.5=.xml)
+XMLPAGES = $(XML1) $(XML3) $(XML5)
+
+.PHONY : report
+report: htmlcount manpagecount
+
+.PHONY : manpagecount
+manpagecount:
+	@echo Number of actual / expected troff man pages in current directory:
+	@echo Section 1: $(words $(wildcard $(MAN1))) / $(words $(MAN1)) 
+	@echo Section 3: $(words $(wildcard $(MAN3))) / $(words $(MAN3)) 
+	@echo Section 5: $(words $(wildcard $(MAN5))) / $(words $(MAN5)) 
+	@echo total: $(words $(wildcard $(MANPAGES))) / $(words $(MANPAGES))
+	@echo
+
+.PHONY : htmlcount
+htmlcount:
+	@echo HTML files in USERGUIDE directory: $(USERGUIDE)
+	@echo Total HTML files: $(words $(HTML_ALL))
+	@echo Rejected HTML files: $(HTML_REJECT) : $(words $(HTML_REJECT))
+	@echo Valid HTML files: $(words $(HTMLMANUALS))
+	@echo
+
+.PHONY : reportvalid
+reportvalid:
+	@echo Source HTML files in USERGUIDE directory: $(USERGUIDE)
+	@echo $(HTMLMANUALS)
+
+# Note that this may give different results from "ls ."
+.PHONY : reportman
+reportman:
+	@echo $(MANPAGES)
+
+# Static rules for converting HTML to troff man -- reports bad lines
+# to standard error.
+%.1 %.3 %.5: $(USERGUIDE)/%.html
+	@echo Converting $< to $@
+	@python $(MAKEMAN) -d $(USERGUIDE) $(<F) 
+
+netpbm.1: $(USERGUIDE)/index.html
+	@echo Converting $< to $@
+	@python $(MAKEMAN) -d $(USERGUIDE) index.html
 	@mv index.1 netpbm.1
 
-# Make XML pages, and validate them.
-xmlpages:
-	@for x in $(MANPAGES); do doclifter -v $$x; done
-	@for x in $(MANPAGES); do xmllint -xinclude --postvalid $$x.xml >/dev/null; done
-
-# This will install the generated man pages
-installman:
+# Generate man pages
+.PHONY : manpages
+manpages: $(MANPAGES)
+
+# Static rules for converting troff man to XML.
+$(XML1): %.xml: %.1
+	doclifter -v $<
+	mv $<.xml $@
+
+$(XML3): %.xml: %.3
+	doclifter -v $<
+	mv $<.xml $@
+
+$(XML5): %.xml: %.5
+	doclifter -v $<
+	mv $<.xml $@
+
+# Generate XML pages.
+# TODO: Does not work completely.  Some pages have glitches.
+.PHONY : xmlpages
+xmlpages: manpages $(XMLPAGES)
+
+# Validate XML pages.
+# TODO: Not working.
+.PHONY : xmlvalidate
+xmlvalidate: xmlpages
+	xmllint -xinclude --postvalid $< >> /dev/null
+
+# This will install the generated man pages.
+# Note that lists MAN1 MAN3 MAN5 depend upon the names of HTML files
+# in the USERGUIDE directory, even after man page generation.
+# If the current directory has "pbm.1" but USERGUIDE does not have
+# "pbm.html", the document will not be installed.
+# If the USERGUIDE directory is empty, no documents will be installed.
+
+.PHONY : installman
+installman: report
 	set -x
-	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 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
-
-oldclean:
-	# Clean up old locations on Fedora Core 2
-	rm -f $(MANDIR)/man1/extendedopacity.1.gz 
-	rm -f $(MANDIR)/man3/directory.3.gz
-	rm -f $(MANDIR)/man3/libnetpbm_dir.3.gz
-	# remove pointer man pages (that say, "The man page isn't here")
-	# which might have been installed previously
-	for f in $(MAN1); do rm -f $(MANDIR)/man1/$$f; done
-	for f in $(MAN3); do rm -f $(MANDIR)/man3/$$f; done
-	for f in $(MAN5); do rm -f $(MANDIR)/man5/$$f; done
-
+	for f in $(wildcard $(MAN1)); do if [ -f $$f ]; then gzip <$$f >$(MANDIR)/man1/$$f.gz; fi; done
+	for f in $(wildcard $(MAN3)); do if [ -f $$f ]; then gzip <$$f >$(MANDIR)/man3/$$f.gz; fi; done
+	for f in $(wildcard $(MAN5)); do if [ -f $$f ]; then gzip <$$f >$(MANDIR)/man5/$$f.gz; fi; done
+
+
+# This will uninstall the man pages.
+# Only pages with corresponding files in USERGUIDE are deleted.
+.PHONY : uninstallman
+uninstallman: report
+	for f in $(MAN1); do if [ -f $(MANDIR)/man1/$$f.gz ]; then rm -f $(MANDIR)/man1/$$f.gz; fi; done
+	for f in $(MAN3); do if [ -f $(MANDIR)/man3/$$f.gz ]; then rm -f $(MANDIR)/man3/$$f.gz; fi; done
+	for f in $(MAN5); do if [ -f $(MANDIR)/man5/$$f.gz ]; then rm -f $(MANDIR)/man5/$$f.gz; fi; done
+
+
+# Legacy uninstall target.
+#oldclean:
+#	# Clean up old locations on Fedora Core 2
+#	rm -f $(MANDIR)/man1/extendedopacity.1.gz 
+#	rm -f $(MANDIR)/man3/directory.3.gz
+#	rm -f $(MANDIR)/man3/libnetpbm_dir.3.gz
+#	# remove pointer man pages (that say, "The man page isn't here")
+#	# which might have been installed previously
+#	for f in $(MAN1); do rm -f $(MANDIR)/man1/$$f; done
+#	for f in $(MAN3); do rm -f $(MANDIR)/man3/$$f; done
+#	for f in $(MAN5); do rm -f $(MANDIR)/man5/$$f; done
+
+
+.PHONY: clean
 clean:
-	@rm -f *.[135] $(XML)
-
+	  @rm -f *.[135] $(XML)
diff --git a/buildtools/stamp-date b/buildtools/stamp-date
index 32839e94..902c82e4 100755
--- a/buildtools/stamp-date
+++ b/buildtools/stamp-date
@@ -8,16 +8,25 @@
 # copyright notice and this permission notice appear in supporting
 # documentation.  This software is provided "as is" without express or
 # implied warranty.
-#
-DATE=$(date)
+
+# SOURCE_DATE_EPOCH is an environment variable as described here:
+# https://reproducible-builds.org/specs/source-date-epoch/ on 2017.03.16.
+
+SOURCE_DATE_OR_NONE=${SOURCE_DATE_EPOCH:-NONE}
+
+BUILD_DATETIME=$(date +%s)
+
 LOGNAME_OR_UNKNOWN=${LOGNAME:-UNKNOWN}
 USER=${USER:-$LOGNAME_OR_UNKNOWN}
 if [ "$USER" = "UNKNOWN" ]; then
-    USER=`whoami`
+    USER=$(whoami)
 fi
 
-echo "/* This file tells the package when it was compiled */"
+echo "/* This file tells some facts about the building of the package */"
 echo "/* DO NOT EDIT - THIS FILE IS MAINTAINED AUTOMATICALLY */"
 echo "/* Created by the program 'stamp-date'  */"
-echo "#define COMPILE_TIME \"$DATE\""
+if [ "$SOURCE_DATE_OR_NONE" != "NONE" ]; then
+  echo "#define SOURCE_DATETIME $SOURCE_DATE_OR_NONE"
+fi
+echo "#define BUILD_DATETIME $BUILD_DATETIME"
 echo "#define COMPILED_BY \"$USER\""
diff --git a/common.mk b/common.mk
index dd7f4ed3..749488c2 100644
--- a/common.mk
+++ b/common.mk
@@ -150,10 +150,7 @@ IMPORTINC_LIB_HEADERS := \
 IMPORTINC_LIB_UTIL_HEADERS := \
   bitarith.h bitio.h bitreverse.h filename.h intcode.h floatcode.h io.h \
   matrix.h mallocvar.h \
-  nsleep.h nstring.h pm_c_util.h 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
+  nsleep.h nstring.h pm_c_util.h runlength.h shhopt.h token.h
 
 IMPORTINC_HEADERS := \
   $(IMPORTINC_ROOT_HEADERS) \
@@ -275,24 +272,24 @@ $(OBJECTS): %.o: %.c importinc
 LIBOPT = $(BUILDDIR)/buildtools/libopt
 
 ifneq ($(OMIT_BUILDTOOL_RULE),1)
-$(LIBOPT) $(TYPEGEN): $(BUILDDIR)/buildtools
+$(LIBOPT) $(TYPEGEN): $(BUILDDIR)/buildtools FORCE
 	$(MAKE) -C $(dir $@) -f $(SRCDIR)/buildtools/Makefile \
 	    SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@) 
 endif
 
 ifneq ($(OMIT_LIBRARY_RULE),1)
-$(NETPBMLIB): $(BUILDDIR)/lib
+$(NETPBMLIB): $(BUILDDIR)/lib FORCE
 	$(MAKE) -C $(dir $@) -f $(SRCDIR)/lib/Makefile \
 	    SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@) 
 endif
 
 ifneq ($(OMIT_URT_RULE),1)
-$(BUNDLED_URTLIB): $(BUILDDIR)/urt
+$(BUNDLED_URTLIB): $(BUILDDIR)/urt FORCE
 	$(MAKE) -C $(dir $@) -f $(SRCDIR)/urt/Makefile \
 	    SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@) 
 endif
 
-$(BUILDDIR)/icon/netpbm.o: $(BUILDDIR)/icon
+$(BUILDDIR)/icon/netpbm.o: $(BUILDDIR)/icon FORCE
 	$(MAKE) -C $(dir $@) -f $(SRCDIR)/icon/Makefile \
 	    SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@) 
 
@@ -403,14 +400,29 @@ $(PORTBINARIES) $(MATHBINARIES): %: %.o \
 	  "-Dmain=main_$*" \
           $(CFLAGS_MERGE) $(CFLAGS_PERSONAL) $(CADD) -o $@ $<
 
-# The "merge try list" is a file full of TRY macro invocations, one for
-# each Netpbm program in this directory or any subdirectory that can be
-# invoked via the merged Netpbm program.  You will find it #included in
-# netpbm.c.
+# The "merge try list" is a file full of TRY macro invocations, one for each
+# Netpbm program in this directory or any subdirectory that can be invoked via
+# the merged Netpbm program.  There are additional TRYs for backward
+# compatility program names (e.g. 'pnmcomp' for 'pamcomp').  You will find the
+# merge try list #included in netpbm.c.
+
+# The file 'mergecomptrylist' contains the backward compatibility TRYs for the
+# current directory.  Just the current directory itself - not subdirectories.
+# Only directories that contain programs with backward compatibility names
+# have a 'mergecomptrylist'.  The make file for a directory that has
+# 'mergecomptrylist' sets make variable HAVE_MERGE_COMPAT to "YES".
+
+ifeq ($(HAVE_MERGE_COMPAT),YES)
+mergetrylist: mergecomptrylist
+endif
 
 mergetrylist: $(SUBDIRS:%=%/mergetrylist) 
 	cat /dev/null $(SUBDIRS:%=%/mergetrylist) >$@
 	$(SRCDIR)/buildtools/make_merge.sh $(MERGEBINARIES) >>$@
+ifeq ($(HAVE_MERGE_COMPAT),YES)
+	echo "/* Backward compatibility names from mergecomptrylist: */" >>$@
+	cat mergecomptrylist >>$@
+endif
 
 # The "merge list" is a list of all the object files from this directory and
 # any subdirectories that have to be linked into the merged Netpbm program.
@@ -470,7 +482,7 @@ endif
 
 PKGMANSUBDIRS = man1 man3 man5 web
 
-PKGSUBDIRS = bin include include/netpbm lib link misc \
+PKGSUBDIRS = bin include include/netpbm lib sharedlink staticlink misc \
   $(PKGMANSUBDIRS:%=$(PKGMANDIR)/%)
 
 $(PKGSUBDIRS:%=$(PKGDIR)/%):
@@ -509,36 +521,6 @@ $(DATAFILES:%=%_installdata): $(PKGDIR)/misc
 	  $(SRCDIR)/$(SUBDIR)/$(@:%_installdata=%) $<
 
 
-.PHONY: install.man install.man1 install.man3 install.man5
-install.man: install.man1 install.man3 install.man5 \
-	$(SUBDIRS:%=%/install.man)
-
-MANUALS1 = $(BINARIES) $(SCRIPTS)
-
-install.man1: $(MANUALS1:%=%_installman1)
-
-install.man3: $(MANUALS3:%=%_installman3)
-
-install.man5: $(MANUALS5:%=%_installman5)
-
-install.manweb: $(MANUALS1:%=%_installmanweb) $(SUBDIRS:%=%/install.manweb)
-
-%_installman1: $(PKGDIR)/$(PKGMANDIR)/man1
-	perl -w $(SRCDIR)/buildtools/makepointerman $(@:%_installman1=%) \
-          $(NETPBM_DOCURL) $< 1 $(MANPAGE_FORMAT) $(INSTALL_PERM_MAN)
-
-%_installman3: $(PKGDIR)/$(PKGMANDIR)/man3
-	perl -w $(SRCDIR)/buildtools/makepointerman $(@:%_installman3=%) \
-          $(NETPBM_DOCURL) $< 3 $(MANPAGE_FORMAT) $(INSTALL_PERM_MAN)
-
-%_installman5: $(PKGDIR)/$(PKGMANDIR)/man5
-	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)
@@ -581,12 +563,6 @@ endif
 %/install.lib:
 	$(MAKE) -C $(dir $@) -f $(SRCDIR)/$(SUBDIR)/$(dir $@)Makefile \
 	    SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@) 
-%/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 0f2ccb6c..ee692332 100644
--- a/config.mk.in
+++ b/config.mk.in
@@ -161,6 +161,12 @@ LEX = flex
 # 
 # LEX = lex
 
+# PKG_CONFIG is the beginning of a shell command that tells things about how
+# some package (e.g. the PNG library) is installed on the build system.  For
+# example, it can tell what link options you need to link the PNG library to a
+# program.
+PKG_CONFIG = pkg-config
+
 # C compiler options 
 
 # gcc:
@@ -662,5 +668,5 @@ NETPBM_DOCURL = http://netpbm.sourceforge.net/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
+# environment variable is not set.  See pm_config.in.h for details.
+RGB_DB_PATH = /usr/local/netpbm/rgb.txt:/usr/share/netpbm/rgb.txt:/etc/X11/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 524bbf7e..78c469d1 100644
--- a/converter/bmp.h
+++ b/converter/bmp.h
@@ -83,7 +83,37 @@
 
 #include "pm.h"  /* For pm_error() */
 
-enum bmpClass {C_WIN=1, C_OS2=2};
+enum bmpClass {
+    BMP_C_OS2_1x,
+    BMP_C_OS2_2x,
+    BMP_C_WIN_V1,
+    BMP_C_WIN_V2,
+    BMP_C_WIN_V3,
+    BMP_C_WIN_V4,
+    BMP_C_WIN_V5
+};
+
+
+
+static __inline__ const char *
+BMPClassName(enum bmpClass const class) {
+
+    const char * name;
+
+    switch (class) {
+    case BMP_C_OS2_1x: name = "OS/2 (v1)";    break;
+    case BMP_C_OS2_2x: name = "OS/2 (v2)";    break;
+    case BMP_C_WIN_V1: name = "Windows (v1)"; break;
+    case BMP_C_WIN_V2: name = "Windows (v2)"; break;
+    case BMP_C_WIN_V3: name = "Windows (v3)"; break;
+    case BMP_C_WIN_V4: name = "Windows (v4)"; break;
+    case BMP_C_WIN_V5: name = "Windows (v5)"; break;
+    }
+
+  return name;
+}
+
+
 
 static char const er_internal[] = "%s: internal error!";
 
@@ -114,29 +144,100 @@ BMPCompTypeName(BMPCompType const compression) {
 
 
 static __inline__ unsigned int
-BMPlenfileheader(enum bmpClass const class) {
+BMPlenfileheader(void) {
+
+    return 14;
+}
+
+
+
+enum BMPinfoHeaderLen {
+/*----------------------------------------------------------------------------
+   BMPs come in various kinds, distinguished by the length of their
+   info header, which is the first field in that header.
+
+   These are those lengths.
+-----------------------------------------------------------------------------*/
+    BMP_HDRLEN_OS2_1x =  12,
+        /* BITMAPCOREHEADER; since Windows 2.0, OS/2 1.x */
+    BMP_HDRLEN_OS2_2x =  64,
+        /* not documented by Microsoft; since OS/2 2.x */
+    BMP_HDRLEN_WIN_V1 =  40,
+        /* BITMAPINFOHEADER; since Windows NT 3, Windows 3.x */
+    BMP_HDRLEN_WIN_V2 =  52,
+        /* not documented by Microsoft */
+    BMP_HDRLEN_WIN_V3 =  56,
+        /* not documented by Microsoft */
+    BMP_HDRLEN_WIN_V4 = 108,
+        /* BITMAPV4HEADER; since Windows NT 4, Windows 95 */
+    BMP_HDRLEN_WIN_V5 = 124
+        /* BITMAPV5HEADER; since Windows 2000, Windows 98 */
+};
+
+
+
+static __inline__ unsigned int
+BMPleninfoheader(enum bmpClass const class) {
 
     unsigned int retval;
 
     switch (class) {
-    case C_WIN: retval = 14; break;
-    case C_OS2: retval = 14; break;
+    case BMP_C_WIN_V1: retval = BMP_HDRLEN_WIN_V1; break;
+    case BMP_C_WIN_V2: retval = BMP_HDRLEN_WIN_V2; break;
+    case BMP_C_WIN_V3: retval = BMP_HDRLEN_WIN_V3; break;
+    case BMP_C_WIN_V4: retval = BMP_HDRLEN_WIN_V4; break;
+    case BMP_C_WIN_V5: retval = BMP_HDRLEN_WIN_V5; break;
+    case BMP_C_OS2_1x: retval = BMP_HDRLEN_OS2_1x; break;
+    case BMP_C_OS2_2x: retval = BMP_HDRLEN_OS2_2x; break;
     }
     return retval;
 }
 
 
 
+static __inline__ void
+BMPdetermineclass(unsigned int    const infoHdrLen,
+                  enum bmpClass * const classP,
+                  const char **   const errorP) {
+/*----------------------------------------------------------------------------
+   Determine the class of BMP, based on the fact that the info header is
+   'infoHdrLen' bytes long.
+-----------------------------------------------------------------------------*/
+    switch (infoHdrLen) {
+    case BMP_HDRLEN_OS2_1x: *errorP = NULL; *classP = BMP_C_OS2_1x; break;
+    case BMP_HDRLEN_OS2_2x: *errorP = NULL; *classP = BMP_C_OS2_2x; break;
+    case BMP_HDRLEN_WIN_V1: *errorP = NULL; *classP = BMP_C_WIN_V1; break;
+    case BMP_HDRLEN_WIN_V2: *errorP = NULL; *classP = BMP_C_WIN_V2; break;
+    case BMP_HDRLEN_WIN_V3: *errorP = NULL; *classP = BMP_C_WIN_V3; break;
+    case BMP_HDRLEN_WIN_V4: *errorP = NULL; *classP = BMP_C_WIN_V4; break;
+    case BMP_HDRLEN_WIN_V5: *errorP = NULL; *classP = BMP_C_WIN_V5; break;
+
+    default:
+        pm_asprintf(errorP, "Not one of the 7 lengths we recognize");
+    }
+}
+
+
+
 static __inline__ unsigned int
-BMPleninfoheader(enum bmpClass const class) {
+BMPlenrgb(enum bmpClass const class) {
 
-    unsigned int retval;
+    unsigned int lenrgb;
 
     switch (class) {
-    case C_WIN: retval = 40; break;
-    case C_OS2: retval = 12; break;
+    case BMP_C_OS2_1x:
+    case BMP_C_OS2_2x:
+        lenrgb = 3;
+        break;
+    case BMP_C_WIN_V1:
+    case BMP_C_WIN_V2:
+    case BMP_C_WIN_V3:
+    case BMP_C_WIN_V4:
+    case BMP_C_WIN_V5:
+        lenrgb = 4;
+        break;
     }
-    return retval;
+    return lenrgb;
 }
 
 
@@ -152,7 +253,6 @@ BMPlencolormap(enum bmpClass const class,
 
    'cmapsize' == 0 means there is no palette.
 -----------------------------------------------------------------------------*/
-    unsigned int lenrgb;
     unsigned int lencolormap;
 
     if (bitcount < 1)
@@ -160,15 +260,10 @@ BMPlencolormap(enum bmpClass const class,
     else if (bitcount > 8) 
         lencolormap = 0;
     else {
-        switch (class) {
-        case C_WIN: lenrgb = 4; break;
-        case C_OS2: lenrgb = 3; break;
-        }
-
-        if (!cmapsize) 
-            lencolormap = (1 << bitcount) * lenrgb;
+        if (cmapsize) 
+            lencolormap = cmapsize * BMPlenrgb(class);
         else 
-            lencolormap = cmapsize * lenrgb;
+            lencolormap = (1 << bitcount) * BMPlenrgb(class);
     }
     return lencolormap;
 }
@@ -231,7 +326,7 @@ BMPoffbits(enum bmpClass const class,
 /*----------------------------------------------------------------------------
   return the offset to the BMP image bits.
 -----------------------------------------------------------------------------*/
-    return BMPlenfileheader(class)
+    return BMPlenfileheader()
         + BMPleninfoheader(class)
         + BMPlencolormap(class, bitcount, cmapsize);
 }
diff --git a/converter/other/Makefile b/converter/other/Makefile
index 6a3d14ed..2be88781 100644
--- a/converter/other/Makefile
+++ b/converter/other/Makefile
@@ -7,12 +7,12 @@ VPATH=.:$(SRCDIR)/$(SUBDIR)
 
 include $(BUILDDIR)/config.mk
 
-TEST_PKGCONFIG_LIBXML2 = if pkg-config libxml-2.0; then echo exists; fi
+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)
+  XML2_LIBS = $(shell $(PKG_CONFIG) libxml-2.0 --libs)
+  XML2_CFLAGS = $(shell $(PKG_CONFIG) libxml-2.0 --cflags)
 else
   ifeq ($(shell xml2-config --version),)
     XML2_LIBS=NONE
@@ -34,12 +34,12 @@ ifneq ($(TIFFLIB),NONE)
   endif
 endif
 
-TEST_PKGCONFIG_LIBPNG = if pkg-config libpng$(PNGVER); then echo exists; fi
+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)
+  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
@@ -166,6 +166,8 @@ endif
 OBJECTS = $(BINARIES:%=%.o) $(EXTRA_OBJECTS)
 MERGE_OBJECTS = $(MERGEBINARIES:%=%.o2) $(EXTRA_OBJECTS)
 
+HAVE_MERGE_COMPAT=YES
+
 pnmtops.o pnmtops.o2: CFLAGS_TARGET=$(PNMTOPS_NOFLATE_OPT)
 
 SCRIPTS = anytopnm pnmtoplainpnm
@@ -192,7 +194,7 @@ tifftopnm pamtotiff pnmtotiffcmyk: \
 
 ifneq ($(shell $(TEST_PKGCONFIG_LIBPNG)),)
   # pkg-config libpng works on this system
-  PNGLIB_LIBOPTS = $(shell pkg-config libpng$(PNGVER) --libs)
+  PNGLIB_LIBOPTS = $(shell $(PKG_CONFIG) libpng$(PNGVER) --libs)
 else
   ifneq ($(shell libpng$(PNGVER)-config --version),)
     # No pkg-config, but we have libpng-config on this system
@@ -248,7 +250,7 @@ bmptopnm.o bmptopnm.o2: bmp.h
 
 pamtotga.o pamtotga.o2: tga.h
 
-install.bin: install.bin.local
+install.bin install.merge: install.bin.local
 .PHONY: install.bin.local
 install.bin.local: $(PKGDIR)/bin
 # Remember that $(SYMLINK) might just be a copy command.
@@ -296,3 +298,25 @@ ifeq ($(HAVE_PNGLIB),Y)
 	cd $(PKGDIR)/bin ; \
 	$(SYMLINK) pamtopng$(EXE) pamrgbatopng$(EXE)
 endif
+
+mergecomptrylist:
+	cat /dev/null >$@
+	echo "TRY(\"bmptoppm\",     main_bmptopnm);"     >>$@
+	echo "TRY(\"gemtopbm\",     main_gemtopnm);"     >>$@
+ifneq ($(JPEGLIB),NONE)
+	echo "TRY(\"ppmtojpeg\",    main_pnmtojpeg);"    >>$@
+endif
+	echo "TRY(\"ppmtouil\",     main_pamtouil);"     >>$@
+	echo "TRY(\"ppmtotga\",     main_pamtotga);"     >>$@
+	echo "TRY(\"pnmtopnm\",     main_pamtopnm);"     >>$@
+	echo "TRY(\"pnmtofits\",    main_pamtofits);"    >>$@
+ifneq ($(TIFF_PREREQ_MISSING),Y)
+	echo "TRY(\"pnmtotiff\",    main_pamtotiff);"    >>$@
+endif
+ifeq ($(HAVE_PNGLIB),Y)
+	echo "TRY(\"pngtopnm\",     main_pngtopam);"     >>$@
+endif
+	echo "TRY(\"icontopbm\",    main_sunicontopnm);" >>$@
+ifeq ($(HAVE_PNGLIB),Y)
+	echo "TRY(\"pamrgbatopng\", main_pamtopng);"     >>$@
+endif
diff --git a/converter/other/anytopnm b/converter/other/anytopnm
index 397faae5..e5de85cc 100755
--- a/converter/other/anytopnm
+++ b/converter/other/anytopnm
@@ -527,14 +527,18 @@ if [ $# -gt 1 ] ; then
          "input file name." 1>&2
     exit 1
 elif [ $# -eq 1 ] ; then
-    inputFile="$1"
+    if [ $1 = "--version" -o $1 = "-version" ] ; then
+        pamtopnm --version ; exit $?
+    else 
+        inputFile="$1"
+    fi
 else
-    inputFile="-"
+        inputFile="-"
+
 fi
 
-tempdir="${TMPDIR-/tmp}/anytopnm.$$"
-mkdir -m 0700 $tempdir || \
-  { echo "Could not create temporary file. Exiting."; exit 1;}
+tempdir=$(mktemp -d "${TMPDIR:-/tmp}/anytopnm.XXXXXXXX") ||
+    ( echo "Could not create temporary file. Exiting." 1>&2; exit 1; ) 
 trap 'rm -rf $tempdir' 0
 
 # Take out all spaces
diff --git a/converter/other/bmptopnm.c b/converter/other/bmptopnm.c
index bf4d10f8..3f66634b 100644
--- a/converter/other/bmptopnm.c
+++ b/converter/other/bmptopnm.c
@@ -1,7 +1,7 @@
 /*****************************************************************************
                                     bmptopnm.c
 ******************************************************************************
- 
+
  Bmptopnm - Converts from a Microsoft Windows or OS/2 .BMP file to a
  PBM, PGM, or PPM file.
 
@@ -9,7 +9,7 @@
  The name was changed in March 2002.
 
  Copyright (C) 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
@@ -62,14 +62,14 @@ struct bitPosition {
 };
 
 struct pixelformat {
-    /* The format of a pixel representation from the raster.  i.e. which 
-       bits apply to red, green, blue, and transparency 
+    /* The format of a pixel representation from the raster.  i.e. which
+       bits apply to red, green, blue, and transparency
     */
     struct bitPosition red;
     struct bitPosition blu;
     struct bitPosition grn;
     struct bitPosition trn;
-    
+
     bool conventionalBgr;
         /* This means that the above bit positions are just the conventional
            BGR format -- one byte Blue, one byte Green, one byte Red,
@@ -80,6 +80,19 @@ struct pixelformat {
         */
 };
 
+typedef struct {
+    /* These are all encodings of floating point */
+    unsigned long x;
+    unsigned long y;
+    unsigned long z;
+} cieXyz;
+
+typedef struct {
+    cieXyz red;
+    cieXyz grn;
+    cieXyz blu;
+} cieXyzTriple;
+
 struct bmpInfoHeader {
     enum rowOrder rowOrder;
     unsigned int cols;
@@ -92,17 +105,18 @@ struct bmpInfoHeader {
            described by the "mask" values in the header, rather than
            fixed formats.
         */
-    unsigned int cmapsize;
+    unsigned int cmapSize;
         /* Size in bytes of the colormap (palette) in the BMP file.
 
            Zero means there is no colormap.
         */
     unsigned int imageSize;
-        /* Size in bytes of the image data.  We only reference this 
-           when the image is compressed. */    
+        /* Size in bytes of the image data.  We only reference this
+           when the image is compressed. */
     unsigned short cPlanes;
     BMPCompType compression;
     struct pixelformat pixelformat;
+    cieXyzTriple endPoints;
 };
 
 
@@ -137,7 +151,7 @@ parseCommandLine(int argc, const char ** argv,
 
     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 */
@@ -145,7 +159,7 @@ parseCommandLine(int argc, const char ** argv,
     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 (argc-1 == 0)
         cmdlineP->inputFileName = "-";
     else if (argc-1 != 1)
         pm_error("Program takes zero or one argument (filename).  You "
@@ -208,27 +222,74 @@ GetLong(FILE * const fp) {
 
 
 
-typedef struct {
-    long dummy[12];
-} cieXyzTriple;
+static cieXyz
+GetCieXyz(FILE * const ifP) {
+
+    cieXyz retval;
+
+    retval.x = GetLong(ifP);
+    retval.y = GetLong(ifP);
+    retval.z = GetLong(ifP);
+
+    return retval;
+}
+
+
 
 static cieXyzTriple
-GetCieXyzTriple(FILE * const fp) {
+GetCieXyzTriple(FILE *         const ifP) {
 
-    cieXyzTriple v;
-    unsigned int i;
+    cieXyzTriple retval;
 
-    for (i = 0; i < 12; ++i) 
-        if (pm_readlittlelong(fp, &v.dummy[i]) == -1)
-            pm_error(er_read, ifname);
+    retval.red = GetCieXyz(ifP);
+    retval.grn = GetCieXyz(ifP);
+    retval.blu = GetCieXyz(ifP);
 
-    return v;
+    return retval;
 }
 
 
 
 static struct pixelformat
-defaultPixelformat(unsigned int const bitCount);
+defaultPixelformat(unsigned int const bitCount) {
+
+    struct pixelformat retval;
+
+    switch (bitCount) {
+    case 16:
+        /* This layout is sometimes called "RGB555".  A document from
+           Microsoft says this is the default (when the "compression"
+           field of the header says COMP_BITFIELDS).
+        */
+        retval.conventionalBgr = FALSE;
+        retval.red.shift = 10;
+        retval.grn.shift = 5;
+        retval.blu.shift = 0;
+        retval.trn.shift = 0;
+        retval.red.mask = 0x1f;  /* 5 bits */
+        retval.grn.mask = 0x1f;  /* 5 bits */
+        retval.blu.mask = 0x1f;  /* 5 bits */
+        retval.trn.mask = 0;
+        break;
+    case 24:
+    case 32:
+        retval.conventionalBgr = TRUE;
+        retval.red.shift = 16;
+        retval.grn.shift = 8;
+        retval.blu.shift = 0;
+        retval.trn.shift = 0;
+        retval.red.mask = 0xff;  /* 8 bits */
+        retval.grn.mask = 0xff;  /* 8 bits */
+        retval.blu.mask = 0xff;  /* 8 bits */
+        retval.trn.mask = 0;
+        break;
+    default:
+        /* colormapped - masks are undefined */
+        break;
+    }
+
+    return retval;
+}
 
 
 
@@ -238,7 +299,7 @@ readOffBytes(FILE * const fp, unsigned int const nbytes) {
    Read 'nbytes' from file 'fp'.  Abort program if read error.
 -----------------------------------------------------------------------------*/
     int i;
-    
+
     for(i = 0; i < nbytes; ++i) {
         int rc;
         rc = getc(fp);
@@ -250,15 +311,11 @@ readOffBytes(FILE * const fp, unsigned int const nbytes) {
 
 
 static void
-bmpReadfileheader(FILE *         const ifP, 
-                  unsigned int * const bytesReadP, 
+bmpReadfileheader(FILE *         const ifP,
+                  unsigned int * const bytesReadP,
                   unsigned int * const offBitsP) {
 
-    unsigned short    xHotSpot;
-    unsigned short    yHotSpot;
     unsigned long     offBits;
-    unsigned long int fileSize;
-
 
     if (GetByte(ifP) != 'B')
         pm_error("'%s' is not a BMP file.  (It doesn't start with 'BM')",
@@ -268,33 +325,47 @@ bmpReadfileheader(FILE *         const ifP,
                  ifname);
 
 
-    fileSize = GetLong(ifP);  /* This is not always reliable. */
-    xHotSpot = GetShort(ifP);
-    yHotSpot = GetShort(ifP);
+    /* fileSize = */ GetLong(ifP);  /* This is not always reliable. */
+    /* xHotSpot = */ GetShort(ifP);
+    /* yHotSpot = */ GetShort(ifP);
     offBits  = GetLong(ifP);
 
     *offBitsP = offBits;
 
+    assert(BMPlenfileheader() == 14);
+
     *bytesReadP = 14;
 }
 
 
 
 static void
-readOs2InfoHeader(FILE *                 const ifP,
-                  struct bmpInfoHeader * const headerP) {
+readOs2InfoHeaderRest(FILE *                 const ifP,
+                      struct bmpInfoHeader * const headerP) {
+/*----------------------------------------------------------------------------
+   Read the rest of the info header, after its size field, of an OS2 BMP from
+   *ifP.
+
+   Add the information from it to *headerP, in particular these members:
 
+     cols
+     rows
+     rowOrder
+     cPlanes
+     cBitCount
+     cmapSize
+     pixelformat
+     compression
+-----------------------------------------------------------------------------*/
     unsigned short colsField, rowsField;
     unsigned short planesField, bitCountField;
 
-    headerP->class = C_OS2;
-
     pm_readlittleshortu(ifP, &colsField);
     if (colsField == 0)
         pm_error("Invalid BMP file: says width is zero");
     else
         headerP->cols = colsField;
-    
+
     pm_readlittleshortu(ifP, &rowsField);
     if (rowsField == 0)
         pm_error("Invalid BMP file: says height is zero");
@@ -313,22 +384,19 @@ readOs2InfoHeader(FILE *                 const ifP,
        same as for Windows.
     */
     if (headerP->cBitCount <= 8)
-        headerP->cmapsize = 1 << headerP->cBitCount;
+        headerP->cmapSize = 1 << headerP->cBitCount;
     else if (headerP->cBitCount == 24)
-        headerP->cmapsize = 0;
+        headerP->cmapSize = 0;
     /* There is a 16 bit truecolor format, but we don't know how the
        bits are divided among red, green, and blue, so we can't handle it.
     */
     else
         pm_error("Unrecognized bits per pixel in OS/2 BMP file header: %d",
                  headerP->cBitCount);
-                 
+
     headerP->pixelformat = defaultPixelformat(headerP->cBitCount);
 
     headerP->compression = BMPCOMP_RGB;
-    
-    pm_message("OS/2 BMP, %dx%dx%d",
-               headerP->cols, headerP->rows, headerP->cBitCount);
 }
 
 
@@ -337,24 +405,24 @@ static void
 validateCompression(unsigned long const compression,
                     enum rowOrder const rowOrder,
                     unsigned int  const cBitCount) {
-    
+
     if (compression != BMPCOMP_RGB && compression != BMPCOMP_BITFIELDS &&
-        compression != BMPCOMP_RLE4 && compression != BMPCOMP_RLE8) 
+        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, BMPCOMP_RGB, BMPCOMP_BITFIELDS,
                  BMPCOMP_RLE4, BMPCOMP_RLE8);
-                     
+
     if ((compression == BMPCOMP_RLE4 || compression == BMPCOMP_RLE8) &&
-        rowOrder == TOPDOWN )                        
+        rowOrder == TOPDOWN )
         pm_error("Invalid BMP header.  Claims image is top-down and also "
                  "compressed, which is an impossible combination.");
 
     if ((compression == BMPCOMP_RLE4 && cBitCount !=4 ) ||
-        (compression == BMPCOMP_RLE8 && cBitCount !=8 )) 
-        pm_error("Invalid BMP header.  " 
+        (compression == BMPCOMP_RLE8 && cBitCount !=8 ))
+        pm_error("Invalid BMP header.  "
                  "Compression type (%s) disagrees with "
                  "number of bits per pixel (%u).",
                  compression == BMPCOMP_RLE4 ? "RLE4" : "RLE8",
@@ -373,13 +441,10 @@ readWindowsBasic40ByteInfoHeader(FILE *                 const ifP,
 
    Return the information from the info header as *headerP.
 -----------------------------------------------------------------------------*/
-    int colorsimportant;   /* ColorsImportant value from header */
     int colorsused;        /* ColorsUsed value from header */
     unsigned short planesField, bitCountField;
     int32_t colsField;
 
-    headerP->class = C_WIN;
-
     pm_readlittlelong2(ifP, &colsField);
 
     if (colsField == 0)
@@ -388,6 +453,7 @@ readWindowsBasic40ByteInfoHeader(FILE *                 const ifP,
         pm_error("Invalid BMP file: says width is negative (%d)", colsField);
     else
         headerP->cols = (unsigned int)colsField;
+
     {
         long const cy = GetLong(ifP);
 
@@ -413,7 +479,7 @@ readWindowsBasic40ByteInfoHeader(FILE *                 const ifP,
 
         headerP->bitFields = (compression == BMPCOMP_BITFIELDS);
 
-        headerP->compression = compression;             
+        headerP->compression = compression;
     }
     /* And read the rest of the junk in the 40 byte header */
     headerP->imageSize = GetLong(ifP);   /* ImageSize */
@@ -421,24 +487,30 @@ readWindowsBasic40ByteInfoHeader(FILE *                 const ifP,
     GetLong(ifP);   /* YpixelsPerMeter */
     colorsused = GetLong(ifP);   /* ColorsUsed */
     /* See comments in bmp.h for info about the definition of the following
-       word and its relationship to the color map size (headerP->cmapsize).
+       word and its relationship to the color map size (headerP->cmapSize).
     */
-    colorsimportant = GetLong(ifP);  /* ColorsImportant */
+    /* colorsimportant = */ GetLong(ifP);  /* ColorsImportant */
 
     if (headerP->cBitCount <= 8) {
         if (colorsused != 0) {
             if (colorsused > 1 << headerP->cBitCount)
                 pm_error("Invalid BMP header.  Says %u bits per pixel, "
-                         "but %d colors used", 
+                         "but %d colors used",
                          headerP->cBitCount, colorsused);
+            else if (colorsused == 1 && headerP->cBitCount == 1) {
+                pm_message("Abnormal BMP header.  Says 1 bit per pixel. "
+                           "Should have 2 colors, but says only 1 color used. "
+                    );
+                headerP->cmapSize = colorsused;
+        }
             else
-                headerP->cmapsize = colorsused;
-        } else 
-            headerP->cmapsize = 1 << headerP->cBitCount;
-    } else if (headerP->cBitCount == 24 || 
-               headerP->cBitCount == 16 || 
+                headerP->cmapSize = colorsused;
+        } else
+            headerP->cmapSize = 1 << headerP->cBitCount;
+    } else if (headerP->cBitCount == 24 ||
+               headerP->cBitCount == 16 ||
                headerP->cBitCount == 32)
-        headerP->cmapsize = 0;
+        headerP->cmapSize = 0;
     else
         pm_error("Unrecognized bits per pixel in Windows BMP file header: %d",
                  headerP->cBitCount);
@@ -451,18 +523,18 @@ lsbZeroCount(unsigned int const mask)
 /*----------------------------------------------------------------------------
    Return the number of consecutive zeroes in the mask 'mask', starting with
    the least significant bit and going up.  E.g. for 0x20, it would be 5.
-   
+
    Use GCC built-in when available.
 -----------------------------------------------------------------------------*/
 
-#if HAVE_GCC_BITCOUNT 
+#if HAVE_GCC_BITCOUNT
 {
       return ( mask==0 ? sizeof(mask)*8 : __builtin_ctz(mask) );
 }
 #else
 {
       unsigned int i=0;
-    
+
       while (((mask >> i) & 0x1) == 0 && i < sizeof(mask)*8)
         ++i;
 
@@ -512,52 +584,17 @@ computeConventionalBgr(struct pixelformat * const fP,
 
 
 
-static struct pixelformat
-defaultPixelformat(unsigned int const bitCount) {
-
-    struct pixelformat retval;
-
-    switch (bitCount) {
-    case 16:
-        /* This layout is sometimes called "RGB555".  A document from
-           Microsoft says this is the default (when the "compression"
-           field of the header says COMP_BITFIELDS).
-        */
-        retval.conventionalBgr = FALSE;
-        retval.red.shift = 10;
-        retval.grn.shift = 5;
-        retval.blu.shift = 0;
-        retval.trn.shift = 0;
-        retval.red.mask = 0x1f;  /* 5 bits */
-        retval.grn.mask = 0x1f;  /* 5 bits */
-        retval.blu.mask = 0x1f;  /* 5 bits */
-        retval.trn.mask = 0;
-        break;
-    case 24:
-    case 32:
-        retval.conventionalBgr = TRUE;
-        retval.red.shift = 16;
-        retval.grn.shift = 8;
-        retval.blu.shift = 0;
-        retval.trn.shift = 0;
-        retval.red.mask = 0xff;  /* 8 bits */
-        retval.grn.mask = 0xff;  /* 8 bits */
-        retval.blu.mask = 0xff;  /* 8 bits */
-        retval.trn.mask = 0;
-        break;
-    default:
-        /* colormapped - masks are undefined */
-        break;
-    }
-
-    return retval;
-}
-
+static void
+readV4InfoHeaderExtension(FILE *                 const ifP,
+                          struct bmpInfoHeader * const headerP,
+                          unsigned int *         const bytesReadP) {
 
+    unsigned long redMsk, grnMsk, bluMsk, trnMsk;
 
-static void
-readV4InfoHeaderExtension(FILE *                 const ifP, 
-                          struct bmpInfoHeader * const headerP) {
+    redMsk = GetLong(ifP);
+    grnMsk = GetLong(ifP);
+    bluMsk = GetLong(ifP);
+    trnMsk = GetLong(ifP);
 
     if (headerP->bitFields) {
         /* A document from Microsoft says on Windows 95 there is no
@@ -565,21 +602,40 @@ readV4InfoHeaderExtension(FILE *                 const ifP,
            (5,5,5) or (5,6,5) for 16 bit and (8,8,8) for 32 bit.
            It calls these RGB555, RGB565, RGB888.
         */
-        headerP->pixelformat.red = bitPositionFromMask(GetLong(ifP));
-        headerP->pixelformat.grn = bitPositionFromMask(GetLong(ifP));
-        headerP->pixelformat.blu = bitPositionFromMask(GetLong(ifP));
-        headerP->pixelformat.trn = bitPositionFromMask(GetLong(ifP));
+        headerP->pixelformat.red = bitPositionFromMask(redMsk);
+        headerP->pixelformat.grn = bitPositionFromMask(grnMsk);
+        headerP->pixelformat.blu = bitPositionFromMask(bluMsk);
+        headerP->pixelformat.trn = bitPositionFromMask(trnMsk);
 
         computeConventionalBgr(&headerP->pixelformat, headerP->cBitCount);
     } else
         headerP->pixelformat = defaultPixelformat(headerP->cBitCount);
 
     GetLong(ifP);  /* Color space */
-    GetCieXyzTriple(ifP);  /* Endpoints */
+
+    headerP->endPoints = GetCieXyzTriple(ifP);  /* 36 bytes */
+
     GetLong(ifP);  /* GammaRed */
     GetLong(ifP);  /* GammaGreen */
     GetLong(ifP);  /* GammaBlue */
-} 
+
+    *bytesReadP = 68;
+}
+
+
+
+static void
+readV5InfoHeaderExtension(FILE *                 const ifP,
+                          struct bmpInfoHeader * const headerP,
+                          unsigned int *         const bytesReadP) {
+
+    GetLong(ifP);  /* Intent */
+    GetLong(ifP);  /* ProfileData */
+    GetLong(ifP);  /* ProfileSize */
+    GetLong(ifP);  /* Reserved */
+
+    *bytesReadP = 16;
+}
 
 
 
@@ -593,117 +649,157 @@ defaultV4InfoHeaderExtension(struct bmpInfoHeader * const headerP) {
 
 
 static void
-readWindowsInfoHeader(FILE *                 const ifP, 
-                      unsigned int           const cInfoHeaderSize,
-                      struct bmpInfoHeader * const headerP) {
-
+readWindowsInfoHeaderRest(FILE *                 const ifP,
+                          unsigned int           const cInfoHeaderSize,
+                          struct bmpInfoHeader * const headerP) {
+/*----------------------------------------------------------------------------
+   Read the rest of the info header, after the length field, of a Windows BMP
+   from *ifP.
+
+   'cInfoHeaderSize' is the size of the info header, not counting its size
+   field.  Note that besides telling us how much data to read, this also
+   implies which of the three major formats the data is in.
+
+   Add the information from it to *headerP, in particular these members:
+
+     cols
+     rows
+     rowOrder
+     cPlanes
+     cBitCount
+     bitFields
+     compression
+     imageSize
+     cmapSize
+     pixelformat
+     endPoints
+-----------------------------------------------------------------------------*/
     /* There are 3 major formats of Windows
        BMP, identified by the 3 info header lengths.  The original
        one is 40 bytes.  The "V4 header" is 108 bytes and was
        new with Windows 95 and NT 4.0.  The "V5 header" is 124 bytes
        and was new with Windows 98 and Windows 2000.
     */
+    unsigned int bytesRead;
+
     readWindowsBasic40ByteInfoHeader(ifP, headerP);
 
-    if (cInfoHeaderSize >= 108) 
-        readV4InfoHeaderExtension(ifP, headerP);
-    else 
+    bytesRead = 40;
+
+    if (cInfoHeaderSize >= BMP_HDRLEN_WIN_V4) {
+        unsigned int v4BytesRead;
+        readV4InfoHeaderExtension(ifP, headerP, &v4BytesRead);
+        bytesRead += v4BytesRead;
+
+        assert(bytesRead == BMP_HDRLEN_WIN_V4);
+    } else
         defaultV4InfoHeaderExtension(headerP);
 
-    if (cInfoHeaderSize >= 124) {
-        /* Read off the V5 info header extension. */
-        GetLong(ifP);  /* Intent */
-        GetLong(ifP);  /* ProfileData */
-        GetLong(ifP);  /* ProfileSize */
-        GetLong(ifP);  /* Reserved */
+    if (cInfoHeaderSize >= BMP_HDRLEN_WIN_V5) {
+        unsigned int v5BytesRead;
+        readV5InfoHeaderExtension(ifP, headerP, &v5BytesRead);
+        bytesRead += v5BytesRead;
+        assert(bytesRead == BMP_HDRLEN_WIN_V5);
     }
 
-    pm_message("Windows BMP, %dx%dx%d",
-               headerP->cols, headerP->rows, headerP->cBitCount);
+    for (; bytesRead < cInfoHeaderSize;) {
+        GetByte(ifP);
+        ++bytesRead;
+    }
+
+    assert(bytesRead == cInfoHeaderSize);
 }
 
 
 
 static void
-bmpReadinfoheader(FILE *                 const ifP, 
+bmpReadinfoheader(FILE *                 const ifP,
                   unsigned int *         const bytesReadP,
-                  struct bmpInfoHeader * const headerP) {
+                  struct bmpInfoHeader * const headerP,
+                  const char **          const errorP) {
 
     unsigned int const cInfoHeaderSize = GetLong(ifP);
 
-    switch (cInfoHeaderSize) {
-    case 12:
-        readOs2InfoHeader(ifP, headerP);
-        break;
-    case 40: 
-        readWindowsInfoHeader(ifP, cInfoHeaderSize, headerP);
-        break;
-    case 108:
-        pm_error("This is a Version 4 Windows BMP; "
-                 "this program knows only Version 1");
-        break;
-    case 124:
-        pm_error("This is a Version 5 Windows BMP; "
-                 "this program knows only Version 1");
-        break;
-    default:
-        pm_error("%s: unknown Info Header size: %u bytes", 
-                 ifname, cInfoHeaderSize);
-        break;
+    const char * error;
+
+    BMPdetermineclass(cInfoHeaderSize, &headerP->class, &error);
+
+    if (error) {
+        pm_asprintf(errorP, "Cannot determine the class of BMP from the "
+                    "info header size %u.  %s", cInfoHeaderSize, error);
+        pm_strfree(error);
+    } else {
+        switch (headerP->class) {
+        case BMP_C_WIN_V1:
+        case BMP_C_WIN_V2:
+        case BMP_C_WIN_V3:
+        case BMP_C_WIN_V4:
+        case BMP_C_WIN_V5:
+            readWindowsInfoHeaderRest(ifP, cInfoHeaderSize, headerP);
+            break;
+        case BMP_C_OS2_1x:
+        case BMP_C_OS2_2x:
+            readOs2InfoHeaderRest(ifP, headerP);
+            break;
+        }
+        *errorP = NULL;
+        *bytesReadP = cInfoHeaderSize;
     }
-    *bytesReadP = cInfoHeaderSize;
+    /* Part of our anti-arithmetic overflow strategy is to make sure height
+       and width always fit in 16 bits, so they can be multiplied together.
+       This shouldn't be a problem, since they come from 16 bit fields in
+       the BMP info header.
+    */
+    assert(headerP->cols < (1<<16));
+    assert(headerP->rows < (1<<16));
 }
 
 
 
 static void
-bmpReadColormap(FILE *         const ifP, 
-                int            const class, 
-                xel **         const colormapP, 
-                unsigned int   const cmapsize,
+bmpReadColormap(FILE *         const ifP,
+                enum bmpClass  const class,
+                xel **         const colormapP,
+                unsigned int   const cmapSize,
                 unsigned int * const bytesReadP) {
 /*----------------------------------------------------------------------------
    Read the color map from the present position in the input BMP file
    *ifP.
 
-   The map has 'cmapsize' entries in it.  cmapsize == 0 means there is
+   The map has 'cmapSize' entries in it.  cmapSize == 0 means there is
    no color map.
 
    We return a color map as *colormapP.  If there is no color map in the
    BMP, this is just an arbitrary color map.
- 
+
    'class' is the class of BMP image - Windows or OS/2.
 -----------------------------------------------------------------------------*/
+    xel * const colormap = pnm_allocrow(MAX(1, cmapSize));
 
     unsigned int i;
-
-    xel * colormap;
     unsigned int bytesRead;
 
-    colormap = pnm_allocrow(MAX(1,cmapsize));
-    
-    bytesRead = 0;  /* initial value */
-
-    for (i = 0; i < cmapsize; ++i) {
+    for (i = 0, bytesRead = 0; i < cmapSize; ++i) {
         /* There is a document that says the bytes are ordered R,G,B,Z,
            but in practice it appears to be the following instead:
         */
-        unsigned int r, g, b;
-        
-        b = GetByte(ifP);
-        g = GetByte(ifP);
-        r = GetByte(ifP);
+        unsigned int const b = GetByte(ifP);
+        unsigned int const g = GetByte(ifP);
+        unsigned int const r = GetByte(ifP);
+
+        unsigned int j;
 
         PNM_ASSIGN(colormap[i], r, g, b);
 
         bytesRead += 3;
 
-        if (class == C_WIN) {
+        for (j = 3; j < BMPlenrgb(class); ++j) {
             GetByte(ifP);
             bytesRead += 1;
         }
     }
-    *colormapP = colormap;
+
+    *colormapP  = colormap;
     *bytesReadP = bytesRead;
 }
 
@@ -718,13 +814,13 @@ extractBitFields(unsigned int       const rasterval,
                  pixval *           const bP,
                  pixval *           const aP) {
 
-    unsigned int const rbits = 
+    unsigned int const rbits =
         (rasterval >> pixelformat.red.shift) & pixelformat.red.mask;
-    unsigned int const gbits = 
+    unsigned int const gbits =
         (rasterval >> pixelformat.grn.shift) & pixelformat.grn.mask;
-    unsigned int const bbits = 
+    unsigned int const bbits =
         (rasterval >> pixelformat.blu.shift) & pixelformat.blu.mask;
-    unsigned int const abits = 
+    unsigned int const abits =
         (rasterval >> pixelformat.trn.shift) & pixelformat.trn.mask;
 
     *rP = pixelformat.red.mask > 0 ?
@@ -735,7 +831,7 @@ extractBitFields(unsigned int       const rasterval,
         (unsigned int) bbits * maxval / pixelformat.blu.mask : 0;
     *aP = pixelformat.trn.mask > 0 ?
         (unsigned int) abits * maxval / pixelformat.trn.mask : 0;
-}        
+}
 
 
 
@@ -758,7 +854,7 @@ convertRow16(unsigned char      const bmprow[],
         extractBitFields(rasterval, pixelformat, 255, &r, &g, &b, &a);
 
         PNM_ASSIGN(xelrow[col], r, g, b);
-        
+
         cursor += 2;
     }
 }
@@ -770,7 +866,7 @@ convertRow24(unsigned char      const bmprow[],
              xel                      xelrow[],
              int                const cols,
              struct pixelformat const pixelformat) {
-    
+
     /* It's truecolor */
     /* There is a document that gives a much different format for
        24 bit BMPs.  But this seems to be the de facto standard, and is,
@@ -780,7 +876,7 @@ convertRow24(unsigned char      const bmprow[],
 
     unsigned int col;
     unsigned int cursor;
-    
+
     cursor = 0;
     for (col = 0; col < cols; ++col) {
         pixval r, g, b, a;
@@ -791,17 +887,17 @@ convertRow24(unsigned char      const bmprow[],
             b = bmprow[cursor+0];
             a = 0;
         } else {
-            unsigned int const rasterval = 
+            unsigned int const rasterval =
                 (bmprow[cursor+0] << 16) +
                 (bmprow[cursor+1] << 8) +
                 (bmprow[cursor+2] << 0);
-            
+
             extractBitFields(rasterval, pixelformat, 255, &r, &g, &b, &a);
         }
         PNM_ASSIGN(xelrow[col], r, g, b);
         cursor += 3;
     }
-} 
+}
 
 
 
@@ -810,7 +906,7 @@ convertRow32(unsigned char      const bmprow[],
              xel                      xelrow[],
              int                const cols,
              struct pixelformat const pixelformat) {
-    
+
     /* It's truecolor */
 
     unsigned int col;
@@ -826,40 +922,40 @@ convertRow32(unsigned char      const bmprow[],
             b = bmprow[cursor+0];
             a = 0;
         } else {
-            unsigned int const rasterval = 
+            unsigned int const rasterval =
                 (bmprow[cursor+0] << 24) +
                 (bmprow[cursor+1] << 16) +
                 (bmprow[cursor+2] << 8) +
                 (bmprow[cursor+3] << 0);
-            
+
             extractBitFields(rasterval, pixelformat, 255, &r, &g, &b, &a);
         }
 
-        PNM_ASSIGN(xelrow[col], 
+        PNM_ASSIGN(xelrow[col],
                    bmprow[cursor+2], bmprow[cursor+1], bmprow[cursor+0]);
         cursor += 4;
     }
-} 
+}
 
 
 static void
 validateIndex(unsigned int const index,
-	      unsigned int const cmapsize ) {
+              unsigned int const cmapSize ) {
 
-    if (index >= cmapsize)
+    if (index >= cmapSize)
         pm_error("Error: invalid index to color palette.");
 }
 
 
 
 static void
-convertRow(unsigned char      const bmprow[], 
+convertRow(unsigned char      const bmprow[],
            xel                      xelrow[],
-           int                const cols, 
-           unsigned int       const cBitCount, 
+           int                const cols,
+           unsigned int       const cBitCount,
            struct pixelformat const pixelformat,
            xel                const colormap[],
-           unsigned int       const cmapsize
+           unsigned int       const cmapSize
            ) {
 /*----------------------------------------------------------------------------
    Convert a row in raw BMP raster format bmprow[] to a row of xels xelrow[].
@@ -871,20 +967,20 @@ convertRow(unsigned char      const bmprow[],
    If the image is colormapped, colormap[] is the colormap
    (colormap[i] is the color with color index i).
 -----------------------------------------------------------------------------*/
-    if (cBitCount == 24) 
+    if (cBitCount == 24)
         convertRow24(bmprow, xelrow, cols, pixelformat);
-    else if (cBitCount == 16) 
+    else if (cBitCount == 16)
         convertRow16(bmprow, xelrow, cols, pixelformat);
-    else if (cBitCount == 32) 
+    else if (cBitCount == 32)
         convertRow32(bmprow, xelrow, cols, pixelformat);
-    else if (cBitCount == 8) {            
+    else if (cBitCount == 8) {
         /* It's a whole byte colormap index */
-        unsigned int col; 
+        unsigned int col;
         for (col = 0; col < cols; ++col) {
             unsigned int const index = bmprow[col];
-            validateIndex(index, cmapsize);
+            validateIndex(index, cmapSize);
             xelrow[col] = colormap[index];
-	}
+    }
     } else if (cBitCount == 1 || cBitCount == 2 || cBitCount == 4) {
         /* It's a bit field color index */
         unsigned char const mask = ( 1 << cBitCount ) - 1;
@@ -894,9 +990,9 @@ convertRow(unsigned char      const bmprow[],
         for (col = 0; col < cols; ++col) {
             unsigned int const cursor = (col*cBitCount)/8;
             unsigned int const shift = 8 - ((col*cBitCount) % 8) - cBitCount;
-            unsigned int const index = 
+            unsigned int const index =
                 (bmprow[cursor] & (mask << shift)) >> shift;
-            validateIndex(index, cmapsize);
+            validateIndex(index, cmapSize);
             xelrow[col] = colormap[index];
         }
     } else {
@@ -911,8 +1007,8 @@ static unsigned char **
 allocBmpRaster(unsigned int const rows,
                unsigned int const bytesPerRow) {
 
-    unsigned int const storageSize = 
-        rows * sizeof(unsigned char *) + rows * bytesPerRow;        
+    unsigned int const storageSize =
+        rows * sizeof(unsigned char *) + rows * bytesPerRow;
     unsigned char ** bmpRaster;
     unsigned int row;
     unsigned char * startOfRows;
@@ -932,7 +1028,7 @@ allocBmpRaster(unsigned int const rows,
 
     startOfRows = (unsigned char *)(bmpRaster + rows);
 
-    for (row = 0; row < rows; ++row) 
+    for (row = 0; row < rows; ++row)
         bmpRaster[row] = startOfRows + row * bytesPerRow;
 
     return bmpRaster;
@@ -950,47 +1046,51 @@ readrow(FILE *           const ifP,
     size_t bytesRead;
 
     assert(bytesPerRow > 0);
-    
+
     bytesRead = fread(bmpRaster[row], 1, bytesPerRow, ifP);
 
     if (bytesRead < bytesPerRow) {
         if (feof(ifP))
             pm_error("End of file reading row %u of BMP raster.", row);
-        else 
+        else
             pm_error("Error reading BMP raster.  Errno=%d (%s)",
                      errno, strerror(errno));
     }
     *bytesReadP += bytesRead;
 }
- 
+
 
 
 static void
-nibbleAlign(unsigned char * const ptr,
-            unsigned int    const nibbles){
+nybbleAlign(unsigned char * const bytes,
+            unsigned int    const nybbleCt){
 /*----------------------------------------------------------------------------
-  Shift data pointed by ptr one half byte toward the MSB (to the left).
- 
+  Shift the 'nybbleCt' nybbles of bytes[], after the first byte, one nybble
+  toward the left, with the first of those nybble shifting into the right half
+  of the first byte.  Leave the left half of the first byte alone.
+
   Example:
- 
-  (Numbers in hex, 8 nibbles)
-            5F 13 7E 89 A1
+
+  (Numbers in hex, 8 nybbles)
+            5? 13 7E 89 A1
    becomes  51 37 E8 9A 10
 -----------------------------------------------------------------------------*/
-    unsigned int const fullByteCount = (nibbles-1) / 2;
+    unsigned int const fullByteCt = (nybbleCt + 1) / 2;
     unsigned int i;
-              
-    ptr[0] = ptr[0] & ptr[1] >> 4;
-                                    
-    for (i = 0; i < fullByteCount; ++i)
-        ptr[i+1] = ptr[i+1] << 4 & ptr[i+2] >> 4;
-    
-    if (nibbles % 2 == 1)   /* if there is a final odd nibble */
-        ptr[fullByteCount+1] <<= 4; /* shift it a half byte */
+
+    bytes[0] >>= 4;
+
+    for (i = 0; i < fullByteCt; ++i)
+        bytes[i] = bytes[i] << 4 | bytes[i+1] >> 4;
+
+    if (nybbleCt % 2 == 0) {
+        /* There is a final right nybble.  Shift it. */
+        bytes[fullByteCt] <<= 4;
+    }
 }
 
 
-           
+
 enum rleStatus { ABS_MODE, ENC_MODE, END_OF_ROW, END_OF_BMP, DELTA };
 
 static enum rleStatus
@@ -1000,9 +1100,9 @@ readRLEcode(FILE *          const ifP,
 
     unsigned short s;
     enum rleStatus retval;
-    
+
     s = GetBigShort(ifP);
-    
+
     if      (s == 0) retval = END_OF_ROW;
     else if (s == 1) retval = END_OF_BMP;
     else if (s == 2) retval = DELTA;
@@ -1034,16 +1134,18 @@ readrowRLE(FILE *           const ifP,
     bool const rle4 = (compression == BMPCOMP_RLE4);
     int  const pixelsPerRowMargin = rle4 ? cols % 2 : 0;
 
-    char const err_decode[] = 
+    char const err_decode[] =
         "Error while decoding compressed BMP image.  "
-        "%s.  Row: %u  Pixel: %u" ; 
-     
+        "%s.  Row: %u  Pixel: %u" ;
+
     unsigned int totalBytesRead;
     unsigned int pixelsRead;
 
-    /* There are RLE4 images with rows coded up the byte boundary, resulting
-       in each row one pixel larger than the column length stated in the
-       BMP info header (header.cols) when the column length is odd.
+    /* There are RLE4 images with rows coded up to the byte boundary,
+       resulting in each row one pixel larger than the column length
+       stated in the BMP info header (header.cols) when the column length
+       is odd.
+
        pixelsPerRowMargin is a "wart" to provide for this case.
     */
 
@@ -1052,7 +1154,7 @@ readrowRLE(FILE *           const ifP,
 
     while (true) {
         unsigned int n;
-            /* decompressed bytes already read; current write point */ 
+            /* decompressed bytes already read; current write point */
         unsigned int cnt;
         unsigned char code;
 
@@ -1061,23 +1163,23 @@ readrowRLE(FILE *           const ifP,
         switch (readRLEcode(ifP, &cnt, &code)) {
         case ENC_MODE: {
             unsigned int const byteCnt = rle4 ? (cnt + 1) /2 : cnt;
-            unsigned int i; 
+            unsigned int i;
 
             if (pixelsRead + cnt > cols + pixelsPerRowMargin)
                 pm_error(err_decode,  "Too many pixels in encoded mode",
-                         row, pixelsRead ); 
-                 
+                         row, pixelsRead );
+
             for (i = 0; i < byteCnt; ++i)
                 bmpRaster[row][n+i] = code;
-                 
+
             if (rle4 && pixelsRead % 2 == 1)
                 /* previous read ended odd */
-                nibbleAlign(&bmpRaster[row][n-1], cnt); 
-            
+                nybbleAlign(&bmpRaster[row][n-1], cnt);
+
             pixelsRead += cnt;
             totalBytesRead += 2;
         } break;
-        
+
         case ABS_MODE: {
             unsigned int cmpBytesRead; /* compressed bytes read */
             /* align read-end to 16 bit boundary */
@@ -1086,43 +1188,43 @@ readrowRLE(FILE *           const ifP,
 
             if (pixelsRead + cnt > cols + pixelsPerRowMargin)
                 pm_error(err_decode,  "Too many pixels in absolute mode",
-                         row, pixelsRead); 
+                         row, pixelsRead);
 
-            cmpBytesRead = fread(&bmpRaster[row][n], 
+            cmpBytesRead = fread(&bmpRaster[row][n],
                                  sizeof(char), bytesToRead, ifP);
 
             if (cmpBytesRead < bytesToRead) {
                 if (feof(ifP))
                     pm_error("End of file reading row %u "
                              "of compressed BMP raster.", row);
-                else 
+                else
                     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); 
-    
+                nybbleAlign(&bmpRaster[row][n-1], cnt);
+
             pixelsRead += cnt;
             totalBytesRead += cmpBytesRead + 2;
         } break;
-            
+
         case END_OF_ROW: {
             if (cols == pixelsRead ||
                 cols + pixelsPerRowMargin == pixelsRead) {
                 if (!lastrow) {
                     *bytesReadP += totalBytesRead + 2;
                     return;
-                } else if (readRLEcode(ifP, NULL, NULL) == END_OF_BMP) { 
+                } else if (readRLEcode(ifP, NULL, NULL) == END_OF_BMP) {
                     *bytesReadP += totalBytesRead +4;
                     return;
                 } else
                     /* lastrow and END_OF_BITMAP not detected */
                     pm_error(err_decode,  "End of bitmap not marked",
-                             row, pixelsRead ); 
+                             row, pixelsRead );
             } else
                 pm_error(err_decode,  "Premature end of row",
                          row, pixelsRead);
-        } break;  
+        } break;
 
         case END_OF_BMP: {
             if (lastrow && (cols == pixelsRead ||
@@ -1132,15 +1234,27 @@ readrowRLE(FILE *           const ifP,
             } else
                 pm_error(err_decode,  "Premature end of bitmap",
                          row, pixelsRead );
+            /* Windows programs do not reject premature end of bitmap.
+               Rather, they set the remaining pixels of the raster to
+               an arbitrary value.  In practice, images with incomplete
+               bitmaps are rare.
+            */
         } break;
 
         case DELTA: {
+            /* Delta means "move the point (col,row) by the amount given
+               in the next two bytes."  Like premature end of bitmap, the
+               official specs do not specify what value the skipped pixels
+               should be set to.  Judging from Windows utilities, there is
+               no consensus within Microsoft either.
+            */
             pm_error(err_decode,
                      "Delta code in compressed BMP image.  "
-                     "This program does not process deltas.",
+                     "This program does not process deltas",
                      row, pixelsRead);
+
         } break;
-         
+
         default:
             pm_error("Internal error processing RLE code in row %u", row);
         }
@@ -1150,13 +1264,13 @@ readrowRLE(FILE *           const ifP,
 
 
 static void
-bmpReadraster(FILE *            const ifP, 
-              unsigned int      const cols, 
-              unsigned int      const rows, 
+bmpReadraster(FILE *            const ifP,
+              unsigned int      const cols,
+              unsigned int      const rows,
               enum rowOrder     const rowOrder,
-              unsigned int      const cBitCount, 
+              unsigned int      const cBitCount,
               BMPCompType       const compression,
-              unsigned char *** const bmpRasterP, 
+              unsigned char *** const bmpRasterP,
               unsigned int *    const bytesReadP) {
 /*----------------------------------------------------------------------------
    Read the raster from the BMP file on *ifP (which is positioned to the
@@ -1179,37 +1293,40 @@ bmpReadraster(FILE *            const ifP,
         */
     unsigned char ** bmpRaster;
 
+    assert(cols < (1<<16));
+    assert(bytesPerRow < (1<<16));
+
     bmpRaster = allocBmpRaster(rows, bytesPerRow);
 
     *bytesReadP = 0;
 
-    /* row order BOTTOMUP is by far the most common case - the bottom 
+    /* row order BOTTOMUP is by far the most common case - the bottom
        line is first in the file, the top line last.
-       
+
        We have never actually seen TOPDOWN, except in a Microsoft spec
     */
-    
+
     switch(compression){
     case BMPCOMP_RGB:
     case BMPCOMP_BITFIELDS: {
         unsigned int i;
         for (i = 0; i < rows; ++i)
-            readrow(ifP, rowOrder == TOPDOWN ? i : rows - i - 1, 
+            readrow(ifP, rowOrder == TOPDOWN ? i : rows - i - 1,
                     bytesPerRow, bmpRaster, bytesReadP);
     } break;
-    case BMPCOMP_RLE4: 
+    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, 
+            readrowRLE(ifP, rowOrder == TOPDOWN ? i : rows - i - 1,
                        cols, FALSE, compression, bmpRaster, bytesReadP);
         }
         /* Read last row */
-        readrowRLE(ifP, rowOrder == TOPDOWN ? i : rows - i - 1, 
+        readrowRLE(ifP, rowOrder == TOPDOWN ? i : rows - i - 1,
                    cols, TRUE,  compression, bmpRaster, bytesReadP);
-    } break;             
+    } break;
     case BMPCOMP_JPEG:
         pm_error("BMP file uses JPEG compression.  We don't know how to "
                  "interpret that.");
@@ -1226,36 +1343,51 @@ bmpReadraster(FILE *            const ifP,
 
 static void
 reportHeader(struct bmpInfoHeader const header,
-             unsigned int         const offBits) {
-             
-    pm_message("BMP image header says:");
-    pm_message("  Class of BMP: %s", 
-               header.class == C_WIN ? "Windows" : 
-               header.class == C_OS2 ? "OS/2" :
-               "???");
-    pm_message("  Width: %d pixels", header.cols);
-    pm_message("  Height: %d pixels", header.rows);
-    pm_message("  Depth: %d planes", header.cPlanes);
-    pm_message("  Row order: %s", 
-               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", BMPCompTypeName(header.compression));
-    pm_message("  Colors in color map: %u", header.cmapsize);
-}        
+             unsigned int         const offBits,
+             bool                 const verbose) {
+
+    if (verbose) {
+        pm_message("BMP image header says:");
+        pm_message("  Class of BMP: %s", BMPClassName(header.class));
+        pm_message("  Width: %d pixels", header.cols);
+        pm_message("  Height: %d pixels", header.rows);
+        pm_message("  Depth: %d planes", header.cPlanes);
+        pm_message("  Row order: %s",
+                   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", BMPCompTypeName(header.compression));
+        pm_message("  Colors in color map: %u", header.cmapSize);
+    } else {
+        pm_message("%s BMP, %ux%ux%u",
+                   BMPClassName(header.class),
+                   header.cols,
+                   header.rows,
+                   header.cBitCount);
+    }
+}
+
+
+
+static void
+validateCPlanes(unsigned short const cPlanes) {
+
+    if (cPlanes != 1)
+        pm_error("Error: invalid planes value in BMP header.  Must be 1");
+}
 
 
 
 static void
 analyzeColors(xel          const colormap[],
-              unsigned int const cmapsize,
+              unsigned int const cmapSize,
               xelval       const maxval,
               bool *       const grayPresentP,
               bool *       const colorPresentP) {
-    
-    if (cmapsize == 0) {
+
+    if (cmapSize == 0) {
         /* No colormap, and we're not about to search the entire raster,
-           so we just assume it's full color 
+           so we just assume it's full color
         */
         *colorPresentP = TRUE;
         *grayPresentP = TRUE;
@@ -1264,7 +1396,7 @@ analyzeColors(xel          const colormap[],
 
         *colorPresentP = FALSE;  /* initial assumption */
         *grayPresentP = FALSE;   /* initial assumption */
-        for (i = 0; i < cmapsize; ++i) {
+        for (i = 0; i < cmapSize; ++i) {
             if (PPM_ISGRAY(colormap[i])) {
                 if (PPM_GETR(colormap[i]) != 0 &&
                     PPM_GETR(colormap[i]) != maxval)
@@ -1281,8 +1413,8 @@ static void
 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), "
@@ -1290,8 +1422,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));
     }
 }
 
@@ -1305,8 +1437,8 @@ readColorMap(FILE *               const ifP,
 
     unsigned int bytesRead;
 
-    bmpReadColormap(ifP, bmpHeader.class, 
-                    colorMapP, bmpHeader.cmapsize, &bytesRead);
+    bmpReadColormap(ifP, bmpHeader.class,
+                    colorMapP, bmpHeader.cmapSize, &bytesRead);
 
     *posP += bytesRead;
 }
@@ -1316,7 +1448,7 @@ readColorMap(FILE *               const ifP,
 static void
 readRaster(FILE *               const ifP,
            struct bmpInfoHeader const bmpHeader,
-           unsigned char ***    const bmpRasterP, 
+           unsigned char ***    const bmpRasterP,
            unsigned int *       const posP) {
 
     unsigned int bytesRead;
@@ -1350,16 +1482,16 @@ isValidBmpBpp(unsigned int const cBitCount) {
 
 
 static void
-readBmp(FILE *               const ifP, 
-        unsigned char ***    const bmpRasterP, 
-        int *                const colsP, 
-        int *                const rowsP,
-        bool *               const grayPresentP, 
+readBmp(FILE *               const ifP,
+        unsigned char ***    const bmpRasterP,
+        unsigned int *       const colsP,
+        unsigned int *       const rowsP,
+        bool *               const grayPresentP,
         bool *               const colorPresentP,
-        unsigned int *       const cBitCountP, 
+        unsigned int *       const cBitCountP,
         struct pixelformat * const pixelformatP,
         xel **               const colormapP,
-        unsigned int *       const cmapsizeP,
+        unsigned int *       const cmapSizeP,
         bool                 const verbose) {
 
     xel * colormap;  /* malloc'ed */
@@ -1367,33 +1499,39 @@ readBmp(FILE *               const ifP,
         /* Current byte position in the BMP file */
 
     /* The following are all information from the BMP headers */
-    
+
     unsigned int offBits;
         /* Byte offset into file of raster */
     struct bmpInfoHeader bmpHeader;
 
     pos = 0;  /* Starting at the beginning ... */
-    { 
+    {
         unsigned int bytesRead;
         bmpReadfileheader(ifP, &bytesRead, &offBits);
         pos += bytesRead;
     }
     {
         unsigned int bytesRead;
-        bmpReadinfoheader(ifP, &bytesRead, &bmpHeader);
+        const char * error;
+        bmpReadinfoheader(ifP, &bytesRead, &bmpHeader, &error);
+        if (error)
+            pm_error("Failed to read the BMP info header.  Image may "
+                     "not be a valid BMP.  %s", error);
+
         if (verbose)
             pm_message("Read %u bytes of header", bytesRead);
         pos += bytesRead;
     }
 
-    if (verbose) 
-        reportHeader(bmpHeader, offBits);
+    reportHeader(bmpHeader, offBits, verbose);
+
+    validateCPlanes(bmpHeader.cPlanes);
 
     warnIfOffBitsWrong(bmpHeader, offBits);
 
     readColorMap(ifP, bmpHeader, &colormap, &pos);
 
-    analyzeColors(colormap, bmpHeader.cmapsize, bmpMaxval, 
+    analyzeColors(colormap, bmpHeader.cmapSize, bmpMaxval,
                   grayPresentP, colorPresentP);
 
     readOffBytes(ifP, offBits - pos);
@@ -1404,7 +1542,7 @@ readBmp(FILE *               const ifP,
 
     if (fgetc(ifP) != EOF)
         pm_message("warning: some image data remains unread.");
-    
+
     if (!isValidBmpBpp(bmpHeader.cBitCount))
         pm_error("Invalid BMP image: 'cBitCount' field of header "
                  "(number of bits for each pixel in raster) is %u",
@@ -1416,23 +1554,23 @@ readBmp(FILE *               const ifP,
     *rowsP        = bmpHeader.rows;
     *pixelformatP = bmpHeader.pixelformat;
     *colormapP    = colormap;
-    *cmapsizeP    = bmpHeader.cmapsize;
+    *cmapSizeP    = bmpHeader.cmapSize;
 }
 
 
 
 static void
 writeRasterGen(unsigned char **   const bmpRaster,
-               int                const cols, 
-               int                const rows, 
+               unsigned int       const cols,
+               unsigned int       const rows,
                int                const format,
-               unsigned int       const cBitCount, 
+               unsigned int       const cBitCount,
                struct pixelformat const pixelformat,
                xel                const colormap[],
-               unsigned int       const cmapsize) {
+               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
@@ -1440,7 +1578,7 @@ writeRasterGen(unsigned char **   const bmpRaster,
 
   If the image is colormapped, colormap[] is the colormap
   (colormap[i] is the color with color index i).
-  
+
   writeRasterPbm() is faster for a PBM image.
 -----------------------------------------------------------------------------*/
     xel * xelrow;
@@ -1450,7 +1588,7 @@ writeRasterGen(unsigned char **   const bmpRaster,
 
     for (row = 0; row < rows; ++row) {
         convertRow(bmpRaster[row], xelrow, cols, cBitCount, pixelformat,
-                   colormap, cmapsize);
+                   colormap, cmapSize);
         pnm_writepnmrow(stdout, xelrow, cols, bmpMaxval, format, FALSE);
     }
     pnm_freerow(xelrow);
@@ -1460,12 +1598,12 @@ writeRasterGen(unsigned char **   const bmpRaster,
 
 static void
 writeRasterPbm(unsigned char ** const bmpRaster,
-               int              const cols, 
-               int              const rows, 
+               unsigned int     const cols,
+               unsigned 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
@@ -1475,28 +1613,28 @@ writeRasterPbm(unsigned char ** const bmpRaster,
   (colormap[i] is the color with color index i).  We cannot handle the
   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.
 -----------------------------------------------------------------------------*/
-    unsigned int const colChars = pbm_packed_bytes(cols);
-    
-    int row;
+    unsigned int const colCharCt = pbm_packed_bytes(cols);
+
+    unsigned int row;
     enum colorFormat {BlackWhite, WhiteBlack};
     enum colorFormat colorformat;
-                  
+
     if (PPM_GETR(colormap[0]) > 0)
         colorformat = WhiteBlack;
-    else                  
+    else
         colorformat = BlackWhite;
-        
-    for (row=0; row < rows; ++row){
-        unsigned char * const bitrow = bmpRaster[row]; 
+
+    for (row = 0; row < rows; ++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 */ 
-        }   
+            for (i = 0; i < colCharCt; ++i)
+                bitrow[i] = ~bitrow[i]; /* flip all pixels */
+        }
 
         pbm_cleanrowend_packed(bitrow, cols);
         pbm_writepbmrow_packed(stdout, bitrow, cols, FALSE);
@@ -1517,8 +1655,8 @@ main(int argc, const char ** argv) {
            black and white and whether it has colors other than black, white,
            and gray.
         */
-    int cols, rows;
-    unsigned char **bmpRaster;
+    unsigned int cols, rows;
+    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
@@ -1532,13 +1670,12 @@ main(int argc, const char ** argv) {
         /* Malloc'ed colormap (palette) from the BMP.  Contents of map
            undefined if not a colormapped BMP.
          */
-    unsigned int cmapsize;
-        /* Number of colormap entries.  Described in the BMP header.
-           Note that a file may be 8 bits per pixel but have less than
-           256 colors.  In the 1 bit per pixel case, there should be
-           2 entries according to the official specification, but we
-           allow files with just 1.
-	 */
+    unsigned int cmapSize;
+        /* Number of colormap entries.  From BMP header.  Note that a file may
+           be 8 bits per pixel but have fewer than 256 colors.  In the 1 bit
+           per pixel case, there should be 2 entries according to the official
+           specification, but we allow files with just 1.
+        */
 
     pm_proginit(&argc, argv);
 
@@ -1547,11 +1684,11 @@ main(int argc, const char ** argv) {
     ifP = pm_openr(cmdline.inputFileName);
     if (streq(cmdline.inputFileName, "-"))
         ifname = "Standard Input";
-    else 
+    else
         ifname = cmdline.inputFileName;
 
-    readBmp(ifP, &bmpRaster, &cols, &rows, &grayPresent, &colorPresent, 
-            &cBitCount, &pixelformat, &colormap, &cmapsize,
+    readBmp(ifP, &bmpRaster, &cols, &rows, &grayPresent, &colorPresent,
+            &cBitCount, &pixelformat, &colormap, &cmapSize,
             cmdline.verbose);
     pm_close(ifP);
 
@@ -1565,14 +1702,14 @@ main(int argc, const char ** argv) {
         outputType = PBM_TYPE;
         pm_message("WRITING PBM IMAGE");
     }
-    
+
     if (outputType == PBM_TYPE  && cBitCount == 1){
         pbm_writepbminit(stdout, cols, rows, FALSE);
         writeRasterPbm(bmpRaster, cols, rows, colormap);
     } else {
         pnm_writepnminit(stdout, cols, rows, bmpMaxval, outputType, FALSE);
         writeRasterGen(bmpRaster, cols, rows, outputType, cBitCount,
-                       pixelformat, colormap, cmapsize); 
+                       pixelformat, colormap, cmapSize);
     }
     free(colormap);
     free(bmpRaster);
diff --git a/converter/other/cameratopam/camera.c b/converter/other/cameratopam/camera.c
index a1adba95..439c9413 100644
--- a/converter/other/cameratopam/camera.c
+++ b/converter/other/cameratopam/camera.c
@@ -1,7 +1,6 @@
-#define _BSD_SOURCE
-    /* Make sure strcasecmp is in string.h */
-#define _XOPEN_SOURCE
-    /* Make sure putenv is in stdlib.h */
+#define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
+#define _XOPEN_SOURCE 500
+    /* Make sure putenv is in stdlib.h, strcaseeq is in nstring.h */
 
 #include <stdlib.h>
 #include <string.h>
@@ -16,6 +15,7 @@
 #include "pm.h"
 #include "mallocvar.h"
 #include "pm_c_util.h"
+#include "nstring.h"
 
 #include "global_variables.h"
 #include "cameratopam.h"
@@ -46,8 +46,8 @@
 #define FORC3 for (c=0; c < 3; c++)
 #define FORC4 for (c=0; c < colors; c++)
 
-static void 
-merror (const void *ptr, const char *where) 
+static void
+merror (const void *ptr, const char *where)
 {
     if (ptr == NULL)
         pm_error ("Out of memory in %s", where);
@@ -56,7 +56,7 @@ merror (const void *ptr, const char *where)
 
 
 
-static void  
+static void
 adobeCopyPixel(Image             const image,
                unsigned int      const row,
                unsigned int      const col,
@@ -306,7 +306,7 @@ minolta_z2()
 
 void
 nikon_e2100_load_raw(Image const image) {
-        
+
   unsigned char   data[3432], *dp;
   unsigned short pixel[2288], *pix;
   int row, col;
@@ -383,7 +383,7 @@ fuji_s3_load_raw(Image const image) {
   }
 }
 
-static void 
+static void
 fuji_common_load_raw(Image        const image,
                      unsigned int const ncol,
                      unsigned int const icol,
@@ -1022,6 +1022,7 @@ static void  sony_decrypt (unsigned *data, int len, int start, int key)
 {
   static uint32_t pad[128];
   unsigned int p;
+  unsigned int i;
 
   if (start) {
     for (p=0; p < 4; p++)
@@ -1042,12 +1043,13 @@ static void  sony_decrypt (unsigned *data, int len, int start, int key)
         u.bytes[1] = pad[p] >> 16;
         u.bytes[2] = pad[p] >>  8;
         u.bytes[3] = pad[p] >>  0;
-        
+
         pad[p] = u.word;
     }
   }
-  while (len--)
-    *data++ ^= pad[p++ & 0x7f] = pad[(p+1) & 0x7f] ^ pad[(p+65) & 0x7f];
+  for (i = 0, p = 0; i < len; ++i, ++p) {
+    *data++ ^= pad[p & 0x7f] = pad[(p+1) & 0x7f] ^ pad[(p+65) & 0x7f];
+  }
 }
 
 void
@@ -1151,9 +1153,9 @@ static void  ciff_block_1030()
 /*
    Parse a CIFF file, better known as Canon CRW format.
  */
-void 
-parse_ciff(FILE * const ifp, 
-           int    const offset, 
+void
+parse_ciff(FILE * const ifp,
+           int    const offset,
            int    const length)
 {
   int tboff, nrecs, i, type, len, roff, aoff, save, wbi=-1;
@@ -1306,7 +1308,7 @@ parse_rollei(FILE * const ifp)
 
 
 void
-parse_mos(FILE * const ifp, 
+parse_mos(FILE * const ifp,
           int    const offset)
 {
     char data[40];
@@ -1355,7 +1357,7 @@ static double getrat()
 
 
 
-static void 
+static void
 parse_makernote(FILE * const ifp)
 {
   unsigned base=0, offset=0, entries, tag, type, len, save;
@@ -1501,15 +1503,15 @@ get_timestamp(FILE * const ifp)
     timestamp = ts;
 }
 
-static void 
+static void
 parse_exif(FILE * const ifp, int base)
 {
-  int entries, tag, type, len, val, save;
+  int entries, tag, len, val, save;
 
   entries = get2(ifp);
   while (entries--) {
     tag  = get2(ifp);
-    type = get2(ifp);
+    /* type = */ get2(ifp);
     len  = get4(ifp);
     val  = get4(ifp);
     save = ftell(ifp);
@@ -1526,7 +1528,7 @@ parse_exif(FILE * const ifp, int base)
   }
 }
 
-static int 
+static int
 parse_tiff_ifd(FILE * const ifp, int base, int level)
 {
   unsigned entries, tag, type, len, plen=16, save;
@@ -1687,7 +1689,7 @@ guess_cfa_pc:
       case 50723:           /* CameraCalibration1 */
       case 50724:           /* CameraCalibration2 */
     for (i=0; i < colors; i++)
-      FORC4 cc[i][c] = getrat();    
+      FORC4 cc[i][c] = getrat();
       case 50727:           /* AnalogBalance */
     FORC4 ab[c] = getrat();
     break;
@@ -1767,7 +1769,7 @@ parse_external_jpeg(const char * const ifname)
     strcpy (jname, ifname);
     jfile = jname + (file - ifname);
     jext  = jname + (ext  - ifname);
-    if (strcasecmp (ext, ".jpg")) {
+    if (!strcaseeq (ext, ".jpg")) {
         strcpy (jext, isupper(ext[1]) ? ".JPG":".jpg");
         memcpy (jfile, file+4, 4);
         memcpy (jfile+4, file, 4);
diff --git a/converter/other/cameratopam/cameratopam.c b/converter/other/cameratopam/cameratopam.c
index ec33dd31..2c832714 100644
--- a/converter/other/cameratopam/cameratopam.c
+++ b/converter/other/cameratopam/cameratopam.c
@@ -7,6 +7,7 @@
  */
 
 
+#define _DEFAULT_SOURCE 1 /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE 1   /* Make sure string.h contains strdup() */
 #define _XOPEN_SOURCE 500
    /* Make sure unistd.h contains swab(), string.h constains strdup() */
diff --git a/converter/other/cameratopam/foveon.c b/converter/other/cameratopam/foveon.c
index a3e5449a..992f3883 100644
--- a/converter/other/cameratopam/foveon.c
+++ b/converter/other/cameratopam/foveon.c
@@ -1,6 +1,6 @@
 /* This code is licensed to the public by its copyright owners under GPL. */
 
-#define _XOPEN_SOURCE   /* get M_PI */
+#define _XOPEN_SOURCE 500  /* get M_PI in math.h */
 
 #include <stdio.h>
 #include <assert.h>
diff --git a/converter/other/cameratopam/identify.c b/converter/other/cameratopam/identify.c
index 02208be6..7e77adb2 100644
--- a/converter/other/cameratopam/identify.c
+++ b/converter/other/cameratopam/identify.c
@@ -1,3 +1,4 @@
+#define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE   /* Make sure strcasecmp() is in string.h */
 #include <string.h>
 
diff --git a/converter/other/cameratopam/ljpeg.c b/converter/other/cameratopam/ljpeg.c
index 07791e25..331d258c 100644
--- a/converter/other/cameratopam/ljpeg.c
+++ b/converter/other/cameratopam/ljpeg.c
@@ -1,3 +1,4 @@
+#define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE    /* Make sure string.h containst strcasecmp() */
 #include <stdlib.h>
 #include <string.h>
diff --git a/converter/other/fiasco/binerror.c b/converter/other/fiasco/binerror.c
index 77243c64..9820d853 100644
--- a/converter/other/fiasco/binerror.c
+++ b/converter/other/fiasco/binerror.c
@@ -7,8 +7,8 @@
  *  Credits:	Modelled after variable argument routines from Jef
  *		Poskanzer's pbmplus package. 
  *
- *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
@@ -18,6 +18,7 @@
  *  $State: Exp $
  */
 
+#define _DEFAULT_SOURCE 1 /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE 1   /* Make sure strdup() is in string.h */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _ERROR_C
diff --git a/converter/other/fiasco/binerror.h b/converter/other/fiasco/binerror.h
index e7ff43c9..4f313aca 100644
--- a/converter/other/fiasco/binerror.h
+++ b/converter/other/fiasco/binerror.h
@@ -4,8 +4,8 @@
  *  Written by:		Stefan Frank
  *			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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/buttons.c b/converter/other/fiasco/buttons.c
index 82ed18cd..fc54d84a 100644
--- a/converter/other/fiasco/buttons.c
+++ b/converter/other/fiasco/buttons.c
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/buttons.h b/converter/other/fiasco/buttons.h
index a09f3423..079715f5 100644
--- a/converter/other/fiasco/buttons.h
+++ b/converter/other/fiasco/buttons.h
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/codec/approx.c b/converter/other/fiasco/codec/approx.c
index 5072fae3..d8fefcaa 100644
--- a/converter/other/fiasco/codec/approx.c
+++ b/converter/other/fiasco/codec/approx.c
@@ -1,10 +1,10 @@
 /*
- *  approx.c:		Approximation of range images with matching pursuit
+ *  approx.c:       Approximation of range images with matching pursuit
  *
- *  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>
+ *  Written by:     Ullrich Hafner
+ *      
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
@@ -34,7 +34,7 @@
 
 /*****************************************************************************
 
-			     local variables
+                 local variables
   
 *****************************************************************************/
 
@@ -52,227 +52,205 @@ typedef struct mp
 
 /*****************************************************************************
 
-			     prototypes
+                 prototypes
   
 *****************************************************************************/
 
 static void
 orthogonalize (unsigned index, unsigned n, unsigned level, real_t min_norm,
-	       const word_t *domain_blocks, const coding_t *c);
+           const word_t *domain_blocks, const coding_t *c);
 static void 
 matching_pursuit (mp_t *mp, bool_t full_search, real_t price,
-		  unsigned max_edges, int y_state, const range_t *range,
-		  const domain_pool_t *domain_pool, const coeff_t *coeff,
-		  const wfa_t *wfa, const coding_t *c);
+          unsigned max_edges, int y_state, const range_t *range,
+          const domain_pool_t *domain_pool, const coeff_t *coeff,
+          const wfa_t *wfa, const coding_t *c);
 
 /*****************************************************************************
 
-				public code
+                public code
   
 *****************************************************************************/
 
 real_t 
 approximate_range (real_t max_costs, real_t price, int max_edges,
-		   int y_state, range_t *range, domain_pool_t *domain_pool,
-		   coeff_t *coeff, const wfa_t *wfa, const coding_t *c)
-/*
- *  Approximate image block 'range' by matching pursuit. This functions
- *  calls the matching pursuit algorithm several times (with different
- *  parameters) in order to find the best approximation. Refer to function
- *  'matching_pursuit()' for more details about parameters.
- *
- *  Return value:
- *	approximation costs
- */
-{
-   mp_t	  mp;
-   bool_t success = NO;
-
-   /*
-    *  First approximation attempt: default matching pursuit algorithm.
-    */
-   mp.exclude [0] = NO_EDGE;
-   matching_pursuit (&mp, c->options.full_search, price, max_edges,
-		     y_state, range, domain_pool, coeff, wfa, c);
-
-   /*
-    *  Next approximation attempt: remove domain block mp->indices [0]
-    *  from domain pool (vector with smallest costs) and run the
-    *  matching pursuit again.
-    */
-   if (c->options.second_domain_block)
-   {
-      mp_t tmp_mp = mp;
+                   int y_state, range_t *range, domain_pool_t *domain_pool,
+                   coeff_t *coeff, const wfa_t *wfa, const coding_t *c) {
+/*---------------------------------------------------------------------------- 
+  Approximate image block 'range' by matching pursuit. This functions
+  calls the matching pursuit algorithm several times (with different
+  parameters) in order to find the best approximation. Refer to function
+  'matching_pursuit()' for more details about parameters.
+
+  Return value: approximation costs
+-----------------------------------------------------------------------------*/
+    mp_t mp;
+
+    /*
+     *  First approximation attempt: default matching pursuit algorithm.
+     */
+    mp.exclude [0] = NO_EDGE;
+    matching_pursuit(&mp, c->options.full_search, price, max_edges,
+                     y_state, range, domain_pool, coeff, wfa, c);
+
+    /*
+     *  Next approximation attempt: remove domain block mp->indices [0]
+     *  from domain pool (vector with smallest costs) and run the
+     *  matching pursuit again.
+     */
+    if (c->options.second_domain_block) {
+        mp_t tmp_mp;
       
-      tmp_mp.exclude [0] = tmp_mp.indices [0];
-      tmp_mp.exclude [1] = NO_EDGE;
-	    
-      matching_pursuit (&tmp_mp, c->options.full_search, price, max_edges,
-			y_state, range, domain_pool, coeff, wfa, c);
-      if (tmp_mp.costs < mp.costs)	/* success */
-      {
-	 success = YES;
-	 mp      = tmp_mp;
-      }
-   }
-
-   /*
-    *  Next approximation attempt: check whether some coefficients have
-    *  been quantized to zero. Vectors causing the underflow are
-    *  removed from the domain pool and then the matching pursuit
-    *  algorithm is run again (until underflow doesn't occur anymore).
-    */
-   if (c->options.check_for_underflow)
-   {
-      int  iteration = -1;
-      mp_t tmp_mp    = mp;
+        tmp_mp = mp;  /* initial value */
+
+        tmp_mp.exclude[0] = tmp_mp.indices [0];
+        tmp_mp.exclude[1] = NO_EDGE;
+        
+        matching_pursuit(&tmp_mp, c->options.full_search, price, max_edges,
+                         y_state, range, domain_pool, coeff, wfa, c);
+        if (tmp_mp.costs < mp.costs)  /* success */ 
+            mp = tmp_mp;
+    }
+
+    /*
+     *  Next approximation attempt: check whether some coefficients have
+     *  been quantized to zero. Vectors causing the underflow are
+     *  removed from the domain pool and then the matching pursuit
+     *  algorithm is run again (until underflow doesn't occur anymore).
+     */
+    if (c->options.check_for_underflow) {
+        mp_t tmp_mp;
+        int  iteration;
+
+        tmp_mp = mp;  /* initial value */
+        iteration = -1;  /* initial value */
       
-      do
-      {
-	 int i;
+        do {
+            int i;
+            
+            ++iteration;
+            tmp_mp.exclude[iteration] = NO_EDGE;
+     
+            for (i = 0; isdomain(tmp_mp.indices[i]); ++i) {
+                if (tmp_mp.weight [i] == 0) {
+                    tmp_mp.exclude[iteration] = tmp_mp.indices [i];
+                    break;
+                }
+            }      
+            if (isdomain (tmp_mp.exclude [iteration])) {
+                /* try again */
+                tmp_mp.exclude [iteration + 1] = NO_EDGE;
+        
+                matching_pursuit(&tmp_mp, c->options.full_search, price,
+                                 max_edges, y_state, range, domain_pool,
+                                 coeff, wfa, c);
+                if (tmp_mp.costs < mp.costs)     /* success */
+                    mp = tmp_mp;
+            }
+        } while (isdomain (tmp_mp.exclude [iteration])
+                 && iteration < MAXEDGES - 1);
+    }
+
+    /*
+     *  Next approximation attempt: check whether some coefficients have
+     *  been quantized to +/- max-value. Vectors causing the overflow are
+     *  removed from the domain pool and then the matching pursuit
+     *  algorithm is run again (until overflow doesn't occur anymore).
+     */
+    if (c->options.check_for_overflow) {
+        mp_t tmp_mp;
+        int  iteration;
+
+        tmp_mp = mp;  /* initial value */
+        iteration = -1;  /* initial value */
+
+        do {
+            int i;
  
-	 iteration++;
-	 tmp_mp.exclude [iteration] = NO_EDGE;
-	 
-	 for (i = 0; isdomain (tmp_mp.indices [i]); i++)
-	    if (tmp_mp.weight [i] == 0)
-	    {
-	       tmp_mp.exclude [iteration] = tmp_mp.indices [i];
-	       break;
-	    }
+            ++iteration;
+            tmp_mp.exclude[iteration] = NO_EDGE;
+     
+            for (i = 0; isdomain (tmp_mp.indices [i]); ++i) {
+                rpf_t * const rpf =
+                    tmp_mp.indices [i] ? coeff->rpf : coeff->dc_rpf;
+        
+                if (tmp_mp.weight [i] == btor (rtob (200, rpf), rpf)
+                    || tmp_mp.weight [i] == btor (rtob (-200, rpf), rpf)) {
+                    tmp_mp.exclude [iteration] = tmp_mp.indices [i];
+                    break;
+                }
+            }
       
-	 if (isdomain (tmp_mp.exclude [iteration])) /* try again */
-	 {
-	    tmp_mp.exclude [iteration + 1] = NO_EDGE;
-	    
-	    matching_pursuit (&tmp_mp, c->options.full_search, price,
-			      max_edges, y_state, range, domain_pool,
-			      coeff, wfa, c);
-	    if (tmp_mp.costs < mp.costs)	/* success */
-	    {
-	       success = YES;
-	       mp      = tmp_mp;
-	    }
-	 }
-      } while (isdomain (tmp_mp.exclude [iteration])
-	       && iteration < MAXEDGES - 1);
-   }
-
-   /*
-    *  Next approximation attempt: check whether some coefficients have
-    *  been quantized to +/- max-value. Vectors causing the overflow are
-    *  removed from the domain pool and then the matching pursuit
-    *  algorithm is run again (until overflow doesn't occur anymore).
-    */
-   if (c->options.check_for_overflow)
-   {
-      int  iteration = -1;
-      mp_t tmp_mp    = mp;
-      
-      do
-      {
-	 int i;
- 
-	 iteration++;
-	 tmp_mp.exclude [iteration] = NO_EDGE;
-	 
-	 for (i = 0; isdomain (tmp_mp.indices [i]); i++)
-	 {
-	    rpf_t *rpf = tmp_mp.indices [i] ? coeff->rpf : coeff->dc_rpf;
-	    
-	    if (tmp_mp.weight [i] == btor (rtob (200, rpf), rpf)
-		|| tmp_mp.weight [i] == btor (rtob (-200, rpf), rpf))
-	    {
-	       tmp_mp.exclude [iteration] = tmp_mp.indices [i];
-	       break;
-	    }
-	 }
+            if (isdomain(tmp_mp.exclude[iteration])) {
+                /* try again */
+                tmp_mp.exclude[iteration + 1] = NO_EDGE;
+        
+                matching_pursuit(&tmp_mp, c->options.full_search, price,
+                                 max_edges, y_state, range, domain_pool,
+                                 coeff, wfa, c);
+                if (tmp_mp.costs < mp.costs)   /* success */
+                    mp = tmp_mp;
+            }
+        } while (isdomain (tmp_mp.exclude [iteration])
+                 && iteration < MAXEDGES - 1);
+    }
+
+    /*
+     *  Finally, check whether the best approximation has costs
+     *  smaller than 'max_costs'.
+     */
+    if (mp.costs < max_costs) {
+        int    edge;
+        int    new_index, old_index;
+
+        new_index = 0;
+        for (old_index = 0; isdomain (mp.indices[old_index]); ++old_index) {
+            if (mp.weight [old_index] != 0) {
+                mp.indices [new_index] = mp.indices [old_index];
+                mp.into    [new_index] = mp.into    [old_index];
+                mp.weight  [new_index] = mp.weight  [old_index];
+                ++new_index;
+            }
+        }
+        mp.indices [new_index] = NO_EDGE;
+        mp.into    [new_index] = NO_EDGE;
+
+        /*
+         *  Update of probability models
+         */
+        {
+            word_t * const domain_blocks =
+                domain_pool->generate(range->level, y_state,
+                                      wfa,
+                                      domain_pool->model);
+            domain_pool->update(domain_blocks, mp.indices,
+                                range->level, y_state, wfa,
+                                domain_pool->model);
+            coeff->update (mp.weight, mp.into, range->level, coeff);
+     
+            Free(domain_blocks);
+        }
       
-	 if (isdomain (tmp_mp.exclude [iteration])) /* try again */
-	 {
-	    tmp_mp.exclude [iteration + 1] = NO_EDGE;
-	    
-	    matching_pursuit (&tmp_mp, c->options.full_search, price,
-			      max_edges, y_state, range, domain_pool,
-			      coeff, wfa, c);
-	    if (tmp_mp.costs < mp.costs)	/* success */
-	    {
-	       success = YES;
-	       mp      = tmp_mp;
-	    }
-	 }
-      } while (isdomain (tmp_mp.exclude [iteration])
-	       && iteration < MAXEDGES - 1);
-   }
-
-   /*
-    *  Finally, check whether the best approximation has costs
-    *  smaller than 'max_costs'.
-    */
-   if (mp.costs < max_costs) 
-   {
-      int    edge;
-      bool_t overflow  = NO;
-      bool_t underflow = NO;
-      int    new_index, old_index;
-
-      new_index = 0;
-      for (old_index = 0; isdomain (mp.indices [old_index]); old_index++)
-	 if (mp.weight [old_index] != 0)
-	 {
-	    rpf_t *rpf = mp.indices [old_index] ? coeff->rpf : coeff->dc_rpf;
-	    
-	    if (mp.weight [old_index] == btor (rtob (200, rpf), rpf)
-		|| mp.weight [old_index] == btor (rtob (-200, rpf), rpf))
-	       overflow = YES;
-	    
-	    mp.indices [new_index] = mp.indices [old_index];
-	    mp.into [new_index]    = mp.into [old_index];
-	    mp.weight [new_index]  = mp.weight [old_index];
-	    new_index++;
-	 }
-	 else
-	    underflow = YES;
-      
-      mp.indices [new_index] = NO_EDGE;
-      mp.into  [new_index]   = NO_EDGE;
-
-      /*
-       *  Update of probability models
-       */
-      {
-	 word_t *domain_blocks = domain_pool->generate (range->level, y_state,
-							wfa,
-							domain_pool->model);
-	 domain_pool->update (domain_blocks, mp.indices,
-			      range->level, y_state, wfa, domain_pool->model);
-	 coeff->update (mp.weight, mp.into, range->level, coeff);
-	 
-	 Free (domain_blocks);
-      }
-      
-      for (edge = 0; isedge (mp.indices [edge]); edge++)
-      {
-	 range->into [edge]   = mp.into [edge];
-	 range->weight [edge] = mp.weight [edge];
-      }
-      range->into [edge]  = NO_EDGE;
-      range->matrix_bits  = mp.matrix_bits;
-      range->weights_bits = mp.weights_bits;
-      range->err          = mp.err;
-   }
-   else
-   {
-      range->into [0] = NO_EDGE;
-      mp.costs	      = MAXCOSTS;
-   }
+        for (edge = 0; isedge (mp.indices [edge]); ++edge) {
+            range->into   [edge] = mp.into   [edge];
+            range->weight [edge] = mp.weight [edge];
+        }
+        range->into [edge]  = NO_EDGE;
+        range->matrix_bits  = mp.matrix_bits;
+        range->weights_bits = mp.weights_bits;
+        range->err          = mp.err;
+    } else {
+        range->into [0] = NO_EDGE;
+        mp.costs        = MAXCOSTS;
+    }
    
-   return mp.costs;
+    return mp.costs;
 }
 
+
+
 /*****************************************************************************
 
-			     local variables
+                 local variables
   
 *****************************************************************************/
 
@@ -310,15 +288,15 @@ static bool_t used [MAXSTATES];
 
 /*****************************************************************************
 
-				private code
+                private code
   
 *****************************************************************************/
 
 static void 
 matching_pursuit (mp_t *mp, bool_t full_search, real_t price,
-		  unsigned max_edges, int y_state, const range_t *range,
-		  const domain_pool_t *domain_pool, const coeff_t *coeff,
-		  const wfa_t *wfa, const coding_t *c)
+                  unsigned max_edges, int y_state, const range_t *range,
+                  const domain_pool_t *domain_pool, const coeff_t *coeff,
+                  const wfa_t *wfa, const coding_t *c)
 /*
  *  Find an approximation of the current 'range' with a linear
  *  combination of vectors of the 'domain_pool'. The linear
@@ -339,311 +317,318 @@ matching_pursuit (mp_t *mp, bool_t full_search, real_t price,
  *  No return value.
  *
  *  Side effects:
- *	vectors, factors, rate, distortion and costs are stored in 'mp'
+ *  vectors, factors, rate, distortion and costs are stored in 'mp'
  */
 {
-   unsigned	 n;			/* current vector of the OB */
-   int		 index;			/* best fitting domain image */
-   unsigned	 domain;		/* counter */
-   real_t	 norm;			/* norm of range image */
-   real_t	 additional_bits;	/* bits for mc, nd, and tree */
-   word_t	*domain_blocks;		/* current set of domain images */
-   const real_t  min_norm = 2e-3;	/* lower bound of norm */
-   unsigned 	 best_n   = 0;
-   unsigned	 size 	  = size_of_level (range->level);
+    unsigned  n;         /* current vector of the OB */
+    int       index;         /* best fitting domain image */
+    unsigned  domain;        /* counter */
+    real_t    norm;          /* norm of range image */
+    real_t    additional_bits;   /* bits for mc, nd, and tree */
+    word_t   *domain_blocks;     /* current set of domain images */
+    const real_t  min_norm = 2e-3;   /* lower bound of norm */
+    unsigned      best_n   = 0;
+    unsigned  size     = size_of_level (range->level);
  
-   /*
-    *  Initialize domain pool and inner product arrays
-    */
-   domain_blocks = domain_pool->generate (range->level, y_state, wfa,
-					  domain_pool->model);
-   for (domain = 0; domain_blocks [domain] >= 0; domain++)
-   {
-      used [domain] = NO;
-      rem_denominator [domain]		/* norm of domain */
-	 = get_ip_state_state (domain_blocks [domain], domain_blocks [domain],
-			       range->level, c);
-      if (rem_denominator [domain] / size < min_norm)
-	 used [domain] = YES;		/* don't use domains with small norm */
-      else
-	 rem_numerator [domain]		/* inner product <s_domain, b> */
-	    = get_ip_image_state (range->image, range->address,
-				  range->level, domain_blocks [domain], c);
-      if (!used [domain] && fabs (rem_numerator [domain]) < min_norm)
-	 used [domain] = YES;
-   }
-
-   /*
-    *  Exclude all domain blocks given in array 'mp->exclude'
-    */
-   for (n = 0; isdomain (mp->exclude [n]); n++)
-      used [mp->exclude [n]] = YES;
-
-   /*
-    *  Compute the approximation costs if 'range' is approximated with
-    *  no linear combination, i.e. the error is equal to the square
-    *  of the image norm and the size of the automaton is determined by
-    *  storing only zero elements in the current matrix row
-    */
-   for (norm = 0, n = 0; n < size; n++)
-      norm += square (c->pixels [range->address * size + n]);
-
-   additional_bits = range->tree_bits + range->mv_tree_bits
-		     + range->mv_coord_bits + range->nd_tree_bits
-		     + range->nd_weights_bits;
-
-   mp->err          = norm;
-   mp->weights_bits = 0;
-   mp->matrix_bits  = domain_pool->bits (domain_blocks, NULL, range->level,
-					 y_state, wfa, domain_pool->model);
-   mp->costs        = (mp->matrix_bits + mp->weights_bits
-		       + additional_bits) * price + mp->err;
-
-   n = 0;
-   do 
-   {
-      /*
-       *  Current approximation is: b = d_0 o_0 + ... + d_(n-1) o_(n-1)
-       *  with corresponding costs 'range->err + range->bits * p'.
-       *  For all remaining state images s_i (used[s_i] == NO) set
-       *  o_n :	= s_i - \sum(k = 0, ... , n-1) {(<s_i, o_k> / ||o_k||^2) o_k}
-       *  and try to beat current costs.
-       *  Choose that vector for the next orthogonalization step,
-       *  which has minimal costs: s_index.
-       *  (No progress is indicated by index == -1)
-       */
+    /*
+     *  Initialize domain pool and inner product arrays
+     */
+    domain_blocks = domain_pool->generate (range->level, y_state, wfa,
+                                           domain_pool->model);
+    for (domain = 0; domain_blocks [domain] >= 0; domain++)
+    {
+        used [domain] = NO;
+        rem_denominator [domain] =     /* norm of domain */
+            get_ip_state_state (domain_blocks [domain], domain_blocks [domain],
+                                range->level, c);
+        if (rem_denominator [domain] / size < min_norm)
+            used [domain] = YES;       /* don't use domains with small norm */
+        else
+            rem_numerator [domain]     /* inner product <s_domain, b> */
+                = get_ip_image_state (range->image, range->address,
+                                      range->level, domain_blocks [domain], c);
+        if (!used [domain] && fabs (rem_numerator [domain]) < min_norm)
+            used [domain] = YES;
+    }
+
+    /*
+     *  Exclude all domain blocks given in array 'mp->exclude'
+     */
+    for (n = 0; isdomain (mp->exclude [n]); n++)
+        used [mp->exclude [n]] = YES;
+
+    /*
+     *  Compute the approximation costs if 'range' is approximated with
+     *  no linear combination, i.e. the error is equal to the square
+     *  of the image norm and the size of the automaton is determined by
+     *  storing only zero elements in the current matrix row
+     */
+    for (norm = 0, n = 0; n < size; n++)
+        norm += square (c->pixels [range->address * size + n]);
+
+    additional_bits = range->tree_bits + range->mv_tree_bits
+        + range->mv_coord_bits + range->nd_tree_bits
+        + range->nd_weights_bits;
+
+    mp->err          = norm;
+    mp->weights_bits = 0;
+    mp->matrix_bits  = domain_pool->bits (domain_blocks, NULL, range->level,
+                                          y_state, wfa, domain_pool->model);
+    mp->costs        = (mp->matrix_bits + mp->weights_bits
+                        + additional_bits) * price + mp->err;
+
+    n = 0;
+    do 
+    {
+        /*
+         *  Current approximation is: b = d_0 o_0 + ... + d_(n-1) o_(n-1)
+         *  with corresponding costs 'range->err + range->bits * p'.
+         *  For all remaining state images s_i (used[s_i] == NO) set
+         *  o_n : = s_i - \sum(k = 0, ... , n-1) {(<s_i, o_k> / ||o_k||^2) o_k}
+         *  and try to beat current costs.
+         *  Choose that vector for the next orthogonalization step,
+         *  which has minimal costs: s_index.
+         *  (No progress is indicated by index == -1)
+         */
       
-      real_t min_matrix_bits  = 0;
-      real_t min_weights_bits = 0;
-      real_t min_error 	      = 0;
-      real_t min_weight [MAXEDGES];
-      real_t min_costs = full_search ? MAXCOSTS : mp->costs;
+        real_t min_matrix_bits  = 0;
+        real_t min_weights_bits = 0;
+        real_t min_error        = 0;
+        real_t min_weight [MAXEDGES];
+        real_t min_costs = full_search ? MAXCOSTS : mp->costs;
       
-      for (index = -1, domain = 0; domain_blocks [domain] >= 0; domain++) 
-	 if (!used [domain]) 
-	 {
-	    real_t    matrix_bits, weights_bits;
-	    /*
-	     *  To speed up the search through the domain images,
-	     *  the costs of using domain image 'domain' as next vector
-	     *  can be approximated in a first step:
-	     *  improvement of image quality
-	     *	  <= square (rem_numerator[domain]) / rem_denominator[domain]
-	     */
-	    {
-		  word_t   vectors [MAXEDGES + 1];
-		  word_t   states [MAXEDGES + 1];
-		  real_t   weights [MAXEDGES + 1];
-		  unsigned i, k;
-		  
-		  for (i = 0, k = 0; k < n; k++)
-		     if (mp->weight [k] != 0)
-		     {
-			vectors [i] = mp->indices [k];
-			states [i]  = domain_blocks [vectors [i]];
-			weights [i] = mp->weight [k];
-			i++;
-		     }
-		  vectors [i] 	  = domain;
-		  states [i]  	  = domain_blocks [domain];
-		  weights [i] 	  = 0.5;
-		  vectors [i + 1] = -1;
-		  states [i + 1]  = -1;
-
-		  weights_bits = coeff->bits (weights, states, range->level,
-					      coeff);
-		  matrix_bits = domain_pool->bits (domain_blocks, vectors,
-						   range->level, y_state,
-						   wfa, domain_pool->model);
-	    }
-	    if (((matrix_bits + weights_bits + additional_bits) * price +
-		 mp->err -
-		 square (rem_numerator [domain]) / rem_denominator [domain])
-		< min_costs)
-	    {
-	       /*
-		*  1.) Compute the weights (linear factors) c_i of the
-		*  linear combination
-		*  b = c_0 v_0 + ... + c_(n-1) v_(n-1) + c_n v_'domain'
-		*  Use backward substitution to obtain c_i from the linear
-		*  factors of the lin. comb. b = d_0 o_0 + ... + d_n o_n
-		*  of the corresponding orthogonal vectors {o_0, ..., o_n}.
-		*  Vector o_n of the orthogonal basis is obtained by using
-		*  vector 'v_domain' in step n of the Gram Schmidt
-		*  orthogonalization (see above for definition of o_n).
-		*  Recursive formula for the coefficients c_i:
-		*  c_n := <b, o_n> / ||o_n||^2
-		*  for i = n - 1, ... , 0:
-		*  c_i := <b, o_i> / ||o_i||^2 +
-		*          \sum (k = i + 1, ... , n){ c_k <v_k, o_i>
-		*					/ ||o_i||^2 }
-		*  2.) Because linear factors are stored with reduced precision
-		*  factor c_i is rounded with the given precision in step i
-		*  of the recursive formula. 
-		*/
-
-	       unsigned k;		/* counter */
-	       int    	l;		/* counter */
-	       real_t 	m_bits;		/* number of matrix bits to store */
-	       real_t 	w_bits;		/* number of weights bits to store */
-	       real_t 	r [MAXEDGES];	/* rounded linear factors */
-	       real_t 	f [MAXEDGES];	/* linear factors */
-	       int    	v [MAXEDGES];	/* mapping of domains to vectors */
-	       real_t 	costs;		/* current approximation costs */
-	       real_t 	m_err;		/* current approximation error */
-
-	       f [n] = rem_numerator [domain] / rem_denominator [domain];
-	       v [n] = domain;		/* corresponding mapping */
-	       for (k = 0; k < n; k++)
-	       {
-		  f [k] = ip_image_ortho_vector [k] / norm_ortho_vector [k];
-		  v [k] = mp->indices [k];
-	       }
-	    
-	       for (l = n; l >= 0; l--) 
-	       {
-		  rpf_t *rpf = domain_blocks [v [l]]
-			       ? coeff->rpf : coeff->dc_rpf;
-
-		  r [l] = f [l] = btor (rtob (f [l], rpf), rpf);
-		     
-		  for (k = 0; k < (unsigned) l; k++)
-		     f [k] -= f [l] * ip_domain_ortho_vector [v [l]][k]
-			      / norm_ortho_vector [k] ;
-	       } 
-
-	       /*
-		*  Compute the number of output bits of the linear combination
-		*  and store the weights with reduced precision. The
-		*  resulting linear combination is
-		*  b = r_0 v_0 + ... + r_(n-1) v_(n-1) + r_n v_'domain'
-		*/
-	       {
-		  word_t vectors [MAXEDGES + 1];
-		  word_t states [MAXEDGES + 1];
-		  real_t weights [MAXEDGES + 1];
-		  int	 i;
-		  
-		  for (i = 0, k = 0; k <= n; k++)
-		     if (f [k] != 0)
-		     {
-			vectors [i] = v [k];
-			states [i]  = domain_blocks [v [k]];
-			weights [i] = f [k];
-			i++;
-		     }
-		  vectors [i] = -1;
-		  states [i]  = -1;
-
-		  w_bits = coeff->bits (weights, states, range->level, coeff);
-		  m_bits = domain_pool->bits (domain_blocks, vectors,
-					      range->level, y_state,
-					      wfa, domain_pool->model);
-	       }
-	       
-	       /*
-		*  To compute the approximation error, the corresponding
-		*  linear factors of the linear combination 
-		*  b = r_0 o_0 + ... + r_(n-1) o_(n-1) + r_n o_'domain'
-		*  with orthogonal vectors must be computed with following
-		*  formula:
-		*  r_i := r_i +
-		*          \sum (k = i + 1, ... , n) { r_k <v_k, o_i>
-		*					/ ||o_i||^2 }
-		*/
-	       for (l = 0; (unsigned) l <= n; l++)
-	       {
-		  /*
-		   *  compute <v_n, o_n>
-		   */
-		  real_t a;
-
-		  a = get_ip_state_state (domain_blocks [v [l]],
-					  domain_blocks [domain],
-					  range->level, c);
-		  for (k = 0; k < n; k++) 
-		     a -= ip_domain_ortho_vector [v [l]][k]
-			  / norm_ortho_vector [k]
-			  * ip_domain_ortho_vector [domain][k];
-		  ip_domain_ortho_vector [v [l]][n] = a;
-	       }
-	       norm_ortho_vector [n]     = rem_denominator [domain];
-	       ip_image_ortho_vector [n] = rem_numerator [domain];
- 	    
-	       for (k = 0; k <= n; k++)
-		  for (l = k + 1; (unsigned) l <= n; l++)
-		     r [k] += ip_domain_ortho_vector [v [l]][k] * r [l]
-			      / norm_ortho_vector [k];
-	       /*
-		*  Compute approximation error:
-		*  error := ||b||^2 +
-		*  \sum (k = 0, ... , n){r_k^2 ||o_k||^2 - 2 r_k <b, o_k>}
-		*/
-	       m_err = norm;
-	       for (k = 0; k <= n; k++)
-		  m_err += square (r [k]) * norm_ortho_vector [k]
-			 - 2 * r [k] * ip_image_ortho_vector [k];
-	       if (m_err < 0)		/* TODO: return MAXCOSTS */
-		  warning ("Negative image norm: %f"
-			   " (current domain: %d, level = %d)",
-			   (double) m_err, domain, range->level);
-
-	       costs = (m_bits + w_bits + additional_bits) * price + m_err;
-	       if (costs < min_costs)	/* found a better approximation */
-	       {
-		  index            = domain;
-		  min_costs        = costs;
-		  min_matrix_bits  = m_bits;
-		  min_weights_bits = w_bits;
-		  min_error        = m_err;
-		  for (k = 0; k <= n; k++)
-		     min_weight [k] = f [k];
-	       }
-	    }
-	 }
+        for (index = -1, domain = 0; domain_blocks [domain] >= 0; domain++) 
+            if (!used [domain]) 
+            {
+                real_t    matrix_bits, weights_bits;
+                /*
+                 *  To speed up the search through the domain images,
+                 *  the costs of using domain image 'domain' as next vector
+                 *  can be approximated in a first step:
+                 *  improvement of image quality
+                 *  <= square (rem_numerator[domain]) / rem_denominator[domain]
+                 */
+                {
+                    word_t   vectors [MAXEDGES + 1];
+                    word_t   states [MAXEDGES + 1];
+                    real_t   weights [MAXEDGES + 1];
+                    unsigned i, k;
+          
+                    for (i = 0, k = 0; k < n; k++)
+                        if (mp->weight [k] != 0)
+                        {
+                            vectors [i] = mp->indices [k];
+                            states [i]  = domain_blocks [vectors [i]];
+                            weights [i] = mp->weight [k];
+                            i++;
+                        }
+                    vectors [i]     = domain;
+                    states [i]      = domain_blocks [domain];
+                    weights [i]     = 0.5;
+                    vectors [i + 1] = -1;
+                    states [i + 1]  = -1;
+
+                    weights_bits = coeff->bits (weights, states, range->level,
+                                                coeff);
+                    matrix_bits = domain_pool->bits (domain_blocks, vectors,
+                                                     range->level, y_state,
+                                                     wfa, domain_pool->model);
+                }
+                if (((matrix_bits + weights_bits + additional_bits) * price +
+                     mp->err -
+                     square (rem_numerator[domain]) / rem_denominator[domain])
+                    < min_costs)
+                {
+                    /*
+                     *  1.) Compute the weights (linear factors) c_i of the
+                     *  linear combination
+                     *  b = c_0 v_0 + ... + c_(n-1) v_(n-1) + c_n v_'domain'
+                     *  Use backward substitution to obtain c_i from the linear
+                     *  factors of the lin. comb. b = d_0 o_0 + ... + d_n o_n
+                     *  of the corresponding orthogonal vectors {o_0, ..., o_n}.
+                     *  Vector o_n of the orthogonal basis is obtained by using
+                     *  vector 'v_domain' in step n of the Gram Schmidt
+                     *  orthogonalization (see above for definition of o_n).
+                     *  Recursive formula for the coefficients c_i:
+                     *  c_n := <b, o_n> / ||o_n||^2
+                     *  for i = n - 1, ... , 0:
+                     *  c_i := <b, o_i> / ||o_i||^2 +
+                     *          \sum (k = i + 1, ... , n){ c_k <v_k, o_i>
+                     *                   / ||o_i||^2 }
+                     *  2.) Because linear factors are stored with reduced
+                     *  precision factor c_i is rounded with the given
+                     *  precision in step i of the recursive formula.
+                     */
+
+                    unsigned k;      /* counter */
+                    int      l;      /* counter */
+                    real_t   m_bits;     /* number of matrix bits to store */
+                    real_t   w_bits;     /* number of weights bits to store */
+                    real_t   r [MAXEDGES];   /* rounded linear factors */
+                    real_t   f [MAXEDGES];   /* linear factors */
+                    int      v [MAXEDGES];   /* mapping of domains to vectors*/
+                    real_t   costs;      /* current approximation costs */
+                    real_t   m_err;      /* current approximation error */
+
+                    f [n] = rem_numerator [domain] / rem_denominator [domain];
+                    v [n] = domain;      /* corresponding mapping */
+                    for (k = 0; k < n; k++)
+                    {
+                        f[k] = ip_image_ortho_vector[k] / norm_ortho_vector[k];
+                        v [k] = mp->indices [k];
+                    }
+        
+                    for (l = n; l >= 0; --l) {
+                        rpf_t * const rpf = domain_blocks[v[l]]
+                            ? coeff->rpf : coeff->dc_rpf;
+
+                        unsigned int k;
+
+                        r[l] = f[l] = btor(rtob(f[l], rpf), rpf);
+
+                        {
+                            real_t const fl = f[l];
+             
+                            for (k = 0; k < l; ++k) {
+                                f[k] -= fl * ip_domain_ortho_vector[v[l]][k]
+                                    / norm_ortho_vector[k];
+                            }
+                        }
+                    } 
+
+                    /*
+                     *  Compute the number of output bits of the linear
+                     *  combination and store the weights with reduced
+                     *  precision. The resulting linear combination is
+                     *  b = r_0 v_0 + ... + r_(n-1) v_(n-1) + r_n v_'domain'
+                     */
+                    {
+                        word_t vectors [MAXEDGES + 1];
+                        word_t states [MAXEDGES + 1];
+                        real_t weights [MAXEDGES + 1];
+                        int    i;
+          
+                        for (i = 0, k = 0; k <= n; k++)
+                            if (f [k] != 0)
+                            {
+                                vectors [i] = v [k];
+                                states [i]  = domain_blocks [v [k]];
+                                weights [i] = f [k];
+                                i++;
+                            }
+                        vectors [i] = -1;
+                        states [i]  = -1;
+
+                        w_bits =
+                            coeff->bits(weights, states, range->level, coeff);
+                        m_bits = domain_pool->bits (domain_blocks, vectors,
+                                                    range->level, y_state,
+                                                    wfa, domain_pool->model);
+                    }
+           
+                    /*
+                     *  To compute the approximation error, the corresponding
+                     *  linear factors of the linear combination 
+                     *  b = r_0 o_0 + ... + r_(n-1) o_(n-1) + r_n o_'domain'
+                     *  with orthogonal vectors must be computed with following
+                     *  formula:
+                     *  r_i := r_i +
+                     *          \sum (k = i + 1, ... , n) { r_k <v_k, o_i>
+                     *                   / ||o_i||^2 }
+                     */
+                    for (l = 0; (unsigned) l <= n; l++)
+                    {
+                        /*
+                         *  compute <v_n, o_n>
+                         */
+                        real_t a;
+
+                        a = get_ip_state_state (domain_blocks [v [l]],
+                                                domain_blocks [domain],
+                                                range->level, c);
+                        for (k = 0; k < n; k++) 
+                            a -= ip_domain_ortho_vector [v [l]][k]
+                                / norm_ortho_vector [k]
+                                * ip_domain_ortho_vector [domain][k];
+                        ip_domain_ortho_vector [v [l]][n] = a;
+                    }
+                    norm_ortho_vector [n]     = rem_denominator [domain];
+                    ip_image_ortho_vector [n] = rem_numerator [domain];
+        
+                    for (k = 0; k <= n; k++)
+                        for (l = k + 1; (unsigned) l <= n; l++)
+                            r [k] += ip_domain_ortho_vector [v [l]][k] * r [l]
+                                / norm_ortho_vector [k];
+                    /*
+                     *  Compute approximation error:
+                     *  error := ||b||^2 +
+                     *  \sum (k = 0, ... , n){r_k^2 ||o_k||^2 - 2 r_k <b, o_k>}
+                     */
+                    m_err = norm;
+                    for (k = 0; k <= n; k++)
+                        m_err += square (r [k]) * norm_ortho_vector [k]
+                            - 2 * r [k] * ip_image_ortho_vector [k];
+                    if (m_err < 0)       /* TODO: return MAXCOSTS */
+                        warning ("Negative image norm: %f"
+                                 " (current domain: %d, level = %d)",
+                                 (double) m_err, domain, range->level);
+
+                    costs = (m_bits + w_bits + additional_bits) * price + m_err;
+                    if (costs < min_costs)   /* found a better approximation */
+                    {
+                        index            = domain;
+                        min_costs        = costs;
+                        min_matrix_bits  = m_bits;
+                        min_weights_bits = w_bits;
+                        min_error        = m_err;
+                        for (k = 0; k <= n; k++)
+                            min_weight [k] = f [k];
+                    }
+                }
+            }
       
-      if (index >= 0)			/* found a better approximation */
-      {
-	 if (min_costs < mp->costs)
-	 {
-	    unsigned k;
-	    
-	    mp->costs        = min_costs;
-	    mp->err          = min_error;
-	    mp->matrix_bits  = min_matrix_bits;
-	    mp->weights_bits = min_weights_bits;
-	    
-	    for (k = 0; k <= n; k++)
-	       mp->weight [k] = min_weight [k];
-
-	    best_n = n + 1;
-	 }
-	 
-	 mp->indices [n] = index;
-	 mp->into [n]    = domain_blocks [index];
-
-	 used [index] = YES;
-
-	 /* 
-	  *  Gram-Schmidt orthogonalization step n 
-	  */
-	 orthogonalize (index, n, range->level, min_norm, domain_blocks, c);
-	 n++;
-      }	
-   } 
-   while (n < max_edges && index >= 0);
-
-   mp->indices [best_n] = NO_EDGE;
+        if (index >= 0)           /* found a better approximation */
+        {
+            if (min_costs < mp->costs)
+            {
+                unsigned k;
+        
+                mp->costs        = min_costs;
+                mp->err          = min_error;
+                mp->matrix_bits  = min_matrix_bits;
+                mp->weights_bits = min_weights_bits;
+        
+                for (k = 0; k <= n; k++)
+                    mp->weight [k] = min_weight [k];
+
+                best_n = n + 1;
+            }
+     
+            mp->indices [n] = index;
+            mp->into [n]    = domain_blocks [index];
+
+            used [index] = YES;
+
+            /* 
+             *  Gram-Schmidt orthogonalization step n 
+             */
+            orthogonalize (index, n, range->level, min_norm, domain_blocks, c);
+            n++;
+        } 
+    } 
+    while (n < max_edges && index >= 0);
+
+    mp->indices [best_n] = NO_EDGE;
    
-   mp->costs = (mp->matrix_bits + mp->weights_bits + additional_bits) * price
-	       + mp->err;
+    mp->costs = (mp->matrix_bits + mp->weights_bits + additional_bits) * price
+        + mp->err;
 
-   Free (domain_blocks);
+    Free (domain_blocks);
 }
 
 static void
 orthogonalize (unsigned index, unsigned n, unsigned level, real_t min_norm,
-	       const word_t *domain_blocks, const coding_t *c)
+           const word_t *domain_blocks, const coding_t *c)
 /*
  *  Step 'n' of the Gram-Schmidt orthogonalization procedure:
  *  vector 'index' is orthogonalized with respect to the set
@@ -655,8 +640,8 @@ orthogonalize (unsigned index, unsigned n, unsigned level, real_t min_norm,
  *  No return value.
  *
  *  Side effects:
- *	The remainder values (numerator and denominator) of
- *	all 'domain_blocks' are updated. 
+ *  The remainder values (numerator and denominator) of
+ *  all 'domain_blocks' are updated. 
  */
 {
    unsigned domain;
@@ -676,25 +661,25 @@ orthogonalize (unsigned index, unsigned n, unsigned level, real_t min_norm,
    for (domain = 0; domain_blocks [domain] >= 0; domain++) 
       if (!used [domain]) 
       {
-	 unsigned k;
-	 real_t   tmp = get_ip_state_state (domain_blocks [index],
-					    domain_blocks [domain], level, c);
-	 
-	 for (k = 0; k < n; k++) 
-	    tmp -= ip_domain_ortho_vector [domain][k] / norm_ortho_vector [k]
-		   * ip_domain_ortho_vector [index][k];
-	 ip_domain_ortho_vector [domain][n] = tmp;
-	 rem_denominator [domain] -= square (tmp) / norm_ortho_vector [n];
-	 rem_numerator [domain]   -= ip_image_ortho_vector [n]
-				     / norm_ortho_vector [n]
-				     * ip_domain_ortho_vector [domain][n] ;
-
-	 /*
-	  *  Exclude vectors with small denominator
-	  */
-	 if (!used [domain]) 
-	    if (rem_denominator [domain] / size_of_level (level) < min_norm) 
-	       used [domain] = YES;
+     unsigned k;
+     real_t   tmp = get_ip_state_state (domain_blocks [index],
+                        domain_blocks [domain], level, c);
+     
+     for (k = 0; k < n; k++) 
+        tmp -= ip_domain_ortho_vector [domain][k] / norm_ortho_vector [k]
+           * ip_domain_ortho_vector [index][k];
+     ip_domain_ortho_vector [domain][n] = tmp;
+     rem_denominator [domain] -= square (tmp) / norm_ortho_vector [n];
+     rem_numerator [domain]   -= ip_image_ortho_vector [n]
+                     / norm_ortho_vector [n]
+                     * ip_domain_ortho_vector [domain][n] ;
+
+     /*
+      *  Exclude vectors with small denominator
+      */
+     if (!used [domain]) 
+        if (rem_denominator [domain] / size_of_level (level) < min_norm) 
+           used [domain] = YES;
       }
 }
 
diff --git a/converter/other/fiasco/codec/approx.h b/converter/other/fiasco/codec/approx.h
index c54b78c9..42f1458a 100644
--- a/converter/other/fiasco/codec/approx.h
+++ b/converter/other/fiasco/codec/approx.h
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/codec/bintree.c b/converter/other/fiasco/codec/bintree.c
index ddd74e15..b050ab63 100644
--- a/converter/other/fiasco/codec/bintree.c
+++ b/converter/other/fiasco/codec/bintree.c
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/codec/bintree.h b/converter/other/fiasco/codec/bintree.h
index cdb80c94..3dc88ac1 100644
--- a/converter/other/fiasco/codec/bintree.h
+++ b/converter/other/fiasco/codec/bintree.h
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/codec/coder.c b/converter/other/fiasco/codec/coder.c
index 94e367dd..1e21994d 100644
--- a/converter/other/fiasco/codec/coder.c
+++ b/converter/other/fiasco/codec/coder.c
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
@@ -14,6 +14,7 @@
  *  $State: Exp $
  */
 
+#define _DEFAULT_SOURCE 1 /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE 1   /* Make sure strdup() is in string.h */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
diff --git a/converter/other/fiasco/codec/coder.h b/converter/other/fiasco/codec/coder.h
index c6f4bb7c..2fe81d81 100644
--- a/converter/other/fiasco/codec/coder.h
+++ b/converter/other/fiasco/codec/coder.h
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/codec/coeff.c b/converter/other/fiasco/codec/coeff.c
index 0cd64f17..6d33bad5 100644
--- a/converter/other/fiasco/codec/coeff.c
+++ b/converter/other/fiasco/codec/coeff.c
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/codec/coeff.h b/converter/other/fiasco/codec/coeff.h
index 118cd0fc..6193e2ee 100644
--- a/converter/other/fiasco/codec/coeff.h
+++ b/converter/other/fiasco/codec/coeff.h
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/codec/control.c b/converter/other/fiasco/codec/control.c
index 94c23c83..2ea06052 100644
--- a/converter/other/fiasco/codec/control.c
+++ b/converter/other/fiasco/codec/control.c
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/codec/control.h b/converter/other/fiasco/codec/control.h
index f601d8b8..6c0834a1 100644
--- a/converter/other/fiasco/codec/control.h
+++ b/converter/other/fiasco/codec/control.h
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/codec/cwfa.h b/converter/other/fiasco/codec/cwfa.h
index 9c4e7fee..dd86fbfc 100644
--- a/converter/other/fiasco/codec/cwfa.h
+++ b/converter/other/fiasco/codec/cwfa.h
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/codec/decoder.c b/converter/other/fiasco/codec/decoder.c
index 26284596..229996b6 100644
--- a/converter/other/fiasco/codec/decoder.c
+++ b/converter/other/fiasco/codec/decoder.c
@@ -3,11 +3,11 @@
  *
  *  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>
+ *
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
- 
+
 /*
  *  $Date: 2000/10/22 10:44:48 $
  *  $Author: hafner $
@@ -37,7 +37,7 @@
 /*****************************************************************************
 
                 prototypes
-  
+
 *****************************************************************************/
 
 static void
@@ -63,7 +63,7 @@ duplicate_state_image (const word_t *domain, unsigned offset, unsigned level);
 /*****************************************************************************
 
                 public code
-  
+
 *****************************************************************************/
 
 video_t *
@@ -78,7 +78,7 @@ alloc_video (bool_t store_wfa)
  */
 {
    video_t *video = Calloc (1, sizeof (video_t));
-   
+
    video->future_display = -1;
    video->display        = 0;
 
@@ -138,13 +138,13 @@ get_next_frame (bool_t store_wfa, int enlarge_factor,
  *  Get next frame of the WFA 'video' from stream 'input'.
  *  'orig_wfa' is the constant part of the WFA used by all frames.
  *  Depending on values of 'enlarge_factor' and 'smoothing' enlarge and
- *  smooth image, respectively. 
+ *  smooth image, respectively.
  *  If 'store_wfa' is TRUE, then store WFA structure of reference frames
  *  (used by analysis tool xwfa).
  *  If 'reference_frame' is not NULL, then load image 'reference_frame'
  *  from disk.
  *  'format' gives the color format to be used (either 4:2:0 or 4:4:4).
- *  If 'timer' is not NULL, then accumulate running time statistics. 
+ *  If 'timer' is not NULL, then accumulate running time statistics.
  *
  *  Return value:
  *  pointer to decoded frame
@@ -157,7 +157,7 @@ get_next_frame (bool_t store_wfa, int enlarge_factor,
    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
@@ -189,7 +189,7 @@ get_next_frame (bool_t store_wfa, int enlarge_factor,
      clock_t       ptimer;
      unsigned int  stop_timer [3];
      wfa_t        *tmp_wfa = NULL;
-     
+
      if (!store_wfa)
         video->wfa = orig_wfa;
      else
@@ -198,7 +198,7 @@ get_next_frame (bool_t store_wfa, int enlarge_factor,
         copy_wfa (tmp_wfa, video->wfa);
         copy_wfa (video->wfa, orig_wfa);
      }
-   
+
      /*
       *  First step: read WFA from disk
       */
@@ -210,7 +210,7 @@ get_next_frame (bool_t store_wfa, int enlarge_factor,
         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)
@@ -225,7 +225,7 @@ get_next_frame (bool_t store_wfa, int enlarge_factor,
         video->frame  = read_image_file (reference_frame);
         video->sframe = NULL;
      }
-   
+
      /*
       *  Depending on current frame type update past and future frames
       */
@@ -313,7 +313,7 @@ get_next_frame (bool_t store_wfa, int enlarge_factor,
      }
      if (tmp_wfa)
         free_wfa (tmp_wfa);
-     
+
      current_frame_is_future_frame = NO;
      /*
       *  Second step: decode image
@@ -323,7 +323,7 @@ get_next_frame (bool_t store_wfa, int 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)
@@ -332,10 +332,10 @@ get_next_frame (bool_t store_wfa, int enlarge_factor,
         if (enlarge_factor > 0)
         {
            orig_width  = video->wfa->wfainfo->width  << enlarge_factor;
-           orig_height = video->wfa->wfainfo->height << 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)
@@ -343,7 +343,7 @@ get_next_frame (bool_t store_wfa, int enlarge_factor,
            if (orig_height & 1)
           orig_height++;
         }
-     
+
         frame = decode_image (orig_width, orig_height, format,
                   timer != NULL ? stop_timer : NULL,
                   video->wfa);
@@ -381,7 +381,7 @@ get_next_frame (bool_t store_wfa, int enlarge_factor,
      }
      else
         sframe = NULL;
-     
+
      stop_timer [0] = prg_timer (&ptimer, STOP);
      if (timer)
         timer->smooth [video->wfa->frame_type] += stop_timer [0];
@@ -399,7 +399,7 @@ get_next_frame (bool_t store_wfa, int enlarge_factor,
         video->future_display     = frame_number;
         current_frame_is_future_frame = YES;
      }
-      
+
      if (!store_wfa)
         remove_states (video->wfa->basis_states, video->wfa);
       } while (!video->frame);
@@ -407,7 +407,7 @@ get_next_frame (bool_t store_wfa, int enlarge_factor,
       if (!store_wfa)
      video->wfa = NULL;
    }
-   
+
    return video->sframe ? video->sframe : video->frame;
 }
 
@@ -418,8 +418,8 @@ decode_image (unsigned orig_width, unsigned orig_height, format_e format,
  *  Compute image which is represented by the given 'wfa'.
  *  'orig_width'x'orig_height' gives the resolution of the image at
  *  coding time. Use 4:2:0 subsampling or 4:4:4 'format' for color images.
- *  If 'dec_timer' is given, accumulate running time statistics. 
- *  
+ *  If 'dec_timer' is given, accumulate running time statistics.
+ *
  *  Return value:
  *  pointer to decoded image
  *
@@ -458,7 +458,7 @@ 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]);
-   
+
 
    /*
     *  Allocate frame buffer for decoded image
@@ -468,7 +468,7 @@ decode_image (unsigned orig_width, unsigned orig_height, format_e format,
    width  = MAX(width, orig_width);
    height = MAX(height, orig_height);
    frame = alloc_image (width, height, wfa->wfainfo->color, format);
-   
+
    /*
     *  Allocate buffers for intermediate state images
     */
@@ -478,7 +478,7 @@ decode_image (unsigned orig_width, unsigned orig_height, format_e format,
       wfa->level_of_state [wfa->tree[wfa->root_state][0]] = 128;
       wfa->level_of_state [wfa->tree[wfa->root_state][1]] = 128;
    }
-   alloc_state_images (&images, &offsets, frame, root_state, 0, max_level, 
+   alloc_state_images (&images, &offsets, frame, root_state, 0, max_level,
                format, wfa);
 
    if (dec_timer)
@@ -498,20 +498,20 @@ 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);
-   
+
    /*
     *  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++)
      {
@@ -546,7 +546,7 @@ decode_state (unsigned state, unsigned level, wfa_t *wfa)
  *  pointer to decoded state image
  *
  *  Side effects:
- *  'wfa' states > 'state' are removed.  
+ *  'wfa' states > 'state' are removed.
  */
 {
    word_t  *domains [2];
@@ -571,7 +571,7 @@ decode_state (unsigned state, unsigned level, wfa_t *wfa)
    {
       word_t   *src, *dst;
       unsigned  y;
-        
+
       src = domains [0];
       dst = img->pixels [GRAY];
       for (y = img->height; y; y--)
@@ -603,75 +603,73 @@ decode_range (unsigned range_state, unsigned range_label, unsigned range_level,
  *  '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 */
-   word_t    *range;
-
-   enlarge_image (range_level - (wfa->level_of_state [range_state] - 1),
-          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);
-   alloc_state_images (&images, &offsets, state_image, NULL, range_state,
-               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 */
-   {
-      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));
-   }
-   else                 /* rectangle */
-   {
-      word_t   *src, *dst;
-      unsigned  y;
-      
-      src = images [range_state + (range_level + 1) * wfa->states]
-        + 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);
-      }
-   }
-
-   if (domain != NULL)          /* copy domain images */
-   {
-      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);
-      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);
-      *domain = NULL;
-   }
-   
-   free_state_images (range_level + 1, NO, images, offsets, NULL, range_state,
-              NO, wfa);
-   free_image (state_image);
-   
-   return range;
+    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);
+    state_image = alloc_image (width_of_level (range_level + 1),
+                               height_of_level (range_level + 1),
+                               NO, FORMAT_4_4_4);
+    alloc_state_images (&images, &offsets, state_image, NULL, range_state,
+                        range_level + 1, FORMAT_4_4_4, 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 */
+    {
+        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));
+    }
+    else                 /* rectangle */
+    {
+        word_t   *src, *dst;
+        unsigned  y;
+
+        src = images [range_state + (range_level + 1) * wfa->states]
+            + 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);
+        }
+    }
+
+    if (domain != NULL)          /* copy domain images */
+    {
+        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);
+        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);
+        *domain = NULL;
+    }
+
+    free_state_images (range_level + 1, NO, images, offsets, NULL, range_state,
+                       FORMAT_4_4_4, wfa);
+    free_image (state_image);
+
+    return range;
 }
 
 void
@@ -687,7 +685,7 @@ smooth_image (unsigned sf, const wfa_t *wfa, image_t *image)
  */
 {
    int      is, inegs;          /* integer factors of s and 1 - s*/
-   unsigned state;          
+   unsigned state;
    unsigned img_width  = image->width;
    unsigned img_height = image->height;
    real_t   s          = 1.0 - sf / 200.0;
@@ -697,7 +695,7 @@ smooth_image (unsigned sf, const wfa_t *wfa, image_t *image)
 
    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]
@@ -708,10 +706,10 @@ smooth_image (unsigned sf, const wfa_t *wfa, image_t *image)
       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 */
       {
      unsigned  i;           /* line counter */
@@ -721,12 +719,12 @@ smooth_image (unsigned sf, const wfa_t *wfa, image_t *image)
      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);
@@ -748,12 +746,12 @@ smooth_image (unsigned sf, const wfa_t *wfa, image_t *image)
 
      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);
@@ -773,7 +771,7 @@ smooth_image (unsigned sf, const wfa_t *wfa, image_t *image)
 /*****************************************************************************
 
                 private code
-  
+
 *****************************************************************************/
 
 static void
@@ -791,7 +789,7 @@ enlarge_image (int enlarge_factor, format_e format, unsigned y_root,
  *  are modified.
  */
 {
-   
+
    if (enlarge_factor != 0 || format == FORMAT_4_2_0)
    {
       unsigned state;
@@ -803,11 +801,11 @@ enlarge_image (int enlarge_factor, format_e format, unsigned y_root,
       }
       else
      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);
 
@@ -857,12 +855,12 @@ compute_actual_size (unsigned luminance_root,
 {
    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]
@@ -888,7 +886,7 @@ alloc_state_images (word_t ***images, u_word_t **offsets, const image_t *frame,
  *  'max_level' fives the max. level of a linear combination.
  *  Memory is allocated for every required state image.
  *  Use 4:2:0 subsampling or 4:4:4 'format' for color images.
- *  If 'range_state' > 0 then rather compute image of 'range_state' than 
+ *  If 'range_state' > 0 then rather compute image of 'range_state' than
  *  image of 'wfa->root_state'.
  *
  *  Return values:
@@ -902,7 +900,7 @@ alloc_state_images (word_t ***images, u_word_t **offsets, const image_t *frame,
    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));
 
@@ -940,7 +938,7 @@ alloc_state_images (word_t ***images, u_word_t **offsets, const image_t *frame,
         }
       }
    }
-   
+
    /*
     *  Generate list of state images which must be computed at each level
     */
@@ -948,9 +946,9 @@ alloc_state_images (word_t ***images, u_word_t **offsets, const image_t *frame,
    {
       int      child, domain;
       unsigned state, label, edge;
-      
+
       /*
-       *  Range approximation with child. 
+       *  Range approximation with child.
        */
       for (state = 1; state < (range_state > 0 ?
                    range_state + 1 : wfa->states); state++)
@@ -992,7 +990,7 @@ alloc_state_images (word_t ***images, u_word_t **offsets, const image_t *frame,
           }
            }
       /*
-       *  Range approximation with linear combination 
+       *  Range approximation with linear combination
        */
       for (state = 1; state < (range_state > 0 ?
                    range_state + 1 : wfa->states); state++)
@@ -1010,7 +1008,7 @@ alloc_state_images (word_t ***images, u_word_t **offsets, const image_t *frame,
             = width_of_level (level - 1);
           }
            }
-      
+
    }
 
    *images  = simg;
@@ -1041,7 +1039,7 @@ free_state_images (unsigned max_level, bool_t color, word_t **state_image,
    else
    {
       unsigned state;
-      
+
       /*
        *  Initialize state image array with states at 'max_level'
        */
@@ -1055,19 +1053,19 @@ free_state_images (unsigned max_level, bool_t color, word_t **state_image,
         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 (level = max_level; level > 0; level--)
    {
       int      domain, child;
       unsigned state, label, edge;
       /*
-       *  Range approximation with child. 
+       *  Range approximation with child.
        */
       for (state = 1; state < (range_state > 0 ?
                    range_state + 1 : wfa->states); state++)
@@ -1082,7 +1080,7 @@ free_state_images (unsigned max_level, bool_t color, word_t **state_image,
           state_image [child + (level - 1) * wfa->states] = &marker;
            }
       /*
-       *  Range approximation with linear combination 
+       *  Range approximation with linear combination
        */
       for (state = 1; state < (range_state > 0 ?
                    range_state + 1 : wfa->states);
@@ -1091,7 +1089,7 @@ free_state_images (unsigned max_level, bool_t color, word_t **state_image,
         for (label = 0; label < MAXLABELS; label++)
            for (edge = 0; isedge (domain = wfa->into[state][label][edge]);
             edge++)
-          if (domain > 0    
+          if (domain > 0
               && (state_image [domain + (level - 1) * wfa->states]
               != NULL)
               && (state_image [domain + (level - 1) * wfa->states]
@@ -1114,7 +1112,7 @@ compute_state_images (unsigned max_level, word_t **simg,
  *  which are marked in the array 'simg' (offsets of state images
  *  are given by 'offset').
  *
- *  Warning: Several optimizations are used in this function making 
+ *  Warning: Several optimizations are used in this function making
  *  it difficult to understand.
  *
  *  No return value.
@@ -1125,7 +1123,7 @@ compute_state_images (unsigned max_level, word_t **simg,
  */
 {
    unsigned level, state;
-     
+
    /*
     *  Copy one-pixel images in case state_image pointer != &final distr.
     */
@@ -1140,17 +1138,17 @@ compute_state_images (unsigned max_level, word_t **simg,
     *  'weight' gives the weight in integer notation
     *  'src', 'dst', and 'idst' are pointers to the source and
     *  destination pixels (short or integer format), respectively.
-    *  Short format : one operation per register (16 bit mode). 
-    *  Integer format : two operations per register (32 bit mode). 
+    *  Short format : one operation per register (16 bit mode).
+    *  Integer format : two operations per register (32 bit mode).
     *  'src_offset', 'dst_offset', and 'dst_offset' give the number of
     *  pixels which have to be omitted when jumping to the next image row.
     */
-   for (level = 1; level <= max_level; level++) 
+   for (level = 1; level <= max_level; level++)
    {
       unsigned label;
       unsigned width  = width_of_level (level - 1);
       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++)
@@ -1178,7 +1176,7 @@ compute_state_images (unsigned max_level, word_t **simg,
           }
 
           /*
-           *  Generate the state images by adding the corresponding 
+           *  Generate the state images by adding the corresponding
            *  weighted state images:
            *  subimage [label] =
            *       weight_1 * image_1 + ... + weight_n * image_n
@@ -1228,12 +1226,12 @@ compute_state_images (unsigned max_level, word_t **simg,
             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;
@@ -1242,7 +1240,7 @@ compute_state_images (unsigned max_level, word_t **simg,
 #else                   /* not HAVE_SIGNED_SHIFT */
                *dst++ = ((weight * (int) *src++) / 1024) * 2;
 #endif /* not HAVE_SIGNED_SHIFT */
-               if (height == 2) 
+               if (height == 2)
                {
                   src += src_offset;
                   dst += dst_offset;
@@ -1258,14 +1256,14 @@ compute_state_images (unsigned max_level, word_t **simg,
                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 */
@@ -1314,11 +1312,11 @@ compute_state_images (unsigned max_level, word_t **simg,
             {
                word_t   *dst;
                unsigned  dst_offset;
-               
+
                dst        = range;
                dst_offset = offset [state + level * wfa->states]
                     - width;
-               
+
                *dst++ = weight;
                if (height == 2)
                {
@@ -1331,7 +1329,7 @@ compute_state_images (unsigned max_level, word_t **simg,
                unsigned  x, y;
                int      *idst;
                unsigned  idst_offset;
-               
+
                weight      = (weight * 65536) | (weight & 0xffff);
                idst        = (int *) range;
                idst_offset = offset [state + level * wfa->states]
@@ -1353,7 +1351,7 @@ compute_state_images (unsigned max_level, word_t **simg,
           }
           else
              edge = 0;
-          
+
           /*
            *  Add remaining weighted domain images to current range
            */
@@ -1370,12 +1368,12 @@ compute_state_images (unsigned max_level, word_t **simg,
             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;
@@ -1385,7 +1383,7 @@ compute_state_images (unsigned max_level, word_t **simg,
 #else /* not HAVE_SIGNED_SHIFT */
                *dst++ += ((weight * (int) *src++) / 1024) * 2;
 #endif /* not HAVE_SIGNED_SHIFT */
-               if (height == 2) 
+               if (height == 2)
                {
                   src += src_offset;
                   dst += dst_offset;
@@ -1401,15 +1399,15 @@ compute_state_images (unsigned max_level, word_t **simg,
                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 */
@@ -1459,11 +1457,11 @@ compute_state_images (unsigned max_level, word_t **simg,
             {
                word_t   *dst;
                unsigned  dst_offset;
-               
+
                dst        = range;
                dst_offset = offset [state + level * wfa->states]
                     - width;
-               
+
                *dst++ += weight;
                if (height == 2)
                {
@@ -1476,16 +1474,16 @@ compute_state_images (unsigned max_level, word_t **simg,
                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;
@@ -1496,14 +1494,14 @@ compute_state_images (unsigned max_level, word_t **simg,
             }
              }
           }
-           } 
+           }
    }
 }
 
 static word_t *
 duplicate_state_image (const word_t *domain, unsigned offset, unsigned level)
 /*
- *  Allocate new memory block 'pixels' and copy pixel values of 'domain' 
+ *  Allocate new memory block 'pixels' and copy pixel values of 'domain'
  *  (size and pixel offset are given by 'level' and 'offset')
  *  to the lock 'pixels'.
  *
diff --git a/converter/other/fiasco/codec/decoder.h b/converter/other/fiasco/codec/decoder.h
index 8cd211e0..7823ab91 100644
--- a/converter/other/fiasco/codec/decoder.h
+++ b/converter/other/fiasco/codec/decoder.h
@@ -3,8 +3,8 @@
  *		
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/codec/dfiasco.c b/converter/other/fiasco/codec/dfiasco.c
index 2fdec573..d45c9e39 100644
--- a/converter/other/fiasco/codec/dfiasco.c
+++ b/converter/other/fiasco/codec/dfiasco.c
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
  
 /*
diff --git a/converter/other/fiasco/codec/dfiasco.h b/converter/other/fiasco/codec/dfiasco.h
index bcc3c7f9..eed12b6b 100644
--- a/converter/other/fiasco/codec/dfiasco.h
+++ b/converter/other/fiasco/codec/dfiasco.h
@@ -3,8 +3,8 @@
  *		
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/codec/domain-pool.c b/converter/other/fiasco/codec/domain-pool.c
index 7cc3900e..1c14a30c 100644
--- a/converter/other/fiasco/codec/domain-pool.c
+++ b/converter/other/fiasco/codec/domain-pool.c
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/codec/domain-pool.h b/converter/other/fiasco/codec/domain-pool.h
index d1488779..a2c46141 100644
--- a/converter/other/fiasco/codec/domain-pool.h
+++ b/converter/other/fiasco/codec/domain-pool.h
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/codec/ip.c b/converter/other/fiasco/codec/ip.c
index ade0d916..56b0ae9a 100644
--- a/converter/other/fiasco/codec/ip.c
+++ b/converter/other/fiasco/codec/ip.c
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/codec/ip.h b/converter/other/fiasco/codec/ip.h
index e5e4dd65..0c693826 100644
--- a/converter/other/fiasco/codec/ip.h
+++ b/converter/other/fiasco/codec/ip.h
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/codec/motion.c b/converter/other/fiasco/codec/motion.c
index 876a2998..0d1fa099 100644
--- a/converter/other/fiasco/codec/motion.c
+++ b/converter/other/fiasco/codec/motion.c
@@ -4,8 +4,8 @@
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/codec/motion.h b/converter/other/fiasco/codec/motion.h
index 2ea382f7..82d1f1ec 100644
--- a/converter/other/fiasco/codec/motion.h
+++ b/converter/other/fiasco/codec/motion.h
@@ -4,8 +4,8 @@
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/codec/mwfa.c b/converter/other/fiasco/codec/mwfa.c
index 43a7dae2..0be08a4f 100644
--- a/converter/other/fiasco/codec/mwfa.c
+++ b/converter/other/fiasco/codec/mwfa.c
@@ -4,8 +4,8 @@
  *  Written by:		Michael Unger
  *			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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/codec/mwfa.h b/converter/other/fiasco/codec/mwfa.h
index 52f41866..2c1e7c9f 100644
--- a/converter/other/fiasco/codec/mwfa.h
+++ b/converter/other/fiasco/codec/mwfa.h
@@ -4,8 +4,8 @@
  *  Written by:		Michael Unger
  *			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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/codec/options.c b/converter/other/fiasco/codec/options.c
index c8e4d2e2..b9c64efd 100644
--- a/converter/other/fiasco/codec/options.c
+++ b/converter/other/fiasco/codec/options.c
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
@@ -14,6 +14,7 @@
  *  $State: Exp $
  */
 
+#define _DEFAULT_SOURCE 1 /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE 1   /* Make sure strdup() is in string.h */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
diff --git a/converter/other/fiasco/codec/options.h b/converter/other/fiasco/codec/options.h
index 3af6be01..783c41ef 100644
--- a/converter/other/fiasco/codec/options.h
+++ b/converter/other/fiasco/codec/options.h
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/codec/prediction.c b/converter/other/fiasco/codec/prediction.c
index e056d10f..e78e5acc 100644
--- a/converter/other/fiasco/codec/prediction.c
+++ b/converter/other/fiasco/codec/prediction.c
@@ -4,8 +4,8 @@
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/codec/prediction.h b/converter/other/fiasco/codec/prediction.h
index 1068501a..0646dfd8 100644
--- a/converter/other/fiasco/codec/prediction.h
+++ b/converter/other/fiasco/codec/prediction.h
@@ -4,8 +4,8 @@
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/codec/subdivide.c b/converter/other/fiasco/codec/subdivide.c
index 2ace18e4..348f838a 100644
--- a/converter/other/fiasco/codec/subdivide.c
+++ b/converter/other/fiasco/codec/subdivide.c
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/codec/subdivide.h b/converter/other/fiasco/codec/subdivide.h
index b6840e58..bd9cd10e 100644
--- a/converter/other/fiasco/codec/subdivide.h
+++ b/converter/other/fiasco/codec/subdivide.h
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/codec/tiling.c b/converter/other/fiasco/codec/tiling.c
index 21e4428a..9af43da0 100644
--- a/converter/other/fiasco/codec/tiling.c
+++ b/converter/other/fiasco/codec/tiling.c
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/codec/tiling.h b/converter/other/fiasco/codec/tiling.h
index 2eb04fe0..6668ad8b 100644
--- a/converter/other/fiasco/codec/tiling.h
+++ b/converter/other/fiasco/codec/tiling.h
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/codec/wfa.h b/converter/other/fiasco/codec/wfa.h
index 9253affd..59020fc1 100644
--- a/converter/other/fiasco/codec/wfa.h
+++ b/converter/other/fiasco/codec/wfa.h
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/codec/wfalib.c b/converter/other/fiasco/codec/wfalib.c
index 61d64d2f..90420d6f 100644
--- a/converter/other/fiasco/codec/wfalib.c
+++ b/converter/other/fiasco/codec/wfalib.c
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
@@ -14,6 +14,7 @@
  *  $State: Exp $
  */
 
+#define _DEFAULT_SOURCE 1 /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE 1   /* Make sure strdup() is in string.h */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
diff --git a/converter/other/fiasco/codec/wfalib.h b/converter/other/fiasco/codec/wfalib.h
index 4622fcd2..5c1e0907 100644
--- a/converter/other/fiasco/codec/wfalib.h
+++ b/converter/other/fiasco/codec/wfalib.h
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/display.c b/converter/other/fiasco/display.c
index cf160329..d8d32fa6 100644
--- a/converter/other/fiasco/display.c
+++ b/converter/other/fiasco/display.c
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  *		
  *  Based on mpeg2decode, (C) 1994, MPEG Software Simulation Group
  *  and      mpeg2play,   (C) 1994 Stefan Eckart
diff --git a/converter/other/fiasco/display.h b/converter/other/fiasco/display.h
index 5f30b117..8049456a 100644
--- a/converter/other/fiasco/display.h
+++ b/converter/other/fiasco/display.h
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/doc/README.LIB b/converter/other/fiasco/doc/README.LIB
index 4bf8c382..ce8a6b2b 100644
--- a/converter/other/fiasco/doc/README.LIB
+++ b/converter/other/fiasco/doc/README.LIB
@@ -1,5 +1,5 @@
 ---------------------------------------------------------------------------
-	 FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
+	 FIASCO (Fractal Image And Sequence COdec)
 		       Copyright (C) 1994-2000
      Ullrich Hafner <hafner@bigfoot.de>, http://ulli.linuxave.net
 			     Version 1.0
diff --git a/converter/other/fiasco/doc/fiasco_c_options_new.3 b/converter/other/fiasco/doc/fiasco_c_options_new.3
index 52efb86c..d873b642 100644
--- a/converter/other/fiasco/doc/fiasco_c_options_new.3
+++ b/converter/other/fiasco/doc/fiasco_c_options_new.3
@@ -429,4 +429,4 @@ Finite Automata\fP, Ph.D. thesis, Mensch & Buch Verlag, ISBN
 3-89820-002-7, October 1999.
 
 .SH AUTHOR
-Ullrich Hafner <hafner@bigfoot.de>
+Ullrich Hafner
diff --git a/converter/other/fiasco/doc/fiasco_coder.3 b/converter/other/fiasco/doc/fiasco_coder.3
index 3d1c6b87..9f2e0f8e 100644
--- a/converter/other/fiasco/doc/fiasco_coder.3
+++ b/converter/other/fiasco/doc/fiasco_coder.3
@@ -103,4 +103,4 @@ Finite Automata\fP, Ph.D. thesis, Mensch & Buch Verlag, ISBN
 3-89820-002-7, October 1999.
 
 .SH AUTHOR
-Ullrich Hafner <hafner@bigfoot.de>
+Ullrich Hafner
diff --git a/converter/other/fiasco/doc/fiasco_d_options_new.3 b/converter/other/fiasco/doc/fiasco_d_options_new.3
index 4294330a..d7f2181a 100644
--- a/converter/other/fiasco/doc/fiasco_d_options_new.3
+++ b/converter/other/fiasco/doc/fiasco_d_options_new.3
@@ -119,4 +119,4 @@ Finite Automata\fP, Ph.D. thesis, Mensch & Buch Verlag, ISBN
 3-89820-002-7, October 1999.
 
 .SH AUTHOR
-Ullrich Hafner <hafner@bigfoot.de>
+Ullrich Hafner
diff --git a/converter/other/fiasco/doc/fiasco_decoder_new.3 b/converter/other/fiasco/doc/fiasco_decoder_new.3
index 05e981a9..5d1d0d08 100644
--- a/converter/other/fiasco/doc/fiasco_decoder_new.3
+++ b/converter/other/fiasco/doc/fiasco_decoder_new.3
@@ -191,4 +191,4 @@ Finite Automata\fP, Ph.D. thesis, Mensch & Buch Verlag, ISBN
 3-89820-002-7, October 1999.
 
 .SH AUTHOR
-Ullrich Hafner <hafner@bigfoot.de>
+Ullrich Hafner
diff --git a/converter/other/fiasco/doc/fiasco_get_error_message.3 b/converter/other/fiasco/doc/fiasco_get_error_message.3
index 09d593fb..ce53e6d9 100644
--- a/converter/other/fiasco/doc/fiasco_get_error_message.3
+++ b/converter/other/fiasco/doc/fiasco_get_error_message.3
@@ -38,4 +38,4 @@ Finite Automata\fP, Ph.D. thesis, Mensch & Buch Verlag, ISBN
 3-89820-002-7, October 1999.
 
 .SH AUTHOR
-Ullrich Hafner <hafner@bigfoot.de>
+Ullrich Hafner
diff --git a/converter/other/fiasco/doc/fiasco_image_new.3 b/converter/other/fiasco/doc/fiasco_image_new.3
index 10625b63..ac3297bf 100644
--- a/converter/other/fiasco/doc/fiasco_image_new.3
+++ b/converter/other/fiasco/doc/fiasco_image_new.3
@@ -92,4 +92,4 @@ Finite Automata\fP, Ph.D. thesis, Mensch & Buch Verlag, ISBN
 3-89820-002-7, October 1999.
 
 .SH AUTHOR
-Ullrich Hafner <hafner@bigfoot.de>
+Ullrich Hafner
diff --git a/converter/other/fiasco/doc/fiasco_options_new.3 b/converter/other/fiasco/doc/fiasco_options_new.3
index 26e070ca..f26586a7 100644
--- a/converter/other/fiasco/doc/fiasco_options_new.3
+++ b/converter/other/fiasco/doc/fiasco_options_new.3
@@ -438,4 +438,4 @@ Finite Automata\fP, Ph.D. thesis, Mensch & Buch Verlag, ISBN
 3-89820-002-7, October 1999.
 
 .SH AUTHOR
-Ullrich Hafner <hafner@bigfoot.de>
+Ullrich Hafner
diff --git a/converter/other/fiasco/doc/fiasco_renderer_new.3 b/converter/other/fiasco/doc/fiasco_renderer_new.3
index b24d8462..8b5e47cd 100644
--- a/converter/other/fiasco/doc/fiasco_renderer_new.3
+++ b/converter/other/fiasco/doc/fiasco_renderer_new.3
@@ -122,4 +122,4 @@ Finite Automata\fP, Ph.D. thesis, Mensch & Buch Verlag, ISBN
 3-89820-002-7, October 1999.
 
 .SH AUTHOR
-Ullrich Hafner <hafner@bigfoot.de>
+Ullrich Hafner
diff --git a/converter/other/fiasco/doc/fiasco_set_verbosity.3 b/converter/other/fiasco/doc/fiasco_set_verbosity.3
index 746854b1..3e88399a 100644
--- a/converter/other/fiasco/doc/fiasco_set_verbosity.3
+++ b/converter/other/fiasco/doc/fiasco_set_verbosity.3
@@ -43,4 +43,4 @@ Finite Automata\fP, Ph.D. thesis, Mensch & Buch Verlag, ISBN
 3-89820-002-7, October 1999.
 
 .SH AUTHOR
-Ullrich Hafner <hafner@bigfoot.de>
+Ullrich Hafner
diff --git a/converter/other/fiasco/fiasco.h b/converter/other/fiasco/fiasco.h
index 59367bb8..88f999fc 100644
--- a/converter/other/fiasco/fiasco.h
+++ b/converter/other/fiasco/fiasco.h
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  *
  *  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
diff --git a/converter/other/fiasco/fiascotopnm.c b/converter/other/fiasco/fiascotopnm.c
index dfba2256..e95e27fc 100644
--- a/converter/other/fiasco/fiascotopnm.c
+++ b/converter/other/fiasco/fiascotopnm.c
@@ -4,8 +4,8 @@
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
  
 /*
@@ -15,6 +15,7 @@
  *  $State: Exp $
  */
 
+#define _DEFAULT_SOURCE 1 /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE 1   /* Make sure strdup() is in string.h */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
@@ -361,6 +362,8 @@ video_decoder (const char *wfa_name, const char *image_name, bool_t panel,
                 while (prg_timer (&fps_timer, STOP) < frame_time) /* wait */
                     ;
             }
+#else
+            if (frame_time) {/* defeat compiler warning */}
 #endif /* not X_DISPLAY_MISSING */   
         }
         free (filename);
diff --git a/converter/other/fiasco/input/basis.c b/converter/other/fiasco/input/basis.c
index 4a748f61..aa371ea1 100644
--- a/converter/other/fiasco/input/basis.c
+++ b/converter/other/fiasco/input/basis.c
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/input/basis.h b/converter/other/fiasco/input/basis.h
index fa26bca2..e2242198 100644
--- a/converter/other/fiasco/input/basis.h
+++ b/converter/other/fiasco/input/basis.h
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/input/matrices.c b/converter/other/fiasco/input/matrices.c
index 47cde1aa..388855ea 100644
--- a/converter/other/fiasco/input/matrices.c
+++ b/converter/other/fiasco/input/matrices.c
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/input/matrices.h b/converter/other/fiasco/input/matrices.h
index ba8fd9bf..6d1cef15 100644
--- a/converter/other/fiasco/input/matrices.h
+++ b/converter/other/fiasco/input/matrices.h
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/input/mc.c b/converter/other/fiasco/input/mc.c
index 070d839e..2d4a073d 100644
--- a/converter/other/fiasco/input/mc.c
+++ b/converter/other/fiasco/input/mc.c
@@ -4,8 +4,8 @@
  *  written by: Michael Unger
  *		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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/input/mc.h b/converter/other/fiasco/input/mc.h
index 1e14d287..9d9d714d 100644
--- a/converter/other/fiasco/input/mc.h
+++ b/converter/other/fiasco/input/mc.h
@@ -4,8 +4,8 @@
  *  written by: Michael Unger
  *		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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/input/nd.c b/converter/other/fiasco/input/nd.c
index 1a68bfbf..52d48e61 100644
--- a/converter/other/fiasco/input/nd.c
+++ b/converter/other/fiasco/input/nd.c
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/input/nd.h b/converter/other/fiasco/input/nd.h
index 2c2fff4b..0fe06cf1 100644
--- a/converter/other/fiasco/input/nd.h
+++ b/converter/other/fiasco/input/nd.h
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/input/read.c b/converter/other/fiasco/input/read.c
index e6e2d7e8..9f4ac993 100644
--- a/converter/other/fiasco/input/read.c
+++ b/converter/other/fiasco/input/read.c
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
@@ -14,6 +14,7 @@
  *  $State: Exp $
  */
 
+#define _DEFAULT_SOURCE 1 /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE 1   /* Make sure strdup() is in string.h */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
diff --git a/converter/other/fiasco/input/read.h b/converter/other/fiasco/input/read.h
index d0d0ee13..1f59f103 100644
--- a/converter/other/fiasco/input/read.h
+++ b/converter/other/fiasco/input/read.h
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/input/tree.c b/converter/other/fiasco/input/tree.c
index e3e7117e..0ac2b6ae 100644
--- a/converter/other/fiasco/input/tree.c
+++ b/converter/other/fiasco/input/tree.c
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/input/tree.h b/converter/other/fiasco/input/tree.h
index e4b5f2d8..f743ee98 100644
--- a/converter/other/fiasco/input/tree.h
+++ b/converter/other/fiasco/input/tree.h
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/input/weights.c b/converter/other/fiasco/input/weights.c
index 15c35731..8a1e8899 100644
--- a/converter/other/fiasco/input/weights.c
+++ b/converter/other/fiasco/input/weights.c
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/input/weights.h b/converter/other/fiasco/input/weights.h
index 1e2285a9..36cea5c2 100644
--- a/converter/other/fiasco/input/weights.h
+++ b/converter/other/fiasco/input/weights.h
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/lib/arith.c b/converter/other/fiasco/lib/arith.c
index dc35d1d1..825d4757 100644
--- a/converter/other/fiasco/lib/arith.c
+++ b/converter/other/fiasco/lib/arith.c
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/lib/arith.h b/converter/other/fiasco/lib/arith.h
index 744eb9d7..04297eb5 100644
--- a/converter/other/fiasco/lib/arith.h
+++ b/converter/other/fiasco/lib/arith.h
@@ -1,8 +1,8 @@
 /*
  *  arith.h
  *
- *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/lib/bit-io.c b/converter/other/fiasco/lib/bit-io.c
index 1bfef598..f69343e6 100644
--- a/converter/other/fiasco/lib/bit-io.c
+++ b/converter/other/fiasco/lib/bit-io.c
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
  
 /*
@@ -14,6 +14,7 @@
  *  $State: Exp $
  */
 
+#define _DEFAULT_SOURCE 1 /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE 1   /* Make sure strdup() is in string.h */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
diff --git a/converter/other/fiasco/lib/bit-io.h b/converter/other/fiasco/lib/bit-io.h
index d37cc47c..08029824 100644
--- a/converter/other/fiasco/lib/bit-io.h
+++ b/converter/other/fiasco/lib/bit-io.h
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/lib/dither.c b/converter/other/fiasco/lib/dither.c
index accd9dd6..669bb16c 100644
--- a/converter/other/fiasco/lib/dither.c
+++ b/converter/other/fiasco/lib/dither.c
@@ -3,8 +3,8 @@
  *
  *  Adapted 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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
@@ -713,15 +713,11 @@ display_24_bit_bgr (const struct fiasco_renderer *this, unsigned char *ximage,
       word_t 	   *cbptr, *crptr;	/* pointer to chroma bands */
       word_t 	   *yptr;		/* pointers to lumincance band */
       int 	   *Cr_r_tab, *Cr_g_tab, *Cb_g_tab, *Cb_b_tab;
-      unsigned int *r_table, *g_table, *b_table;
 
       Cr_g_tab = private->Cr_g_tab;
       Cr_r_tab = private->Cr_r_tab;
       Cb_b_tab = private->Cb_b_tab;
       Cb_g_tab = private->Cb_g_tab;
-      r_table  = private->r_table;
-      g_table  = private->g_table;
-      b_table  = private->b_table;
       yptr     = image->pixels [Y];
       cbptr    = image->pixels [Cb];
       crptr    = image->pixels [Cr];
@@ -1044,9 +1040,7 @@ display_24_bit_bgr (const struct fiasco_renderer *this, unsigned char *ximage,
    {
       unsigned int *dst;		/* pointer to dithered pixels */
       word_t	   *src;		/* current pixel of frame */
-      unsigned int *y_table;
 
-      y_table = private->y_table;
       dst     = (unsigned int *) out;
       src     = image->pixels [GRAY];
 
@@ -1164,15 +1158,11 @@ display_24_bit_rgb (const struct fiasco_renderer *this, unsigned char *ximage,
       word_t 	   *cbptr, *crptr;	/* pointer to chroma bands */
       word_t 	   *yptr;		/* pointers to lumincance band */
       int 	   *Cr_r_tab, *Cr_g_tab, *Cb_g_tab, *Cb_b_tab;
-      unsigned int *r_table, *g_table, *b_table;
 
       Cr_g_tab = private->Cr_g_tab;
       Cr_r_tab = private->Cr_r_tab;
       Cb_b_tab = private->Cb_b_tab;
       Cb_g_tab = private->Cb_g_tab;
-      r_table  = private->r_table;
-      g_table  = private->g_table;
-      b_table  = private->b_table;
       yptr     = image->pixels [Y];
       cbptr    = image->pixels [Cb];
       crptr    = image->pixels [Cr];
@@ -1495,9 +1485,7 @@ display_24_bit_rgb (const struct fiasco_renderer *this, unsigned char *ximage,
    {
       unsigned int *dst;		/* pointer to dithered pixels */
       word_t	   *src;		/* current pixel of frame */
-      unsigned int *y_table;
 
-      y_table = private->y_table;
       dst     = (unsigned int *) out;
       src     = image->pixels [GRAY];
 
diff --git a/converter/other/fiasco/lib/dither.h b/converter/other/fiasco/lib/dither.h
index 71f9d3c3..767bca28 100644
--- a/converter/other/fiasco/lib/dither.h
+++ b/converter/other/fiasco/lib/dither.h
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/lib/error.c b/converter/other/fiasco/lib/error.c
index ee3afe1f..394f896f 100644
--- a/converter/other/fiasco/lib/error.c
+++ b/converter/other/fiasco/lib/error.c
@@ -3,29 +3,19 @@
  *
  *  Written by:		Stefan Frank
  *			Ullrich Hafner
- *  
+ *
  *  Credits:	Modelled after variable argument routines from Jef
- *		Poskanzer's pbmplus package. 
+ *		Poskanzer's pbmplus package.
  *
- *  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>
-
-    "int dummy = " change to int dummy; dummy =" for Netpbm to avoid 
-    unused variable warning.
-
- */
-
-/*
- *  $Date: 2000/06/14 20:49:37 $
- *  $Author: hafner $
- *  $Revision: 5.1 $
- *  $State: Exp $
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 #define _ERROR_C
 
 #include "config.h"
 
+#include <stdbool.h>
 #include <stdio.h>
 #include <errno.h>
 
@@ -47,7 +37,7 @@
 /*****************************************************************************
 
 			     local variables
-  
+
 *****************************************************************************/
 
 static fiasco_verbosity_e  verboselevel  = FIASCO_SOME_VERBOSITY;
@@ -60,234 +50,237 @@ jmp_buf env;
 /*****************************************************************************
 
 			       public code
-  
+
 *****************************************************************************/
 
 void
-set_error (const char *format, ...)
-/*
- *  Set error text to given string.
- */
-{
-   va_list     args;
-   unsigned    len = 0;
-   const char *str = format;
-   
-   VA_START (args, format);
-
-   len = strlen (format);
-   while ((str = strchr (str, '%')))
-   {
-      str++;
-      if (*str == 's')
-      {
-	 char *vstring = va_arg (args, char *);
-	 len += strlen (vstring);
-      }
-      else if (*str == 'd')
-      {
-	 int dummy;
-     dummy = va_arg (args, int);
-	 len += 10;
-      }
-      else if (*str == 'c')
-      {
-	 int dummy;
-     dummy = va_arg (args, int);
-	 len += 1;
-      }
-      else
-	 return;
-      str++;
-   }
-   va_end(args);
-
-   VA_START (args, format);
-
-   if (error_message)
-      Free (error_message);
-   error_message = Calloc (len, sizeof (char));
-   
-   vsprintf (error_message, format, args);
-
-   va_end (args);
+set_error(const char * const format, ...) {
+/*----------------------------------------------------------------------------
+  Set error text to given string.
+-----------------------------------------------------------------------------*/
+    va_list      args;
+    unsigned     len;
+    bool         error;
+    const char * str;
+
+    VA_START (args, format);
+
+    /* Compute how long the error text will be: 'len' */
+
+    for (len = strlen(format), str = &format[0], error = false;
+         *str && !error; ) {
+
+        str = strchr(str, '%');
+
+        if (*str) {
+            ++str; /* Move past % */
+            if (*str == 's') {
+                char * const vstring = va_arg (args, char *);
+                len += strlen(vstring);
+            } else if (*str == 'd') {
+                (void)va_arg(args, int);
+                len += 10;
+            } else if (*str == 'c') {
+                (void)va_arg(args, int);
+                len += 1;
+            } else
+                error = true;
+            if (!error)
+                ++str;
+        }
+    }
+    va_end(args);
+
+    if (!error) {
+        VA_START(args, format);
+
+        if (error_message)
+            Free(error_message);
+        error_message = Calloc(len, sizeof (char));
+
+        vsprintf(error_message, format, args);
+
+        va_end(args);
+    }
 }
 
+
+
 void
-error (const char *format, ...)
-/*
- *  Set error text to given string.
- */
-{
-   va_list     args;
-   unsigned    len = 0;
-   const char *str = format;
-   
-   VA_START (args, format);
-
-   len = strlen (format);
-   while ((str = strchr (str, '%')))
-   {
-      str++;
-      if (*str == 's')
-      {
-	 char *vstring = va_arg (args, char *);
-	 len += strlen (vstring);
-      }
-      else if (*str == 'd')
-      {
-	 int dummy;
-     dummy = va_arg (args, int);
-	 len += 10;
-      }
-      else if (*str == 'c')
-      {
-	 int dummy;
-     dummy = va_arg (args, int);
-	 len += 1;
-      }
-      else
-      {
+error(const char * const format, ...) {
+/*----------------------------------------------------------------------------
+  Set error text to given string.
+-----------------------------------------------------------------------------*/
+    va_list      args;
+    unsigned     len;
+    const char * str;
+
+    len = 0; /* initial value */
+    str = &format[0];  /* initial value */
+
+    VA_START (args, format);
+
+    len = strlen (format);
+    while ((str = strchr (str, '%'))) {
+        ++str;
+        if (*str == 's') {
+            char * const vstring = va_arg (args, char *);
+            len += strlen(vstring);
+        } else if (*str == 'd') {
+            (void)va_arg(args, int);
+            len += 10;
+        } else if (*str == 'c') {
+            (void)va_arg(args, int);
+            len += 1;
+        } else {
 #if HAVE_SETJMP_H
-	 longjmp (env, 1);
-#else /* not HAVE_SETJMP_H */
-	 exit (1);
-#endif /* HAVE_SETJMP_H */
-      };
-      
-      str++;
-   }
-   va_end(args);
-
-   VA_START (args, format);
-
-   if (error_message)
-      Free (error_message);
-   error_message = Calloc (len, sizeof (char));
-   
-   vsprintf (error_message, format, args);
-
-   va_end (args);
-   
+            longjmp(env, 1);
+#else
+            exit(1);
+#endif
+        };
+
+        ++str;
+    }
+    va_end(args);
+
+    VA_START(args, format);
+
+    if (error_message)
+        Free(error_message);
+    error_message = Calloc(len, sizeof (char));
+
+    vsprintf(error_message, format, args);
+
+    va_end(args);
+
 #if HAVE_SETJMP_H
-   longjmp (env, 1);
-#else /* not HAVE_SETJMP_H */
-   exit (1);
-#endif /* HAVE_SETJMP_H */
+    longjmp(env, 1);
+#else
+    exit(1);
+#endif
 }
 
+
+
 const char *
-fiasco_get_error_message (void)
-/*
- *  Return value:
- *	Last error message of FIASCO library.
- */
-{
-   return error_message ? error_message : "";
+fiasco_get_error_message(void) {
+/*----------------------------------------------------------------------------
+  Last error message of FIASCO library.
+-----------------------------------------------------------------------------*/
+    return error_message ? error_message : "";
 }
 
+
+
 const char *
-get_system_error (void)
-{
-   return strerror (errno);
+get_system_error(void) {
+    return strerror(errno);
 }
 
+
+
 void
-file_error (const char *filename)
-/*
- *  Print file error message and exit.
- *
- *  No return value.
- */
-{
-   error ("File `%s': I/O Error - %s.", filename, get_system_error ());
+file_error(const char * const filename) {
+/*----------------------------------------------------------------------------
+   Print file error message and exit.
+-----------------------------------------------------------------------------*/
+    error("File `%s': I/O Error - %s.", filename, get_system_error ());
 }
 
-void 
-warning (const char *format, ...)
-/*
- *  Issue a warning and continue execution.
- *
- *  No return value.
- */
-{
-   va_list	args;
-
-   VA_START (args, format);
 
-   if (verboselevel == FIASCO_NO_VERBOSITY)
-      return;
-	
-   fprintf (stderr, "Warning: ");
-   vfprintf (stderr, format, args);
-   fputc ('\n', stderr);
 
-   va_end (args);
+void
+warning(const char * const format, ...) {
+/*----------------------------------------------------------------------------
+  Issue a warning.
+-----------------------------------------------------------------------------*/
+    va_list	args;
+
+    VA_START (args, format);
+
+    if (verboselevel == FIASCO_NO_VERBOSITY) {
+        /* User doesn't want warnings */
+    } else {
+        fprintf (stderr, "Warning: ");
+        vfprintf (stderr, format, args);
+        fputc ('\n', stderr);
+    }
+    va_end (args);
 }
 
-void 
-message (const char *format, ...)
-/*
- *  Print a message to stderr.
- */
-{
-   va_list args;
-
-   VA_START (args, format);
 
-   if (verboselevel == FIASCO_NO_VERBOSITY)
-      return;
 
-   vfprintf (stderr, format, args);
-   fputc ('\n', stderr);
-   va_end (args);
+void
+message(const char * const format, ...) {
+/*----------------------------------------------------------------------------
+   Print a message to Standard Error
+-----------------------------------------------------------------------------*/
+    va_list args;
+
+    VA_START (args, format);
+
+    if (verboselevel == FIASCO_NO_VERBOSITY) {
+        /* User doesn't want messages */
+    } else {
+        vfprintf (stderr, format, args);
+        fputc ('\n', stderr);
+    }
+    va_end (args);
 }
 
-void 
-debug_message (const char *format, ...)
-/*
- *  Print a message to stderr.
- */
-{
-   va_list args;
 
-   VA_START (args, format);
-
-   if (verboselevel < FIASCO_ULTIMATE_VERBOSITY)
-      return;
-
-   fprintf (stderr, "*** ");
-   vfprintf (stderr, format, args);
-   fputc ('\n', stderr);
-   va_end (args);
-}
 
 void
-info (const char *format, ...)
-/*
- *  Print a message to stderr. Do not append a newline.
- */
-{
-   va_list args;
+debug_message(const char * const format, ...) {
+/*----------------------------------------------------------------------------
+   Print a message to Standard Error if debug messages are enabled.
+-----------------------------------------------------------------------------*/
+    va_list args;
+
+    VA_START (args, format);
+
+    if (verboselevel >= FIASCO_ULTIMATE_VERBOSITY) {
+        fprintf (stderr, "*** ");
+        vfprintf (stderr, format, args);
+        fputc ('\n', stderr);
+    }
+    va_end (args);
+}
 
-   VA_START (args, format);
 
-   if (verboselevel == FIASCO_NO_VERBOSITY)
-      return;
 
-   vfprintf (stderr, format, args);
-   fflush (stderr);
-   va_end (args);
+void
+info(const char * const format, ...) {
+/*----------------------------------------------------------------------------
+   Print a message to stderr. Do not append a newline.
+-----------------------------------------------------------------------------*/
+    va_list args;
+
+    VA_START (args, format);
+
+    if (verboselevel == FIASCO_NO_VERBOSITY) {
+        /* User doesn't want informational messages */
+    } else {
+        vfprintf (stderr, format, args);
+        fflush (stderr);
+    }
+    va_end (args);
 }
 
+
+
 void
-fiasco_set_verbosity (fiasco_verbosity_e level)
-{
+fiasco_set_verbosity(fiasco_verbosity_e const level) {
    verboselevel = level;
 }
 
+
+
 fiasco_verbosity_e
-fiasco_get_verbosity (void)
-{
+fiasco_get_verbosity(void) {
    return verboselevel;
 }
+
+
+
diff --git a/converter/other/fiasco/lib/error.h b/converter/other/fiasco/lib/error.h
index 288b25f4..4159ccd2 100644
--- a/converter/other/fiasco/lib/error.h
+++ b/converter/other/fiasco/lib/error.h
@@ -4,8 +4,8 @@
  *  Written by:     Stefan Frank
  *          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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 #ifndef ERROR_H_INCLUDED
diff --git a/converter/other/fiasco/lib/image.c b/converter/other/fiasco/lib/image.c
index fa3b2db5..a700fe88 100644
--- a/converter/other/fiasco/lib/image.c
+++ b/converter/other/fiasco/lib/image.c
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/lib/image.h b/converter/other/fiasco/lib/image.h
index 00978526..a87a3c05 100644
--- a/converter/other/fiasco/lib/image.h
+++ b/converter/other/fiasco/lib/image.h
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/lib/list.c b/converter/other/fiasco/lib/list.c
index bb4efae1..b67ff4b8 100644
--- a/converter/other/fiasco/lib/list.c
+++ b/converter/other/fiasco/lib/list.c
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/lib/list.h b/converter/other/fiasco/lib/list.h
index db7c08b2..68577dfd 100644
--- a/converter/other/fiasco/lib/list.h
+++ b/converter/other/fiasco/lib/list.h
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/lib/macros.h b/converter/other/fiasco/lib/macros.h
index 9968110a..0bc80e7c 100644
--- a/converter/other/fiasco/lib/macros.h
+++ b/converter/other/fiasco/lib/macros.h
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/lib/misc.c b/converter/other/fiasco/lib/misc.c
index 782ed1e9..c5629c5c 100644
--- a/converter/other/fiasco/lib/misc.c
+++ b/converter/other/fiasco/lib/misc.c
@@ -6,8 +6,8 @@
  *  Written by:		Stefan Frank
  *			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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/lib/misc.h b/converter/other/fiasco/lib/misc.h
index 28fd8b5a..6f643b7a 100644
--- a/converter/other/fiasco/lib/misc.h
+++ b/converter/other/fiasco/lib/misc.h
@@ -4,8 +4,8 @@
  *  Written by:		Stefan Frank
  *			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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/lib/rpf.c b/converter/other/fiasco/lib/rpf.c
index 84c1f9b6..e6ff6e09 100644
--- a/converter/other/fiasco/lib/rpf.c
+++ b/converter/other/fiasco/lib/rpf.c
@@ -1,10 +1,10 @@
 /*
- *  rpf.c:		Conversion of float to reduced precision format values
+ *  rpf.c:      Conversion of float to reduced precision format values
  *
- *  Written by:		Stefan Frank
- *			Richard Krampfl
- *			Ullrich Hafner
- *		
+ *  Written by:     Stefan Frank
+ *          Richard Krampfl
+ *          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>
  */
@@ -18,6 +18,7 @@
 
 #include "pm_config.h"
 #include "config.h"
+#include "mallocvar.h"
 
 #include "types.h"
 #include "macros.h"
@@ -26,39 +27,37 @@
 #include "misc.h"
 #include "rpf.h"
 
-/* 
- * CAUTION: The IEEE float format must be used by your compiler,
- *          or all following code is void!
- */
+int const RPF_ZERO = -1;
 
-#if BYTE_ORDER == BIG_ENDIAN
-/*
- *  Big-Endian Architecture (e.g. SUN, Motorola)
- *  Memory representation of integer 0x00112233 is 00,11,22,33
- */
+/*****************************************************************************
 
-enum real_bytes {BYTE_0, BYTE_1, BYTE_2, BYTE_3};
+                   private code
+  
+*****************************************************************************/
 
-#else
-/*
- *  Little-Endian Architecture (e.g. Intel, VAX, Alpha)
- *  Memory representation of integer 0x00112233 is 33,22,11,00
- */
 
-enum real_bytes {BYTE_3, BYTE_2, BYTE_1, BYTE_0};
+typedef struct {
+    double fraction;
+    int    exponent;
+}  FracExp;
 
-#endif
 
-const int RPF_ZERO = -1;
 
-/*****************************************************************************
+static FracExp
+fracExpFromDouble(double const x) {
+
+    FracExp retval;
+
+    retval.fraction = frexp(x, &retval.exponent);
+
+    return retval;
+}
+
 
-			       private code
-  
-*****************************************************************************/
 
 int
-rtob (real_t f, const rpf_t *rpf)
+rtob (real_t        const f,
+      const rpf_t * const rpfP)
 /*
  *  Convert real number 'f' into fixed point format.
  *  The real number in [-'range'; +'range'] is scaled to [-1 ; +1].
@@ -66,111 +65,98 @@ rtob (real_t f, const rpf_t *rpf)
  *  packed into one integer.  
  *
  *  Return value:
- *	real value in reduced precision format
+ *  real value in reduced precision format
  */
 {  
-   unsigned int	mantissa;
-   int		exponent, sign;
-   union
-   {
-      float f;
-      unsigned char c[4];
-   } v;					/* conversion dummy */
-
-   f  /= rpf->range;			/* scale f to [-1,+1] */	
-   v.f = f;
-
-   /*
-    *  Extract mantissa (23 Bits), exponent (8 Bits) and sign (1 Bit)
-    */
-
-   mantissa = ((((v.c[BYTE_1] & 127) << 8 ) | v.c[BYTE_2]) << 8) | v.c[BYTE_3];
-   exponent = (((v.c[BYTE_0] & 127) << 1) | (v.c[BYTE_1] & 128 ? 1 : 0)) - 126;
-   sign     = v.c[BYTE_0] & 128 ? 1 : 0;
-		
-   /*
-    *  Generate reduced precision mantissa.
-    */
-   mantissa >>= 1;				/* shift 1 into from left */
-   mantissa  |= (1 << 22);
-   if (exponent > 0) 
-      mantissa <<= exponent;
-   else
-      mantissa >>= -exponent;  
+    /*
+     *  Extract mantissa (23 Bits), exponent (8 Bits) and sign (1 Bit)
+     */
+
+    double const normalized = f / rpfP->range;
+        /* 'f' scaled to [-1,+1] */    
+    FracExp const fracExp = fracExpFromDouble(normalized);
+    unsigned int const signedMantissa =
+        (unsigned int) (fracExp.fraction * (1<<23));
+
+    unsigned int mantissa;
+    unsigned int sign;  /* 0 for positive; 1 for negative */
+    
+    if (signedMantissa < 0) {
+        mantissa = -signedMantissa;
+        sign = 1;
+    } else {
+        mantissa = +signedMantissa;
+        sign = 0;
+    }
+
+    /*
+     *  Generate reduced precision mantissa.
+     */
+    if (fracExp.exponent > 0) 
+        mantissa <<= fracExp.exponent;
+    else
+        mantissa >>= -fracExp.exponent;  
+    
+    mantissa >>= (23 - rpfP->mantissa_bits - 1);
+
+    mantissa +=  1;          /* Round last bit. */
+    mantissa >>= 1;
    
-   mantissa >>= (23 - rpf->mantissa_bits - 1);
-
-   mantissa +=  1;			/* Round last bit. */
-   mantissa >>= 1;
-   
-   if (mantissa == 0)			/* close to zero */
-      return RPF_ZERO;
-   else if (mantissa >= (1U << rpf->mantissa_bits)) /* overflow */
-      return sign;
-   else
-      return ((mantissa & ((1U << rpf->mantissa_bits) - 1)) << 1) | sign;
+    if (mantissa == 0)           /* close to zero */
+        return RPF_ZERO;
+    else if (mantissa >= (1U << rpfP->mantissa_bits)) /* overflow */
+        return sign;
+    else
+        return ((mantissa & ((1U << rpfP->mantissa_bits) - 1)) << 1) | sign;
 }
 
+
+
 float
-btor (int binary, const rpf_t *rpf)
+btor (int           const binary,
+      const rpf_t * const rpfP)
 /*
  *  Convert value 'binary' in reduced precision format to a real value.
- *  For more information refer to function lin_rtob() above.
+ *  For more information refer to function rtob() above.
  *
  *  Return value:
- *	converted value
+ *  converted value
  */
 {
-   unsigned int	mantissa;
-   int		sign, exponent;
-   union
-   {
-      float f;
-      unsigned char c[4];
-   } value;
-
-   if (binary == RPF_ZERO)
-      return 0;
-
-   if (binary < 0 || binary >= 1 << (rpf->mantissa_bits + 1))
-      error ("Reduced precision format: value %d out of range.", binary);
-
-   /*
-    *  Restore IEEE float format:
-    *  mantissa (23 Bits), exponent (8 Bits) and sign (1 Bit)
-    */
+    unsigned int mantissa;
+    float sign;
+    float f;
+ 
+    if (binary == RPF_ZERO)
+        return 0;
+
+    if (binary < 0 || binary >= 1 << (rpfP->mantissa_bits + 1))
+        error ("Reduced precision format: value %d out of range.", binary);
+
+    /*
+     *  Restore IEEE float format:
+     *  mantissa (23 Bits), exponent (8 Bits) and sign (1 Bit)
+     */
    
-   sign       = binary & 1;
-   mantissa   = (binary & ((1 << (rpf->mantissa_bits + 1)) - 1)) >> 1; 
-   mantissa <<= (23 - rpf->mantissa_bits);
-   exponent   = 0;
-
-   if (mantissa == 0)
-   {
-      value.f = (sign ? -1.0 : 1.0);
-   }
-   else
-   {
-      while (!(mantissa & (1 << 22)))	/* normalize mantissa */
-      {
-	 exponent--;
-	 mantissa <<= 1;
-      }
-      mantissa <<= 1;
-
-      value.c[BYTE_0] = (sign << 7) | ((exponent + 126) >> 1);
-      value.c[BYTE_1] = (((exponent + 126) & 1) << 7)
-			| ((mantissa  >> 16) & 127);
-      value.c[BYTE_2] = (mantissa >> 8) & 255;
-      value.c[BYTE_3] = mantissa & 255;
-   }
+    sign       = (binary & 0x1) == 0 ? 1.0 : -1.0;
+    mantissa   = (binary & ((0x1 << (rpfP->mantissa_bits + 1)) - 1)) >> 1; 
+    mantissa <<= (23 - rpfP->mantissa_bits);
+
+    if (mantissa == 0) 
+        f = sign;
+    else
+        f =  sign * (float) mantissa / 8388608;
    
-   return value.f * rpf->range;		/* expand [ -1 ; +1 ] to
-					   [ -range ; +range ] */
+    return f * rpfP->range;       /* expand [ -1 ; +1 ] to
+                                     [ -range ; +range ] */
 }
 
+
+
+
 rpf_t *
-alloc_rpf (unsigned mantissa, fiasco_rpf_range_e range)
+alloc_rpf (unsigned           const mantissa,
+           fiasco_rpf_range_e const range)
 /*
  *  Reduced precision format constructor.
  *  Allocate memory for the rpf_t structure.
@@ -180,45 +166,47 @@ alloc_rpf (unsigned mantissa, fiasco_rpf_range_e range)
  *  returned. 
  *
  *  Return value
- *	pointer to the new rpf structure
+ *  pointer to the new rpf structure
  */
 {
-   rpf_t *rpf = Calloc (1, sizeof (rpf_t));
+    rpf_t * rpfP;
+
+    MALLOCVAR(rpfP);
    
-   if (mantissa < 2)
-   {
-      warning (_("Size of RPF mantissa has to be in the interval [2,8]. "
-		 "Using minimum value 2.\n"));
-      mantissa = 2;
-   }
-   else if (mantissa > 8)
-   {
-      warning (_("Size of RPF mantissa has to be in the interval [2,8]. "
-		 "Using maximum value 8.\n"));
-      mantissa = 2;
-   }
-
-   rpf->mantissa_bits = mantissa;
-   rpf->range_e       = range;
-   switch (range)
-   {
-      case FIASCO_RPF_RANGE_0_75:
-	 rpf->range = 0.75;
-	 break;
-      case FIASCO_RPF_RANGE_1_50:
-	 rpf->range = 1.50;
-	 break;
-      case FIASCO_RPF_RANGE_2_00:
-	 rpf->range = 2.00;
-	 break;
-      case FIASCO_RPF_RANGE_1_00:
-	 rpf->range = 1.00;
-	 break;
-      default:
-	 warning (_("Invalid RPF range specified. Using default value 1.0."));
-	 rpf->range   = 1.00;
-	 rpf->range_e = FIASCO_RPF_RANGE_1_00;
-	 break;
-   }
-   return rpf;
+    if (mantissa < 2) {
+        warning (_("Size of RPF mantissa has to be in the interval [2,8]. "
+                   "Using minimum value 2.\n"));
+        rpfP->mantissa_bits = 2;
+    } else if (mantissa > 8) {
+        warning (_("Size of RPF mantissa has to be in the interval [2,8]. "
+                   "Using maximum value 8.\n"));
+        rpfP->mantissa_bits = 2;
+    } else
+        rpfP->mantissa_bits = mantissa;
+
+    switch (range) {
+    case FIASCO_RPF_RANGE_0_75:
+        rpfP->range   = 0.75;
+        rpfP->range_e = range;
+        break;
+    case FIASCO_RPF_RANGE_1_50:
+        rpfP->range   = 1.50;
+        rpfP->range_e = range;
+        break;
+    case FIASCO_RPF_RANGE_2_00:
+        rpfP->range   = 2.00;
+        rpfP->range_e = range;
+        break;
+    case FIASCO_RPF_RANGE_1_00:
+        rpfP->range   = 1.00;
+        rpfP->range_e = range;
+        break;
+    default:
+        warning (_("Invalid RPF range specified. Using default value 1.0."));
+        rpfP->range   = 1.00;
+        rpfP->range_e = FIASCO_RPF_RANGE_1_00;
+        break;
+    }
+    return rpfP;
 }
+
diff --git a/converter/other/fiasco/lib/rpf.h b/converter/other/fiasco/lib/rpf.h
index ba3ff6be..e543f855 100644
--- a/converter/other/fiasco/lib/rpf.h
+++ b/converter/other/fiasco/lib/rpf.h
@@ -5,8 +5,8 @@
  *			Richard Krampfl
  *			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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/lib/types.h b/converter/other/fiasco/lib/types.h
index 16d8028c..02e40a90 100644
--- a/converter/other/fiasco/lib/types.h
+++ b/converter/other/fiasco/lib/types.h
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/output/matrices.c b/converter/other/fiasco/output/matrices.c
index 01189669..7dc0e6d9 100644
--- a/converter/other/fiasco/output/matrices.c
+++ b/converter/other/fiasco/output/matrices.c
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /* NETPBM: When you call delta_encoding() with last_domain < 4, it
diff --git a/converter/other/fiasco/output/matrices.h b/converter/other/fiasco/output/matrices.h
index f880fef8..44671496 100644
--- a/converter/other/fiasco/output/matrices.h
+++ b/converter/other/fiasco/output/matrices.h
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/output/mc.c b/converter/other/fiasco/output/mc.c
index afff586b..d048bef5 100644
--- a/converter/other/fiasco/output/mc.c
+++ b/converter/other/fiasco/output/mc.c
@@ -4,8 +4,8 @@
  *  Written by:		Michael Unger
  *			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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/output/mc.h b/converter/other/fiasco/output/mc.h
index b7843fd8..cacddfe7 100644
--- a/converter/other/fiasco/output/mc.h
+++ b/converter/other/fiasco/output/mc.h
@@ -4,8 +4,8 @@
  *  Written by:		Michael Unger
  *			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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/output/nd.c b/converter/other/fiasco/output/nd.c
index a09ff762..65a85467 100644
--- a/converter/other/fiasco/output/nd.c
+++ b/converter/other/fiasco/output/nd.c
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/output/nd.h b/converter/other/fiasco/output/nd.h
index 600b3d73..01a37838 100644
--- a/converter/other/fiasco/output/nd.h
+++ b/converter/other/fiasco/output/nd.h
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/output/tree.c b/converter/other/fiasco/output/tree.c
index 0056d7dd..2eae2df9 100644
--- a/converter/other/fiasco/output/tree.c
+++ b/converter/other/fiasco/output/tree.c
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/output/tree.h b/converter/other/fiasco/output/tree.h
index 6f8a3800..50fe2279 100644
--- a/converter/other/fiasco/output/tree.h
+++ b/converter/other/fiasco/output/tree.h
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/output/weights.c b/converter/other/fiasco/output/weights.c
index 5aa17674..0203fef0 100644
--- a/converter/other/fiasco/output/weights.c
+++ b/converter/other/fiasco/output/weights.c
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/output/weights.h b/converter/other/fiasco/output/weights.h
index 271203ad..f22bd9f8 100644
--- a/converter/other/fiasco/output/weights.h
+++ b/converter/other/fiasco/output/weights.h
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/output/write.c b/converter/other/fiasco/output/write.c
index e6185ad3..d6faee26 100644
--- a/converter/other/fiasco/output/write.c
+++ b/converter/other/fiasco/output/write.c
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/output/write.h b/converter/other/fiasco/output/write.h
index a3ede1f4..6bdc2f1b 100644
--- a/converter/other/fiasco/output/write.h
+++ b/converter/other/fiasco/output/write.h
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/params.c b/converter/other/fiasco/params.c
index afacbada..7d0b84ab 100644
--- a/converter/other/fiasco/params.c
+++ b/converter/other/fiasco/params.c
@@ -4,8 +4,8 @@
  *  Written by:		Stefan Frank
  *			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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
@@ -15,6 +15,7 @@
  *  $State: Exp $
  */
 
+#define _DEFAULT_SOURCE 1 /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE 1
     /* Make sure strdup() is in string.h and strcaseeq() is in nstring.h */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
@@ -325,8 +326,17 @@ parseargs (param_t *usr_params,
 		      NO, sys_path, usr_file_name);
 	    else if (streq (params [param_index].name, "version"))
 	    {
-	       fprintf (stderr, "%s " VERSION "\n", argv [0]);
-	       exit (2);
+           fprintf (stderr, "%s " VERSION "\n", argv [0]);
+           {
+              /* Kludge for standard Netpbm version announcement */
+              char * modified_argv[2];
+              int argc;
+              modified_argv[0] = argv[0];
+              modified_argv[1] = (char *) "--version";
+              argc = 2;
+              pm_proginit(&argc, (const char **) modified_argv);
+           }
+           exit (2);
 	    }
 	    else if (streq (params [param_index].name, "verbose"))
 	       fiasco_set_verbosity (
diff --git a/converter/other/fiasco/params.h b/converter/other/fiasco/params.h
index 810a9ff0..a1164cf7 100644
--- a/converter/other/fiasco/params.h
+++ b/converter/other/fiasco/params.h
@@ -4,8 +4,8 @@
  *  Written by:     Stefan Frank
  *          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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
 
 /*
diff --git a/converter/other/fiasco/pnmtofiasco.c b/converter/other/fiasco/pnmtofiasco.c
index eebd09a9..e2656c71 100644
--- a/converter/other/fiasco/pnmtofiasco.c
+++ b/converter/other/fiasco/pnmtofiasco.c
@@ -3,8 +3,8 @@
  *
  *  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>
+ *  This file is part of FIASCO (Fractal Image And Sequence COdec)
+ *  Copyright (C) 1994-2000 Ullrich Hafner
  */
  
 /*
diff --git a/converter/other/fiasco/system.fiascorc b/converter/other/fiasco/system.fiascorc
index 86ff2da2..77d60603 100644
--- a/converter/other/fiasco/system.fiascorc
+++ b/converter/other/fiasco/system.fiascorc
@@ -3,8 +3,8 @@
 # 
 #  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>
+#  This file is part of FIASCO (Fractal Image And Sequence COdec)
+#  Copyright (C) 1994-2000 Ullrich Hafner
 
 #
 #  $Date: 2000/06/25 16:38:01 $
diff --git a/converter/other/gemtopnm.c b/converter/other/gemtopnm.c
index aac74793..d862213f 100644
--- a/converter/other/gemtopnm.c
+++ b/converter/other/gemtopnm.c
@@ -36,7 +36,7 @@
  * read 4-plane color IMG files.  Therefore changed from PBM to PPM.
  * Bryan changed it further to use the PNM facilities so it outputs
  * both PBM and PPM in the Netpbm tradition.  Name changed from
- * gemtopbm to gemtopnm.  
+ * gemtopbm to gemtopnm.
  */
 
 #include <assert.h>
@@ -50,106 +50,106 @@
 char pattern[8];
 
 static void getinit ARGS ((FILE *file, int *colsP, int *rowsP, int *padrightP,
-			   int *patlenP, int *planesP));
+               int *patlenP, int *planesP));
 
 int
 main(argc, argv)
-	int             argc;
-	char           *argv[];
+    int             argc;
+    char           *argv[];
 {
-	int     debug = 0;
-	FILE    *f;
+    int     debug = 0;
+    FILE    *f;
     int     row;
-	int     rows, cols, padright, patlen, planes;
+    int     rows, cols, padright, patlen, planes;
       /* attributes of input image */
     int type;  /* The format type (PBM/PPM) of the output image */
-	bit	*bitrow[4];
+    bit *bitrow[4];
       /* One row of input, one or four planes.  (If one, only [0] is defined)*/
     xel * xelrow;  /* One row of output */
-	const char * const usage = "[-debug] [gem IMG file]";
-	int argn;
+    const char * const usage = "[-debug] [gem IMG file]";
+    int argn;
 
 /* Process multiple planes by maintaining a separate row of bits for each
- * plane. In a single-plane image, all we have to do is write out the 
+ * plane. In a single-plane image, all we have to do is write out the
  * first plane; in a multiple-plane image, we combine them just before writing
  * out the row.
  */
-	pnm_init( &argc, argv );
-    
+    pnm_init( &argc, argv );
+
     argn = 1;
 
-	while (argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0')
-	  {
-	    if (pm_keymatch(argv[1], "-debug", 2))
-	      debug = 1;
-	    else
-	      pm_usage (usage);
-	    ++argn;
-	  }
+    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 (argc == argn)
-		f = stdin;
-	else {
-		f = pm_openr (argv[argn]);
-		++argn;
-	}
+    if (argc == argn)
+        f = stdin;
+    else {
+        f = pm_openr (argv[argn]);
+        ++argn;
+    }
 
-	if (argn != argc)
-	  pm_usage (usage);
+    if (argn != argc)
+      pm_usage (usage);
 
-	getinit (f, &cols, &rows, &padright, &patlen, &planes);
+    getinit (f, &cols, &rows, &padright, &patlen, &planes);
 
-    if (planes == 1) 
+    if (planes == 1)
         type = PBM_TYPE;
-    else 
+    else
         type = PPM_TYPE;
 
-	pnm_writepnminit( stdout, cols, rows, MAXVAL, type, 0 );
+    pnm_writepnminit( stdout, cols, rows, MAXVAL, type, 0 );
 
-    { 
+    {
         /* allocate input row data structure */
         int plane;
-        for (plane = 0; plane < planes; plane++) 
+        for (plane = 0; plane < planes; plane++)
             bitrow[plane] = malloc (cols + padright);
     }
     xelrow = pnm_allocrow(cols+padright);   /* Output row */
 
-	for (row = 0; row < rows; ) {
+    for (row = 0; row < rows; ) {
       int linerep;
       int plane;
 
-	  linerep = 1;
-	  for (plane = 0; plane < planes; plane++) {
+      linerep = 1;
+      for (plane = 0; plane < planes; plane++) {
         int col;
-		col = 0;
-		while (col < cols) {
+        col = 0;
+        while (col < cols) {
             int c;
-			switch (c = getc(f)) {
-			case 0x80:	/* Bit String */
+            switch (c = getc(f)) {
+            case 0x80:  /* Bit String */
             {
                 int j;
-				c = getc(f);	/* Byte count */
-				if (debug)
+                c = getc(f);    /* Byte count */
+                if (debug)
                   pm_message("bit string of %d bytes", c);
-				
-				if (col + c * 8 > cols + padright)
-				  pm_error ("bad byte count");
-				for (j = 0; j < c; ++j) {
+
+                if (col + c * 8 > cols + padright)
+                  pm_error ("bad byte count");
+                for (j = 0; j < c; ++j) {
                     int cc, k;
-					cc = getc(f);
-					for (k = 0x80; k; k >>= 1) {
-						bitrow[plane][col] = (k & cc) ? 0 : 1;
-						++col;
-					}
-				}
+                    cc = getc(f);
+                    for (k = 0x80; k; k >>= 1) {
+                        bitrow[plane][col] = (k & cc) ? 0 : 1;
+                        ++col;
+                    }
+                }
             }
             break;
-			case 0:		/* Pattern run */
+            case 0:     /* Pattern run */
             {
                 int j, l;
-				c = getc(f);	/* Repeat count */
-				if (debug)
-					pm_message("pattern run of %d repetitions",	c);
+                c = getc(f);    /* Repeat count */
+                if (debug)
+                    pm_message("pattern run of %d repetitions", c);
                 /* line repeat */
                 if (c == 0) {
                     c = getc(f);
@@ -158,25 +158,25 @@ main(argc, argv)
                     linerep = getc(f);
                     break;
                 }
-				fread (pattern, 1, patlen, f);
-				if (col + c * patlen * 8 > cols + padright)
-				  pm_error ("bad pattern repeat count");
-				for (j = 0; j < c; ++j)
-					for (l = 0; l < patlen; ++l) {
+                fread (pattern, 1, patlen, f);
+                if (col + c * patlen * 8 > cols + padright)
+                  pm_error ("bad pattern repeat count");
+                for (j = 0; j < c; ++j)
+                    for (l = 0; l < patlen; ++l) {
                         int k;
-						for (k = 0x80; k; k >>= 1) {
-							bitrow[plane][col] = (k & pattern[l]) ? 0 : 1;
-							++col;
-						}
+                        for (k = 0x80; k; k >>= 1) {
+                            bitrow[plane][col] = (k & pattern[l]) ? 0 : 1;
+                            ++col;
+                        }
                     }
             }
             break;
 
-			default:	/* Solid run */
+            default:    /* Solid run */
             {
                 int l, j;
-				if (debug)
-					pm_message("solid run of %d bytes %s", c & 0x7f,
+                if (debug)
+                    pm_message("solid run of %d bytes %s", c & 0x7f,
                                c & 0x80 ? "on" : "off" );
                 /* each byte had eight bits DSB */
                 l = (c & 0x80) ? 0: 1;
@@ -185,23 +185,23 @@ main(argc, argv)
                     pm_error ("bad solid run repeat count");
                 for (j = 0; j < c; ++j) {
                     bitrow[plane][col] = l;
-					++col;
+                    ++col;
                 }
             }
-				break;
+                break;
 
-			case EOF:	/* End of file */
-				pm_error( "end of file reached" );
+            case EOF:   /* End of file */
+                pm_error( "end of file reached" );
 
-			}
-		}
+            }
+        }
                 if ( debug )
                         pm_message( "EOL plane %d row %d", plane, row );
                 if (col != cols + padright)
                         pm_error( "EOL beyond edge" );
-	  }
+      }
 
-	  if (planes == 4) {
+      if (planes == 4) {
           /* Construct a pixel from the 4 planes of bits for this row */
           int col;
           for (col = 0; col < cols; col++) {
@@ -212,21 +212,21 @@ main(argc, argv)
             const int b_bit = !bitrow[2][col];
             i = bitrow[3][col];
 
-			/* Deal with weird GEM palette - white/black/gray are
-               encoded oddly 
+            /* Deal with weird GEM palette - white/black/gray are
+               encoded oddly
             */
-			if (r_bit == g_bit && g_bit == b_bit) {
+            if (r_bit == g_bit && g_bit == b_bit) {
                 /* It's black, white, or gray */
-				if (r_bit && i) r = LIGHT;
-				else if (r_bit) r = BLACK;
-				else if (i) r = MAXVAL;
-				else r = DARK;
-				g = b = r;	
-			} else {
+                if (r_bit && i) r = LIGHT;
+                else if (r_bit) r = BLACK;
+                else if (i) r = MAXVAL;
+                else r = DARK;
+                g = b = r;
+            } else {
                 /* It's one of the twelve colored colors */
                 if (!i) {
                     /* Low intensity */
-                    r = r_bit * LIGHT; 
+                    r = r_bit * LIGHT;
                     g = g_bit * LIGHT;
                     b = b_bit * LIGHT;
                 } else {
@@ -237,21 +237,21 @@ main(argc, argv)
                 }
             }
             PPM_ASSIGN(xelrow[col], r, g, b);
-		}
-	  } else {
+        }
+      } else {
           int col;
-          for (col = 0; col < cols; col++) 
+          for (col = 0; col < cols; col++)
               PNM_ASSIGN1(xelrow[col], bitrow[0][col]);
       }
-	  while (linerep--) {
-		pnm_writepnmrow( stdout, xelrow, cols, MAXVAL, type, 0 );
-		++row;
-	  }
-	}
+      while (linerep--) {
+        pnm_writepnmrow( stdout, xelrow, cols, MAXVAL, type, 0 );
+        ++row;
+      }
+    }
     pnm_freerow(xelrow);
-	pm_close( f );
-	pm_close( stdout );
-	exit(0);
+    pm_close( f );
+    pm_close( stdout );
+    exit(0);
 }
 
 
@@ -303,3 +303,5 @@ getinit (file, colsP, rowsP, padrightP, patlenP, planesP)
     }
 }
 
+
+
diff --git a/converter/other/giftopnm.c b/converter/other/giftopnm.c
index 76cf4bff..e2d5923d 100644
--- a/converter/other/giftopnm.c
+++ b/converter/other/giftopnm.c
@@ -9,7 +9,7 @@
 /* +-------------------------------------------------------------------+ */
 
 /* There is a copy of the GIF89 specification, as defined by its inventor,
-   Compuserve, in 1990 at: 
+   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"
@@ -19,10 +19,12 @@
    describe the Lempel-Ziv base.
 */
 
+#define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE   /* for strcaseeq */
 #include <string.h>
 #include <assert.h>
+#include <stdbool.h>
 
 #include "pm_config.h"
 #include "pm_c_util.h"
@@ -41,13 +43,13 @@
 #define MAX_LZW_BITS  12
 
 #ifndef   FASTPBMRENDER
-  #define FASTPBMRENDER TRUE
+  #define FASTPBMRENDER true
 #endif
 
 static const bool useFastPbmRender = FASTPBMRENDER;
 
 #ifndef   REPORTLZWCODES
-  #define REPORTLZWCODES FALSE
+  #define REPORTLZWCODES false
 #endif
 
 static const bool wantLzwCodes = REPORTLZWCODES;
@@ -60,7 +62,11 @@ readFile(FILE *          const ifP,
          unsigned char * const buffer,
          size_t          const len,
          const char **   const errorP) {
+/*----------------------------------------------------------------------------
+   Read the next 'len' bytes from *ifP into 'buffer'.
 
+   Fail if there aren't that many bytes to read.
+-----------------------------------------------------------------------------*/
     size_t bytesRead;
 
     bytesRead = fread(buffer, 1, len, ifP);
@@ -91,7 +97,7 @@ struct CmdlineInfo {
     bool allImages;  /* He wants all the images */
     unsigned int imageNum;
         /* image number he wants from input, starting at 0.  Undefined
-           if allImages is TRUE
+           if allImages is true
         */
     const char * alphaFileName;
     unsigned int quitearly;
@@ -101,7 +107,7 @@ struct CmdlineInfo {
 
 
 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
@@ -110,7 +116,7 @@ parseCommandLine(int argc, char ** argv,
     optEntry * option_def;
         /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
-    
+
     optStruct3 opt;
 
     unsigned int alphaSpec, imageSpec;
@@ -119,9 +125,9 @@ parseCommandLine(int argc, char ** argv,
     unsigned int option_def_index;
 
     MALLOCARRAY_NOFAIL(option_def, 100);
-    
+
     option_def_index = 0;   /* incremented by OPTENTRY */
-    OPTENT3(0, "verbose",     OPT_FLAG, NULL, 
+    OPTENT3(0, "verbose",     OPT_FLAG, NULL,
             &cmdlineP->verbose,         0);
     OPTENT3(0, "comments",    OPT_FLAG, NULL,
             &cmdlineP->comments,        0);
@@ -131,24 +137,24 @@ parseCommandLine(int argc, char ** argv,
             &cmdlineP->repair,          0);
     OPTENT3(0, "image",       OPT_STRING, &image,
             &imageSpec,                 0);
-    OPTENT3(0, "alphaout",    OPT_STRING, &cmdlineP->alphaFileName, 
+    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 */
+    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, 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 (!imageSpec) {
         cmdlineP->imageNum = 0;
-        cmdlineP->allImages = FALSE;
+        cmdlineP->allImages = false;
     } else {
-        if (strcaseeq(image, "all")) { 
-            cmdlineP->allImages = TRUE;
+        if (strcaseeq(image, "all")) {
+            cmdlineP->allImages = true;
         } else {
             char * tailptr;
 
@@ -164,12 +170,12 @@ parseCommandLine(int argc, char ** argv,
                 pm_error("Invalid value for 'image' option.  You specified "
                          "zero.  The first image is 1.");
 
-            cmdlineP->allImages = FALSE;
+            cmdlineP->allImages = false;
             cmdlineP->imageNum = (unsigned int) imageNum - 1;
         }
     }
-    
-    if (argc-1 == 0) 
+
+    if (argc-1 == 0)
         cmdlineP->inputFilespec = "-";
     else if (argc-1 != 1)
         pm_error("Program takes zero or one argument (filename).  You "
@@ -177,7 +183,7 @@ parseCommandLine(int argc, char ** argv,
     else
         cmdlineP->inputFilespec = argv[1];
 
-    if (!alphaSpec) 
+    if (!alphaSpec)
         cmdlineP->alphaFileName = NULL;
 }
 
@@ -249,13 +255,13 @@ struct GifScreen {
         /* Aspect ratio of each pixel, times 64, minus 15.  (i.e. 1 => 1:4).
            But Zero means 1:1.
         */
-    bool     hasGray;  
+    bool     hasGray;
         /* Boolean: global colormap has at least one gray color
-           (not counting black and white) 
+           (not counting black and white)
         */
     bool     hasColor;
         /* Boolean: global colormap has at least one non-gray,
-           non-black, non-white color 
+           non-black, non-white color
         */
 };
 
@@ -263,7 +269,7 @@ struct Gif89 {
     bool         haveTransColor;
         /* The GIF specifies a transparent background color */
     unsigned int transparentIndex;
-        /* The color index of the color which is the transparent 
+        /* The color index of the color which is the transparent
            background color.
 
            Meaningful only when 'haveTransColor' is true
@@ -282,7 +288,7 @@ initGif89(struct Gif89 * const gif89P) {
     gif89P->haveDelayTime  = false;
     gif89P->haveInputFlag  = false;
     gif89P->haveDisposal   = false;
-}       
+}
 
 
 static bool verbose;
@@ -311,8 +317,8 @@ readColorMap(FILE *        const ifP,
 
     assert(cmapSize <= MAXCOLORMAPSIZE);
 
-    *hasGrayP = FALSE;  /* initial assumption */
-    *hasColorP = FALSE;  /* initial assumption */
+    *hasGrayP = false;  /* initial assumption */
+    *hasColorP = false;  /* initial assumption */
 
     for (i = 0; i < cmapSize; ++i) {
         const char * error;
@@ -326,23 +332,23 @@ readColorMap(FILE *        const ifP,
 
         if (rgb[0] == rgb[1] && rgb[1] == rgb[2]) {
             if (rgb[0] != 0 && rgb[0] != GIFMAXVAL)
-                *hasGrayP = TRUE;
+                *hasGrayP = true;
         } else
-            *hasColorP = TRUE;
+            *hasColorP = true;
     }
     cmapP->size = cmapSize;
 }
 
 
 
-static bool zeroDataBlock = FALSE;
+static bool zeroDataBlock = false;
     /* the most recently read DataBlock was an EOD marker, i.e. had
        zero length
     */
 
 static void
-getDataBlock(FILE *          const ifP, 
-             unsigned char * const buf, 
+getDataBlock(FILE *          const ifP,
+             unsigned char * const buf,
              bool *          const eofP,
              unsigned int *  const lengthP,
              const char **   const errorP) {
@@ -354,10 +360,10 @@ getDataBlock(FILE *          const ifP,
    of the datablock at 'buf', and its length as *lengthP.
 
    Except that if we hit EOF or have an I/O error reading the first
-   byte (size field) of the DataBlock, we return *eofP == TRUE and
+   byte (size field) of the DataBlock, we return *eofP == true and
    *lengthP == 0.
 
-   We return *eofP == FALSE if we don't hit EOF or have an I/O error.
+   We return *eofP == false if we don't hit EOF or have an I/O error.
 
    If we hit EOF or have an I/O error reading the data portion of the
    DataBlock, we exit the program with pm_error().
@@ -366,7 +372,7 @@ getDataBlock(FILE *          const ifP,
 
     unsigned char count;
     const char * error;
-    
+
     readFile(ifP, &count, sizeof(count), &error);
 
     if (error) {
@@ -374,22 +380,22 @@ getDataBlock(FILE *          const ifP,
                    error);
         pm_strfree(error);
         *errorP = NULL;
-        *eofP = TRUE;
+        *eofP = true;
         *lengthP = 0;
     } else {
         if (verbose)
             pm_message("%d byte block at Position %ld", count, pos);
-        *eofP = FALSE;
+        *eofP = false;
         *lengthP = count;
 
         if (count == 0) {
             *errorP = NULL;
-            zeroDataBlock = TRUE;
+            zeroDataBlock = true;
         } else {
             const char * error;
 
-            zeroDataBlock = FALSE;
-            readFile(ifP, buf, count, &error); 
+            zeroDataBlock = false;
+            readFile(ifP, buf, count, &error);
 
             if (error) {
                 pm_asprintf(errorP,
@@ -416,7 +422,7 @@ readThroughEod(FILE * const ifP) {
     unsigned char buf[256];
     bool eod;
 
-    eod = FALSE;  /* initial value */
+    eod = false;  /* initial value */
     while (!eod) {
         bool eof;
         unsigned int count;
@@ -429,7 +435,7 @@ readThroughEod(FILE * const ifP) {
                        "anyway as if an EOD marker were at the end "
                        "of the file.");
         if (error || eof || count == 0)
-            eod = TRUE;
+            eod = true;
     }
 }
 
@@ -440,7 +446,7 @@ 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]);
@@ -461,25 +467,25 @@ doCommentExtension(FILE * const ifP) {
 /*----------------------------------------------------------------------------
    Read the rest of a comment extension from the input file 'ifP' and handle
    it.
-   
+
    We ought to deal with the possibility that the comment is not text.  I.e.
    it could have nonprintable characters or embedded nulls.  I don't know if
    the GIF spec requires regular text or not.
 -----------------------------------------------------------------------------*/
     char buf[256];
-    unsigned int blocklen;  
+    unsigned int blocklen;
     bool done;
 
-    done = FALSE;
+    done = false;
     while (!done) {
         bool eof;
         const char * error;
-        getDataBlock(ifP, (unsigned char*) buf, &eof, &blocklen, &error); 
+        getDataBlock(ifP, (unsigned char*) buf, &eof, &blocklen, &error);
         if (error)
             pm_error("Error reading a data block in a comment extension.  %s",
                      error);
         if (blocklen == 0 || eof)
-            done = TRUE;
+            done = true;
         else {
             buf[blocklen] = '\0';
             if (showComment) {
@@ -500,7 +506,7 @@ LM_to_uint(unsigned char const a,
 
 
 
-static void 
+static void
 doGraphicControlExtension(FILE *         const ifP,
                           struct Gif89 * const gif89P) {
 
@@ -515,7 +521,7 @@ doGraphicControlExtension(FILE *         const ifP,
     if (eof)
         pm_error("EOF/error encountered reading "
                  "1st DataBlock of Graphic Control Extension.");
-    else if (length < 4) 
+    else if (length < 4)
         pm_error("graphic control extension 1st DataBlock too short.  "
                  "It must be at least 4 bytes; it is %d bytes.",
                  length);
@@ -542,7 +548,7 @@ doExtension(FILE *         const ifP,
             struct Gif89 * const gif89P) {
 
     const char * str;
-    
+
     switch (label) {
     case 0x01:              /* Plain Text Extension */
         str = "Plain Text";
@@ -576,7 +582,7 @@ doExtension(FILE *         const ifP,
 
 struct GetCodeState {
     unsigned char buf[280];
-        /* This is the buffer through which we read the data from the 
+        /* This is the buffer through which we read the data from the
            stream.  We must buffer it because we have to read whole data
            blocks at a time, but our client wants one code at a time.
            The buffer typically contains the contents of one data block
@@ -599,7 +605,7 @@ struct GetCodeState {
 
 
 static void
-getAnotherBlock(FILE *                const ifP, 
+getAnotherBlock(FILE *                const ifP,
                 struct GetCodeState * const gsP,
                 const char **         const errorP) {
 
@@ -617,11 +623,11 @@ getAnotherBlock(FILE *                const ifP,
 
     gsP->curbit -= (gsP->bufCount-2)*8;
     gsP->bufCount = 2;
-        
+
     /* Add the next block to the buffer */
     getDataBlock(ifP, &gsP->buf[gsP->bufCount], &eof, &count, errorP);
     if (*errorP)
-        gsP->streamExhausted = TRUE;
+        gsP->streamExhausted = true;
     else {
         if (eof) {
             pm_message("EOF encountered in image "
@@ -634,7 +640,7 @@ getAnotherBlock(FILE *                const ifP,
             assumedCount = count;
 
         gsP->streamExhausted = (assumedCount == 0);
-        
+
         gsP->bufCount += assumedCount;
     }
 }
@@ -645,13 +651,13 @@ static struct GetCodeState getCodeState;
 
 static void
 getCode_init(struct GetCodeState * const getCodeStateP) {
-    
+
     /* Fake a previous data block */
     getCodeStateP->buf[0] = 0;
     getCodeStateP->buf[1] = 0;
     getCodeStateP->bufCount = 2;
     getCodeStateP->curbit = getCodeStateP->bufCount * 8;
-    getCodeStateP->streamExhausted = FALSE;
+    getCodeStateP->streamExhausted = false;
 }
 
 
@@ -669,7 +675,7 @@ bitsOfLeBuffer(const unsigned char * const buf,
    first byte of buf[].
 
    We return the string as an integer such that its pure binary encoding with
-   the bits numbered Intel-style is the string.  E.g. the string 0,1,1 
+   the bits numbered Intel-style is the string.  E.g. the string 0,1,1
    yields six.
 -----------------------------------------------------------------------------*/
     uint32_t codeBlock;
@@ -688,8 +694,8 @@ bitsOfLeBuffer(const unsigned char * const buf,
             (buf[start/8+0] <<  0) |
             (buf[start/8+1] <<  8) |
             (buf[start/8+2] << 16);
-            
-    return (unsigned int) 
+
+    return (unsigned int)
         (codeBlock >> (start % 8)) & ((1 << len) - 1);
 }
 
@@ -697,7 +703,7 @@ bitsOfLeBuffer(const unsigned char * const buf,
 
 static void
 getCode_get(struct GetCodeState * const gsP,
-            FILE *                const ifP, 
+            FILE *                const ifP,
             int                   const codeSize,
             bool *                const eofP,
             unsigned int *        const codeP,
@@ -707,21 +713,21 @@ getCode_get(struct GetCodeState * const gsP,
 
   'codeSize' is the number of bits in the code we are to get.
 
-  Return *eofP == TRUE iff we hit the end of the stream.  That means a legal
+  Return *eofP == true iff we hit the end of the stream.  That means a legal
   end of stream, marked by an EOD marker, not just end of file.  An end of
   file in the middle of the GIF stream is an error.
 
   If there are bits left in the stream, but not 'codeSize' of them, we
-  call that a success with *eofP == TRUE.
+  call that a success with *eofP == true.
 
-  Return the code read (assuming *eofP == FALSE and *errorP == NULL)
+  Return the code read (assuming *eofP == false and *errorP == NULL)
   as *codeP.
 -----------------------------------------------------------------------------*/
 
     *errorP = NULL;
 
     while (gsP->curbit + codeSize > gsP->bufCount * 8 &&
-           !gsP->streamExhausted && !*errorP) 
+           !gsP->streamExhausted && !*errorP)
         /* Not enough left in buffer to satisfy request.  Get the next
            data block into the buffer.
 
@@ -736,7 +742,7 @@ getCode_get(struct GetCodeState * const gsP,
             /* The buffer still doesn't have enough bits in it; that means
                there were no data blocks left to read.
             */
-            *eofP = TRUE;
+            *eofP = true;
 
             {
                 int const bitsUnused = gsP->bufCount * 8 - gsP->curbit;
@@ -753,7 +759,7 @@ getCode_get(struct GetCodeState * const gsP,
                 pm_message("LZW code=0x%03x [%d]", *codeP, codeSize);
 
             gsP->curbit += codeSize;
-            *eofP = FALSE;
+            *eofP = false;
         }
     }
 }
@@ -769,7 +775,7 @@ struct Stack {
 
 
 
-static void 
+static void
 initStack(struct Stack * const stackP, unsigned int const size) {
 
     MALLOCARRAY(stackP->stack, size);
@@ -804,7 +810,7 @@ popStack(struct Stack * const stackP) {
 
     if (stackP->sp <= stackP->stack)
         pm_error("stack underflow");
-    
+
     return *(--stackP->sp);
 }
 
@@ -816,14 +822,14 @@ termStack(struct Stack * const stackP) {
     stackP->stack = NULL;
 }
 
-    
+
 
 /*----------------------------------------------------------------------------
    Some notes on LZW.
 
    LZW is an extension of Limpel-Ziv.  The two extensions are:
 
-     1) in Limpel-Ziv, codes are all the same number of bits.  In
+     1) In Limpel-Ziv, codes are all the same number of bits.  In
         LZW, they start out small and increase as the stream progresses.
 
      2) LZW has a clear code that resets the string table and code
@@ -833,33 +839,55 @@ termStack(struct Stack * const stackP) {
 
    The true data elements are dataWidth bits wide, so the maximum
    value of a true data element is 2**dataWidth-1.  We call that
-   max_dataVal.  The first byte in the stream tells you what dataWidth
+   maxDataVal.  The first byte in the stream tells you what dataWidth
    is.
 
-   LZW codes 0 - max_dataVal are direct codes.  Each one represents
+   LZW codes 0 - maxDataVal are direct codes.  Each one represents
    the true data element whose value is that of the LZW code itself.
    No decompression is required.
 
-   max_dataVal + 1 and up are compression codes.  They encode
+   maxDataVal + 1 and up are compression codes.  They encode
    true data elements:
 
-   max_dataVal + 1 is the clear code.
-         
-   max_dataVal + 2 is the end code.
+   maxDataVal + 1 is the clear code.
+
+   maxDataVal + 2 is the end code.
 
-   max_dataVal + 3 and up are string codes.  Each string code 
+   maxDataVal + 3 and up are string codes.  Each string code
    represents a string of true data elements.  The translation from a
    string code to the string of true data elements varies as the stream
    progresses.  In the beginning and after every clear code, the
    translation table is empty, so no string codes are valid.  As the
-   stream progresses, the table gets filled and more string codes 
+   stream progresses, the table gets filled and more string codes
    become valid.
 
+   At the beginning of the stream, string codes are represented by
+   dataWidth + 1 bits.  When enough codes have been defined to use up that
+   space, they start being represented by dataWidth + 2 bits, and so on.
+
+   What we call 'dataWidth', others call the "minimum code size," which is a
+   misnomer, because the minimum code size in a stream must be at least one
+   more than 'dataWidth', to accomodate the clear and end codes.
 -----------------------------------------------------------------------------*/
 
 static int const maxLzwCodeCt = (1<<MAX_LZW_BITS);
 
-struct Decompressor {
+typedef struct {
+/*----------------------------------------------------------------------------
+   An entry in the decompressor LZW code table.
+-----------------------------------------------------------------------------*/
+    unsigned int next;
+        /* The next code in the expansion after the one this entry specifies;
+           this is either another index into the LZW code table or a direct
+           code, which means it's the last data element in the expansion.
+        */
+    unsigned int dataElement;
+        /* The data element (color map index or gray level) to add to the
+           expansion for this entry.
+        */
+} CodeTableEntry;
+
+typedef struct {
     struct Stack stack;
     bool fresh;
         /* The stream is right after a clear code or at the very beginning */
@@ -869,7 +897,7 @@ struct Decompressor {
            the stream.
         */
     unsigned int maxCodeCt;
-        /* The maximum number of LZW codes that can be represented with the 
+        /* The maximum number of LZW codes that can be represented with the
            current code size.  (1 << codeSize)
         */
     unsigned int nextTableSlot;
@@ -882,6 +910,9 @@ struct Decompressor {
     /* The following are constant for the life of the decompressor */
     FILE * ifP;
     unsigned int initCodeSize;
+        /* The code size, in bits, at the start of the stream and immediately
+           after a Clear code.
+        */
     unsigned int cmapSize;
     unsigned int maxDataVal;
     unsigned int clearCode;
@@ -889,22 +920,28 @@ struct Decompressor {
     bool haveTransColor;
     unsigned int transparentIndex;
         /* meaningful only when 'haveTransColor' is true */
-    bool tolerateBadInput; 
+    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 */  
-};
+    CodeTableEntry table[(1 << MAX_LZW_BITS)];   /* LZW code table */
+        /* This contains the strings of expansions of LZW string codes, in
+           linked list form.  table[N] gives the first data element for the
+           string with string code N and the LZW code for the rest of the
+           string.  The latter is often a string code itself, which can also
+           be looked up in this table.
+        */
+} Decompressor;
 
 
 
 static void
-resetDecompressor(struct Decompressor * const decompP) {
+resetDecompressor(Decompressor * const decompP) {
 
-    decompP->codeSize      = decompP->initCodeSize+1;
+    decompP->codeSize      = decompP->initCodeSize;
     decompP->maxCodeCt     = 1 << decompP->codeSize;
     decompP->nextTableSlot = decompP->maxDataVal + 3;
-    decompP->fresh         = TRUE;
+    decompP->fresh         = true;
 }
 
 
@@ -918,7 +955,7 @@ validateTransparentIndex(unsigned int const transparentIndex,
     if (transparentIndex >= cmapSize) {
         if (tolerateBadInput) {
             if (transparentIndex > maxDataVal)
-                pm_error("Invalid transparent index value: %d",
+                pm_error("Invalid transparent index value: %u",
                          transparentIndex);
         } else {
             pm_error("Invalid transparent index value %d in image with "
@@ -935,23 +972,21 @@ validateTransparentIndex(unsigned int const transparentIndex,
 
 
 static void
-lzwInit(struct Decompressor * const decompP, 
-        FILE *                const ifP,
-        int                   const initCodeSize,
-        unsigned int          const cmapSize,
-        bool                  const haveTransColor,
-        unsigned int          const transparentIndex,
-        bool                  const tolerateBadInput) {
-
-    unsigned int const maxDataVal = (1 << initCodeSize) - 1;
-    
+lzwInit(Decompressor * const decompP,
+        FILE *         const ifP,
+        int            const dataWidth,
+        unsigned int   const cmapSize,
+        bool           const haveTransColor,
+        unsigned int   const transparentIndex,
+        bool           const tolerateBadInput) {
+
+    unsigned int const maxDataVal = (1 << dataWidth) - 1;
+
     if (verbose)
-        pm_message("Image says the initial compression code size is "
-                   "%d bits", 
-                   initCodeSize);
-    
+        pm_message("Image says the data width is %u bits", dataWidth);
+
     decompP->ifP              = ifP;
-    decompP->initCodeSize     = initCodeSize;
+    decompP->initCodeSize     = dataWidth + 1;
     decompP->cmapSize         = cmapSize;
     decompP->tolerateBadInput = tolerateBadInput;
     decompP->maxDataVal       = maxDataVal;
@@ -961,9 +996,9 @@ lzwInit(struct Decompressor * const decompP,
     if (verbose)
         pm_message("Initial code size is %u bits; clear code = 0x%03x, "
                    "end code = 0x%03x",
-                   decompP->initCodeSize, 
+                   decompP->initCodeSize,
                    decompP->clearCode, decompP->endCode);
-    
+
     /* The entries in the translation table for true data codes are
        constant throughout the image.  For PBM output we make an
        adjustment later.  Once set entries never change.
@@ -971,8 +1006,8 @@ lzwInit(struct Decompressor * const decompP,
     {
         unsigned int i;
         for (i = 0; i <= maxDataVal; ++i) {
-            decompP->table[i][0] = 0;
-            decompP->table[i][1] = i < cmapSize ? i : 0;
+            decompP->table[i].next = 0;
+            decompP->table[i].dataElement = i < cmapSize ? i : 0;
         }
     }
     decompP->haveTransColor   = haveTransColor;
@@ -985,19 +1020,19 @@ lzwInit(struct Decompressor * const decompP,
     resetDecompressor(decompP);
 
     getCode_init(&getCodeState);
-    
-    decompP->fresh = TRUE;
-    
+
+    decompP->fresh = true;
+
     initStack(&decompP->stack, maxLzwCodeCt);
 
-    assert(decompP->initCodeSize < sizeof(decompP->maxDataVal) * 8);
+    assert(decompP->initCodeSize < sizeof(decompP->maxDataVal) * 8 + 1);
 }
 
 
 
 static void
-lzwAdjustForPBM(struct Decompressor * const decompP,
-                GifColorMap           const cmap) {
+lzwAdjustForPBM(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.
@@ -1008,13 +1043,14 @@ lzwAdjustForPBM(struct Decompressor * const decompP,
 ----------------------------------------------------------------------------*/
     unsigned int i;
     for (i = 0; i < cmap.size; ++i)
-        decompP->table[i][1] = cmap.map[i][0] == 0 ? PBM_BLACK : PBM_WHITE;
+        decompP->table[i].dataElement =
+            cmap.map[i][0] == 0 ? PBM_BLACK : PBM_WHITE;
 }
 
 
 
 static void
-lzwTerm(struct Decompressor * const decompP) {
+lzwTerm(Decompressor * const decompP) {
 
     termStack(&decompP->stack);
 }
@@ -1022,8 +1058,8 @@ lzwTerm(struct Decompressor * const decompP) {
 
 
 static void
-pushWholeStringOnStack(struct Decompressor * const decompP,
-                       unsigned int          const code0) {
+pushWholeStringOnStack(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
@@ -1034,21 +1070,47 @@ pushWholeStringOnStack(struct Decompressor * const decompP,
 
     for (stringCount = 0, code = code0;
          code > decompP->maxDataVal;
-         ++stringCount, code = decompP->table[code][0]
+         ++stringCount, code = decompP->table[code].next
         ) {
 
-        pushStack(&decompP->stack, decompP->table[code][1]);
+        pushStack(&decompP->stack, decompP->table[code].dataElement);
     }
-    decompP->firstcode = decompP->table[code][1];
+    decompP->firstcode = decompP->table[code].dataElement;
     pushStack(&decompP->stack, decompP->firstcode);
 }
 
 
 
 static void
-expandCodeOntoStack(struct Decompressor * const decompP,
-                    unsigned int          const incode,
-                    const char **         const errorP) {
+addLzwStringCode(Decompressor * const decompP) {
+
+    if (decompP->nextTableSlot < maxLzwCodeCt) {
+        decompP->table[decompP->nextTableSlot].next =
+            decompP->prevcode;
+        decompP->table[decompP->nextTableSlot].dataElement =
+            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 and no new ones will be defined.
+            */
+            if (decompP->codeSize < MAX_LZW_BITS) {
+                ++decompP->codeSize;
+                decompP->maxCodeCt = 1 << decompP->codeSize;
+            }
+        }
+    }
+}
+
+
+
+static void
+expandCodeOntoStack(Decompressor * const decompP,
+                    unsigned int   const incode,
+                    const char **  const errorP) {
 /*----------------------------------------------------------------------------
    'incode' is an LZW string code.  It represents a string of true data
    elements, as defined by the string translation table in *decompP.
@@ -1063,58 +1125,56 @@ expandCodeOntoStack(struct Decompressor * const decompP,
    as *errorP).
 -----------------------------------------------------------------------------*/
     unsigned int code;
+    const char * gifError;
 
-    *errorP = NULL; /* Initial value */
+    gifError = 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)
+                 decompP->table[incode].dataElement ==
+                     decompP->transparentIndex)
             /* transparent code outside cmap   exceptional case */
             code = incode;
         else
-            pm_asprintf(errorP, "Error in GIF image: invalid color code %u. "
+            pm_asprintf(&gifError, "Invalid color code %u. "
                         "Valid color values are 0 - %u",
                         incode, decompP->cmapSize - 1);
-    }
-    else if (incode < decompP->nextTableSlot)  
+    } else if (incode < decompP->nextTableSlot)
         /* LZW string, defined */
         code = incode;
-    else if  (incode == decompP->nextTableSlot && !decompP->fresh) {
+    else if (incode == decompP->nextTableSlot) {
         /* 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");
+        if (decompP->fresh)
+            pm_asprintf(&gifError, "LZW string code encountered with "
+                        "decompressor in fresh state");
+        else {
+            if (wantLzwCodes && verbose)
+                pm_message ("LZW code valid, but not in decoder table");
 
-        pushStack(&decompP->stack, decompP->firstcode);
-        code = decompP->prevcode;
+            pushStack(&decompP->stack, decompP->firstcode);
+            code = decompP->prevcode;
+        }
     } else
-        pm_asprintf(errorP, "Error in GIF image: invalid LZW code");
-
-    if (!*errorP) {
+        pm_asprintf(&gifError, "LZW string code %u "
+                    "is neither a previously defined one nor the "
+                    "next in sequence to define (%u)",
+                    incode, decompP->nextTableSlot);
+
+    if (gifError) {
+        pm_asprintf(errorP, "INVALID GIF IMAGE: %s", gifError);
+        pm_strfree(gifError);
+    } else {
         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;
-                }
-            }
-        }
+        addLzwStringCode(decompP);
+
         decompP->prevcode = incode;
+
+        *errorP = NULL;
     }
 }
 
@@ -1122,7 +1182,7 @@ expandCodeOntoStack(struct Decompressor * const decompP,
 
 static void
 lzwReadByteFresh(struct GetCodeState * const getCodeStateP,
-                 struct Decompressor * const decompP,
+                 Decompressor *        const decompP,
                  bool *                const endOfImageP,
                  unsigned char *       const dataReadP,
                  const char **         const errorP) {
@@ -1142,7 +1202,7 @@ lzwReadByteFresh(struct GetCodeState * const getCodeStateP,
 
     assert(decompP->fresh);  /* Entry requirement */
 
-    decompP->fresh = FALSE;
+    decompP->fresh = false;
 
     do {
         getCode_get(getCodeStateP, decompP->ifP, decompP->codeSize,
@@ -1151,12 +1211,12 @@ lzwReadByteFresh(struct GetCodeState * const getCodeStateP,
 
     if (!*errorP) {
         if (eof)
-            *endOfImageP = TRUE;
+            *endOfImageP = true;
         else if (code == decompP->endCode) {
             if (!zeroDataBlock)
                 readThroughEod(decompP->ifP);
-            *endOfImageP = TRUE;
-        } else if (code >= decompP->cmapSize) { 
+            *endOfImageP = true;
+        } 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);
@@ -1165,12 +1225,12 @@ lzwReadByteFresh(struct GetCodeState * const getCodeStateP,
             */
             decompP->prevcode = decompP->firstcode = 0;
 
-            *endOfImageP = FALSE;
+            *endOfImageP = false;
         } else {    /* valid code */
             decompP->prevcode  = code;
-            decompP->firstcode = decompP->table[code][1];
+            decompP->firstcode = decompP->table[code].dataElement;
             *dataReadP = decompP->firstcode;
-            *endOfImageP = FALSE;
+            *endOfImageP = false;
         }
     }
 }
@@ -1179,10 +1239,10 @@ lzwReadByteFresh(struct GetCodeState * const getCodeStateP,
 
 
 static void
-lzwReadByte(struct Decompressor * const decompP,
-            unsigned char *       const dataReadP,
-            bool *                const endOfImageP,
-            const char **         const errorP) {
+lzwReadByte(Decompressor *  const decompP,
+            unsigned char * const dataReadP,
+            bool *          const endOfImageP,
+            const char **   const errorP) {
 /*----------------------------------------------------------------------------
   Return the next data element of the decompressed image.  In the context
   of a GIF, a data element is the color table index of one pixel.
@@ -1201,7 +1261,7 @@ lzwReadByte(struct Decompressor * const decompP,
 -----------------------------------------------------------------------------*/
     if (!stackIsEmpty(&decompP->stack)) {
         *errorP = NULL;
-        *endOfImageP = FALSE;
+        *endOfImageP = false;
         *dataReadP = popStack(&decompP->stack);
     } else if (decompP->fresh) {
         lzwReadByteFresh(&getCodeState, decompP, endOfImageP, dataReadP,
@@ -1224,10 +1284,10 @@ lzwReadByte(struct Decompressor * const decompP,
                     if (code == decompP->endCode) {
                         if (!zeroDataBlock)
                             readThroughEod(decompP->ifP);
-                        *endOfImageP = TRUE;
+                        *endOfImageP = true;
                         *errorP = NULL;
                     } else {
-                        *endOfImageP = FALSE;
+                        *endOfImageP = false;
                         expandCodeOntoStack(decompP, code, errorP);
                         if (!*errorP)
                             *dataReadP = popStack(&decompP->stack);
@@ -1255,7 +1315,7 @@ bumpRowInterlace(unsigned int   const rows,
        MULT4PLUS2: Rows 2, 6, 10, 14, etc.
        MULT2PLUS1: Rows 1, 3, 5, 7, 9, etc.
     */
-    
+
     switch (*passP) {
     case MULT8PLUS0:
         *rowP += 8;
@@ -1300,7 +1360,7 @@ bumpRowInterlace(unsigned int   const rows,
 static void
 renderRow(unsigned char *    const cmapIndexRow,
           unsigned int       const cols,
-          GifColorMap        const cmap, 
+          GifColorMap        const cmap,
           bool               const haveTransColor,
           unsigned int       const transparentIndex,
           FILE *             const imageOutfile,
@@ -1311,11 +1371,14 @@ renderRow(unsigned char *    const cmapIndexRow,
 /*----------------------------------------------------------------------------
   Convert one row of cmap indexes to PPM/PGM/PBM output.
 
+  The row is *xelrow, which is 'cols' columns wide and has pixels of format
+  'format'.
+
   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) {
@@ -1332,7 +1395,7 @@ renderRow(unsigned char *    const cmapIndexRow,
     if (imageOutfile) {
         if (useFastPbmRender && format == PBM_FORMAT && !haveTransColor) {
 
-            bit * const bitrow = cmapIndexRow; 
+            bit * const bitrow = cmapIndexRow;
 
             pbm_writepbmrow(imageOutfile, bitrow, cols, false);
         } else {
@@ -1386,7 +1449,7 @@ pnmFormat(bool const hasGray,
 -----------------------------------------------------------------------------*/
     int format;
     const char * formatName;
-           
+
     if (hasColor) {
         format = PPM_FORMAT;
         formatName = "PPM";
@@ -1397,21 +1460,21 @@ pnmFormat(bool const hasGray,
         format = PBM_FORMAT;
         formatName = "PBM";
     }
-    if (verbose) 
+    if (verbose)
         pm_message("writing a %s file", formatName);
- 
+
     return format;
 }
 
 
 
 static void
-makePnmRow(struct Decompressor * const decompP,
-           unsigned int          const cols,
-           unsigned int          const rows,
-           bool                  const fillWithZero,
-           unsigned char *       const cmapIndexRow,
-           const char **         const errorP) {
+makePnmRow(Decompressor * const decompP,
+           unsigned int   const cols,
+           unsigned int   const rows,
+           bool           const fillWithZero,
+           unsigned char *const cmapIndexRow,
+           const char **  const errorP) {
 
     bool fillingWithZero;
     unsigned int col;
@@ -1426,7 +1489,7 @@ makePnmRow(struct Decompressor * const decompP,
 
         if (fillingWithZero)
             colorIndex = 0;
-        else { 
+        else {
             const char *  readError;
             unsigned char readColorIndex;
             bool          endOfImage;
@@ -1455,15 +1518,15 @@ makePnmRow(struct Decompressor * const decompP,
 
 
 static void
-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) {
+convertRaster(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
@@ -1492,11 +1555,11 @@ convertRaster(struct Decompressor * const decompP,
     MALLOCARRAY2(cmapIndexArray, interlace ? rows : 1 , cols);
 
     if (imageOutFileP)
-        pnm_writepnminit(imageOutFileP, cols, rows, GIFMAXVAL, format, FALSE);
+        pnm_writepnminit(imageOutFileP, cols, rows, GIFMAXVAL, format, false);
     if (alphaFileP)
-        pbm_writepbminit(alphaFileP, cols, rows, FALSE);
+        pbm_writepbminit(alphaFileP, cols, rows, false);
 
-    xelrow = pnm_allocrow(cols);  
+    xelrow = pnm_allocrow(cols);
     if (!xelrow)
         pm_error("couldn't alloc space for image" );
 
@@ -1540,10 +1603,10 @@ convertRaster(struct Decompressor * const decompP,
                       decompP->haveTransColor, decompP->transparentIndex,
                       imageOutFileP, format, xelrow, alphaFileP, alphabits);
     }
-    /* All rows decompressed (and rendered and output if non-interlaced) */  
+    /* All rows decompressed (and rendered and output if non-interlaced) */
     if (interlace) {
         unsigned int row;
-        for (row = 0; row < rows; ++row) 
+        for (row = 0; row < rows; ++row)
             renderRow(cmapIndexArray[row], cols, cmap,
                       decompP->haveTransColor, decompP->transparentIndex,
                       imageOutFileP, format, xelrow, alphaFileP, alphabits);
@@ -1558,13 +1621,13 @@ convertRaster(struct Decompressor * const decompP,
 
 
 static void
-skipExtraneousData(struct Decompressor * const decompP) {
+skipExtraneousData(Decompressor * const decompP) {
 
     unsigned char byteRead;
     bool endOfImage;
     const char * error;
 
-    endOfImage = FALSE;  /* initial value */
+    endOfImage = false;  /* initial value */
 
     lzwReadByte(decompP, &byteRead, &endOfImage, &error);
 
@@ -1591,13 +1654,13 @@ skipExtraneousData(struct Decompressor * const decompP) {
 
 static void
 issueTransparencyMessage(bool         const haveTransColor,
-                         unsigned int const transparentIndex, 
+                         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.
@@ -1632,10 +1695,10 @@ issueTransparencyMessage(bool         const haveTransColor,
 
 
 static void
-readImageData(FILE *       const ifP, 
+readImageData(FILE *       const ifP,
               unsigned int const cols,
               unsigned int const rows,
-              GifColorMap  const cmap, 
+              GifColorMap  const cmap,
               bool         const interlace,
               bool         const haveTransColor,
               unsigned int const transparentIndex,
@@ -1645,22 +1708,25 @@ readImageData(FILE *       const ifP,
               bool         const hasColor,
               bool         const tolerateBadInput) {
 
-    unsigned char lzwMinCodeSize;      
-    struct Decompressor decomp;
+    unsigned char lzwDataWidth;
+    Decompressor decomp;
     const char * error;
 
-    readFile(ifP, &lzwMinCodeSize, sizeof(lzwMinCodeSize), &error);
+    readFile(ifP, &lzwDataWidth, sizeof(lzwDataWidth), &error);
     if (error)
         pm_error("Can't read GIF stream "
                  "right after an image separator; no "
                  "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);
+    if (lzwDataWidth+1 > MAX_LZW_BITS)
+        pm_error("Invalid data width (bits for a true data item) "
+                 "in image data: %u.  "
+                 "Maximum allowable code size in GIF is %u, "
+                 "and a code has to be wide enough to accomodate both "
+                 "all possible data values and two control codes",
+                 lzwDataWidth, MAX_LZW_BITS);
 
-    lzwInit(&decomp, ifP, lzwMinCodeSize, cmap.size,
+    lzwInit(&decomp, ifP, lzwDataWidth, cmap.size,
             haveTransColor, transparentIndex, tolerateBadInput);
 
     issueTransparencyMessage(haveTransColor, transparentIndex, cmap);
@@ -1721,24 +1787,24 @@ readGifHeader(FILE *             const gifFileP,
     readFile(gifFileP, buf, 6, &error);
     if (error)
         pm_error("Error reading magic number.  %s", error);
-    
+
     if (!strneq((char *)buf, "GIF", 3))
         pm_error("File does not contain a GIF stream.  It does not start "
                  "with 'GIF'.");
-    
+
     strncpy(version, (char *)buf + 3, 3);
     version[3] = '\0';
-    
+
     if (verbose)
         pm_message("GIF format version is '%s'", version);
-    
+
     if ((!streq(version, "87a")) && (!streq(version, "89a")))
         pm_error("Bad version number, not '87a' or '89a'" );
-    
+
     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]);
     cmapSize                    = 1 << ((buf[4] & 0x07) + 1);
@@ -1749,25 +1815,25 @@ readGifHeader(FILE *             const gifFileP,
     if (verbose) {
         pm_message("GIF Width = %u GIF Height = %u "
                    "Pixel aspect ratio = %u (%f:1)",
-                   gifScreenP->width, gifScreenP->height, 
-                   gifScreenP->aspectRatio, 
-                   gifScreenP->aspectRatio == 0 ? 
+                   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 (buf[4] & GLOBALCOLORMAP) {
         gifScreenP->hasGlobalColorMap = true;
         readColorMap(gifFileP, cmapSize, &gifScreenP->colorMap,
                      &gifScreenP->hasGray, &gifScreenP->hasColor);
         if (verbose) {
-            pm_message("Global 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)
         warnUserNotSquare(gifScreenP->aspectRatio);
 
@@ -1777,7 +1843,7 @@ readGifHeader(FILE *             const gifFileP,
 
 
 static void
-readExtensions(FILE*          const ifP, 
+readExtensions(FILE*          const ifP,
                struct Gif89 * const gif89P,
                bool *         const eodP,
                const char **  const errorP) {
@@ -1786,8 +1852,8 @@ readExtensions(FILE*          const ifP,
    positioned.  Read up through the image separator that begins the
    next image or GIF stream terminator.
 
-   If we encounter EOD (end of GIF stream) before we find an image 
-   separator, we return *eodP == TRUE.  Else *eodP == FALSE.
+   If we encounter EOD (end of GIF stream) before we find an image
+   separator, we return *eodP == true.  Else *eodP == false.
 
    If we hit end of file before an EOD marker, we fail.
 -----------------------------------------------------------------------------*/
@@ -1796,8 +1862,8 @@ readExtensions(FILE*          const ifP,
 
     *errorP = NULL;  /* initial value */
 
-    eod = FALSE;
-    imageStart = FALSE;
+    eod = false;
+    imageStart = false;
 
     /* Read the image descriptor */
     while (!imageStart && !eod && !*errorP) {
@@ -1813,7 +1879,7 @@ readExtensions(FILE*          const ifP,
             pm_strfree(error);
         } else {
             if (c == ';') {         /* GIF terminator */
-                eod = TRUE;
+                eod = true;
             } else if (c == '!') {         /* Extension */
                 unsigned char functionCode;
                 const char * error;
@@ -1828,9 +1894,9 @@ readExtensions(FILE*          const ifP,
                 } else {
                     doExtension(ifP, functionCode, gif89P);
                 }
-            } else if (c == ',') 
-                imageStart = TRUE;
-            else 
+            } else if (c == ',')
+                imageStart = true;
+            else
                 pm_message("Encountered invalid character 0x%02x while "
                            "seeking extension block, ignoring", (int)c);
         }
@@ -1846,7 +1912,7 @@ struct GifImageHeader {
 -----------------------------------------------------------------------------*/
     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 
+        /* (If an image does not have its own color map, the image uses the
            global color map for the GIF stream)
         */
     unsigned int localColorMapSize;
@@ -1875,7 +1941,7 @@ reportImageHeader(struct GifImageHeader const imageHeader) {
     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);
@@ -1921,7 +1987,7 @@ 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; 
+    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",
@@ -1950,17 +2016,17 @@ skipImageData(FILE * const ifP) {
 
 
 static void
-convertImage(FILE *           const ifP, 
-             bool             const skipIt, 
-             FILE *           const imageoutFileP, 
-             FILE *           const alphafileP, 
+convertImage(FILE *           const ifP,
+             bool             const skipIt,
+             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
+   If 'skipIt' is true, don't do anything else.  Otherwise, write the
    image to the current position of files *imageoutFileP and *alphafileP.
    If *alphafileP is NULL, though, don't write any alpha information.
 -----------------------------------------------------------------------------*/
@@ -1974,7 +2040,7 @@ convertImage(FILE *           const ifP,
     validateWithinGlobalScreen(imageHeader, gifScreen);
 
     if (imageHeader.hasLocalColormap) {
-        readColorMap(ifP, imageHeader.localColorMapSize, &localColorMap, 
+        readColorMap(ifP, imageHeader.localColorMapSize, &localColorMap,
                      &hasGray, &hasColor);
         currentColorMapP = &localColorMap;
     } else if (gifScreen.hasGlobalColorMap) {
@@ -2015,18 +2081,18 @@ disposeOfReadExtensionsError(const char * const error,
             pm_error("Error accessing Image %u of stream.  %s",
                      imageSeq, error);
         pm_strfree(error);
-        *eodP = TRUE;
+        *eodP = true;
     }
 }
 
 
 
 static void
-convertImages(FILE *       const ifP, 
+convertImages(FILE *       const ifP,
               bool         const allImages,
-              unsigned int const requestedImageSeq, 
+              unsigned int const requestedImageSeq,
               bool         const drainStream,
-              FILE *       const imageOutFileP, 
+              FILE *       const imageOutFileP,
               FILE *       const alphaFileP,
               bool         const tolerateBadInput) {
 /*----------------------------------------------------------------------------
@@ -2034,9 +2100,9 @@ convertImages(FILE *       const ifP,
    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.  
+   'allImages' means Caller wants all the images in the stream.
 
-   'requestedImageSeq' is meaningful only when 'allImages' is FALSE.  It 
+   'requestedImageSeq' is meaningful only when 'allImages' is false.  It
    is the sequence number of the one image Caller wants from the stream,
    with the first image being 0.
 
@@ -2062,7 +2128,7 @@ convertImages(FILE *       const ifP,
 
     readGifHeader(ifP, &gifScreen);
 
-    for (imageSeq = 0, eod = FALSE;
+    for (imageSeq = 0, eod = false;
          !eod && (allImages || imageSeq <= requestedImageSeq || drainStream);
          ++imageSeq) {
 
@@ -2083,7 +2149,7 @@ convertImages(FILE *       const ifP,
             if (verbose)
                 pm_message("Reading Image Sequence %u", imageSeq);
 
-            convertImage(ifP, !allImages && (imageSeq != requestedImageSeq), 
+            convertImage(ifP, !allImages && (imageSeq != requestedImageSeq),
                          imageOutFileP, alphaFileP, gifScreen, gif89,
                          tolerateBadInput);
         }
@@ -2093,19 +2159,19 @@ convertImages(FILE *       const ifP,
 
 
 int
-main(int argc, char **argv) {
+main(int argc, const char **argv) {
 
     struct CmdlineInfo cmdline;
     FILE * ifP;
     FILE * alphaFileP;
     FILE * imageOutFileP;
 
-    pnm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
     verbose = cmdline.verbose;
     showComment = cmdline.comments;
-   
+
     ifP = pm_openr(cmdline.inputFilespec);
 
     if (cmdline.alphaFileName == NULL)
@@ -2118,12 +2184,12 @@ main(int argc, char **argv) {
     else
         imageOutFileP = stdout;
 
-    convertImages(ifP, cmdline.allImages, cmdline.imageNum, 
+    convertImages(ifP, cmdline.allImages, cmdline.imageNum,
                   !cmdline.quitearly, imageOutFileP, alphaFileP,
                   cmdline.repair);
 
     pm_close(ifP);
-    if (imageOutFileP != NULL) 
+    if (imageOutFileP != NULL)
         pm_close(imageOutFileP);
     if (alphaFileP != NULL)
         pm_close(alphaFileP);
@@ -2132,4 +2198,3 @@ main(int argc, char **argv) {
 }
 
 
-
diff --git a/converter/other/ipdb.c b/converter/other/ipdb.c
index 7ee37872..5e4dc82e 100644
--- a/converter/other/ipdb.c
+++ b/converter/other/ipdb.c
@@ -19,6 +19,7 @@
  *   Authors:  Eric A. Howe (mu@trends.net)
  *             Bryan Henderson, 2010
  */
+#define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE   /* Ensure strdup() is in <string.h> */
 #include <assert.h>
@@ -282,8 +283,8 @@ ipdb_image_alloc(const char * const name,
             if (w != 0 && h != 0) {
                 MALLOCARRAY(imgP->data, w * h);
 
-                if (imgP->data) {
-                    memset(imgP->data, 0, sizeof(*(imgP->data)) * w * h);
+                if (imgP->data != NULL) {
+                  memset(imgP->data, 0, sizeof(*(imgP->data)) * w * h);
                 } else
                     failed = true;
             }
diff --git a/converter/other/jbig/jbigtopnm.c b/converter/other/jbig/jbigtopnm.c
index 733ba227..0d55ccc1 100644
--- a/converter/other/jbig/jbigtopnm.c
+++ b/converter/other/jbig/jbigtopnm.c
@@ -1,22 +1,93 @@
 /*
     jbigtopnm - JBIG to PNM converter
-  
+
     This program was derived from jbgtopbm.c in Markus Kuhn's
     JBIG-KIT package by Bryan Henderson on 2000.05.11
 
     The main difference is that this version uses the Netpbm libraries.
-  
+
  */
 
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <ctype.h>
+#include <limits.h>
+
 #include <jbig.h>
+
 #include "pnm.h"
+#include "shhopt.h"
+#include "mallocvar.h"
 
 #define BUFSIZE 8192
 
 
+
+typedef struct {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * inputFileName;
+    const char * outputFileName;
+    unsigned long xmax;
+    unsigned long ymax;
+    unsigned int binary;
+    unsigned int diagnose;
+    unsigned int planeSpec;
+    unsigned int plane;
+} CmdlineInfo;
+
+
+
+static void
+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.
+-----------------------------------------------------------------------------*/
+    optEntry * option_def;
+
+    optStruct3 opt;
+
+    unsigned int xmaxSpec, ymaxSpec;
+
+    unsigned int option_def_index;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "binary",   OPT_FLAG, NULL,             &cmdlineP->binary,   0);
+    OPTENT3(0, "diagnose", OPT_FLAG, NULL,             &cmdlineP->diagnose, 0);
+    OPTENT3(0, "plane",    OPT_UINT, &cmdlineP->plane, &cmdlineP->planeSpec,0);
+    OPTENT3(0, "xmax",     OPT_UINT, &cmdlineP->xmax,  &xmaxSpec,           0);
+    OPTENT3(0, "ymax",     OPT_UINT, &cmdlineP->ymax,  &ymaxSpec,           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 (!xmaxSpec)
+        cmdlineP->xmax = UINT_MAX;
+    if (!ymaxSpec)
+        cmdlineP->ymax = UINT_MAX;
+
+    cmdlineP->inputFileName  = (argc-1 >= 1) ? argv[1] : "-";
+    cmdlineP->outputFileName = (argc-1 >= 2) ? argv[2] : "-";
+
+    if (argc-1 > 2)
+        pm_error("Too  many arguments: %u.  The only possible "
+                 "non-option arguments are input file name and "
+                 "output file name", argc-1);
+}
+
+
+
 static void
 collect_image (unsigned char *data, size_t len, void *image) {
     static int cursor = 0;
@@ -29,7 +100,7 @@ collect_image (unsigned char *data, size_t len, void *image) {
 
 
 
-static void 
+static void
 write_pnm (FILE *fout, const unsigned char * const image, const int bpp,
            const int rows, const int cols, const int maxval,
            const int format) {
@@ -46,22 +117,22 @@ write_pnm (FILE *fout, const unsigned char * const image, const int bpp,
         for (col = 0; col < cols; col++) {
             int j;
             for (j = 0; j < bpp; j++)
-                PNM_ASSIGN1(pnm_row[col], 
+                PNM_ASSIGN1(pnm_row[col],
                             image[(((row*cols)+col) * bpp) + j]);
         }
         pnm_writepnmrow(fout, pnm_row, cols, maxval, format, 0);
     }
-    
+
     pnm_freerow(pnm_row);
 }
 
 
 
 static void
-write_raw_pbm(FILE * const fout, 
+write_raw_pbm(FILE * const fout,
               const unsigned char * const binary_image,
               int                   const cols,
-              int                   const rows) { 
+              int                   const rows) {
 
     unsigned int const bytes_per_row = pbm_packed_bytes(cols);
 
@@ -70,7 +141,7 @@ write_raw_pbm(FILE * const fout,
     pbm_writepbminit(fout, cols, rows, 0);
 
     for (row = 0; row < rows; ++row)
-        pbm_writepbmrow_packed(fout, &binary_image[row*bytes_per_row], cols, 
+        pbm_writepbmrow_packed(fout, &binary_image[row*bytes_per_row], cols,
                                0);
 }
 
@@ -79,7 +150,7 @@ write_raw_pbm(FILE * const fout,
 /*
  *
  */
-static void 
+static void
 diagnose_bie(FILE *f)
 {
   unsigned char bih[20];
@@ -89,198 +160,153 @@ diagnose_bie(FILE *f)
   len = fread(bih, 1, 20, f);
   if (len < 20) {
     printf("Input file is %d < 20 bytes long and does therefore not "
-	   "contain an intact BIE header!\n", len);
+       "contain an intact BIE header!\n", len);
     return;
   }
 
   printf("Decomposition of BIH:\n\n  DL = %d\n  D  = %d\n  P  = %d\n"
-	 "  -  = %d\n  XD = %lu\n  YD = %lu\n  L0 = %lu\n  MX = %d\n"
-	 "  MY = %d\n",
-	 bih[0], bih[1], bih[2], bih[3],
-	 xd = ((unsigned long) bih[ 4] << 24) | ((unsigned long)bih[ 5] << 16)|
-	 ((unsigned long) bih[ 6] <<  8) | ((unsigned long) bih[ 7]),
-	 yd = ((unsigned long) bih[ 8] << 24) | ((unsigned long)bih[ 9] << 16)|
-	 ((unsigned long) bih[10] <<  8) | ((unsigned long) bih[11]),
-	 l0 = ((unsigned long) bih[12] << 24) | ((unsigned long)bih[13] << 16)|
-	 ((unsigned long) bih[14] <<  8) | ((unsigned long) bih[15]),
-	 bih[16], bih[17]);
+     "  -  = %d\n  XD = %lu\n  YD = %lu\n  L0 = %lu\n  MX = %d\n"
+     "  MY = %d\n",
+     bih[0], bih[1], bih[2], bih[3],
+     xd = ((unsigned long) bih[ 4] << 24) | ((unsigned long)bih[ 5] << 16)|
+     ((unsigned long) bih[ 6] <<  8) | ((unsigned long) bih[ 7]),
+     yd = ((unsigned long) bih[ 8] << 24) | ((unsigned long)bih[ 9] << 16)|
+     ((unsigned long) bih[10] <<  8) | ((unsigned long) bih[11]),
+     l0 = ((unsigned long) bih[12] << 24) | ((unsigned long)bih[13] << 16)|
+     ((unsigned long) bih[14] <<  8) | ((unsigned long) bih[15]),
+     bih[16], bih[17]);
   printf("  order   = %d %s%s%s%s%s\n", bih[18],
-	 bih[18] & JBG_HITOLO ? " HITOLO" : "",
-	 bih[18] & JBG_SEQ ? " SEQ" : "",
-	 bih[18] & JBG_ILEAVE ? " ILEAVE" : "",
-	 bih[18] & JBG_SMID ? " SMID" : "",
-	 bih[18] & 0xf0 ? " other" : "");
+     bih[18] & JBG_HITOLO ? " HITOLO" : "",
+     bih[18] & JBG_SEQ ? " SEQ" : "",
+     bih[18] & JBG_ILEAVE ? " ILEAVE" : "",
+     bih[18] & JBG_SMID ? " SMID" : "",
+     bih[18] & 0xf0 ? " other" : "");
   printf("  options = %d %s%s%s%s%s%s%s%s\n", bih[19],
-	 bih[19] & JBG_LRLTWO ? " LRLTWO" : "",
-	 bih[19] & JBG_VLENGTH ? " VLENGTH" : "",
-	 bih[19] & JBG_TPDON ? " TPDON" : "",
-	 bih[19] & JBG_TPBON ? " TPBON" : "",
-	 bih[19] & JBG_DPON ? " DPON" : "",
-	 bih[19] & JBG_DPPRIV ? " DPPRIV" : "",
-	 bih[19] & JBG_DPLAST ? " DPLAST" : "",
-	 bih[19] & 0x80 ? " other" : "");
+     bih[19] & JBG_LRLTWO ? " LRLTWO" : "",
+     bih[19] & JBG_VLENGTH ? " VLENGTH" : "",
+     bih[19] & JBG_TPDON ? " TPDON" : "",
+     bih[19] & JBG_TPBON ? " TPBON" : "",
+     bih[19] & JBG_DPON ? " DPON" : "",
+     bih[19] & JBG_DPPRIV ? " DPPRIV" : "",
+     bih[19] & JBG_DPLAST ? " DPLAST" : "",
+     bih[19] & 0x80 ? " other" : "");
   printf("\n  %lu stripes, %d layers, %d planes\n\n",
-	 ((yd >> bih[1]) +  ((((1UL << bih[1]) - 1) & xd) != 0) + l0 - 1) / l0,
-	 bih[1] - bih[0], bih[2]);
+     ((yd >> bih[1]) +  ((((1UL << bih[1]) - 1) & xd) != 0) + l0 - 1) / l0,
+     bih[1] - bih[0], bih[2]);
 
   return;
 }
 
 
-int main (int argc, char **argv)
+int main (int argc, const char **argv)
 {
-    FILE *fin = stdin, *fout = stdout;
-    const char *fnin = "<stdin>", *fnout = "<stdout>";
-    int i, j, result;
-    int all_args = 0, files = 0;
-    struct jbg_dec_state s;
-    char *buffer;
-    unsigned char *p;
-    size_t len, cnt;
-    unsigned long xmax = 4294967295UL, ymax = 4294967295UL;
-    int plane = -1, use_graycode = 1, diagnose = 0;
-
-    pnm_init(&argc, argv);
-
-    buffer = malloc(BUFSIZE);
-    if (!buffer)
-        pm_error("Sorry, not enough memory available!");
-
-    /* parse command line arguments */
-    for (i = 1; i < argc; i++) {
-        if (!all_args && argv[i][0] == '-') {
-            if (argv[i][1] == '\0' && files == 0)
-                ++files;
-            else {
-                for (j = 1; j > 0 && argv[i][j]; j++) {
-                    switch(tolower(argv[i][j])) {
-                    case '-' :
-                        all_args = 1;
-                        break;
-                    case 'b':
-                        use_graycode = 0;
-                        break;
-                    case 'd':
-                        diagnose = 1;
-                        break;
-                    case 'x':
-                        if (++i >= argc)
-                            pm_error("-x needs a value");
-                        xmax = atol(argv[i]);
-                        j = -1;
-                        break;
-                    case 'y':
-                        if (++i >= argc)
-                            pm_error("-y needs a value");
-                        ymax = atol(argv[i]);
-                        j = -1;
-                        break;
-                    case 'p':
-                        if (++i >= argc)
-                            pm_error("-p needs a value");
-                        plane = atoi(argv[i]);
-                        j = -1;
-                        break;
-                    default:
-                        pm_error("Unrecognized option: %c", argv[i][j]);
-                    }
-                }
-            }
-        } else {
-            switch (files++) {
-            case 0:
-                if (argv[i][0] != '-' || argv[i][1] != '\0') {
-                    fnin = argv[i];
-                    fin = fopen(fnin, "rb");
-                    if (!fin)
-                        pm_error("Can't open input file '%s'", fnin);
-                }
-                if (diagnose) {
-                    diagnose_bie(fin);
-                    exit(0);
-                }
-                break;
-            case 1:
-                fnout = argv[i];
-                fout = fopen(fnout, "wb");
-                if (!fout)
-                    pm_error("Can't open output file '%s'", fnout);
+    CmdlineInfo cmdline;
+    FILE * ifP;
+    FILE * ofP;
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFileName);
+    ofP = pm_openw(cmdline.outputFileName);
+
+    if (cmdline.diagnose)
+        diagnose_bie(ifP);
+    else {
+        struct jbg_dec_state s;
+        unsigned char * buffer;
+        int result;
+
+        MALLOCARRAY(buffer, BUFSIZE);
+        if (!buffer)
+            pm_error("Failed to get %u bytes of memory for buffer", BUFSIZE);
+
+        /* send input file to decoder */
+        jbg_dec_init(&s);
+        jbg_dec_maxsize(&s, cmdline.xmax, cmdline.ymax);
+        result = JBG_EAGAIN;
+        do {
+            size_t len;
+            size_t cnt;
+            unsigned char * p;
+
+            len = fread(buffer, 1, BUFSIZE, ifP);
+            if (len == 0)
                 break;
-            default:
-                pm_error("Too many non-option arguments");
+            cnt = 0;
+            p = &buffer[0];
+            while (len > 0 && (result == JBG_EAGAIN || result == JBG_EOK)) {
+                result = jbg_dec_in(&s, p, len, &cnt);
+                p += cnt;
+                len -= cnt;
+            }
+        } while (result == JBG_EAGAIN || result == JBG_EOK);
+        if (ferror(ifP))
+            pm_error("Error reading input file");
+        if (result != JBG_EOK && result != JBG_EOK_INTR)
+            pm_error("Invalid contents of input file.  %s",
+                     jbg_strerror(result));
+        if (cmdline.planeSpec && jbg_dec_getplanes(&s) <= cmdline.plane)
+            pm_error("Image has only %u planes", jbg_dec_getplanes(&s));
+
+        {
+            /* Write it out */
+
+            int rows, cols;
+            int maxval;
+            int bpp;
+            bool justOnePlane;
+            unsigned int plane_to_write;
+
+            cols = jbg_dec_getwidth(&s);
+            rows = jbg_dec_getheight(&s);
+            maxval = pm_bitstomaxval(jbg_dec_getplanes(&s));
+            bpp = (jbg_dec_getplanes(&s)+7)/8;
+
+            if (jbg_dec_getplanes(&s) == 1) {
+                justOnePlane = true;
+                plane_to_write = 0;
+            } else {
+                if (cmdline.planeSpec) {
+                    justOnePlane = true;
+                    plane_to_write = cmdline.plane;
+                } else
+                    justOnePlane = false;
             }
-        }
-    }
 
-    /* send input file to decoder */
-    jbg_dec_init(&s);
-    jbg_dec_maxsize(&s, xmax, ymax);
-    result = JBG_EAGAIN;
-    do {
-        len = fread(buffer, 1, BUFSIZE, fin);
-        if (!len) break;
-        cnt = 0;
-        p = (unsigned char *) buffer;
-        while (len > 0 && (result == JBG_EAGAIN || result == JBG_EOK)) {
-            result = jbg_dec_in(&s, p, len, &cnt);
-            p += cnt;
-            len -= cnt;
-        }
-    } while (result == JBG_EAGAIN || result == JBG_EOK);
-    if (ferror(fin)) 
-        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));
-    if (plane >= 0 && jbg_dec_getplanes(&s) <= plane) 
-        pm_error("Image has only %d planes!\n", jbg_dec_getplanes(&s));
-
-    {
-        /* Write it out */
-
-        int rows, cols;
-        int maxval;
-        int bpp;
-        int plane_to_write;
-
-        cols = jbg_dec_getwidth(&s);
-        rows = jbg_dec_getheight(&s);
-        maxval = pm_bitstomaxval(jbg_dec_getplanes(&s));
-        bpp = (jbg_dec_getplanes(&s)+7)/8;
-
-        if (jbg_dec_getplanes(&s) == 1) 
-            plane_to_write = 0;
-        else 
-            plane_to_write = plane;
-
-        if (plane_to_write >= 0) {
-            /* Write just one plane */
-            unsigned char * binary_image;
-
-            pm_message("WRITING PBM FILE");
-
-            binary_image=jbg_dec_getimage(&s, plane_to_write);
-            write_raw_pbm(fout, binary_image, cols, rows);
-        } else {
-            unsigned char *image;
-            pm_message("WRITING PGM FILE");
-
-            /* Write out all the planes */
-            /* What jbig.doc doesn't tell you is that jbg_dec_merge_planes
-               delivers the image in chunks, in consecutive calls to 
-               the data-out callback function.  And a row can span two
-               chunks.
-            */
-            image = malloc(cols*rows*bpp);
-            jbg_dec_merge_planes(&s, use_graycode, collect_image, image);
-            write_pnm(fout, image, bpp, rows, cols, maxval, PGM_TYPE);
-            free(image);
+            if (justOnePlane) {
+                unsigned char * binary_image;
+
+                pm_message("WRITING PBM FILE");
+
+                binary_image=jbg_dec_getimage(&s, plane_to_write);
+                write_raw_pbm(ofP, binary_image, cols, rows);
+            } else {
+                unsigned char *image;
+                pm_message("WRITING PGM FILE");
+
+                /* Write out all the planes */
+                /* What jbig.doc doesn't tell you is that jbg_dec_merge_planes
+                   delivers the image in chunks, in consecutive calls to
+                   the data-out callback function.  And a row can span two
+                   chunks.
+                */
+                image = malloc(cols*rows*bpp);
+                jbg_dec_merge_planes(&s, !cmdline.binary, collect_image,
+                                     image);
+                write_pnm(ofP, image, bpp, rows, cols, maxval, PGM_TYPE);
+                free(image);
+            }
+            jbg_dec_free(&s);
         }
-    }
-  
-    pm_close(fout);
-
-    jbg_dec_free(&s);
 
+        pm_close(ofP);
+        pm_close(ifP);
+        free(buffer);
+    }
     return 0;
 }
+
+
+
diff --git a/converter/other/jbig/libjbig/jbig.c b/converter/other/jbig/libjbig/jbig.c
index d7141a75..e8141070 100644
--- a/converter/other/jbig/libjbig/jbig.c
+++ b/converter/other/jbig/libjbig/jbig.c
@@ -22,15 +22,13 @@
  *  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.
  */
 
 #ifdef DEBUG
 #include <stdio.h>
-#else
-#define NDEBUG
 #endif
 
 #include <stdlib.h>
@@ -68,7 +66,7 @@
 
 /* object code version id */
 
-const char jbg_version[] = 
+const char jbg_version[] =
   "JBIG-KIT " JBG_VERSION " -- (c) 1995-2014 Markus Kuhn -- "
   "Licence: " JBG_LICENCE "\n";
 
@@ -117,7 +115,7 @@ static const char *errmsg[] = {
  * 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. 
+ * before being passed on to the corresponding C function.
  * This we can catch all overflows during a size_t multiplication a
  * a single place.
  */
@@ -137,7 +135,7 @@ static void *checked_malloc(size_t nmemb, size_t size)
   /* assert that nmemb * size <= SIZE_MAX */
   if (size > SIZE_MAX / nmemb)
     abort();
-  
+
   p = malloc(nmemb * size);
 
   if (!p)
@@ -163,7 +161,7 @@ static void *checked_realloc(void *ptr, size_t nmemb, size_t size)
   /* assert that nmemb * size <= SIZE_MAX */
   if (size > SIZE_MAX / nmemb)
     abort();
-  
+
   p = realloc(ptr, nmemb * size);
 
   if (!p)
@@ -212,7 +210,7 @@ static void checked_free(void *ptr)
 static struct jbg_buf *jbg_buf_init(struct jbg_buf **free_list)
 {
   struct jbg_buf *new_block;
-  
+
   /* Test whether a block from the free list is available */
   if (*free_list) {
     new_block = *free_list;
@@ -238,13 +236,13 @@ static struct jbg_buf *jbg_buf_init(struct jbg_buf **free_list)
 static void jbg_buf_free(struct jbg_buf **free_list)
 {
   struct jbg_buf *tmp;
-  
+
   while (*free_list) {
     tmp = (*free_list)->next;
     checked_free(*free_list);
     *free_list = tmp;
   }
-  
+
   return;
 }
 
@@ -308,7 +306,7 @@ static void jbg_buf_remove_zeros(struct jbg_buf *head)
    */
   if (head->last->len && head->last->d[head->last->len - 1] == MARKER_ESC)
     jbg_buf_write(MARKER_STUFF, head);
- 
+
   return;
 }
 
@@ -324,7 +322,7 @@ static void jbg_buf_prefix(struct jbg_buf *new_prefix, struct jbg_buf **start)
   new_prefix->last->next->previous = new_prefix->last;
   new_prefix->last = new_prefix->last->next->last;
   *start = new_prefix;
-  
+
   return;
 }
 
@@ -341,7 +339,7 @@ static void jbg_buf_output(struct jbg_buf **head,
 			void *file)
 {
   struct jbg_buf *tmp;
-  
+
   while (*head) {
     data_out((*head)->d, (*head)->len, file);
     tmp = (*head)->next;
@@ -349,7 +347,7 @@ static void jbg_buf_output(struct jbg_buf **head,
     *(*head)->free_list = *head;
     *head = tmp;
   }
-  
+
   return;
 }
 
@@ -364,7 +362,7 @@ static void jbg_buf_output(struct jbg_buf **head,
 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);
@@ -796,7 +794,7 @@ void jbg_enc_init(struct jbg_enc_state *s, unsigned long x, unsigned long y,
   s->comment = NULL;
   s->dppriv = jbg_dptable;
   s->res_tab = jbg_resred;
-  
+
   s->highres = (int *) checked_malloc(planes, sizeof(int));
   s->lhp[0] = p;
   s->lhp[1] = (unsigned char **)
@@ -806,9 +804,9 @@ void jbg_enc_init(struct jbg_enc_state *s, unsigned long x, unsigned long y,
     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 *) 
+  s->s = (struct jbg_arenc_state *)
     checked_malloc(s->planes, sizeof(struct jbg_arenc_state));
   s->tx = (int *) checked_malloc(s->planes, sizeof(int));
   lx = jbg_ceil_half(x, 1);
@@ -830,9 +828,9 @@ void jbg_enc_init(struct jbg_enc_state *s, unsigned long x, unsigned long y,
  * l0 (height of one stripe in the lowest resolution layer) is
  * selected, which obeys the recommended limitations for l0 in annex A
  * and C of the JBIG standard. The selected number of resolution layers
- * is returned. 
+ * is returned.
  */
-int jbg_enc_lrlmax(struct jbg_enc_state *s, unsigned long x, 
+int jbg_enc_lrlmax(struct jbg_enc_state *s, unsigned long x,
 		   unsigned long y)
 {
   for (s->d = 0; s->d < 6; s->d++)
@@ -846,8 +844,8 @@ int jbg_enc_lrlmax(struct jbg_enc_state *s, unsigned long x,
 
 
 /*
- * As an alternative to jbg_enc_lrlmax(), the following function allows
- * to specify the number of layers directly. The stripe height and layer
+ * As an alternative to jbg_enc_lrlmax(), the following function allows the
+ * user to specify the number of layers directly. The stripe height and layer
  * range is also adjusted automatically here.
  */
 void jbg_enc_layers(struct jbg_enc_state *s, int d)
@@ -879,9 +877,9 @@ int jbg_enc_lrange(struct jbg_enc_state *s, int dl, int dh)
 
 
 /*
- * The following function allows to specify the bits describing the
- * options of the format as well as the maximum AT movement window and
- * the number of layer 0 lines per stripes.
+ * The following function allows the user to specify the bits describing the
+ * options of the format as well as the maximum AT movement window and the
+ * number of layer 0 lines per stripes.
  */
 void jbg_enc_options(struct jbg_enc_state *s, int order, int options,
 		     unsigned long l0, int mx, int my)
@@ -957,7 +955,7 @@ static void encode_sde(struct jbg_enc_state *s,
   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);
 
@@ -1035,7 +1033,7 @@ static void encode_sde(struct jbg_enc_state *s,
 	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;
@@ -1066,11 +1064,11 @@ static void encode_sde(struct jbg_enc_state *s,
        *          76543210765432107654321076543210     line_h2
        *  76543210765432107654321X76543210             line_h1
        */
-      
+
       line_h1 = line_h2 = line_h3 = 0;
       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;
@@ -1177,7 +1175,7 @@ static void encode_sde(struct jbg_enc_state *s,
     /*
      *  Encode differential layer
      */
-    
+
     for (i = 0; i < hl && y < hy; i++, y++) {
 
       /* check whether it is worth to perform an ATMOVE */
@@ -1213,7 +1211,7 @@ static void encode_sde(struct jbg_enc_state *s,
 	}
 	at_determined = 1;
       }
-      
+
       if ((i >> 1) >= ll - 1 || (y >> 1) >= ly - 1)
 	lp1 = lp2;
 
@@ -1299,7 +1297,7 @@ static void encode_sde(struct jbg_enc_state *s,
        *            76543210 7654321Y 76543210 76543210     line_l2
        *            76543210 76543210 76543210 76543210     line_l1
        */
-      
+
 
       line_h1 = line_h2 = line_h3 = line_l1 = line_l2 = line_l3 = 0;
       if (i > 0 || !reset) line_h2 = (long)*(hp - hbpl) << 8;
@@ -1309,7 +1307,7 @@ static void encode_sde(struct jbg_enc_state *s,
       }
       line_l2 = (long)*lp2 << 8;
       line_l1 = (long)*lp1 << 8;
-      
+
       /* encode line */
       for (j = 0; j < hx; lp1++, lp2++) {
 	if ((j >> 1) < lbpl * 8 - 8) {
@@ -1403,8 +1401,8 @@ static void encode_sde(struct jbg_enc_state *s,
 #endif
 			continue;
 		      }
-		    }	
-		  }	
+		    }
+		  }
 		}
 
 		/* determine context */
@@ -1437,7 +1435,7 @@ static void encode_sde(struct jbg_enc_state *s,
 #ifdef DEBUG
 		encoded_pixels++;
 #endif
-		
+
 		/* statistics for adaptive template changes */
 		if (!at_determined && j >= s->mx) {
 		  c[0] += !(((line_h2 >> 6) ^ line_h1) & 0x100);
@@ -1445,7 +1443,7 @@ static void encode_sde(struct jbg_enc_state *s,
 		    c[t] += !(((line_h1 >> t) ^ line_h1) & 0x100);
 		  ++c_all;
 		}
-		
+
 	      } while (++j & 1 && j < hx);
 	  } while (j & 7 && j < hx);
 	  hp++;
@@ -1457,10 +1455,10 @@ static void encode_sde(struct jbg_enc_state *s,
 	lp1 -= lbpl;
 	lp2 -= lbpl;
       }
-      
+
     } /* for (i = ...) */
   }
-  
+
   arith_encode_flush(se);
   jbg_buf_remove_zeros(s->sde[stripe][layer][plane]);
   jbg_buf_write(MARKER_ESC, s->sde[stripe][layer][plane]);
@@ -1541,7 +1539,7 @@ static void resolution_reduction(struct jbg_enc_state *s, int plane,
   hp1 = hp2 + hbpl;
   hp3 = hp2 - hbpl;
   lp = s->lhp[1 - s->highres[plane]][plane];
-  
+
 #ifdef DEBUG
   fprintf(stderr, "resolution_reduction: plane = %d, higher_layer = %d\n",
 	  plane, higher_layer);
@@ -1608,7 +1606,7 @@ static void resolution_reduction(struct jbg_enc_state *s, int plane,
   {
     FILE *f;
     char fn[50];
-    
+
     sprintf(fn, "dbg_d=%02d.pbm", higher_layer - 1);
     f = fopen(fn, "wb");
     fprintf(f, "P4\n%lu %lu\n", lx, ly);
@@ -1621,7 +1619,7 @@ static void resolution_reduction(struct jbg_enc_state *s, int plane,
 }
 
 
-/* 
+/*
  * This function is called inside the three loops of jbg_enc_out() in
  * 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
@@ -1642,7 +1640,7 @@ static void output_sde(struct jbg_enc_state *s,
   int lfcl;     /* lowest fully coded layer */
   long i;
   unsigned long u;
-  
+
   assert(s->sde[stripe][layer][plane] != SDE_DONE);
 
   if (s->sde[stripe][layer][plane] != SDE_TODO) {
@@ -1679,7 +1677,7 @@ static void output_sde(struct jbg_enc_state *s,
     if (lfcl > 1)
       resolution_reduction(s, plane, lfcl - 1);
   }
-  
+
   encode_sde(s, stripe, layer, plane);
 
 #ifdef DEBUG
@@ -1687,14 +1685,14 @@ static void output_sde(struct jbg_enc_state *s,
 #endif
   jbg_buf_output(&s->sde[stripe][layer][plane], s->data_out, s->file);
   s->sde[stripe][layer][plane] = SDE_DONE;
-  
+
   if (stripe == s->stripes - 1 && layer > 0 &&
       s->sde[0][layer-1][plane] == SDE_TODO) {
     s->highres[plane] ^= 1;
     if (layer > 1)
       resolution_reduction(s, plane, layer - 1);
   }
-  
+
   return;
 }
 
@@ -1729,7 +1727,7 @@ void jbg_int2dppriv(unsigned char *dptable, const char *internal)
   int trans1[ 9] = { 1, 0, 3, 2, 8, 7, 6, 5, 4 };
   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) ;
 
 #define FILL_TABLE1(offset, len, trans) \
@@ -1762,7 +1760,7 @@ void jbg_dppriv2int(char *internal, const unsigned char *dptable)
   int trans1[ 9] = { 1, 0, 3, 2, 8, 7, 6, 5, 4 };
   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 };
-  
+
 #define FILL_TABLE2(offset, len, trans) \
   for (i = 0; i < len; i++) { \
     k = 0; \
@@ -1790,7 +1788,7 @@ void jbg_enc_out(struct jbg_enc_state *s)
   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 */ 
+  long ii[3], is[3], ie[3];    /* generic variables for the 3 nested loops */
   unsigned long stripe;
   int layer, plane;
   int order;
@@ -1918,7 +1916,7 @@ void jbg_enc_out(struct jbg_enc_state *s)
   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[iindex[order][STRIPE]];
 	if (s->order & JBG_HITOLO)
 	  layer = s->dh - (ii[iindex[order][LAYER]] - s->dl);
@@ -1949,7 +1947,7 @@ void jbg_enc_out(struct jbg_enc_state *s)
 	 */
 	if (s->yd1 > s->yd &&
 	    (stripe == s->stripes - 1 ||
-	     (stripe == s->stripes - 2 && 
+	     (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);
@@ -2017,10 +2015,10 @@ 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;
 }
 
@@ -2039,7 +2037,7 @@ const char *jbg_strerror(int errnum)
 
 
 /*
- * The constructor for a decoder 
+ * The constructor for a decoder
  */
 void jbg_dec_init(struct jbg_dec_state *s)
 {
@@ -2077,7 +2075,7 @@ 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 
+ * been read (this will be less than len if a marker segment was
  * part of the data or if the final byte was 0xff, in which case
  * this code cannot determine whether we have a marker segment).
  */
@@ -2106,7 +2104,7 @@ static size_t decode_pscd(struct jbg_dec_state *s, unsigned char *data,
   se = s->s[plane] + layer - s->dl;
   se->pscd_ptr = data;
   se->pscd_end = data + len;
-  
+
   /* number of lines per stripe in highres image */
   hl = s->l0 << layer;
   /* number of lines per stripe in lowres image */
@@ -2194,7 +2192,7 @@ static size_t decode_pscd(struct jbg_dec_state *s, unsigned char *data,
 	/* 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
        * as bits the neighbour pixels of the currently decoded pixel X:
@@ -2203,7 +2201,7 @@ static size_t decode_pscd(struct jbg_dec_state *s, unsigned char *data,
        *                     76543210 76543210 76543210 76543210     line_h2
        *   76543210 76543210 76543210 76543210 X                     line_h1
        */
-      
+
       if (x == 0) {
 	line_h1 = line_h2 = line_h3 = 0;
 	if (s->i > 0 || (y > 0 && !s->reset[plane][layer - s->dl]))
@@ -2211,7 +2209,7 @@ static size_t decode_pscd(struct jbg_dec_state *s, unsigned char *data,
 	if (s->i > 1 || (y > 1 && !s->reset[plane][layer - s->dl]))
 	  line_h3 = (long)*(hp - hbpl - hbpl) << 8;
       }
-      
+
       /*
        * Another tiny JBIG standard bug:
        *
@@ -2315,7 +2313,7 @@ static size_t decode_pscd(struct jbg_dec_state *s, unsigned char *data,
 				      (line_h1 & 0x003)));
 	    if (pix < 0)
 	      goto leave;
-	    
+
 	    line_h1 = (line_h1 << 1) | pix;
 	    line_h2 <<= 1;
 	    line_h3 <<= 1;
@@ -2327,7 +2325,7 @@ static size_t decode_pscd(struct jbg_dec_state *s, unsigned char *data,
       x = 0;
       s->pseudo = 1;
     } /* for (i = ...) */
-    
+
   } else {
 
     /*
@@ -2378,7 +2376,7 @@ static size_t decode_pscd(struct jbg_dec_state *s, unsigned char *data,
        *                     76543210 76543210 Y6543210 76543210     line_l2
        *                     76543210 76543210 76543210 76543210     line_l1
        */
-      
+
 
       if (x == 0) {
 	line_h1 = line_h2 = line_h3 = line_l1 = line_l2 = line_l3 = 0;
@@ -2392,14 +2390,14 @@ static size_t decode_pscd(struct jbg_dec_state *s, unsigned char *data,
 	line_l2 = (long)*lp2 << 8;
 	line_l1 = (long)*lp1 << 8;
       }
-      
+
       /* decode line */
       while (x < hx) {
 	if ((x & 15) == 0)
 	  if ((x >> 1) < lbpl * 8 - 8) {
 	    line_l1 |= *(lp1 + 1);
 	    line_l2 |= *(lp2 + 1);
-	    if (s->i > 1 || 
+	    if (s->i > 1 ||
 		(y > 1 && !s->reset[plane][layer - s->dl]))
 	      line_l3 |= *(lp2 - lbpl + 1);
 	  }
@@ -2430,13 +2428,13 @@ static size_t decode_pscd(struct jbg_dec_state *s, unsigned char *data,
 		line_h1 = (line_h1 << 1) | (cx & 1);
 	      } while ((++x & 1) && x < hx);
 	      line_h2 <<= 2;  line_h3 <<= 2;
-	    } else 
+	    } else
 	      do {
-		
+
 		/* deterministic prediction */
 		if (s->options & JBG_DPON)
 		  if ((y & 1) == 0)
-		    if ((x & 1) == 0) 
+		    if ((x & 1) == 0)
 		      /* phase 0 */
 		      pix = s->dppriv[((line_l3 >> 15) & 0x003) |
 				      ((line_l2 >> 13) & 0x00c) |
@@ -2492,7 +2490,7 @@ static size_t decode_pscd(struct jbg_dec_state *s, unsigned char *data,
 		line_h1 = (line_h1 << 1) | pix;
 		line_h2 <<= 1;
 		line_h3 <<= 1;
-		
+
 	      } while ((++x & 1) && x < hx);
 	    line_l1 <<= 1; line_l2 <<= 1;  line_l3 <<= 1;
 	  } while ((x & 7) && x < hx);
@@ -2502,7 +2500,7 @@ static size_t decode_pscd(struct jbg_dec_state *s, unsigned char *data,
 	++lp2;
       } /* while */
       x = 0;
-      
+
       *(hp - 1) <<= hbpl * 8 - hx;
       if ((s->i & 1) == 0) {
 	/* low resolution pixels are used twice */
@@ -2510,9 +2508,9 @@ static size_t decode_pscd(struct jbg_dec_state *s, unsigned char *data,
 	lp2 -= lbpl;
       } else
 	s->pseudo = 1;
-      
+
     } /* for (i = ...) */
-    
+
   }
 
  leave:
@@ -2581,7 +2579,7 @@ int jbg_dec_in(struct jbg_dec_state *s, unsigned char *data, size_t len,
   if (s->bie_len < 20) {
     while (s->bie_len < 20 && *cnt < len)
       s->buffer[s->bie_len++] = data[(*cnt)++];
-    if (s->bie_len < 20) 
+    if (s->bie_len < 20)
       return JBG_EAGAIN;
     /* test whether this looks like a valid JBIG header at all */
     if (s->buffer[1] < s->buffer[0])
@@ -2642,7 +2640,7 @@ int jbg_dec_in(struct jbg_dec_state *s, unsigned char *data, size_t len,
 
     /* calculate number of stripes that will be required */
     s->stripes = jbg_stripes(s->l0, s->yd, s->d);
-    
+
     /* some initialization */
     s->ii[iindex[s->order & 7][STRIPE]] = 0;
     s->ii[iindex[s->order & 7][LAYER]] = s->dl;
@@ -2705,7 +2703,7 @@ int jbg_dec_in(struct jbg_dec_state *s, unsigned char *data, size_t len,
   }
 
   /* read in DPTABLE */
-  if (s->bie_len < 20 + 1728 && 
+  if (s->bie_len < 20 + 1728 &&
       (s->options & (JBG_DPON | JBG_DPPRIV | JBG_DPLAST)) ==
       (JBG_DPON | JBG_DPPRIV)) {
     assert(s->bie_len >= 20);
@@ -2713,7 +2711,7 @@ int jbg_dec_in(struct jbg_dec_state *s, unsigned char *data, size_t len,
       s->dppriv = (char *) checked_malloc(1728, sizeof(char));
     while (s->bie_len < 20 + 1728 && *cnt < len)
       s->dppriv[s->bie_len++ - 20] = data[(*cnt)++];
-    if (s->bie_len < 20 + 1728) 
+    if (s->bie_len < 20 + 1728)
       return JBG_EAGAIN;
     dppriv = (unsigned char *) s->dppriv;
     s->dppriv = (char *) checked_malloc(6912, sizeof(char));
@@ -2724,7 +2722,7 @@ int jbg_dec_in(struct jbg_dec_state *s, unsigned char *data, size_t len,
   /*
    * BID processing loop
    */
-  
+
   while (*cnt < len) {
 
     /* process floating marker segments */
@@ -2801,27 +2799,27 @@ int jbg_dec_in(struct jbg_dec_state *s, unsigned char *data, size_t len,
 	break;
       case MARKER_ABORT:
 	return JBG_EABORT;
-	
+
       case MARKER_SDNORM:
       case MARKER_SDRST:
 	/* decode final pixels based on trailing zero bytes */
 	decode_pscd(s, s->buffer, 2);
 
-	arith_decode_init(s->s[s->ii[iindex[s->order & 7][PLANE]]] + 
+	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[iindex[s->order & 7][PLANE]]]
 	  [s->ii[iindex[s->order & 7][LAYER]] - s->dl] =
 	    (s->buffer[1] == MARKER_SDRST);
-	
+
 	/* prepare for next SDE */
 	s->x = 0;
 	s->i = 0;
 	s->pseudo = 1;
 	s->at_moves = 0;
-	
+
 	/* increment layer/stripe/plane loop variables */
 	/* start and end value for each loop: */
 	is[iindex[s->order & 7][STRIPE]] = 0;
@@ -2842,7 +2840,7 @@ int jbg_dec_in(struct jbg_dec_state *s, unsigned char *data, size_t len,
 	} while (--i >= 0 && j);
 
 	s->buf_len = 0;
-	
+
 	/* check whether this have been all SDEs */
 	if (j) {
 #ifdef DEBUG
@@ -2887,7 +2885,7 @@ int jbg_dec_in(struct jbg_dec_state *s, unsigned char *data, size_t len,
 #endif
 	return JBG_EINVAL | 14;
       }
-      
+
     }
   }  /* of BID processing loop 'while (*cnt < len) ...' */
 
@@ -2930,7 +2928,7 @@ unsigned long jbg_dec_getheight(const struct jbg_dec_state *s)
     else
       return jbg_ceil_half(s->yd, s->d - (s->ii[0] - 1));
   }
-  
+
   return s->yd;
 }
 
@@ -2950,7 +2948,7 @@ unsigned char *jbg_dec_getimage(const struct jbg_dec_state *s, int plane)
     else
       return s->lhp[(s->ii[0] - 1) & 1][plane];
   }
-  
+
   return s->lhp[s->d & 1][plane];
 }
 
@@ -2968,11 +2966,11 @@ unsigned long jbg_dec_getsize(const struct jbg_dec_state *s)
     if (s->ii[0] < 1)
       return 0;
     else
-      return 
+      return
 	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 jbg_ceil_half(s->xd, 3) * s->yd;
 }
 
@@ -2990,17 +2988,17 @@ unsigned long jbg_dec_getsize_merged(const struct jbg_dec_state *s)
     if (s->ii[0] < 1)
       return 0;
     else
-      return 
+      return
 	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);
   }
-  
+
   return s->xd * s->yd * ((s->planes + 7) / 8);
 }
 
 
-/* 
+/*
  * The destructor function which releases any resources obtained by the
  * other decoder functions.
  */
@@ -3021,7 +3019,7 @@ void jbg_dec_free(struct jbg_dec_state *s)
     checked_free(s->lhp[0][i]);
     checked_free(s->lhp[1][i]);
   }
-  
+
   checked_free(s->s);
   checked_free(s->tx);
   checked_free(s->ty);
@@ -3063,10 +3061,10 @@ void jbg_split_planes(unsigned long x, unsigned long y, int has_planes,
   if (encode_planes > has_planes)
     encode_planes = has_planes;
   use_graycode = use_graycode != 0 && encode_planes > 1;
-  
+
   for (p = 0; p < encode_planes; p++)
     memset(dest[p], 0, bpl * y);
-  
+
   for (line = 0; line < y; line++) {                 /* lines loop */
     for (i = 0; i * 8 < x; i++) {                    /* dest bytes loop */
       for (k = 0; k < 8 && i * 8 + k < x; k++) {     /* pixel loop */
@@ -3098,11 +3096,11 @@ void jbg_split_planes(unsigned long x, unsigned long y, int has_planes,
     for (p = 0; p < encode_planes; p++)              /* right padding loop */
       dest[p][bpl * (line + 1) - 1] <<= 8 - k;
   }
-  
+
   return;
 }
 
-/* 
+/*
  * Merge the separate bit planes decoded by the JBIG decoder into an
  * integer pixel field. This is essentially the counterpart to
  * jbg_split_planes().
@@ -3123,7 +3121,7 @@ void jbg_dec_merge_planes(const struct jbg_dec_state *s, int use_graycode,
 
   /* sanity check */
   use_graycode = use_graycode != 0;
-  
+
   x = jbg_dec_getwidth(s);
   y = jbg_dec_getheight(s);
   if (x == 0 || y == 0)
@@ -3137,7 +3135,7 @@ void jbg_dec_merge_planes(const struct jbg_dec_state *s, int use_graycode,
       src = s->lhp[(s->ii[0] - 1) & 1];
   else
     src = s->lhp[s->d & 1];
-  
+
   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 */
@@ -3157,10 +3155,10 @@ void jbg_dec_merge_planes(const struct jbg_dec_state *s, int use_graycode,
       }
     }
   }
-  
+
   if (bp - buf > 0)
     data_out(buf, bp - buf, file);
-  
+
   return;
 }
 
@@ -3175,7 +3173,7 @@ void jbg_dec_merge_planes(const struct jbg_dec_state *s, int use_graycode,
  *  - 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)
 {
diff --git a/converter/other/jpeg2000/jpeg2ktopam.c b/converter/other/jpeg2000/jpeg2ktopam.c
index 405de9c9..b507f56e 100644
--- a/converter/other/jpeg2000/jpeg2ktopam.c
+++ b/converter/other/jpeg2000/jpeg2ktopam.c
@@ -8,6 +8,7 @@
 
 *****************************************************************************/
 
+#define _DEFAULT_SOURCE 1  /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
 #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
@@ -61,7 +62,7 @@ parseCommandLine(int argc, char ** argv,
     MALLOCARRAY_NOFAIL(option_def, 100);
 
     option_def_index = 0;   /* incremented by OPTENTRY */
-    OPTENT3(0, "verbose",      OPT_FLAG,   NULL, 
+    OPTENT3(0, "verbose",      OPT_FLAG,   NULL,
             &cmdlineP->verbose,   0);
     OPTENT3(0, "debuglevel",   OPT_UINT,   &cmdlineP->debuglevel,
             &debuglevelSpec,      0);
@@ -79,7 +80,7 @@ parseCommandLine(int argc, char ** argv,
         cmdlineP->inputFilename = strdup("-");  /* he wants stdin */
     else if (argc - 1 == 1)
         cmdlineP->inputFilename = strdup(argv[1]);
-    else 
+    else
         pm_error("Too many arguments.  The only argument accepted\n"
                  "is the input file specification");
 
@@ -106,11 +107,11 @@ validateJ2k(jas_stream_t * const instreamP) {
     }
 }
 
-        
+
 
 
 static void
-readJ2k(const char *   const inputFilename, 
+readJ2k(const char *   const inputFilename,
         jas_image_t ** const jasperPP) {
 
     jas_image_t * jasperP;
@@ -126,7 +127,7 @@ readJ2k(const char *   const inputFilename,
         instreamP = jas_stream_fopen(inputFilename, "rb");
         if (instreamP == NULL )
             pm_error("cannot open input image file '%s'", inputFilename);
-    } 
+    }
 
     validateJ2k(instreamP);
 
@@ -138,7 +139,7 @@ readJ2k(const char *   const inputFilename,
         pm_error("Unable to interpret JPEG-2000 input.  "
                  "The Jasper library jas_image_decode() subroutine failed.");
 
-	jas_stream_close(instreamP);
+    jas_stream_close(instreamP);
 
     *jasperPP = jasperP;
 }
@@ -149,7 +150,7 @@ static void
 getRgbComponents(int jasperCmpnt[], jas_image_t * const jasperP) {
 
     {
-        int const rc = 
+        int const rc =
             jas_image_getcmptbytype(jasperP,
                                     JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_RGB_R));
         if (rc < 0)
@@ -158,12 +159,12 @@ getRgbComponents(int jasperCmpnt[], jas_image_t * const jasperP) {
         else
             jasperCmpnt[PAM_RED_PLANE] = rc;
 
-        if (jas_image_cmptsgnd(jasperP, rc)) 
+        if (jas_image_cmptsgnd(jasperP, rc))
             pm_error("Input image says it is RGB, but has signed values "
                      "for what should be the red intensities.");
     }
     {
-        int const rc = 
+        int const rc =
             jas_image_getcmptbytype(jasperP,
                                     JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_RGB_G));
         if (rc < 0)
@@ -172,12 +173,12 @@ getRgbComponents(int jasperCmpnt[], jas_image_t * const jasperP) {
         else
             jasperCmpnt[PAM_GRN_PLANE] = rc;
 
-        if (jas_image_cmptsgnd(jasperP, rc)) 
+        if (jas_image_cmptsgnd(jasperP, rc))
             pm_error("Input image says it is RGB, but has signed values "
                      "for what should be the green intensities.");
     }
     {
-        int const rc = 
+        int const rc =
             jas_image_getcmptbytype(jasperP,
                                     JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_RGB_B));
         if (rc < 0)
@@ -186,18 +187,18 @@ getRgbComponents(int jasperCmpnt[], jas_image_t * const jasperP) {
         else
             jasperCmpnt[PAM_BLU_PLANE] = rc;
 
-        if (jas_image_cmptsgnd(jasperP, rc)) 
+        if (jas_image_cmptsgnd(jasperP, rc))
             pm_error("Input image says it is RGB, but has signed values "
                      "for what should be the blue intensities.");
     }
-}            
+}
 
 
 
 static void
 getGrayComponent(int * jasperCmptP, jas_image_t * const jasperP) {
 
-    int const rc = 
+    int const rc =
         jas_image_getcmptbytype(jasperP,
                                 JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_GRAY_Y));
     if (rc < 0)
@@ -205,7 +206,7 @@ getGrayComponent(int * jasperCmptP, jas_image_t * const jasperP) {
                  "no gray intensity component");
     else
         *jasperCmptP = rc;
-    if (jas_image_cmptsgnd(jasperP, 0)) 
+    if (jas_image_cmptsgnd(jasperP, 0))
         pm_error("Input image says it is grayscale, but has signed values "
                  "for what should be the gray levels.");
 }
@@ -220,13 +221,13 @@ validateComponentsAlike(jas_image_t * const jasperP) {
    all the channels are the same, and abort the program if not.
 -----------------------------------------------------------------------------*/
     int cmptNo;
-    
+
     for (cmptNo = 0; cmptNo < jas_image_numcmpts(jasperP); ++cmptNo) {
-        if (jas_image_cmptwidth(jasperP, cmptNo) != 
+        if (jas_image_cmptwidth(jasperP, cmptNo) !=
             jas_image_cmptwidth(jasperP, 0))
             pm_message("Input image does not have components all the same "
                        "width.");
-        if (jas_image_cmptheight(jasperP, cmptNo) != 
+        if (jas_image_cmptheight(jasperP, cmptNo) !=
             jas_image_cmptheight(jasperP, 0))
             pm_message("Input image does not have components all the same "
                        "height.");
@@ -240,7 +241,7 @@ maxJasperComponentPrecision(jas_image_t * const jasperP) {
 
     int cmptNo;
     unsigned int max;
-    
+
     max = 1;
 
     for (cmptNo = 0; cmptNo < jas_image_numcmpts(jasperP); ++cmptNo)
@@ -261,8 +262,8 @@ computeOutputParm(jas_image_t * const jasperP,
           with the Jasper library that corresponds to Plane P of the PAM.
        */
 
-	switch (jas_clrspc_fam(jas_image_clrspc(jasperP))) {
-	case JAS_CLRSPC_FAM_GRAY:
+    switch (jas_clrspc_fam(jas_image_clrspc(jasperP))) {
+    case JAS_CLRSPC_FAM_GRAY:
         outpamP->depth = 1;
         MALLOCARRAY_NOFAIL(jasperCmptNo, 1);
         getGrayComponent(&jasperCmptNo[0], jasperP);
@@ -274,7 +275,7 @@ computeOutputParm(jas_image_t * const jasperP,
             strcpy(outpamP->tuple_type, PAM_PGM_TUPLETYPE);
         }
         break;
-	case JAS_CLRSPC_FAM_RGB:
+    case JAS_CLRSPC_FAM_RGB:
         outpamP->depth = 3;
         MALLOCARRAY_NOFAIL(jasperCmptNo, 3);
         getRgbComponents(jasperCmptNo, jasperP);
@@ -292,15 +293,15 @@ computeOutputParm(jas_image_t * const jasperP,
                 jasperCmptNo[plane] = plane;
         }
         strcpy(outpamP->tuple_type, "");
-        if (jas_image_cmptsgnd(jasperP, 0)) 
+        if (jas_image_cmptsgnd(jasperP, 0))
             pm_message("Warning: Input image has signed sample values.  "
                        "They will be represented in the PAM output in "
                        "two's complement.");
     }
     outpamP->plainformat = FALSE;
 
-	outpamP->width = jas_image_cmptwidth(jasperP, 0);
-	outpamP->height = jas_image_cmptheight(jasperP, 0);
+    outpamP->width = jas_image_cmptwidth(jasperP, 0);
+    outpamP->height = jas_image_cmptheight(jasperP, 0);
 
     validateComponentsAlike(jasperP);
 
@@ -308,7 +309,7 @@ computeOutputParm(jas_image_t * const jasperP,
         unsigned int const maxPrecision = maxJasperComponentPrecision(jasperP);
 
         outpamP->maxval = pm_bitstomaxval(maxPrecision);
-        
+
         outpamP->bytes_per_sample = (maxPrecision + 7)/8;
     }
     *jasperCmptNoP = jasperCmptNo;
@@ -319,7 +320,7 @@ computeOutputParm(jas_image_t * const jasperP,
 static void
 createMatrices(struct pam * const outpamP, jas_matrix_t *** matrixP) {
 
-    jas_matrix_t ** matrix; 
+    jas_matrix_t ** matrix;
     unsigned int plane;
 
     MALLOCARRAY_NOFAIL(matrix, outpamP->depth);
@@ -330,14 +331,14 @@ createMatrices(struct pam * const outpamP, jas_matrix_t *** matrixP) {
         if (matrix[plane] == NULL)
             pm_error("Unable to create matrix for plane %u.  "
                      "jas_matrix_create() failed.", plane);
-    }   
+    }
     *matrixP = matrix;
 }
 
 
 
 static void
-destroyMatrices(struct pam *    const outpamP, 
+destroyMatrices(struct pam *    const outpamP,
                 jas_matrix_t ** const matrix ) {
 
     unsigned int plane;
@@ -345,7 +346,7 @@ destroyMatrices(struct pam *    const outpamP,
     for (plane = 0; plane < outpamP->depth; ++plane)
         jas_matrix_destroy(matrix[plane]);
     free(matrix);
-}    
+}
 
 
 
@@ -355,7 +356,7 @@ computeComponentMaxval(struct pam *  const outpamP,
                        int           const jasperCmpt[],
                        sample **     const jasperMaxvalP,
                        bool *        const singleMaxvalP) {
-    
+
     sample * jasperMaxval;
     unsigned int plane;
 
@@ -363,7 +364,7 @@ computeComponentMaxval(struct pam *  const outpamP,
 
     *singleMaxvalP = TRUE;  /* initial assumption */
     for (plane = 0; plane < outpamP->depth; ++plane) {
-        jasperMaxval[plane] = 
+        jasperMaxval[plane] =
             pm_bitstomaxval(jas_image_cmptprec(jasperP, jasperCmpt[plane]));
         if (jasperMaxval[plane] != jasperMaxval[0])
             *singleMaxvalP = FALSE;
@@ -371,7 +372,7 @@ computeComponentMaxval(struct pam *  const outpamP,
     *jasperMaxvalP = jasperMaxval;
 }
 
-                       
+
 
 static void
 copyRowSingleMaxval(jas_seqent_t ** const jasperRow,
@@ -386,10 +387,10 @@ copyRowSingleMaxval(jas_seqent_t ** const jasperRow,
    This is significantly faster than copyRowAnyMaxval().
 -----------------------------------------------------------------------------*/
     unsigned int col;
-    
+
     for (col = 0; col < outpamP->width; ++col) {
         unsigned int plane;
-        for (plane = 0; plane < outpamP->depth; ++plane) 
+        for (plane = 0; plane < outpamP->depth; ++plane)
             tuplerow[col][plane] = jasperRow[plane][col];
     }
 }
@@ -410,12 +411,12 @@ copyRowAnyMaxval(jas_seqent_t ** const jasperRow,
    This is significantly slower than copyRowSingleMaxval().
 -----------------------------------------------------------------------------*/
     unsigned int col;
-            
+
     for (col = 0; col < outpamP->width; ++col) {
         unsigned int plane;
-        for (plane = 0; plane < outpamP->depth; ++plane) 
-            tuplerow[col][plane] = 
-                jasperRow[plane][col] * 
+        for (plane = 0; plane < outpamP->depth; ++plane)
+            tuplerow[col][plane] =
+                jasperRow[plane][col] *
                 outpamP->maxval / jasperMaxval[plane];
     }
 }
@@ -460,11 +461,11 @@ convertToPamPnm(struct pam *  const outpamP,
                                     matrix[plane]);
             if (rc != 0)
                 pm_error("jas_image_readcmpt() of row %u plane %u "
-                         "failed.", 
+                         "failed.",
                          row, plane);
             jasperRow[plane] = jas_matrix_getref(matrix[plane], 0, 0);
         }
-        if (singleMaxval) 
+        if (singleMaxval)
             copyRowSingleMaxval(jasperRow, tuplerow, outpamP);
         else
             copyRowAnyMaxval(jasperRow, tuplerow, outpamP, jasperMaxval);
@@ -488,25 +489,25 @@ main(int argc, char **argv)
     struct pam outpam;
     int * jasperCmpt;  /* malloc'ed */
        /* jaspercmpt[P] is the component number for use with the
-          Jasper library that corresponds to Plane P of the PAM.  
+          Jasper library that corresponds to Plane P of the PAM.
        */
     jas_image_t * jasperP;
 
     pnm_init(&argc, argv);
-    
+
     parseCommandLine(argc, argv, &cmdline);
-    
-    { 
+
+    {
         int rc;
-        
+
         rc = jas_init();
         if ( rc != 0 )
             pm_error("Failed to initialize Jasper library.  "
                      "jas_init() returns rc %d", rc );
     }
-    
+
     jas_setdbglevel(cmdline.debuglevel);
-    
+
     readJ2k(cmdline.inputFilename, &jasperP);
 
     outpam.file = stdout;
@@ -516,13 +517,16 @@ main(int argc, char **argv)
     computeOutputParm(jasperP, &outpam, &jasperCmpt);
 
     pnm_writepaminit(&outpam);
-    
+
     convertToPamPnm(&outpam, jasperP, jasperCmpt);
-    
+
     free(jasperCmpt);
-	jas_image_destroy(jasperP);
+    jas_image_destroy(jasperP);
 
     pm_close(stdout);
-    
+
     return 0;
 }
+
+
+
diff --git a/converter/other/jpeg2000/libjasper/jp2/jp2_dec.c b/converter/other/jpeg2000/libjasper/jp2/jp2_dec.c
index 91ce6c51..a9463a57 100644
--- a/converter/other/jpeg2000/libjasper/jp2/jp2_dec.c
+++ b/converter/other/jpeg2000/libjasper/jp2/jp2_dec.c
@@ -110,16 +110,6 @@
  * __END_OF_JASPER_LICENSE__
  */
 
-/*
- * JP2 Library
- *
- * $Id$
- */
-
-/******************************************************************************\
-* Includes.
-\******************************************************************************/
-
 #include "jasper/jas_image.h"
 #include "jasper/jas_stream.h"
 #include "jasper/jas_math.h"
@@ -130,520 +120,548 @@
 #include "jp2_cod.h"
 #include "jp2_dec.h"
 
-#define	JP2_VALIDATELEN	(JAS_MIN(JP2_JP_LEN + 16, JAS_STREAM_MAXPUTBACK))
-
-static jp2_dec_t *jp2_dec_create(void);
-static void jp2_dec_destroy(jp2_dec_t *dec);
-static int jp2_getcs(jp2_colr_t *colr);
-static int jp2_getct(int colorspace, int type, int assoc);
-static int fromiccpcs(int cs);
-
-/******************************************************************************\
-* Functions.
-\******************************************************************************/
-
-jas_image_t *jp2_decode(jas_stream_t *in, char *optstr)
-{
-	jp2_box_t *box;
-	int found;
-	jas_image_t *image;
-	jp2_dec_t *dec;
-	bool samedtype;
-	int dtype;
-	int i;
-	jp2_cmap_t *cmapd;
-	jp2_pclr_t *pclrd;
-	jp2_cdef_t *cdefd;
-	int channo;
-	int newcmptno;
-	int_fast32_t *lutents;
-	jp2_cmapent_t *cmapent;
-	unsigned char *iccp;
-	int cs;
-
-	dec = 0;
-	box = 0;
-	image = 0;
-
-	if (!(dec = jp2_dec_create())) {
-		goto error;
-	}
-
-	/* Get the first box.  This should be a JP box. */
-	if (!(box = jp2_box_get(in))) {
-		jas_eprintf("error: cannot get box\n");
-		goto error;
-	}
-	if (box->type != JP2_BOX_JP) {
-		jas_eprintf("error: expecting signature box\n");
-		goto error;
-	}
-	if (box->data.jp.magic != JP2_JP_MAGIC) {
-		jas_eprintf("incorrect magic number\n");
-		goto error;
-	}
-	jp2_box_destroy(box);
-	box = 0;
-
-	/* Get the second box.  This should be a FTYP box. */
-	if (!(box = jp2_box_get(in))) {
-		goto error;
-	}
-	if (box->type != JP2_BOX_FTYP) {
-		jas_eprintf("expecting file type box\n");
-		goto error;
-	}
-	jp2_box_destroy(box);
-	box = 0;
-
-	/* Get more boxes... */
-	found = 0;
-	while ((box = jp2_box_get(in))) {
-		if (jas_getdbglevel() >= 1) {
-			fprintf(stderr, "box type %s\n", box->info->name);
-		}
-		switch (box->type) {
-		case JP2_BOX_JP2C:
-			found = 1;
-			break;
-		case JP2_BOX_IHDR:
-			if (!dec->ihdr) {
-				dec->ihdr = box;
-				box = 0;
-			}
-			break;
-		case JP2_BOX_BPCC:
-			if (!dec->bpcc) {
-				dec->bpcc = box;
-				box = 0;
-			}
-			break;
-		case JP2_BOX_CDEF:
-			if (!dec->cdef) {
-				dec->cdef = box;
-				box = 0;
-			}
-			break;
-		case JP2_BOX_PCLR:
-			if (!dec->pclr) {
-				dec->pclr = box;
-				box = 0;
-			}
-			break;
-		case JP2_BOX_CMAP:
-			if (!dec->cmap) {
-				dec->cmap = box;
-				box = 0;
-			}
-			break;
-		case JP2_BOX_COLR:
-			if (!dec->colr) {
-				dec->colr = box;
-				box = 0;
-			}
-			break;
-		}
-		if (box) {
-			jp2_box_destroy(box);
-			box = 0;
-		}
-		if (found) {
-			break;
-		}
-	}
-
-	if (!found) {
-		jas_eprintf("error: no code stream found\n");
-		goto error;
-	}
-
-	if (!(dec->image = jpc_decode(in, optstr))) {
-		jas_eprintf("error: cannot decode code stream\n");
-		goto error;
-	}
-
-	/* An IHDR box must be present. */
-	if (!dec->ihdr) {
-		jas_eprintf("error: missing IHDR box\n");
-		goto error;
-	}
-
-	/* Does the number of components indicated in the IHDR box match
-	  the value specified in the code stream? */
-	if (dec->ihdr->data.ihdr.numcmpts != jas_image_numcmpts(dec->image)) {
-		jas_eprintf("warning: number of components mismatch\n");
-	}
-
-	/* At least one component must be present. */
-	if (!jas_image_numcmpts(dec->image)) {
-		jas_eprintf("error: no components\n");
-		goto error;
-	}
-
-	/* Determine if all components have the same data type. */
-	samedtype = true;
-	dtype = jas_image_cmptdtype(dec->image, 0);
-	for (i = 1; i < jas_image_numcmpts(dec->image); ++i) {
-		if (jas_image_cmptdtype(dec->image, i) != dtype) {
-			samedtype = false;
-			break;
-		}
-	}
-
-	/* Is the component data type indicated in the IHDR box consistent
-	  with the data in the code stream? */
-	if ((samedtype && dec->ihdr->data.ihdr.bpc != JP2_DTYPETOBPC(dtype)) ||
-	  (!samedtype && dec->ihdr->data.ihdr.bpc != JP2_IHDR_BPCNULL)) {
-		jas_eprintf("warning: component data type mismatch\n");
-	}
-
-	/* Can we handle the compression type? */
-	if (dec->ihdr->data.ihdr.comptype != JP2_IHDR_COMPTYPE) {
-		jas_eprintf("error: not capable of this compression type\n");
-		goto error;
-	}
-
-	if (dec->bpcc) {
-		/* Is the number of components indicated in the BPCC box
-		  consistent with the code stream data? */
-		if (dec->bpcc->data.bpcc.numcmpts != jas_image_numcmpts(
-		  dec->image)) {
-			jas_eprintf("warning: number of components mismatch\n");
-		}
-		/* Is the component data type information indicated in the BPCC
-		  box consistent with the code stream data? */
-		if (!samedtype) {
-			for (i = 0; i < jas_image_numcmpts(dec->image); ++i) {
-				if (jas_image_cmptdtype(dec->image, i) != JP2_BPCTODTYPE(dec->bpcc->data.bpcc.bpcs[i])) {
-					jas_eprintf("warning: component data type mismatch\n");
-				}
-			}
-		} else {
-			jas_eprintf("warning: superfluous BPCC box\n");
-		}
-	}
-
-	/* A COLR box must be present. */
-	if (!dec->colr) {
-		jas_eprintf("error: no COLR box\n");
-		goto error;
-	}
-
-	switch (dec->colr->data.colr.method) {
-	case JP2_COLR_ENUM:
-		jas_image_setcolorspace(dec->image, jp2_getcs(&dec->colr->data.colr));
-		break;
-	case JP2_COLR_ICC:
-		if (dec->colr->data.colr.iccplen < 128) {
-			abort();
-		}
-		iccp = dec->colr->data.colr.iccp;
-		cs = (iccp[16] << 24) | (iccp[17] << 16) | (iccp[18] << 8) |
-		  iccp[19];
-        if (jas_getdbglevel() > 1)
-            jas_eprintf("ICC Profile CS %08x\n", cs);
-		jas_image_setcolorspace(dec->image, fromiccpcs(cs));
-		break;
-	}
-
-	/* If a CMAP box is present, a PCLR box must also be present. */
-	if (dec->cmap && !dec->pclr) {
-		jas_eprintf("warning: missing PCLR box or superfluous CMAP box\n");
-		jp2_box_destroy(dec->cmap);
-		dec->cmap = 0;
-	}
-
-	/* If a CMAP box is not present, a PCLR box must not be present. */
-	if (!dec->cmap && dec->pclr) {
-		jas_eprintf("warning: missing CMAP box or superfluous PCLR box\n");
-		jp2_box_destroy(dec->pclr);
-		dec->pclr = 0;
-	}
-
-	/* Determine the number of channels (which is essentially the number
-	  of components after any palette mappings have been applied). */
-	dec->numchans = dec->cmap ? dec->cmap->data.cmap.numchans : jas_image_numcmpts(dec->image);
-
-	/* Perform a basic sanity check on the CMAP box if present. */
-	if (dec->cmap) {
-		for (i = 0; i < dec->numchans; ++i) {
-			/* Is the component number reasonable? */
-			if (dec->cmap->data.cmap.ents[i].cmptno >= jas_image_numcmpts(dec->image)) {
-				jas_eprintf("error: invalid component number in CMAP box\n");
-				goto error;
-			}
-			/* Is the LUT index reasonable? */
-			if (dec->cmap->data.cmap.ents[i].pcol >= dec->pclr->data.pclr.numchans) {
-				jas_eprintf("error: invalid CMAP LUT index\n");
-				goto error;
-			}
-		}
-	}
-
-	/* Allocate space for the channel-number to component-number LUT. */
-	if (!(dec->chantocmptlut = jas_malloc(dec->numchans * sizeof(uint_fast16_t)))) {
-		jas_eprintf("error: no memory\n");
-		goto error;
-	}
-
-	if (!dec->cmap) {
-		for (i = 0; i < dec->numchans; ++i) {
-			dec->chantocmptlut[i] = i;
-		}
-	} else {
-		cmapd = &dec->cmap->data.cmap;
-		pclrd = &dec->pclr->data.pclr;
-		cdefd = &dec->cdef->data.cdef;
-		for (channo = 0; channo < cmapd->numchans; ++channo) {
-			cmapent = &cmapd->ents[channo];
-			if (cmapent->map == JP2_CMAP_DIRECT) {
-				dec->chantocmptlut[channo] = channo;
-			} else if (cmapent->map == JP2_CMAP_PALETTE) {
-				lutents = jas_malloc(pclrd->numlutents * sizeof(int_fast32_t));
-				for (i = 0; i < pclrd->numlutents; ++i) {
-					lutents[i] = pclrd->lutdata[cmapent->pcol + i * pclrd->numchans];
-				}
-				newcmptno = jas_image_numcmpts(dec->image);
-				jas_image_depalettize(dec->image, cmapent->cmptno, pclrd->numlutents, lutents, JP2_BPCTODTYPE(pclrd->bpc[cmapent->pcol]), newcmptno);
-				dec->chantocmptlut[channo] = newcmptno;
-				jas_free(lutents);
-#if 0
-				if (dec->cdef) {
-					cdefent = jp2_cdef_lookup(cdefd, channo);
-					if (!cdefent) {
-						abort();
-					}
-				jas_image_setcmpttype(dec->image, newcmptno, jp2_getct(jas_image_colorspace(dec->image), cdefent->type, cdefent->assoc));
-				} else {
-				jas_image_setcmpttype(dec->image, newcmptno, jp2_getct(jas_image_colorspace(dec->image), 0, channo + 1));
-				}
-#endif
-			}
-		}
-	}
-
-	/* Mark all components as being of unknown type. */
-
-	for (i = 0; i < jas_image_numcmpts(dec->image); ++i) {
-		jas_image_setcmpttype(dec->image, i, JAS_IMAGE_CT_UNKNOWN);
-	}
-
-	/* Determine the type of each component. */
-	if (dec->cdef) {
-		for (i = 0; i < dec->numchans; ++i) {
-			jas_image_setcmpttype(dec->image,
-			  dec->chantocmptlut[dec->cdef->data.cdef.ents[i].channo],
-			  jp2_getct(jas_image_colorspace(dec->image),
-			  dec->cdef->data.cdef.ents[i].type, dec->cdef->data.cdef.ents[i].assoc));
-		}
-	} else {
-		for (i = 0; i < dec->numchans; ++i) {
-			jas_image_setcmpttype(dec->image, dec->chantocmptlut[i],
-			  jp2_getct(jas_image_colorspace(dec->image), 0, i + 1));
-		}
-	}
-
-	/* Delete any components that are not of interest. */
-	for (i = jas_image_numcmpts(dec->image) - 1; i >= 0; --i) {
-		if (jas_image_cmpttype(dec->image, i) == JAS_IMAGE_CT_UNKNOWN) {
-			jas_image_delcmpt(dec->image, i);
-		}
-	}
-
-	/* Ensure that some components survived. */
-	if (!jas_image_numcmpts(dec->image)) {
-		jas_eprintf("error: no components\n");
-		goto error;
-	}
-
-	/* Prevent the image from being destroyed later. */
-	image = dec->image;
-	dec->image = 0;
-
-	jp2_dec_destroy(dec);
-
-	return image;
+#define JP2_VALIDATELEN (JAS_MIN(JP2_JP_LEN + 16, JAS_STREAM_MAXPUTBACK))
 
-error:
-	if (box) {
-		jp2_box_destroy(box);
-	}
-	if (dec) {
-		jp2_dec_destroy(dec);
-	}
-	return 0;
+
+
+static jp2_dec_t *
+jp2_dec_create(void) {
+
+    jp2_dec_t *dec;
+
+    dec = jas_malloc(sizeof(jp2_dec_t));
+    if (dec) {
+        dec->ihdr = 0;
+        dec->bpcc = 0;
+        dec->cdef = 0;
+        dec->pclr = 0;
+        dec->image = 0;
+        dec->chantocmptlut = 0;
+        dec->cmap = 0;
+        dec->colr = 0;
+    }
+    return dec;
 }
 
-int jp2_validate(jas_stream_t *in)
-{
-	char buf[JP2_VALIDATELEN];
-	int i;
-	int n;
-
-	assert(JAS_STREAM_MAXPUTBACK >= JP2_VALIDATELEN);
-
-	/* Read the validation data (i.e., the data used for detecting
-	  the format). */
-	if ((n = jas_stream_read(in, buf, JP2_VALIDATELEN)) < 0) {
-		return -1;
-	}
-
-	/* Put the validation data back onto the stream, so that the
-	  stream position will not be changed. */
-	for (i = n - 1; i >= 0; --i) {
-		if (jas_stream_ungetc(in, buf[i]) == EOF) {
-			return -1;
-		}
-	}
-
-	/* Did we read enough data? */
-	if (n < JP2_VALIDATELEN) {
-		return -1;
-	}
-
-	/* Is the box type correct? */
-	if (((buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7]) !=
-	  JP2_BOX_JP)
-	{
-		return -1;
-	}
-
-	return 0;
+
+
+static void
+jp2_dec_destroy(jp2_dec_t *dec) {
+
+    if (dec->ihdr)
+        jp2_box_destroy(dec->ihdr);
+
+    if (dec->bpcc)
+        jp2_box_destroy(dec->bpcc);
+
+    if (dec->cdef)
+        jp2_box_destroy(dec->cdef);
+
+    if (dec->pclr)
+        jp2_box_destroy(dec->pclr);
+
+    if (dec->image)
+        jas_image_destroy(dec->image);
+
+    if (dec->cmap)
+        jp2_box_destroy(dec->cmap);
+
+    if (dec->colr)
+        jp2_box_destroy(dec->colr);
+
+    if (dec->chantocmptlut)
+        jas_free(dec->chantocmptlut);
+
+    jas_free(dec);
 }
 
-static jp2_dec_t *jp2_dec_create(void)
-{
-	jp2_dec_t *dec;
-
-	if (!(dec = jas_malloc(sizeof(jp2_dec_t)))) {
-		return 0;
-	}
-	dec->ihdr = 0;
-	dec->bpcc = 0;
-	dec->cdef = 0;
-	dec->pclr = 0;
-	dec->image = 0;
-	dec->chantocmptlut = 0;
-	dec->cmap = 0;
-	dec->colr = 0;
-	return dec;
+
+
+static int
+jp2_getct(int colorspace, int type, int assoc) {
+
+    if (type == 1 && assoc == 0)
+        return JAS_IMAGE_CT_OPACITY;
+
+    if (type == 0 && assoc >= 1 && assoc <= 65534) {
+        switch (colorspace) {
+        case JAS_IMAGE_CS_RGB:
+            switch (assoc) {
+            case JP2_CDEF_RGB_R:
+                return JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_RGB_R);
+                break;
+            case JP2_CDEF_RGB_G:
+                return JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_RGB_G);
+                break;
+            case JP2_CDEF_RGB_B:
+                return JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_RGB_B);
+                break;
+            }
+            break;
+        case JAS_IMAGE_CS_YCBCR:
+            switch (assoc) {
+            case JP2_CDEF_YCBCR_Y:
+                return JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_YCBCR_Y);
+                break;
+            case JP2_CDEF_YCBCR_CB:
+                return JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_YCBCR_CB);
+                break;
+            case JP2_CDEF_YCBCR_CR:
+                return JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_YCBCR_CR);
+                break;
+            }
+            break;
+        case JAS_IMAGE_CS_GRAY:
+            switch (assoc) {
+            case JP2_CDEF_GRAY_Y:
+                return JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_GRAY_Y);
+                break;
+            }
+            break;
+#if 0
+        case JAS_IMAGE_CS_ICC:
+#endif
+        default:
+            return JAS_IMAGE_CT_COLOR(assoc - 1);
+            break;
+        }
+    }
+    return JAS_IMAGE_CT_UNKNOWN;
 }
 
-static void jp2_dec_destroy(jp2_dec_t *dec)
-{
-	if (dec->ihdr) {
-		jp2_box_destroy(dec->ihdr);
-	}
-	if (dec->bpcc) {
-		jp2_box_destroy(dec->bpcc);
-	}
-	if (dec->cdef) {
-		jp2_box_destroy(dec->cdef);
-	}
-	if (dec->pclr) {
-		jp2_box_destroy(dec->pclr);
-	}
-	if (dec->image) {
-		jas_image_destroy(dec->image);
-	}
-	if (dec->cmap) {
-		jp2_box_destroy(dec->cmap);
-	}
-	if (dec->colr) {
-		jp2_box_destroy(dec->colr);
-	}
-	if (dec->chantocmptlut) {
-		jas_free(dec->chantocmptlut);
-	}
-	jas_free(dec);
+
+
+static int
+jp2_getcs(jp2_colr_t *colr) {
+
+    if (colr->method == JP2_COLR_ENUM) {
+        switch (colr->csid) {
+        case JP2_COLR_SRGB:
+            return JAS_IMAGE_CS_RGB;
+            break;
+        case JP2_COLR_SYCC:
+            return JAS_IMAGE_CS_YCBCR;
+            break;
+        case JP2_COLR_SGRAY:
+            return JAS_IMAGE_CS_GRAY;
+            break;
+        }
+    }
+    return JAS_IMAGE_CS_UNKNOWN;
 }
 
 
 
+static int
+fromiccpcs(int cs) {
+    
+    switch (cs) {
+    case ICC_CS_RGB:
+        return JAS_IMAGE_CS_RGB;
+        break;
+    case ICC_CS_YCBCR:
+        return JAS_IMAGE_CS_YCBCR;
+        break;
+    case ICC_CS_GRAY:
+        return JAS_IMAGE_CS_GRAY;
+        break;
+    }
+    return JAS_IMAGE_CS_UNKNOWN;
+}
 
 
 
-static int jp2_getct(int colorspace, int type, int assoc)
-{
-	if (type == 1 && assoc == 0) {
-		return JAS_IMAGE_CT_OPACITY;
-	}
-	if (type == 0 && assoc >= 1 && assoc <= 65534) {
-		switch (colorspace) {
-		case JAS_IMAGE_CS_RGB:
-			switch (assoc) {
-			case JP2_CDEF_RGB_R:
-				return JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_RGB_R);
-				break;
-			case JP2_CDEF_RGB_G:
-				return JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_RGB_G);
-				break;
-			case JP2_CDEF_RGB_B:
-				return JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_RGB_B);
-				break;
-			}
-			break;
-		case JAS_IMAGE_CS_YCBCR:
-			switch (assoc) {
-			case JP2_CDEF_YCBCR_Y:
-				return JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_YCBCR_Y);
-				break;
-			case JP2_CDEF_YCBCR_CB:
-				return JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_YCBCR_CB);
-				break;
-			case JP2_CDEF_YCBCR_CR:
-				return JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_YCBCR_CR);
-				break;
-			}
-			break;
-		case JAS_IMAGE_CS_GRAY:
-			switch (assoc) {
-			case JP2_CDEF_GRAY_Y:
-				return JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_GRAY_Y);
-				break;
-			}
-			break;
+jas_image_t *
+jp2_decode(jas_stream_t *in, char *optstr) {
+
+    jp2_box_t *box;
+    int found;
+    jas_image_t *image;
+    jp2_dec_t *dec;
+    bool samedtype;
+    int dtype;
+    int i;
+    jp2_cmap_t *cmapd;
+    jp2_pclr_t *pclrd;
+    int channo;
+    int newcmptno;
+    int_fast32_t *lutents;
+    jp2_cmapent_t *cmapent;
+    unsigned char *iccp;
+    int cs;
+
+    dec = 0;
+    box = 0;
+    image = 0;
+
+    if (!(dec = jp2_dec_create())) {
+        goto error;
+    }
+
+    /* Get the first box.  This should be a JP box. */
+    if (!(box = jp2_box_get(in))) {
+        jas_eprintf("error: cannot get box\n");
+        goto error;
+    }
+    if (box->type != JP2_BOX_JP) {
+        jas_eprintf("error: expecting signature box\n");
+        goto error;
+    }
+    if (box->data.jp.magic != JP2_JP_MAGIC) {
+        jas_eprintf("incorrect magic number\n");
+        goto error;
+    }
+    jp2_box_destroy(box);
+    box = 0;
+
+    /* Get the second box.  This should be a FTYP box. */
+    if (!(box = jp2_box_get(in))) {
+        goto error;
+    }
+    if (box->type != JP2_BOX_FTYP) {
+        jas_eprintf("expecting file type box\n");
+        goto error;
+    }
+    jp2_box_destroy(box);
+    box = 0;
+
+    /* Get more boxes... */
+    found = 0;
+    while ((box = jp2_box_get(in))) {
+        if (jas_getdbglevel() >= 1) {
+            fprintf(stderr, "box type %s\n", box->info->name);
+        }
+        switch (box->type) {
+        case JP2_BOX_JP2C:
+            found = 1;
+            break;
+        case JP2_BOX_IHDR:
+            if (!dec->ihdr) {
+                dec->ihdr = box;
+                box = 0;
+            }
+            break;
+        case JP2_BOX_BPCC:
+            if (!dec->bpcc) {
+                dec->bpcc = box;
+                box = 0;
+            }
+            break;
+        case JP2_BOX_CDEF:
+            if (!dec->cdef) {
+                dec->cdef = box;
+                box = 0;
+            }
+            break;
+        case JP2_BOX_PCLR:
+            if (!dec->pclr) {
+                dec->pclr = box;
+                box = 0;
+            }
+            break;
+        case JP2_BOX_CMAP:
+            if (!dec->cmap) {
+                dec->cmap = box;
+                box = 0;
+            }
+            break;
+        case JP2_BOX_COLR:
+            if (!dec->colr) {
+                dec->colr = box;
+                box = 0;
+            }
+            break;
+        }
+        if (box) {
+            jp2_box_destroy(box);
+            box = 0;
+        }
+        if (found) {
+            break;
+        }
+    }
+
+    if (!found) {
+        jas_eprintf("error: no code stream found\n");
+        goto error;
+    }
+
+    if (!(dec->image = jpc_decode(in, optstr))) {
+        jas_eprintf("error: cannot decode code stream\n");
+        goto error;
+    }
+
+    /* An IHDR box must be present. */
+    if (!dec->ihdr) {
+        jas_eprintf("error: missing IHDR box\n");
+        goto error;
+    }
+
+    /* Does the number of components indicated in the IHDR box match
+      the value specified in the code stream? */
+    if (dec->ihdr->data.ihdr.numcmpts != jas_image_numcmpts(dec->image)) {
+        jas_eprintf("warning: number of components mismatch\n");
+    }
+
+    /* At least one component must be present. */
+    if (!jas_image_numcmpts(dec->image)) {
+        jas_eprintf("error: no components\n");
+        goto error;
+    }
+
+    /* Determine if all components have the same data type. */
+    samedtype = true;
+    dtype = jas_image_cmptdtype(dec->image, 0);
+    for (i = 1; i < jas_image_numcmpts(dec->image); ++i) {
+        if (jas_image_cmptdtype(dec->image, i) != dtype) {
+            samedtype = false;
+            break;
+        }
+    }
+
+    /* Is the component data type indicated in the IHDR box consistent
+      with the data in the code stream? */
+    if ((samedtype && dec->ihdr->data.ihdr.bpc != JP2_DTYPETOBPC(dtype)) ||
+      (!samedtype && dec->ihdr->data.ihdr.bpc != JP2_IHDR_BPCNULL)) {
+        jas_eprintf("warning: component data type mismatch\n");
+    }
+
+    /* Can we handle the compression type? */
+    if (dec->ihdr->data.ihdr.comptype != JP2_IHDR_COMPTYPE) {
+        jas_eprintf("error: not capable of this compression type\n");
+        goto error;
+    }
+
+    if (dec->bpcc) {
+        /* Is the number of components indicated in the BPCC box
+          consistent with the code stream data? */
+        if (dec->bpcc->data.bpcc.numcmpts != jas_image_numcmpts(
+          dec->image)) {
+            jas_eprintf("warning: number of components mismatch\n");
+        }
+        /* Is the component data type information indicated in the BPCC
+          box consistent with the code stream data? */
+        if (!samedtype) {
+            for (i = 0; i < jas_image_numcmpts(dec->image); ++i) {
+                if (jas_image_cmptdtype(dec->image, i) !=
+                    JP2_BPCTODTYPE(dec->bpcc->data.bpcc.bpcs[i])) {
+                    jas_eprintf("warning: component data type mismatch\n");
+                }
+            }
+        } else {
+            jas_eprintf("warning: superfluous BPCC box\n");
+        }
+    }
+
+    /* A COLR box must be present. */
+    if (!dec->colr) {
+        jas_eprintf("error: no COLR box\n");
+        goto error;
+    }
+
+    switch (dec->colr->data.colr.method) {
+    case JP2_COLR_ENUM:
+        jas_image_setcolorspace(dec->image, jp2_getcs(&dec->colr->data.colr));
+        break;
+    case JP2_COLR_ICC:
+        if (dec->colr->data.colr.iccplen < 128) {
+            abort();
+        }
+        iccp = dec->colr->data.colr.iccp;
+        cs = (iccp[16] << 24) | (iccp[17] << 16) | (iccp[18] << 8) |
+          iccp[19];
+        if (jas_getdbglevel() > 1)
+            jas_eprintf("ICC Profile CS %08x\n", cs);
+        jas_image_setcolorspace(dec->image, fromiccpcs(cs));
+        break;
+    }
+
+    /* If a CMAP box is present, a PCLR box must also be present. */
+    if (dec->cmap && !dec->pclr) {
+        jas_eprintf("warning: missing PCLR box or superfluous CMAP box\n");
+        jp2_box_destroy(dec->cmap);
+        dec->cmap = 0;
+    }
+
+    /* If a CMAP box is not present, a PCLR box must not be present. */
+    if (!dec->cmap && dec->pclr) {
+        jas_eprintf("warning: missing CMAP box or superfluous PCLR box\n");
+        jp2_box_destroy(dec->pclr);
+        dec->pclr = 0;
+    }
+
+    /* Determine the number of channels (which is essentially the number
+      of components after any palette mappings have been applied). */
+    dec->numchans = dec->cmap ?
+        dec->cmap->data.cmap.numchans : 
+        jas_image_numcmpts(dec->image);
+
+    /* Perform a basic sanity check on the CMAP box if present. */
+    if (dec->cmap) {
+        for (i = 0; i < dec->numchans; ++i) {
+            /* Is the component number reasonable? */
+            if (dec->cmap->data.cmap.ents[i].cmptno >=
+                jas_image_numcmpts(dec->image)) {
+                jas_eprintf("error: invalid component number in CMAP box\n");
+                goto error;
+            }
+            /* Is the LUT index reasonable? */
+            if (dec->cmap->data.cmap.ents[i].pcol >=
+                dec->pclr->data.pclr.numchans) {
+                jas_eprintf("error: invalid CMAP LUT index\n");
+                goto error;
+            }
+        }
+    }
+
+    /* Allocate space for the channel-number to component-number LUT. */
+    dec->chantocmptlut = jas_malloc(dec->numchans * sizeof(uint_fast16_t));
+    if (!dec->chantocmptlut) {
+        jas_eprintf("error: no memory\n");
+        goto error;
+    }
+
+    if (!dec->cmap) {
+        for (i = 0; i < dec->numchans; ++i) {
+            dec->chantocmptlut[i] = i;
+        }
+    } else {
+        cmapd = &dec->cmap->data.cmap;
+        pclrd = &dec->pclr->data.pclr;
+        for (channo = 0; channo < cmapd->numchans; ++channo) {
+            cmapent = &cmapd->ents[channo];
+            if (cmapent->map == JP2_CMAP_DIRECT) {
+                dec->chantocmptlut[channo] = channo;
+            } else if (cmapent->map == JP2_CMAP_PALETTE) {
+                lutents = jas_malloc(pclrd->numlutents * sizeof(int_fast32_t));
+                for (i = 0; i < pclrd->numlutents; ++i) {
+                    lutents[i] =
+                        pclrd->lutdata[cmapent->pcol + i * pclrd->numchans];
+                }
+                newcmptno = jas_image_numcmpts(dec->image);
+                jas_image_depalettize(
+                    dec->image, cmapent->cmptno, pclrd->numlutents, lutents,
+                    JP2_BPCTODTYPE(pclrd->bpc[cmapent->pcol]), newcmptno);
+                dec->chantocmptlut[channo] = newcmptno;
+                jas_free(lutents);
 #if 0
-		case JAS_IMAGE_CS_ICC:
+                if (dec->cdef) {
+                    jp2_cdef_t * const cdefd = &dec->cdef->data.cdef;
+                    cdefent = jp2_cdef_lookup(cdefd, channo);
+                    if (!cdefent) {
+                        abort();
+                    }
+                jas_image_setcmpttype(
+                    dec->image, newcmptno,
+                    jp2_getct(jas_image_colorspace(dec->image),
+                              cdefent->type, cdefent->assoc));
+                } else {
+                    jas_image_setcmpttype(
+                        dec->image, newcmptno,
+                        jp2_getct(jas_image_colorspace(dec->image),
+                                  0,
+                                  channo + 1));
+                }
 #endif
-		default:
-			return JAS_IMAGE_CT_COLOR(assoc - 1);
-			break;
-		}
-	}
-	return JAS_IMAGE_CT_UNKNOWN;
-}
+            }
+        }
+    }
+
+    /* Mark all components as being of unknown type. */
+
+    for (i = 0; i < jas_image_numcmpts(dec->image); ++i) {
+        jas_image_setcmpttype(dec->image, i, JAS_IMAGE_CT_UNKNOWN);
+    }
+
+    /* Determine the type of each component. */
+    if (dec->cdef) {
+        unsigned int i;
+        for (i = 0; i < dec->numchans; ++i) {
+            jas_image_setcmpttype(
+                dec->image,
+                dec->chantocmptlut[dec->cdef->data.cdef.ents[i].channo],
+                jp2_getct(jas_image_colorspace(dec->image),
+                          dec->cdef->data.cdef.ents[i].type,
+                          dec->cdef->data.cdef.ents[i].assoc));
+        }
+    } else {
+        unsigned int i;
+        for (i = 0; i < dec->numchans; ++i) {
+            jas_image_setcmpttype(dec->image, dec->chantocmptlut[i],
+              jp2_getct(jas_image_colorspace(dec->image), 0, i + 1));
+        }
+    }
+
+    /* Delete any components that are not of interest. */
+    for (i = jas_image_numcmpts(dec->image) - 1; i >= 0; --i) {
+        if (jas_image_cmpttype(dec->image, i) == JAS_IMAGE_CT_UNKNOWN) {
+            jas_image_delcmpt(dec->image, i);
+        }
+    }
+
+    /* Ensure that some components survived. */
+    if (!jas_image_numcmpts(dec->image)) {
+        jas_eprintf("error: no components\n");
+        goto error;
+    }
+
+    /* Prevent the image from being destroyed later. */
+    image = dec->image;
+    dec->image = 0;
+
+    jp2_dec_destroy(dec);
+
+    return image;
 
-static int jp2_getcs(jp2_colr_t *colr)
-{
-	if (colr->method == JP2_COLR_ENUM) {
-		switch (colr->csid) {
-		case JP2_COLR_SRGB:
-			return JAS_IMAGE_CS_RGB;
-			break;
-		case JP2_COLR_SYCC:
-			return JAS_IMAGE_CS_YCBCR;
-			break;
-		case JP2_COLR_SGRAY:
-			return JAS_IMAGE_CS_GRAY;
-			break;
-		}
-	}
-	return JAS_IMAGE_CS_UNKNOWN;
+error:
+    if (box) {
+        jp2_box_destroy(box);
+    }
+    if (dec) {
+        jp2_dec_destroy(dec);
+    }
+    return 0;
 }
 
-static int fromiccpcs(int cs)
-{
-	switch (cs) {
-	case ICC_CS_RGB:
-		return JAS_IMAGE_CS_RGB;
-		break;
-	case ICC_CS_YCBCR:
-		return JAS_IMAGE_CS_YCBCR;
-		break;
-	case ICC_CS_GRAY:
-		return JAS_IMAGE_CS_GRAY;
-		break;
-	}
-	return JAS_IMAGE_CS_UNKNOWN;
+
+
+int
+jp2_validate(jas_stream_t *in) {
+
+    char buf[JP2_VALIDATELEN];
+    int i;
+    int n;
+
+    assert(JAS_STREAM_MAXPUTBACK >= JP2_VALIDATELEN);
+
+    /* Read the validation data (i.e., the data used for detecting
+      the format). */
+    if ((n = jas_stream_read(in, buf, JP2_VALIDATELEN)) < 0) {
+        return -1;
+    }
+
+    /* Put the validation data back onto the stream, so that the
+      stream position will not be changed. */
+    for (i = n - 1; i >= 0; --i) {
+        if (jas_stream_ungetc(in, buf[i]) == EOF) {
+            return -1;
+        }
+    }
+
+    /* Did we read enough data? */
+    if (n < JP2_VALIDATELEN) {
+        return -1;
+    }
+
+    /* Is the box type correct? */
+    if (((buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7]) !=
+      JP2_BOX_JP)
+    {
+        return -1;
+    }
+
+    return 0;
 }
+
+
+
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_dec.c b/converter/other/jpeg2000/libjasper/jpc/jpc_dec.c
index 72bd0126..cada97f8 100644
--- a/converter/other/jpeg2000/libjasper/jpc/jpc_dec.c
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_dec.c
@@ -458,6 +458,12 @@ static int jpc_dec_decode(jpc_dec_t *dec)
 
 static int jpc_dec_process_crg(jpc_dec_t *dec, jpc_ms_t *ms)
 {
+    /* 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 enabled.
+    */
+#ifdef USING_CRG
     uint_fast16_t cmptno;
     jpc_dec_cmpt_t *cmpt;
     jpc_crg_t *crg;
@@ -465,14 +471,10 @@ static int jpc_dec_process_crg(jpc_dec_t *dec, jpc_ms_t *ms)
     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;
-        */
     }
+#endif
     return 0;
 }
 
@@ -701,7 +703,6 @@ static int jpc_dec_tileinit(jpc_dec_t *dec, jpc_dec_tile_t *tile)
     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;
@@ -792,14 +793,12 @@ rlvl->bands = 0;
                 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;
             }
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_enc.c b/converter/other/jpeg2000/libjasper/jpc/jpc_enc.c
index d17e9aa3..9db41ca2 100644
--- a/converter/other/jpeg2000/libjasper/jpc/jpc_enc.c
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_enc.c
@@ -73,9 +73,9 @@ int ratestrtosize(const char *s, uint_fast32_t rawsize, uint_fast32_t *size);
 static void pass_destroy(jpc_enc_pass_t *pass);
 void jpc_enc_dump(jpc_enc_t *enc);
 
-/******************************************************************************\
+/*****************************************************************************\
 * Local prototypes.
-\******************************************************************************/
+\*****************************************************************************/
 
 void quantize(jas_matrix_t *data, jpc_fix_t stepsize);
 static int jpc_enc_encodemainhdr(jpc_enc_t *enc);
@@ -90,8 +90,11 @@ 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)
-{
+
+
+static uint_fast32_t
+jpc_abstorelstepsize(jpc_fix_t absdelta, int scaleexpn) {
+
     int p;
     uint_fast32_t mant;
     uint_fast32_t expn;
@@ -111,6 +114,8 @@ static uint_fast32_t jpc_abstorelstepsize(jpc_fix_t absdelta, int scaleexpn)
     return JPC_QCX_EXPN(expn) | JPC_QCX_MANT(mant);
 }
 
+
+
 typedef enum {
     OPT_DEBUG,
     OPT_IMGAREAOFFX,
@@ -199,6 +204,7 @@ jas_taginfo_t modetab[] = {
 };
 
 
+
 static void
 tracev(const char * const fmt,
        va_list            args) {
@@ -224,12 +230,9 @@ trace(const char * const fmt, ...) {
 
 
 
-/******************************************************************************\
-* The main encoder entry point.
-\******************************************************************************/
+int
+jpc_encode(jas_image_t *image, jas_stream_t *out, char *optstr) {
 
-int jpc_encode(jas_image_t *image, jas_stream_t *out, char *optstr)
-{
     jpc_enc_t *enc;
     jpc_enc_cp_t *cp;
 
@@ -287,12 +290,15 @@ error:
     return -1;
 }
 
-/******************************************************************************\
+
+
+/*****************************************************************************\
 * Option parsing code.
-\******************************************************************************/
+\*****************************************************************************/
+
+static jpc_enc_cp_t *
+cp_create(char *optstr, jas_image_t *image) {
 
-static jpc_enc_cp_t *cp_create(char *optstr, jas_image_t *image)
-{
     jpc_enc_cp_t *cp;
     jas_tvparser_t *tvp;
     int ret;
@@ -343,10 +349,12 @@ static jpc_enc_cp_t *cp_create(char *optstr, jas_image_t *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) <=
+        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");
+            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. */
@@ -495,7 +503,8 @@ static jpc_enc_cp_t *cp_create(char *optstr, jas_image_t *image)
             if (jpc_atoaf(jas_tvparser_getval(tvp), &numilyrrates,
               &ilyrrates)) {
                 fprintf(stderr,
-                  "warning: invalid intermediate layer rates specifier ignored (%s)\n",
+                        "warning: invalid intermediate layer rates specifier "
+                        "ignored (%s)\n",
                   jas_tvparser_getval(tvp));
             }
             break;
@@ -552,20 +561,26 @@ static jpc_enc_cp_t *cp_create(char *optstr, jas_image_t *image)
     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)) {
+            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) {
+    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) {
+    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;
@@ -682,16 +697,20 @@ static jpc_enc_cp_t *cp_create(char *optstr, jas_image_t *image)
         /* The intermediate layers rates must increase monotonically. */
         for (lyrno = 0; lyrno + 2 < tcp->numlyrs; ++lyrno) {
             if (tcp->ilyrrates[lyrno] >= tcp->ilyrrates[lyrno + 1]) {
-                fprintf(stderr, "intermediate layer rates must increase monotonically\n");
+                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");
+                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;
                 }
             }
@@ -718,8 +737,11 @@ error:
     return 0;
 }
 
-void jpc_enc_cp_destroy(jpc_enc_cp_t *cp)
-{
+
+
+void
+jpc_enc_cp_destroy(jpc_enc_cp_t *cp) {
+
     if (cp->ccps) {
         if (cp->tcp.ilyrrates) {
             jas_free(cp->tcp.ilyrrates);
@@ -729,8 +751,11 @@ void jpc_enc_cp_destroy(jpc_enc_cp_t *cp)
     jas_free(cp);
 }
 
-int ratestrtosize(const char *s, uint_fast32_t rawsize, uint_fast32_t *size)
-{
+
+
+int
+ratestrtosize(const char *s, uint_fast32_t rawsize, uint_fast32_t *size) {
+
     char *cp;
     jpc_flt_t f;
 
@@ -750,12 +775,13 @@ int ratestrtosize(const char *s, uint_fast32_t rawsize, uint_fast32_t *size)
     return 0;
 }
 
-/******************************************************************************\
+/*****************************************************************************\
 * Encoder constructor and destructor.
-\******************************************************************************/
+\*****************************************************************************/
+
+jpc_enc_t *
+jpc_enc_create(jpc_enc_cp_t *cp, jas_stream_t *out, jas_image_t *image) {
 
-jpc_enc_t *jpc_enc_create(jpc_enc_cp_t *cp, jas_stream_t *out, jas_image_t *image)
-{
     jpc_enc_t *enc;
 
     enc = 0;
@@ -788,11 +814,15 @@ error:
     return 0;
 }
 
-void jpc_enc_destroy(jpc_enc_t *enc)
-{
+
+
+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. */
+       (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);
@@ -810,24 +840,22 @@ void jpc_enc_destroy(jpc_enc_t *enc)
     jas_free(enc);
 }
 
-/******************************************************************************\
-* Code.
-\******************************************************************************/
 
-static int jpc_enc_encodemainhdr(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;
-long startoff;
-long mainhdrlen;
+    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;
@@ -841,7 +869,7 @@ long mainhdrlen;
 
     cp = enc->cp;
 
-startoff = jas_stream_getrwcount(enc->out);
+    startoff = jas_stream_getrwcount(enc->out);
 
     /* Write SOC marker segment. */
     if (!(enc->mrk = jpc_ms_create(JPC_MS_SOC))) {
@@ -922,7 +950,6 @@ startoff = jas_stream_getrwcount(enc->out);
         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) {
@@ -953,14 +980,17 @@ startoff = jas_stream_getrwcount(enc->out);
     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.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];
+            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)) {
@@ -1023,17 +1053,23 @@ startoff = jas_stream_getrwcount(enc->out);
     return 0;
 }
 
-int jpc_enc_encodetiledata(jpc_enc_t *enc)
-{
-assert(enc->tmpstream);
+
+
+int
+jpc_enc_encodetiledata(jpc_enc_t *enc) {
+
+    assert(enc->tmpstream);
     if (jpc_enc_encpkts(enc, enc->tmpstream)) {
         return -1;
     }
     return 0;
 }
 
-void quantize(jas_matrix_t *data, jpc_fix_t stepsize)
-{
+
+
+void
+quantize(jas_matrix_t *data, jpc_fix_t stepsize) {
+
     int i;
     int j;
     jpc_fix_t t;
@@ -1059,8 +1095,11 @@ void quantize(jas_matrix_t *data, jpc_fix_t stepsize)
     }
 }
 
-static void calcrdslopes(jpc_enc_cblk_t *cblk)
-{
+
+
+static void
+calcrdslopes(jpc_enc_cblk_t *cblk) {
+
     jpc_enc_pass_t *endpasses;
     jpc_enc_pass_t *pass0;
     jpc_enc_pass_t *pass1;
@@ -1113,10 +1152,12 @@ static void calcrdslopes(jpc_enc_cblk_t *cblk)
 
 #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);
-}
+        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
 }
@@ -1196,8 +1237,8 @@ computeLayerSizes(jpc_enc_t *      const encP,
 
 
 
-static void dump_layeringinfo(jpc_enc_t *enc)
-{
+static void
+dump_layeringinfo(jpc_enc_t *enc) {
 
     jpc_enc_tcmpt_t *tcmpt;
     uint_fast16_t tcmptno;
@@ -1626,12 +1667,15 @@ performTier2Coding(jpc_enc_t *     const encP,
     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 *
+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;
@@ -1733,8 +1777,11 @@ error:
     return 0;
 }
 
-void jpc_enc_tile_destroy(jpc_enc_tile_t *tile)
-{
+
+
+void
+jpc_enc_tile_destroy(jpc_enc_tile_t *tile) {
+
     jpc_enc_tcmpt_t *tcmpt;
     uint_fast16_t cmptno;
 
@@ -1754,9 +1801,14 @@ void jpc_enc_tile_destroy(jpc_enc_tile_t *tile)
     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)
-{
+
+
+static jpc_enc_tcmpt_t *
+tcmpt_create(jpc_enc_tcmpt_t * const tcmpt,
+             jpc_enc_cp_t *    const cp,
+             jas_image_t *     const image,
+             jpc_enc_tile_t *  const tile) {
+
     uint_fast16_t cmptno;
     uint_fast16_t rlvlno;
     jpc_enc_rlvl_t *rlvl;
@@ -1787,15 +1839,15 @@ static jpc_enc_tcmpt_t *tcmpt_create(jpc_enc_tcmpt_t *tcmpt, jpc_enc_cp_t *cp,
     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))) {
+    tcmpt->data = jas_seq2d_create(tlx, tly, brx, bry);
+    if (!tcmpt->data)
         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)) {
+                           brx - tlx, bry - tly, tcmpt->data)) {
         goto error;
     }
 
@@ -1803,9 +1855,11 @@ static jpc_enc_tcmpt_t *tcmpt_create(jpc_enc_tcmpt_t *tcmpt, jpc_enc_cp_t *cp,
     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))) {
+
+    tcmpt->tsfb = jpc_cod_gettsfb(tcmpt->qmfbid, tcmpt->numrlvls - 1);
+
+    if (!tcmpt->tsfb)
         goto error;
-    }
 
     for (rlvlno = 0; rlvlno < tcmpt->numrlvls; ++rlvlno) {
         tcmpt->prcwidthexpns[rlvlno] = cp->tccp.prcwidthexpns[rlvlno];
@@ -1818,17 +1872,19 @@ static jpc_enc_tcmpt_t *tcmpt_create(jpc_enc_tcmpt_t *tcmpt, jpc_enc_cp_t *cp,
 
     tcmpt->numstepsizes = tcmpt->numbands;
     assert(tcmpt->numstepsizes <= JPC_MAXBANDS);
-    memset(tcmpt->stepsizes, 0, sizeof(tcmpt->numstepsizes *
-      sizeof(uint_fast16_t)));
+    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);
+                      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)))) {
+    tcmpt->rlvls = jas_malloc(tcmpt->numrlvls * sizeof(jpc_enc_rlvl_t));
+    if (!tcmpt->rlvls)
         goto error;
-    }
+
     for (rlvlno = 0, rlvl = tcmpt->rlvls; rlvlno < tcmpt->numrlvls;
       ++rlvlno, ++rlvl) {
         rlvl->bands = 0;
@@ -1850,8 +1906,11 @@ error:
 
 }
 
-static void tcmpt_destroy(jpc_enc_tcmpt_t *tcmpt)
-{
+
+
+static void
+tcmpt_destroy(jpc_enc_tcmpt_t *tcmpt) {
+
     jpc_enc_rlvl_t *rlvl;
     uint_fast16_t rlvlno;
 
@@ -1871,9 +1930,12 @@ static void tcmpt_destroy(jpc_enc_tcmpt_t *tcmpt)
     }
 }
 
-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)
-{
+
+
+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;
@@ -1891,14 +1953,18 @@ static jpc_enc_rlvl_t *rlvl_create(jpc_enc_rlvl_t *rlvl, jpc_enc_cp_t *cp,
 
     /* 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);
+    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;
@@ -1917,8 +1983,10 @@ static jpc_enc_rlvl_t *rlvl_create(jpc_enc_rlvl_t *rlvl, jpc_enc_cp_t *cp,
         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);
+    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);
@@ -1926,20 +1994,22 @@ static jpc_enc_rlvl_t *rlvl_create(jpc_enc_rlvl_t *rlvl, jpc_enc_cp_t *cp,
     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->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)))) {
+    rlvl->bands = jas_malloc(rlvl->numbands * sizeof(jpc_enc_band_t));
+    if (!rlvl->bands)
         goto error;
-    }
+
     for (bandno = 0, band = rlvl->bands; bandno < rlvl->numbands;
-      ++bandno, ++band) {
+         ++bandno, ++band) {
         band->prcs = 0;
         band->data = 0;
         band->rlvl = rlvl;
     }
     for (bandno = 0, band = rlvl->bands; bandno < rlvl->numbands;
-      ++bandno, ++band) {
+         ++bandno, ++band) {
         if (!band_create(band, cp, rlvl, bandinfos)) {
             goto error;
         }
@@ -1952,8 +2022,11 @@ error:
     return 0;
 }
 
-static void rlvl_destroy(jpc_enc_rlvl_t *rlvl)
-{
+
+
+static void
+rlvl_destroy(jpc_enc_rlvl_t *rlvl) {
+
     jpc_enc_band_t *band;
     uint_fast16_t bandno;
 
@@ -1966,9 +2039,12 @@ static void rlvl_destroy(jpc_enc_rlvl_t *rlvl)
     }
 }
 
-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)
-{
+
+
+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;
@@ -1989,42 +2065,46 @@ static jpc_enc_band_t *band_create(jpc_enc_band_t *band, jpc_enc_cp_t *cp,
 
     bandinfo = &bandinfos[gblbandno];
 
-if (bandinfo->xstart != bandinfo->xend && bandinfo->ystart != bandinfo->yend) {
-    if (!(band->data = jas_seq2d_create(0, 0, 0, 0))) {
-        goto error;
+    if (bandinfo->xstart != bandinfo->xend &&
+        bandinfo->ystart != bandinfo->yend) {
+        band->data = jas_seq2d_create(0, 0, 0, 0);
+        if (!band->data)
+            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);
     }
-    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->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)) {
+    if (band->data) {
+        band->prcs = jas_malloc(rlvl->numprcs * sizeof(jpc_enc_prc_t));
+        if (!band->prcs)
             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;
 
@@ -2033,8 +2113,11 @@ error:
     return 0;
 }
 
-static void band_destroy(jpc_enc_band_t *band)
-{
+
+
+static void
+band_destroy(jpc_enc_band_t *band) {
+
     jpc_enc_prc_t *prc;
     jpc_enc_rlvl_t *rlvl;
     uint_fast32_t prcno;
@@ -2052,8 +2135,11 @@ static void band_destroy(jpc_enc_band_t *band)
     }
 }
 
-static jpc_enc_prc_t *prc_create(jpc_enc_prc_t *prc, jpc_enc_cp_t *cp, jpc_enc_band_t *band)
-{
+
+
+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;
@@ -2081,32 +2167,34 @@ static jpc_enc_prc_t *prc_create(jpc_enc_prc_t *prc, jpc_enc_cp_t *cp, jpc_enc_b
 
     rlvl = band->rlvl;
     tcmpt = rlvl->tcmpt;
-rlvlno = rlvl - tcmpt->rlvls;
+    rlvlno = rlvl - tcmpt->rlvls;
     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;
-} else {
-    tlcbgtlx = JPC_CEILDIVPOW2(tlprctlx, 1);
-    tlcbgtly = JPC_CEILDIVPOW2(tlprctly, 1);
-}
+    tlprctlx = JPC_FLOORTOMULTPOW2(rlvl->tlx, rlvl->prcwidthexpn);
+    tlprctly = JPC_FLOORTOMULTPOW2(rlvl->tly, rlvl->prcheightexpn);
 
-    /* Compute the coordinates of the top-left and bottom-right
-      corners of the precinct. */
+    if (!rlvlno) {
+        tlcbgtlx = tlprctlx;
+        tlcbgtly = tlprctly;
+    } else {
+        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));
+                       (1 << rlvl->cbgwidthexpn));
     prc->bry = JAS_MIN(jas_seq2d_yend(band->data), cbgtly +
-      (1 << rlvl->cbgheightexpn));
+                       (1 << rlvl->cbgheightexpn));
 
     if (prc->tlx < prc->brx && prc->tly < prc->bry) {
         /* The precinct contains at least one code block. */
@@ -2116,33 +2204,35 @@ if (!rlvlno) {
         brcblkbrx = JPC_CEILTOMULTPOW2(prc->brx, rlvl->cblkwidthexpn);
         brcblkbry = JPC_CEILTOMULTPOW2(prc->bry, rlvl->cblkheightexpn);
         prc->numhcblks = JPC_FLOORDIVPOW2(brcblkbrx - tlcblktlx,
-          rlvl->cblkwidthexpn);
+                                          rlvl->cblkwidthexpn);
         prc->numvcblks = JPC_FLOORDIVPOW2(brcblkbry - tlcblktly,
-          rlvl->cblkheightexpn);
+                                          rlvl->cblkheightexpn);
         prc->numcblks = prc->numhcblks * prc->numvcblks;
 
         if (!(prc->incltree = jpc_tagtree_create(prc->numhcblks,
-          prc->numvcblks))) {
+                                                 prc->numvcblks))) {
             goto error;
         }
         if (!(prc->nlibtree = jpc_tagtree_create(prc->numhcblks,
-          prc->numvcblks))) {
+                                                 prc->numvcblks))) {
             goto error;
         }
         if (!(prc->savincltree = jpc_tagtree_create(prc->numhcblks,
-          prc->numvcblks))) {
+                                                    prc->numvcblks))) {
             goto error;
         }
         if (!(prc->savnlibtree = jpc_tagtree_create(prc->numhcblks,
-          prc->numvcblks))) {
+                                                    prc->numvcblks))) {
             goto error;
         }
 
-        if (!(prc->cblks = jas_malloc(prc->numcblks * sizeof(jpc_enc_cblk_t)))) {
+        prc->cblks = jas_malloc(prc->numcblks * sizeof(jpc_enc_cblk_t));
+        
+        if (!prc->cblks)
             goto error;
-        }
-        for (cblkno = 0, cblk = prc->cblks; cblkno < prc->numcblks;
-          ++cblkno, ++cblk) {
+        for (cblkno = 0, cblk = prc->cblks;
+             cblkno < prc->numcblks;
+             ++cblkno, ++cblk) {
             cblk->passes = 0;
             cblk->stream = 0;
             cblk->mqenc = 0;
@@ -2150,8 +2240,9 @@ if (!rlvlno) {
             cblk->flags = 0;
             cblk->prc = prc;
         }
-        for (cblkno = 0, cblk = prc->cblks; cblkno < prc->numcblks;
-          ++cblkno, ++cblk) {
+        for (cblkno = 0, cblk = prc->cblks;
+             cblkno < prc->numcblks;
+             ++cblkno, ++cblk) {
             if (!cblk_create(cblk, cp, prc)) {
                 goto error;
             }
@@ -2177,8 +2268,11 @@ error:
     return 0;
 }
 
-static void prc_destroy(jpc_enc_prc_t *prc)
-{
+
+
+static void
+prc_destroy(jpc_enc_prc_t *prc) {
+
     jpc_enc_cblk_t *cblk;
     uint_fast32_t cblkno;
 
@@ -2203,8 +2297,11 @@ static void prc_destroy(jpc_enc_prc_t *prc)
     }
 }
 
-static jpc_enc_cblk_t *cblk_create(jpc_enc_cblk_t *cblk, jpc_enc_cp_t *cp, jpc_enc_prc_t *prc)
-{
+
+
+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;
@@ -2241,18 +2338,22 @@ static jpc_enc_cblk_t *cblk_create(jpc_enc_cblk_t *cblk, jpc_enc_cp_t *cp, jpc_e
     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);
+    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))) {
+    cblk->data = jas_seq2d_create(0, 0, 0, 0);
+    if (!cblk->data)
         goto error;
-    }
-    jas_seq2d_bindsub(cblk->data, band->data, cblktlx, cblktly, cblkbrx, cblkbry);
+    jas_seq2d_bindsub(cblk->data, band->data,
+                      cblktlx, cblktly, cblkbrx, cblkbry);
 
     return cblk;
 
@@ -2261,8 +2362,11 @@ error:
     return 0;
 }
 
-static void cblk_destroy(jpc_enc_cblk_t *cblk)
-{
+
+
+static void
+cblk_destroy(jpc_enc_cblk_t *cblk) {
+
     uint_fast16_t passno;
     jpc_enc_pass_t *pass;
     if (cblk->passes) {
@@ -2286,13 +2390,19 @@ static void cblk_destroy(jpc_enc_cblk_t *cblk)
     }
 }
 
-static void pass_destroy(jpc_enc_pass_t *pass)
-{
+
+
+static void
+pass_destroy(jpc_enc_pass_t *pass) {
+
     /* XXX - need to free resources here */
 }
 
-void jpc_enc_dump(jpc_enc_t *enc)
-{
+
+
+void
+jpc_enc_dump(jpc_enc_t *enc) {
+
     jpc_enc_tile_t *tile;
     jpc_enc_tcmpt_t *tcmpt;
     jpc_enc_rlvl_t *rlvl;
@@ -2358,11 +2468,10 @@ void jpc_enc_dump(jpc_enc_t *enc)
 
 
 
-static int jpc_enc_encodemainbody(jpc_enc_t *enc)
-{
+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;
@@ -2376,7 +2485,6 @@ static int jpc_enc_encodemainbody(jpc_enc_t *enc)
     int adjust;
     int j;
     int absbandno;
-    long numbytes;
     long tilehdrlen;
     long tilelen;
     jpc_enc_tile_t *tile;
@@ -2398,13 +2506,7 @@ static int jpc_enc_encodemainbody(jpc_enc_t *enc)
 
     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();
@@ -2459,7 +2561,6 @@ static int jpc_enc_encodemainbody(jpc_enc_t *enc)
 
         }
 
-
         endcomps = &tile->tcmpts[tile->numtcmpts];
         for (cmptno = 0, comp = tile->tcmpts;
              comp != endcomps;
@@ -2544,15 +2645,16 @@ static int jpc_enc_encodemainbody(jpc_enc_t *enc)
             return -1;
         }
 
-        if (!(enc->tmpstream = jas_stream_memopen(0, 0))) {
+        enc->tmpstream = jas_stream_memopen(0, 0);
+        if (!enc->tmpstream) {
             fprintf(stderr, "cannot open tmp file\n");
             return -1;
         }
 
         /* Write the tile header. */
-        if (!(enc->mrk = jpc_ms_create(JPC_MS_SOT))) {
+        enc->mrk = jpc_ms_create(JPC_MS_SOT);
+        if (!enc->mrk)
             return -1;
-        }
         sot = &enc->mrk->parms.sot;
         sot->len = 0;
         sot->tileno = tileno;
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_t1enc.c b/converter/other/jpeg2000/libjasper/jpc/jpc_t1enc.c
index 8aa024a9..e1af0f61 100644
--- a/converter/other/jpeg2000/libjasper/jpc/jpc_t1enc.c
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_t1enc.c
@@ -1,4 +1,4 @@
-/*
+ /*
  * Copyright (c) 1999-2000 Image Power, Inc. and the University of
  *   British Columbia.
  * Copyright (c) 2001-2002 Michael David Adams.
@@ -156,853 +156,872 @@ static int jpc_encrawrefpass(jpc_bitstream_t *out, int bitpos, int,
 /* Encode all of the code blocks associated with the current tile. */
 int jpc_enc_enccblks(jpc_enc_t *enc)
 {
-	jpc_enc_tcmpt_t *tcmpt;
-	jpc_enc_tcmpt_t *endcomps;
-	jpc_enc_rlvl_t *lvl;
-	jpc_enc_rlvl_t *endlvls;
-	jpc_enc_band_t *band;
-	jpc_enc_band_t *endbands;
-	jpc_enc_cblk_t *cblk;
-	jpc_enc_cblk_t *endcblks;
-	int i;
-	int j;
-	int mx;
-	int bmx;
-	int v;
-	jpc_enc_tile_t *tile;
-	uint_fast32_t prcno;
-	jpc_enc_prc_t *prc;
-
-	tile = enc->curtile;
-
-	endcomps = &tile->tcmpts[tile->numtcmpts];
-	for (tcmpt = tile->tcmpts; tcmpt != endcomps; ++tcmpt) {
-		endlvls = &tcmpt->rlvls[tcmpt->numrlvls];
-		for (lvl = tcmpt->rlvls; lvl != endlvls; ++lvl) {
-			if (!lvl->bands) {
-				continue;
-			}
-			endbands = &lvl->bands[lvl->numbands];
-			for (band = lvl->bands; band != endbands; ++band) {
-				if (!band->data) {
-					continue;
-				}
-				for (prcno = 0, prc = band->prcs; prcno < lvl->numprcs; ++prcno, ++prc) {
-					if (!prc->cblks) {
-						continue;
-					}
-					bmx = 0;
-					endcblks = &prc->cblks[prc->numcblks];
-					for (cblk = prc->cblks; cblk != endcblks; ++cblk) {
-						mx = 0;
-						for (i = 0; i < jas_matrix_numrows(cblk->data); ++i) {
-							for (j = 0; j < jas_matrix_numcols(cblk->data); ++j) {
-								v = abs(jas_matrix_get(cblk->data, i, j));
-								if (v > mx) {
-									mx = v;
-								}
-							}
-						}
-						if (mx > bmx) {
-							bmx = mx;
-						}
-						cblk->numbps = JAS_MAX(jpc_firstone(mx) + 1 - JPC_NUMEXTRABITS, 0);
-					}
-
-					for (cblk = prc->cblks; cblk != endcblks; ++cblk) {
-						cblk->numimsbs = band->numbps - cblk->numbps;
-						assert(cblk->numimsbs >= 0);
-					}
-
-					for (cblk = prc->cblks; cblk != endcblks; ++cblk) {
-						if (jpc_enc_enccblk(enc, cblk->stream, tcmpt, band, cblk)) {
-							return -1;
-						}
-					}
-				}
-			}
-		}
-	}
-	return 0;
+    jpc_enc_tcmpt_t *tcmpt;
+    jpc_enc_tcmpt_t *endcomps;
+    jpc_enc_rlvl_t *lvl;
+    jpc_enc_rlvl_t *endlvls;
+    jpc_enc_band_t *band;
+    jpc_enc_band_t *endbands;
+    jpc_enc_cblk_t *cblk;
+    jpc_enc_cblk_t *endcblks;
+    int i;
+    int j;
+    int mx;
+    int bmx;
+    int v;
+    jpc_enc_tile_t *tile;
+    uint_fast32_t prcno;
+    jpc_enc_prc_t *prc;
+
+    tile = enc->curtile;
+
+    endcomps = &tile->tcmpts[tile->numtcmpts];
+    for (tcmpt = tile->tcmpts; tcmpt != endcomps; ++tcmpt) {
+        endlvls = &tcmpt->rlvls[tcmpt->numrlvls];
+        for (lvl = tcmpt->rlvls; lvl != endlvls; ++lvl) {
+            if (!lvl->bands) {
+                continue;
+            }
+            endbands = &lvl->bands[lvl->numbands];
+            for (band = lvl->bands; band != endbands; ++band) {
+                if (!band->data) {
+                    continue;
+                }
+                for (prcno = 0, prc = band->prcs; prcno < lvl->numprcs; ++prcno, ++prc) {
+                    if (!prc->cblks) {
+                        continue;
+                    }
+                    bmx = 0;
+                    endcblks = &prc->cblks[prc->numcblks];
+                    for (cblk = prc->cblks; cblk != endcblks; ++cblk) {
+                        mx = 0;
+                        for (i = 0; i < jas_matrix_numrows(cblk->data); ++i) {
+                            for (j = 0; j < jas_matrix_numcols(cblk->data); ++j) {
+                                v = abs(jas_matrix_get(cblk->data, i, j));
+                                if (v > mx) {
+                                    mx = v;
+                                }
+                            }
+                        }
+                        if (mx > bmx) {
+                            bmx = mx;
+                        }
+                        cblk->numbps = JAS_MAX(jpc_firstone(mx) + 1 - JPC_NUMEXTRABITS, 0);
+                    }
+
+                    for (cblk = prc->cblks; cblk != endcblks; ++cblk) {
+                        cblk->numimsbs = band->numbps - cblk->numbps;
+                        assert(cblk->numimsbs >= 0);
+                    }
+
+                    for (cblk = prc->cblks; cblk != endcblks; ++cblk) {
+                        if (jpc_enc_enccblk(enc, cblk->stream, tcmpt, band, cblk)) {
+                            return -1;
+                        }
+                    }
+                }
+            }
+        }
+    }
+    return 0;
 }
 
 static int getthebyte(jas_stream_t *in, long off)
 {
-	int c;
-	long oldpos;
-	oldpos = jas_stream_tell(in);
-	assert(oldpos >= 0);
-	jas_stream_seek(in, off, SEEK_SET);
-	c = jas_stream_peekc(in);
-	jas_stream_seek(in, oldpos, SEEK_SET);
-	return c;
+    int c;
+    long oldpos;
+    oldpos = jas_stream_tell(in);
+    assert(oldpos >= 0);
+    jas_stream_seek(in, off, SEEK_SET);
+    c = jas_stream_peekc(in);
+    jas_stream_seek(in, oldpos, SEEK_SET);
+    return c;
 }
 
-/* Encode a single code block. */
-int jpc_enc_enccblk(jpc_enc_t *enc, jas_stream_t *out, jpc_enc_tcmpt_t *tcmpt, jpc_enc_band_t *band, jpc_enc_cblk_t *cblk)
-{
-	jpc_enc_pass_t *pass;
-	jpc_enc_pass_t *endpasses;
-	int bitpos;
-	int n;
-	int adjust;
-	int ret;
-	int passtype;
-	int t;
-	jpc_bitstream_t *bout;
-	jpc_enc_pass_t *termpass;
-	jpc_enc_rlvl_t *rlvl;
-	int vcausal;
-	int segsym;
-	int termmode;
-	int c;
-
-	bout = 0;
-	rlvl = band->rlvl;
-
-	cblk->stream = jas_stream_memopen(0, 0);
-	assert(cblk->stream);
-	cblk->mqenc = jpc_mqenc_create(JPC_NUMCTXS, cblk->stream);
-	assert(cblk->mqenc);
-	jpc_mqenc_setctxs(cblk->mqenc, JPC_NUMCTXS, jpc_mqctxs);
-
-	cblk->numpasses = (cblk->numbps > 0) ? (3 * cblk->numbps - 2) : 0;
-	if (cblk->numpasses > 0) {
-		cblk->passes = jas_malloc(cblk->numpasses * sizeof(jpc_enc_pass_t));
-		assert(cblk->passes);
-	} else {
-		cblk->passes = 0;
-	}
-	endpasses = &cblk->passes[cblk->numpasses];
-	for (pass = cblk->passes; pass != endpasses; ++pass) {
-		pass->start = 0;
-		pass->end = 0;
-		pass->term = JPC_ISTERMINATED(pass - cblk->passes, 0, cblk->numpasses, (tcmpt->cblksty & JPC_COX_TERMALL) != 0, (tcmpt->cblksty & JPC_COX_LAZY) != 0);
-		pass->type = JPC_SEGTYPE(pass - cblk->passes, 0, (tcmpt->cblksty & JPC_COX_LAZY) != 0);
-		pass->lyrno = -1;
-if (pass == endpasses - 1) {
-assert(pass->term == 1);
-	pass->term = 1;
-}
-	}
-
-	cblk->flags = jas_matrix_create(jas_matrix_numrows(cblk->data) + 2,
-	  jas_matrix_numcols(cblk->data) + 2);
-	assert(cblk->flags);
-
 
-	bitpos = cblk->numbps - 1;
-	pass = cblk->passes;
-	n = cblk->numpasses;
-	while (--n >= 0) {
 
-		if (pass->type == JPC_SEG_MQ) {
-			/* NOP */
-		} else {
-			assert(pass->type == JPC_SEG_RAW);
-			if (!bout) {
-				bout = jpc_bitstream_sopen(cblk->stream, "w");
-				assert(bout);
-			}
-		}
+int
+jpc_enc_enccblk(jpc_enc_t *enc, jas_stream_t *out, jpc_enc_tcmpt_t *tcmpt,
+                jpc_enc_band_t *band, jpc_enc_cblk_t *cblk) {
+/*----------------------------------------------------------------------------
+  Encode a single code block.
+-----------------------------------------------------------------------------*/
+    jpc_enc_pass_t *pass;
+    jpc_enc_pass_t *endpasses;
+    int bitpos;
+    int n;
+    int adjust;
+    int passtype;
+    int t;
+    jpc_bitstream_t *bout;
+    jpc_enc_pass_t *termpass;
+    int vcausal;
+    int segsym;
+    int termmode;
+    int c;
+
+    bout = 0;
+
+    cblk->stream = jas_stream_memopen(0, 0);
+    assert(cblk->stream);
+    cblk->mqenc = jpc_mqenc_create(JPC_NUMCTXS, cblk->stream);
+    assert(cblk->mqenc);
+    jpc_mqenc_setctxs(cblk->mqenc, JPC_NUMCTXS, jpc_mqctxs);
+
+    cblk->numpasses = (cblk->numbps > 0) ? (3 * cblk->numbps - 2) : 0;
+    if (cblk->numpasses > 0) {
+        cblk->passes = jas_malloc(cblk->numpasses * sizeof(jpc_enc_pass_t));
+        assert(cblk->passes);
+    } else {
+        cblk->passes = 0;
+    }
+    endpasses = &cblk->passes[cblk->numpasses];
+    for (pass = cblk->passes; pass != endpasses; ++pass) {
+        pass->start = 0;
+        pass->end = 0;
+        pass->term =
+            JPC_ISTERMINATED(pass - cblk->passes, 0,
+                             cblk->numpasses,
+                             (tcmpt->cblksty & JPC_COX_TERMALL) != 0,
+                             (tcmpt->cblksty & JPC_COX_LAZY) != 0);
+        pass->type = JPC_SEGTYPE(pass - cblk->passes, 0,
+                                 (tcmpt->cblksty & JPC_COX_LAZY) != 0);
+        pass->lyrno = -1;
+        if (pass == endpasses - 1) {
+            assert(pass->term == 1);
+            pass->term = 1;
+        }
+    }
+
+    cblk->flags = jas_matrix_create(jas_matrix_numrows(cblk->data) + 2,
+      jas_matrix_numcols(cblk->data) + 2);
+    assert(cblk->flags);
+
+
+    bitpos = cblk->numbps - 1;
+    pass = cblk->passes;
+    n = cblk->numpasses;
+    while (--n >= 0) {
+
+        if (pass->type == JPC_SEG_MQ) {
+            /* NOP */
+        } else {
+            assert(pass->type == JPC_SEG_RAW);
+            if (!bout) {
+                bout = jpc_bitstream_sopen(cblk->stream, "w");
+                assert(bout);
+            }
+        }
 
 #if 1
-		passtype = (pass - cblk->passes + 2) % 3;
+        passtype = (pass - cblk->passes + 2) % 3;
 #else
-		passtype = JPC_PASSTYPE(pass - cblk->passes + 2);
+        passtype = JPC_PASSTYPE(pass - cblk->passes + 2);
 #endif
-		pass->start = jas_stream_tell(cblk->stream);
+        pass->start = jas_stream_tell(cblk->stream);
 #if 0
 assert(jas_stream_tell(cblk->stream) == jas_stream_getrwcount(cblk->stream));
 #endif
-		assert(bitpos >= 0);
-		vcausal = (tcmpt->cblksty & JPC_COX_VSC) != 0;
-		segsym = (tcmpt->cblksty & JPC_COX_SEGSYM) != 0;
-		if (pass->term) {
-			termmode = ((tcmpt->cblksty & JPC_COX_PTERM) ?
-			  JPC_MQENC_PTERM : JPC_MQENC_DEFTERM) + 1;
-		} else {
-			termmode = 0;
-		}
-		switch (passtype) {
-		case JPC_SIGPASS:
-			ret = (pass->type == JPC_SEG_MQ) ? jpc_encsigpass(cblk->mqenc,
-			  bitpos, band->orient, vcausal, cblk->flags,
-			  cblk->data, termmode, &pass->nmsedec) :
-			  jpc_encrawsigpass(bout, bitpos, vcausal, cblk->flags,
-			  cblk->data, termmode, &pass->nmsedec);
-			break;
-		case JPC_REFPASS:
-			ret = (pass->type == JPC_SEG_MQ) ? jpc_encrefpass(cblk->mqenc,
-			  bitpos, vcausal, cblk->flags, cblk->data, termmode,
-			  &pass->nmsedec) : jpc_encrawrefpass(bout, bitpos,
-			  vcausal, cblk->flags, cblk->data, termmode,
-			  &pass->nmsedec);
-			break;
-		case JPC_CLNPASS:
-			assert(pass->type == JPC_SEG_MQ);
-			ret = jpc_encclnpass(cblk->mqenc, bitpos, band->orient,
-			  vcausal, segsym, cblk->flags, cblk->data, termmode,
-			  &pass->nmsedec);
-			break;
-		default:
-			assert(0);
-			break;
-		}
-
-		if (pass->type == JPC_SEG_MQ) {
-			if (pass->term) {
-				jpc_mqenc_init(cblk->mqenc);
-			}
-			jpc_mqenc_getstate(cblk->mqenc, &pass->mqencstate);
-			pass->end = jas_stream_tell(cblk->stream);
-			if (tcmpt->cblksty & JPC_COX_RESET) {
-				jpc_mqenc_setctxs(cblk->mqenc, JPC_NUMCTXS, jpc_mqctxs);
-			}
-		} else {
-			if (pass->term) {
-				if (jpc_bitstream_pending(bout)) {
-					jpc_bitstream_outalign(bout, 0x2a);
-				}
-				jpc_bitstream_close(bout);
-				bout = 0;
-				pass->end = jas_stream_tell(cblk->stream);
-			} else {
-				pass->end = jas_stream_tell(cblk->stream) +
-				  jpc_bitstream_pending(bout);
-/* NOTE - This will not work.  need to adjust by # of pending output bytes */
-			}
-		}
+        assert(bitpos >= 0);
+        vcausal = (tcmpt->cblksty & JPC_COX_VSC) != 0;
+        segsym = (tcmpt->cblksty & JPC_COX_SEGSYM) != 0;
+        if (pass->term) {
+            termmode = ((tcmpt->cblksty & JPC_COX_PTERM) ?
+              JPC_MQENC_PTERM : JPC_MQENC_DEFTERM) + 1;
+        } else {
+            termmode = 0;
+        }
+        switch (passtype) {
+        case JPC_SIGPASS:
+            if (pass->type == JPC_SEG_MQ)
+            jpc_encsigpass(cblk->mqenc,
+                           bitpos, band->orient, vcausal, cblk->flags,
+                           cblk->data, termmode, &pass->nmsedec);
+            else
+                jpc_encrawsigpass(bout, bitpos, vcausal, cblk->flags,
+                                  cblk->data, termmode, &pass->nmsedec);
+            break;
+        case JPC_REFPASS:
+            if (pass->type == JPC_SEG_MQ)
+            jpc_encrefpass(
+                cblk->mqenc,
+                bitpos, vcausal, cblk->flags, cblk->data, termmode,
+                &pass->nmsedec);
+            else
+                jpc_encrawrefpass(bout, bitpos,
+                                  vcausal, cblk->flags, cblk->data, termmode,
+                                  &pass->nmsedec);
+            break;
+        case JPC_CLNPASS:
+            assert(pass->type == JPC_SEG_MQ);
+            jpc_encclnpass(
+                cblk->mqenc, bitpos, band->orient,
+                vcausal, segsym, cblk->flags, cblk->data, termmode,
+                &pass->nmsedec);
+            break;
+        default:
+            assert(false);
+            break;
+        }
+
+        if (pass->type == JPC_SEG_MQ) {
+            if (pass->term) {
+                jpc_mqenc_init(cblk->mqenc);
+            }
+            jpc_mqenc_getstate(cblk->mqenc, &pass->mqencstate);
+            pass->end = jas_stream_tell(cblk->stream);
+            if (tcmpt->cblksty & JPC_COX_RESET) {
+                jpc_mqenc_setctxs(cblk->mqenc, JPC_NUMCTXS, jpc_mqctxs);
+            }
+        } else {
+            if (pass->term) {
+                if (jpc_bitstream_pending(bout)) {
+                    jpc_bitstream_outalign(bout, 0x2a);
+                }
+                jpc_bitstream_close(bout);
+                bout = 0;
+                pass->end = jas_stream_tell(cblk->stream);
+            } else {
+                pass->end = jas_stream_tell(cblk->stream) +
+                  jpc_bitstream_pending(bout);
+                /* NOTE - This will not work.  need to adjust by # of pending
+                   output bytes
+                */
+            }
+        }
 #if 0
-/* XXX - This assertion fails sometimes when various coding modes are used.
-This seems to be harmless, but why does it happen at all? */
-assert(jas_stream_tell(cblk->stream) == jas_stream_getrwcount(cblk->stream));
+    /* XXX - This assertion fails sometimes when various coding modes are used.
+       This seems to be harmless, but why does it happen at all? */
+        assert(jas_stream_tell(cblk->stream) == 
+               jas_stream_getrwcount(cblk->stream));
 #endif
 
-		pass->wmsedec = jpc_fixtodbl(band->rlvl->tcmpt->synweight) *
-		  jpc_fixtodbl(band->rlvl->tcmpt->synweight) *
-		  jpc_fixtodbl(band->synweight) *
-		  jpc_fixtodbl(band->synweight) *
-		  jpc_fixtodbl(band->absstepsize) * jpc_fixtodbl(band->absstepsize) *
-		  ((double) (1 << bitpos)) * ((double)(1 << bitpos)) *
-		  jpc_fixtodbl(pass->nmsedec);
-		pass->cumwmsedec = pass->wmsedec;
-		if (pass != cblk->passes) {
-			pass->cumwmsedec += pass[-1].cumwmsedec;
-		}
-		if (passtype == JPC_CLNPASS) {
-			--bitpos;
-		}
-		++pass;
-	}
+        pass->wmsedec = jpc_fixtodbl(band->rlvl->tcmpt->synweight) *
+          jpc_fixtodbl(band->rlvl->tcmpt->synweight) *
+          jpc_fixtodbl(band->synweight) *
+          jpc_fixtodbl(band->synweight) *
+          jpc_fixtodbl(band->absstepsize) * jpc_fixtodbl(band->absstepsize) *
+          ((double) (1 << bitpos)) * ((double)(1 << bitpos)) *
+          jpc_fixtodbl(pass->nmsedec);
+        pass->cumwmsedec = pass->wmsedec;
+        if (pass != cblk->passes) {
+            pass->cumwmsedec += pass[-1].cumwmsedec;
+        }
+        if (passtype == JPC_CLNPASS) {
+            --bitpos;
+        }
+        ++pass;
+    }
 
 #if 0
-dump_passes(cblk->passes, cblk->numpasses, cblk);
+    dump_passes(cblk->passes, cblk->numpasses, cblk);
 #endif
 
-	n = 0;
-	endpasses = &cblk->passes[cblk->numpasses];
-	for (pass = cblk->passes; pass != endpasses; ++pass) {
-		if (pass->start < n) {
-			pass->start = n;
-		}
-		if (pass->end < n) {
-			pass->end = n;
-		}
-		if (!pass->term) {
-			termpass = pass;
-			while (termpass - pass < cblk->numpasses &&
-			  !termpass->term) {
-				++termpass;
-			}
-			if (pass->type == JPC_SEG_MQ) {
-				t = (pass->mqencstate.lastbyte == 0xff) ? 1 : 0;
-				if (pass->mqencstate.ctreg >= 5) {
-					adjust = 4 + t;
-				} else {
-					adjust = 5 + t;
-				}
-				pass->end += adjust;
-			}
-			if (pass->end > termpass->end) {
-				pass->end = termpass->end;
-			}
-			if ((c = getthebyte(cblk->stream, pass->end - 1)) == EOF) {
-				abort();
-			}
-			if (c == 0xff) {
-				++pass->end;
-			}
-			n = JAS_MAX(n, pass->end);
-		} else {
-			n = JAS_MAX(n, pass->end);
-		}
-	}
+    n = 0;
+    endpasses = &cblk->passes[cblk->numpasses];
+    for (pass = cblk->passes; pass != endpasses; ++pass) {
+        if (pass->start < n) {
+            pass->start = n;
+        }
+        if (pass->end < n) {
+            pass->end = n;
+        }
+        if (!pass->term) {
+            termpass = pass;
+            while (termpass - pass < cblk->numpasses &&
+              !termpass->term) {
+                ++termpass;
+            }
+            if (pass->type == JPC_SEG_MQ) {
+                t = (pass->mqencstate.lastbyte == 0xff) ? 1 : 0;
+                if (pass->mqencstate.ctreg >= 5) {
+                    adjust = 4 + t;
+                } else {
+                    adjust = 5 + t;
+                }
+                pass->end += adjust;
+            }
+            if (pass->end > termpass->end) {
+                pass->end = termpass->end;
+            }
+            if ((c = getthebyte(cblk->stream, pass->end - 1)) == EOF) {
+                abort();
+            }
+            if (c == 0xff) {
+                ++pass->end;
+            }
+            n = JAS_MAX(n, pass->end);
+        } else {
+            n = JAS_MAX(n, pass->end);
+        }
+    }
 
 #if 0
-dump_passes(cblk->passes, cblk->numpasses, cblk);
+    dump_passes(cblk->passes, cblk->numpasses, cblk);
 #endif
 
-	if (bout) {
-		jpc_bitstream_close(bout);
-	}
+    if (bout) {
+        jpc_bitstream_close(bout);
+    }
 
-	return 0;
+    return 0;
 }
 
-/******************************************************************************\
+
+
+/*****************************************************************************\
 * Code for significance pass.
-\******************************************************************************/
+\*****************************************************************************/
 
-#define	sigpass_step(fp, frowstep, dp, bitpos, one, nmsedec, orient, mqenc, vcausalflag) \
+#define sigpass_step(fp, frowstep, dp, bitpos, one, nmsedec, orient, mqenc, vcausalflag) \
 { \
-	int f; \
-	int v; \
-	f = *(fp); \
-	if ((f & JPC_OTHSIGMSK) && !(f & (JPC_SIG | JPC_VISIT))) { \
-		v = (abs(*(dp)) & (one)) ? 1 : 0; \
-		jpc_mqenc_setcurctx(mqenc, JPC_GETZCCTXNO(f, (orient))); \
-		jpc_mqenc_putbit(mqenc, v); \
-		if (v) { \
-			*(nmsedec) += JPC_GETSIGNMSEDEC(abs(*(dp)), (bitpos) + JPC_NUMEXTRABITS); \
-			v = ((*(dp) < 0) ? 1 : 0); \
-			jpc_mqenc_setcurctx(mqenc, JPC_GETSCCTXNO(f)); \
-			jpc_mqenc_putbit(mqenc, v ^ JPC_GETSPB(f)); \
-			JPC_UPDATEFLAGS4(fp, frowstep, v, vcausalflag); \
-			*(fp) |= JPC_SIG; \
-		} \
-		*(fp) |= JPC_VISIT; \
-	} \
+    int f; \
+    int v; \
+    f = *(fp); \
+    if ((f & JPC_OTHSIGMSK) && !(f & (JPC_SIG | JPC_VISIT))) { \
+        v = (abs(*(dp)) & (one)) ? 1 : 0; \
+        jpc_mqenc_setcurctx(mqenc, JPC_GETZCCTXNO(f, (orient))); \
+        jpc_mqenc_putbit(mqenc, v); \
+        if (v) { \
+            *(nmsedec) += JPC_GETSIGNMSEDEC(abs(*(dp)), (bitpos) + JPC_NUMEXTRABITS); \
+            v = ((*(dp) < 0) ? 1 : 0); \
+            jpc_mqenc_setcurctx(mqenc, JPC_GETSCCTXNO(f)); \
+            jpc_mqenc_putbit(mqenc, v ^ JPC_GETSPB(f)); \
+            JPC_UPDATEFLAGS4(fp, frowstep, v, vcausalflag); \
+            *(fp) |= JPC_SIG; \
+        } \
+        *(fp) |= JPC_VISIT; \
+    } \
 }
 
 static int jpc_encsigpass(jpc_mqenc_t *mqenc, int bitpos, int orient, int vcausalflag,
   jas_matrix_t *flags, jas_matrix_t *data, int term, long *nmsedec)
 {
-	int i;
-	int j;
-	int one;
-	int vscanlen;
-	int width;
-	int height;
-	int frowstep;
-	int drowstep;
-	int fstripestep;
-	int dstripestep;
-	jpc_fix_t *fstripestart;
-	jpc_fix_t *dstripestart;
-	jpc_fix_t *fp;
-	jpc_fix_t *dp;
-	jpc_fix_t *fvscanstart;
-	jpc_fix_t *dvscanstart;
-	int k;
-
-	*nmsedec = 0;
-	width = jas_matrix_numcols(data);
-	height = jas_matrix_numrows(data);
-	frowstep = jas_matrix_rowstep(flags);
-	drowstep = jas_matrix_rowstep(data);
-	fstripestep = frowstep << 2;
-	dstripestep = drowstep << 2;
-
-	one = 1 << (bitpos + JPC_NUMEXTRABITS);
-
-	fstripestart = jas_matrix_getref(flags, 1, 1);
-	dstripestart = jas_matrix_getref(data, 0, 0);
-	for (i = height; i > 0; i -= 4, fstripestart += fstripestep,
-	  dstripestart += dstripestep) {
-		fvscanstart = fstripestart;
-		dvscanstart = dstripestart;
-		vscanlen = JAS_MIN(i, 4);
-		for (j = width; j > 0; --j, ++fvscanstart, ++dvscanstart) {
-			fp = fvscanstart;
-			dp = dvscanstart;
-			k = vscanlen;
-
-			sigpass_step(fp, frowstep, dp, bitpos, one,
-			  nmsedec, orient, mqenc, vcausalflag);
-			if (--k <= 0) {
-				continue;
-			}
-			fp += frowstep;
-			dp += drowstep;
-			sigpass_step(fp, frowstep, dp, bitpos, one,
-			  nmsedec, orient, mqenc, 0);
-			if (--k <= 0) {
-				continue;
-			}
-			fp += frowstep;
-			dp += drowstep;
-			sigpass_step(fp, frowstep, dp, bitpos, one,
-			  nmsedec, orient, mqenc, 0);
-			if (--k <= 0) {
-				continue;
-			}
-			fp += frowstep;
-			dp += drowstep;
-			sigpass_step(fp, frowstep, dp, bitpos, one,
-			  nmsedec, orient, mqenc, 0);
-
-		}
-	}
-
-	if (term) {
-		jpc_mqenc_flush(mqenc, term - 1);
-	}
-
-	return jpc_mqenc_error(mqenc) ? (-1) : 0;
+    int i;
+    int j;
+    int one;
+    int vscanlen;
+    int width;
+    int height;
+    int frowstep;
+    int drowstep;
+    int fstripestep;
+    int dstripestep;
+    jpc_fix_t *fstripestart;
+    jpc_fix_t *dstripestart;
+    jpc_fix_t *fp;
+    jpc_fix_t *dp;
+    jpc_fix_t *fvscanstart;
+    jpc_fix_t *dvscanstart;
+    int k;
+
+    *nmsedec = 0;
+    width = jas_matrix_numcols(data);
+    height = jas_matrix_numrows(data);
+    frowstep = jas_matrix_rowstep(flags);
+    drowstep = jas_matrix_rowstep(data);
+    fstripestep = frowstep << 2;
+    dstripestep = drowstep << 2;
+
+    one = 1 << (bitpos + JPC_NUMEXTRABITS);
+
+    fstripestart = jas_matrix_getref(flags, 1, 1);
+    dstripestart = jas_matrix_getref(data, 0, 0);
+    for (i = height; i > 0; i -= 4, fstripestart += fstripestep,
+      dstripestart += dstripestep) {
+        fvscanstart = fstripestart;
+        dvscanstart = dstripestart;
+        vscanlen = JAS_MIN(i, 4);
+        for (j = width; j > 0; --j, ++fvscanstart, ++dvscanstart) {
+            fp = fvscanstart;
+            dp = dvscanstart;
+            k = vscanlen;
+
+            sigpass_step(fp, frowstep, dp, bitpos, one,
+              nmsedec, orient, mqenc, vcausalflag);
+            if (--k <= 0) {
+                continue;
+            }
+            fp += frowstep;
+            dp += drowstep;
+            sigpass_step(fp, frowstep, dp, bitpos, one,
+              nmsedec, orient, mqenc, 0);
+            if (--k <= 0) {
+                continue;
+            }
+            fp += frowstep;
+            dp += drowstep;
+            sigpass_step(fp, frowstep, dp, bitpos, one,
+              nmsedec, orient, mqenc, 0);
+            if (--k <= 0) {
+                continue;
+            }
+            fp += frowstep;
+            dp += drowstep;
+            sigpass_step(fp, frowstep, dp, bitpos, one,
+              nmsedec, orient, mqenc, 0);
+
+        }
+    }
+
+    if (term) {
+        jpc_mqenc_flush(mqenc, term - 1);
+    }
+
+    return jpc_mqenc_error(mqenc) ? (-1) : 0;
 }
 
-#define	rawsigpass_step(fp, frowstep, dp, bitpos, one, nmsedec, out, vcausalflag) \
+#define rawsigpass_step(fp, frowstep, dp, bitpos, one, nmsedec, out, vcausalflag) \
 { \
-	jpc_fix_t f = *(fp); \
-	jpc_fix_t v; \
-	if ((f & JPC_OTHSIGMSK) && !(f & (JPC_SIG | JPC_VISIT))) { \
-		v = (abs(*(dp)) & (one)) ? 1 : 0; \
-		if ((jpc_bitstream_putbit((out), v)) == EOF) { \
-			return -1; \
-		} \
-		if (v) { \
-			*(nmsedec) += JPC_GETSIGNMSEDEC(abs(*(dp)), (bitpos) + JPC_NUMEXTRABITS); \
-			v = ((*(dp) < 0) ? 1 : 0); \
-			if (jpc_bitstream_putbit(out, v) == EOF) { \
-				return -1; \
-			} \
-			JPC_UPDATEFLAGS4(fp, frowstep, v, vcausalflag); \
-			*(fp) |= JPC_SIG; \
-		} \
-		*(fp) |= JPC_VISIT; \
-	} \
+    jpc_fix_t f = *(fp); \
+    jpc_fix_t v; \
+    if ((f & JPC_OTHSIGMSK) && !(f & (JPC_SIG | JPC_VISIT))) { \
+        v = (abs(*(dp)) & (one)) ? 1 : 0; \
+        if ((jpc_bitstream_putbit((out), v)) == EOF) { \
+            return -1; \
+        } \
+        if (v) { \
+            *(nmsedec) += JPC_GETSIGNMSEDEC(abs(*(dp)), (bitpos) + JPC_NUMEXTRABITS); \
+            v = ((*(dp) < 0) ? 1 : 0); \
+            if (jpc_bitstream_putbit(out, v) == EOF) { \
+                return -1; \
+            } \
+            JPC_UPDATEFLAGS4(fp, frowstep, v, vcausalflag); \
+            *(fp) |= JPC_SIG; \
+        } \
+        *(fp) |= JPC_VISIT; \
+    } \
 }
 
 static int jpc_encrawsigpass(jpc_bitstream_t *out, int bitpos, int vcausalflag, jas_matrix_t *flags,
   jas_matrix_t *data, int term, long *nmsedec)
 {
-	int i;
-	int j;
-	int k;
-	int one;
-	int vscanlen;
-	int width;
-	int height;
-	int frowstep;
-	int drowstep;
-	int fstripestep;
-	int dstripestep;
-	jpc_fix_t *fstripestart;
-	jpc_fix_t *dstripestart;
-	jpc_fix_t *fp;
-	jpc_fix_t *dp;
-	jpc_fix_t *fvscanstart;
-	jpc_fix_t *dvscanstart;
-
-	*nmsedec = 0;
-	width = jas_matrix_numcols(data);
-	height = jas_matrix_numrows(data);
-	frowstep = jas_matrix_rowstep(flags);
-	drowstep = jas_matrix_rowstep(data);
-	fstripestep = frowstep << 2;
-	dstripestep = drowstep << 2;
-
-	one = 1 << (bitpos + JPC_NUMEXTRABITS);
-
-	fstripestart = jas_matrix_getref(flags, 1, 1);
-	dstripestart = jas_matrix_getref(data, 0, 0);
-	for (i = height; i > 0; i -= 4, fstripestart += fstripestep,
-	  dstripestart += dstripestep) {
-		fvscanstart = fstripestart;
-		dvscanstart = dstripestart;
-		vscanlen = JAS_MIN(i, 4);
-		for (j = width; j > 0; --j, ++fvscanstart, ++dvscanstart) {
-			fp = fvscanstart;
-			dp = dvscanstart;
-			k = vscanlen;
-
-			rawsigpass_step(fp, frowstep, dp, bitpos, one,
-			  nmsedec, out, vcausalflag);
-			if (--k <= 0) {
-				continue;
-			}
-			fp += frowstep;
-			dp += drowstep;
-
-			rawsigpass_step(fp, frowstep, dp, bitpos, one,
-			  nmsedec, out, 0);
-			if (--k <= 0) {
-				continue;
-			}
-			fp += frowstep;
-			dp += drowstep;
-
-			rawsigpass_step(fp, frowstep, dp, bitpos, one,
-			  nmsedec, out, 0);
-			if (--k <= 0) {
-				continue;
-			}
-			fp += frowstep;
-			dp += drowstep;
-
-			rawsigpass_step(fp, frowstep, dp, bitpos, one,
-			  nmsedec, out, 0);
-			if (--k <= 0) {
-				continue;
-			}
-			fp += frowstep;
-			dp += drowstep;
-
-		}
-	}
-
-	if (term) {
-		jpc_bitstream_outalign(out, 0x2a);
-	}
-
-	return 0;
+    int i;
+    int j;
+    int k;
+    int one;
+    int vscanlen;
+    int width;
+    int height;
+    int frowstep;
+    int drowstep;
+    int fstripestep;
+    int dstripestep;
+    jpc_fix_t *fstripestart;
+    jpc_fix_t *dstripestart;
+    jpc_fix_t *fp;
+    jpc_fix_t *dp;
+    jpc_fix_t *fvscanstart;
+    jpc_fix_t *dvscanstart;
+
+    *nmsedec = 0;
+    width = jas_matrix_numcols(data);
+    height = jas_matrix_numrows(data);
+    frowstep = jas_matrix_rowstep(flags);
+    drowstep = jas_matrix_rowstep(data);
+    fstripestep = frowstep << 2;
+    dstripestep = drowstep << 2;
+
+    one = 1 << (bitpos + JPC_NUMEXTRABITS);
+
+    fstripestart = jas_matrix_getref(flags, 1, 1);
+    dstripestart = jas_matrix_getref(data, 0, 0);
+    for (i = height; i > 0; i -= 4, fstripestart += fstripestep,
+      dstripestart += dstripestep) {
+        fvscanstart = fstripestart;
+        dvscanstart = dstripestart;
+        vscanlen = JAS_MIN(i, 4);
+        for (j = width; j > 0; --j, ++fvscanstart, ++dvscanstart) {
+            fp = fvscanstart;
+            dp = dvscanstart;
+            k = vscanlen;
+
+            rawsigpass_step(fp, frowstep, dp, bitpos, one,
+              nmsedec, out, vcausalflag);
+            if (--k <= 0) {
+                continue;
+            }
+            fp += frowstep;
+            dp += drowstep;
+
+            rawsigpass_step(fp, frowstep, dp, bitpos, one,
+              nmsedec, out, 0);
+            if (--k <= 0) {
+                continue;
+            }
+            fp += frowstep;
+            dp += drowstep;
+
+            rawsigpass_step(fp, frowstep, dp, bitpos, one,
+              nmsedec, out, 0);
+            if (--k <= 0) {
+                continue;
+            }
+            fp += frowstep;
+            dp += drowstep;
+
+            rawsigpass_step(fp, frowstep, dp, bitpos, one,
+              nmsedec, out, 0);
+            if (--k <= 0) {
+                continue;
+            }
+            fp += frowstep;
+            dp += drowstep;
+
+        }
+    }
+
+    if (term) {
+        jpc_bitstream_outalign(out, 0x2a);
+    }
+
+    return 0;
 }
 
 /******************************************************************************\
 * Code for refinement pass.
 \******************************************************************************/
 
-#define	refpass_step(fp, dp, bitpos, one, nmsedec, mqenc, vcausalflag) \
+#define refpass_step(fp, dp, bitpos, one, nmsedec, mqenc, vcausalflag) \
 { \
-	int v; \
-	if (((*(fp)) & (JPC_SIG | JPC_VISIT)) == JPC_SIG) { \
-		(d) = *(dp); \
-		*(nmsedec) += JPC_GETREFNMSEDEC(abs(d), (bitpos) + JPC_NUMEXTRABITS); \
-		jpc_mqenc_setcurctx((mqenc), JPC_GETMAGCTXNO(*(fp))); \
-		v = (abs(d) & (one)) ? 1 : 0; \
-		jpc_mqenc_putbit((mqenc), v); \
-		*(fp) |= JPC_REFINE; \
-	} \
+    int v; \
+    if (((*(fp)) & (JPC_SIG | JPC_VISIT)) == JPC_SIG) { \
+        (d) = *(dp); \
+        *(nmsedec) += JPC_GETREFNMSEDEC(abs(d), (bitpos) + JPC_NUMEXTRABITS); \
+        jpc_mqenc_setcurctx((mqenc), JPC_GETMAGCTXNO(*(fp))); \
+        v = (abs(d) & (one)) ? 1 : 0; \
+        jpc_mqenc_putbit((mqenc), v); \
+        *(fp) |= JPC_REFINE; \
+    } \
 }
 
 static int jpc_encrefpass(jpc_mqenc_t *mqenc, int bitpos, int vcausalflag, jas_matrix_t *flags, jas_matrix_t *data,
   int term, long *nmsedec)
 {
-	int i;
-	int j;
-	int one;
-	int vscanlen;
-	int d;
-	int width;
-	int height;
-	int frowstep;
-	int drowstep;
-	int fstripestep;
-	int dstripestep;
-	jpc_fix_t *fstripestart;
-	jpc_fix_t *dstripestart;
-	jpc_fix_t *fvscanstart;
-	jpc_fix_t *dvscanstart;
-	jpc_fix_t *dp;
-	jpc_fix_t *fp;
+    int i;
+    int j;
+    int one;
+    int vscanlen;
+    int d;
+    int width;
+    int height;
+    int frowstep;
+    int drowstep;
+    int fstripestep;
+    int dstripestep;
+    jpc_fix_t *fstripestart;
+    jpc_fix_t *dstripestart;
+    jpc_fix_t *fvscanstart;
+    jpc_fix_t *dvscanstart;
+    jpc_fix_t *dp;
+    jpc_fix_t *fp;
 int k;
 
-	*nmsedec = 0;
-	width = jas_matrix_numcols(data);
-	height = jas_matrix_numrows(data);
-	frowstep = jas_matrix_rowstep(flags);
-	drowstep = jas_matrix_rowstep(data);
-	fstripestep = frowstep << 2;
-	dstripestep = drowstep << 2;
-
-	one = 1 << (bitpos + JPC_NUMEXTRABITS);
-
-	fstripestart = jas_matrix_getref(flags, 1, 1);
-	dstripestart = jas_matrix_getref(data, 0, 0);
-	for (i = height; i > 0; i -= 4, fstripestart += fstripestep,
-	  dstripestart += dstripestep) {
-		fvscanstart = fstripestart;
-		dvscanstart = dstripestart;
-		vscanlen = JAS_MIN(i, 4);
-		for (j = width; j > 0; --j, ++fvscanstart, ++dvscanstart) {
-			fp = fvscanstart;
-			dp = dvscanstart;
-			k = vscanlen;
-
-			refpass_step(fp, dp, bitpos, one, nmsedec,
-			  mqenc, vcausalflag);
-			if (--k <= 0) {
-				continue;
-			}
-			fp += frowstep;
-			dp += drowstep;
-			refpass_step(fp, dp, bitpos, one, nmsedec,
-			  mqenc, 0);
-			if (--k <= 0) {
-				continue;
-			}
-			fp += frowstep;
-			dp += drowstep;
-			refpass_step(fp, dp, bitpos, one, nmsedec,
-			  mqenc, 0);
-			if (--k <= 0) {
-				continue;
-			}
-			fp += frowstep;
-			dp += drowstep;
-			refpass_step(fp, dp, bitpos, one, nmsedec,
-			  mqenc, 0);
-
-		}
-	}
-
-	if (term) {
-		jpc_mqenc_flush(mqenc, term - 1);
-	}
-
-	return jpc_mqenc_error(mqenc) ? (-1) : 0;
+    *nmsedec = 0;
+    width = jas_matrix_numcols(data);
+    height = jas_matrix_numrows(data);
+    frowstep = jas_matrix_rowstep(flags);
+    drowstep = jas_matrix_rowstep(data);
+    fstripestep = frowstep << 2;
+    dstripestep = drowstep << 2;
+
+    one = 1 << (bitpos + JPC_NUMEXTRABITS);
+
+    fstripestart = jas_matrix_getref(flags, 1, 1);
+    dstripestart = jas_matrix_getref(data, 0, 0);
+    for (i = height; i > 0; i -= 4, fstripestart += fstripestep,
+      dstripestart += dstripestep) {
+        fvscanstart = fstripestart;
+        dvscanstart = dstripestart;
+        vscanlen = JAS_MIN(i, 4);
+        for (j = width; j > 0; --j, ++fvscanstart, ++dvscanstart) {
+            fp = fvscanstart;
+            dp = dvscanstart;
+            k = vscanlen;
+
+            refpass_step(fp, dp, bitpos, one, nmsedec,
+              mqenc, vcausalflag);
+            if (--k <= 0) {
+                continue;
+            }
+            fp += frowstep;
+            dp += drowstep;
+            refpass_step(fp, dp, bitpos, one, nmsedec,
+              mqenc, 0);
+            if (--k <= 0) {
+                continue;
+            }
+            fp += frowstep;
+            dp += drowstep;
+            refpass_step(fp, dp, bitpos, one, nmsedec,
+              mqenc, 0);
+            if (--k <= 0) {
+                continue;
+            }
+            fp += frowstep;
+            dp += drowstep;
+            refpass_step(fp, dp, bitpos, one, nmsedec,
+              mqenc, 0);
+
+        }
+    }
+
+    if (term) {
+        jpc_mqenc_flush(mqenc, term - 1);
+    }
+
+    return jpc_mqenc_error(mqenc) ? (-1) : 0;
 }
 
-#define	rawrefpass_step(fp, dp, bitpos, one, nmsedec, out, vcausalflag) \
+#define rawrefpass_step(fp, dp, bitpos, one, nmsedec, out, vcausalflag) \
 { \
-	jpc_fix_t d; \
-	jpc_fix_t v; \
-	if (((*(fp)) & (JPC_SIG | JPC_VISIT)) == JPC_SIG) { \
-		d = *(dp); \
-		*(nmsedec) += JPC_GETREFNMSEDEC(abs(d), (bitpos) + JPC_NUMEXTRABITS); \
-		v = (abs(d) & (one)) ? 1 : 0; \
-		if (jpc_bitstream_putbit((out), v) == EOF) { \
-			return -1; \
-		} \
-		*(fp) |= JPC_REFINE; \
-	} \
+    jpc_fix_t d; \
+    jpc_fix_t v; \
+    if (((*(fp)) & (JPC_SIG | JPC_VISIT)) == JPC_SIG) { \
+        d = *(dp); \
+        *(nmsedec) += JPC_GETREFNMSEDEC(abs(d), (bitpos) + JPC_NUMEXTRABITS); \
+        v = (abs(d) & (one)) ? 1 : 0; \
+        if (jpc_bitstream_putbit((out), v) == EOF) { \
+            return -1; \
+        } \
+        *(fp) |= JPC_REFINE; \
+    } \
 }
 
 static int jpc_encrawrefpass(jpc_bitstream_t *out, int bitpos, int vcausalflag, jas_matrix_t *flags,
   jas_matrix_t *data, int term, long *nmsedec)
 {
-	int i;
-	int j;
-	int k;
-	int one;
-	int vscanlen;
-	int width;
-	int height;
-	int frowstep;
-	int drowstep;
-	int fstripestep;
-	int dstripestep;
-	jpc_fix_t *fstripestart;
-	jpc_fix_t *dstripestart;
-	jpc_fix_t *fvscanstart;
-	jpc_fix_t *dvscanstart;
-	jpc_fix_t *dp;
-	jpc_fix_t *fp;
-
-	*nmsedec = 0;
-	width = jas_matrix_numcols(data);
-	height = jas_matrix_numrows(data);
-	frowstep = jas_matrix_rowstep(flags);
-	drowstep = jas_matrix_rowstep(data);
-	fstripestep = frowstep << 2;
-	dstripestep = drowstep << 2;
-
-	one = 1 << (bitpos + JPC_NUMEXTRABITS);
-
-	fstripestart = jas_matrix_getref(flags, 1, 1);
-	dstripestart = jas_matrix_getref(data, 0, 0);
-	for (i = height; i > 0; i -= 4, fstripestart += fstripestep,
-	  dstripestart += dstripestep) {
-		fvscanstart = fstripestart;
-		dvscanstart = dstripestart;
-		vscanlen = JAS_MIN(i, 4);
-		for (j = width; j > 0; --j, ++fvscanstart, ++dvscanstart) {
-			fp = fvscanstart;
-			dp = dvscanstart;
-			k = vscanlen;
-
-			rawrefpass_step(fp, dp, bitpos, one, nmsedec,
-			  out, vcausalflag);
-			if (--k <= 0) {
-				continue;
-			}
-			fp += frowstep;
-			dp += drowstep;
-			rawrefpass_step(fp, dp, bitpos, one, nmsedec,
-			  out, vcausalflag);
-			if (--k <= 0) {
-				continue;
-			}
-			fp += frowstep;
-			dp += drowstep;
-			rawrefpass_step(fp, dp, bitpos, one, nmsedec,
-			  out, vcausalflag);
-			if (--k <= 0) {
-				continue;
-			}
-			fp += frowstep;
-			dp += drowstep;
-			rawrefpass_step(fp, dp, bitpos, one, nmsedec,
-			  out, vcausalflag);
-
-		}
-	}
-
-	if (term) {
-		jpc_bitstream_outalign(out, 0x2a);
-	}
-
-	return 0;
+    int i;
+    int j;
+    int k;
+    int one;
+    int vscanlen;
+    int width;
+    int height;
+    int frowstep;
+    int drowstep;
+    int fstripestep;
+    int dstripestep;
+    jpc_fix_t *fstripestart;
+    jpc_fix_t *dstripestart;
+    jpc_fix_t *fvscanstart;
+    jpc_fix_t *dvscanstart;
+    jpc_fix_t *dp;
+    jpc_fix_t *fp;
+
+    *nmsedec = 0;
+    width = jas_matrix_numcols(data);
+    height = jas_matrix_numrows(data);
+    frowstep = jas_matrix_rowstep(flags);
+    drowstep = jas_matrix_rowstep(data);
+    fstripestep = frowstep << 2;
+    dstripestep = drowstep << 2;
+
+    one = 1 << (bitpos + JPC_NUMEXTRABITS);
+
+    fstripestart = jas_matrix_getref(flags, 1, 1);
+    dstripestart = jas_matrix_getref(data, 0, 0);
+    for (i = height; i > 0; i -= 4, fstripestart += fstripestep,
+      dstripestart += dstripestep) {
+        fvscanstart = fstripestart;
+        dvscanstart = dstripestart;
+        vscanlen = JAS_MIN(i, 4);
+        for (j = width; j > 0; --j, ++fvscanstart, ++dvscanstart) {
+            fp = fvscanstart;
+            dp = dvscanstart;
+            k = vscanlen;
+
+            rawrefpass_step(fp, dp, bitpos, one, nmsedec,
+              out, vcausalflag);
+            if (--k <= 0) {
+                continue;
+            }
+            fp += frowstep;
+            dp += drowstep;
+            rawrefpass_step(fp, dp, bitpos, one, nmsedec,
+              out, vcausalflag);
+            if (--k <= 0) {
+                continue;
+            }
+            fp += frowstep;
+            dp += drowstep;
+            rawrefpass_step(fp, dp, bitpos, one, nmsedec,
+              out, vcausalflag);
+            if (--k <= 0) {
+                continue;
+            }
+            fp += frowstep;
+            dp += drowstep;
+            rawrefpass_step(fp, dp, bitpos, one, nmsedec,
+              out, vcausalflag);
+
+        }
+    }
+
+    if (term) {
+        jpc_bitstream_outalign(out, 0x2a);
+    }
+
+    return 0;
 }
 
 /******************************************************************************\
 * Code for cleanup pass.
 \******************************************************************************/
 
-#define	clnpass_step(fp, frowstep, dp, bitpos, one, orient, nmsedec, mqenc, label1, label2, vcausalflag) \
+#define clnpass_step(fp, frowstep, dp, bitpos, one, orient, nmsedec, mqenc, label1, label2, vcausalflag) \
 { \
-	int f; \
-	int v; \
+    int f; \
+    int v; \
 label1 \
-	f = *(fp); \
-	if (!(f & (JPC_SIG | JPC_VISIT))) { \
-		jpc_mqenc_setcurctx(mqenc, JPC_GETZCCTXNO(f, (orient))); \
-		v = (abs(*(dp)) & (one)) ? 1 : 0; \
-		jpc_mqenc_putbit((mqenc), v); \
-		if (v) { \
+    f = *(fp); \
+    if (!(f & (JPC_SIG | JPC_VISIT))) { \
+        jpc_mqenc_setcurctx(mqenc, JPC_GETZCCTXNO(f, (orient))); \
+        v = (abs(*(dp)) & (one)) ? 1 : 0; \
+        jpc_mqenc_putbit((mqenc), v); \
+        if (v) { \
 label2 \
-			f = *(fp); \
-			/* Coefficient is significant. */ \
-			*(nmsedec) += JPC_GETSIGNMSEDEC(abs(*(dp)), (bitpos) + JPC_NUMEXTRABITS); \
-			jpc_mqenc_setcurctx((mqenc), JPC_GETSCCTXNO(f)); \
-			v = ((*(dp) < 0) ? 1 : 0); \
-			jpc_mqenc_putbit((mqenc), v ^ JPC_GETSPB(f)); \
-			JPC_UPDATEFLAGS4((fp), (frowstep), v, vcausalflag); \
-			*(fp) |= JPC_SIG; \
-		} \
-	} \
-	*(fp) &= ~JPC_VISIT; \
+            f = *(fp); \
+            /* Coefficient is significant. */ \
+            *(nmsedec) += JPC_GETSIGNMSEDEC(abs(*(dp)), (bitpos) + JPC_NUMEXTRABITS); \
+            jpc_mqenc_setcurctx((mqenc), JPC_GETSCCTXNO(f)); \
+            v = ((*(dp) < 0) ? 1 : 0); \
+            jpc_mqenc_putbit((mqenc), v ^ JPC_GETSPB(f)); \
+            JPC_UPDATEFLAGS4((fp), (frowstep), v, vcausalflag); \
+            *(fp) |= JPC_SIG; \
+        } \
+    } \
+    *(fp) &= ~JPC_VISIT; \
 }
 
 static int jpc_encclnpass(jpc_mqenc_t *mqenc, int bitpos, int orient, int vcausalflag, int segsymflag, jas_matrix_t *flags,
   jas_matrix_t *data, int term, long *nmsedec)
 {
-	int i;
-	int j;
-	int k;
-	int vscanlen;
-	int v;
-	int runlen;
-	jpc_fix_t *fp;
-	int width;
-	int height;
-	jpc_fix_t *dp;
-	int one;
-	int frowstep;
-	int drowstep;
-	int fstripestep;
-	int dstripestep;
-	jpc_fix_t *fstripestart;
-	jpc_fix_t *dstripestart;
-	jpc_fix_t *fvscanstart;
-	jpc_fix_t *dvscanstart;
-
-	*nmsedec = 0;
-	width = jas_matrix_numcols(data);
-	height = jas_matrix_numrows(data);
-	frowstep = jas_matrix_rowstep(flags);
-	drowstep = jas_matrix_rowstep(data);
-	fstripestep = frowstep << 2;
-	dstripestep = drowstep << 2;
-
-	one = 1 << (bitpos + JPC_NUMEXTRABITS);
-
-	fstripestart = jas_matrix_getref(flags, 1, 1);
-	dstripestart = jas_matrix_getref(data, 0, 0);
-	for (i = height; i > 0; i -= 4, fstripestart += fstripestep,
-	  dstripestart += dstripestep) {
-		fvscanstart = fstripestart;
-		dvscanstart = dstripestart;
-		vscanlen = JAS_MIN(i, 4);
-		for (j = width; j > 0; --j, ++fvscanstart, ++dvscanstart) {
-
-			fp = fvscanstart;
-			if (vscanlen >= 4 && !((*fp) & (JPC_SIG | JPC_VISIT |
-			  JPC_OTHSIGMSK)) && (fp += frowstep, !((*fp) & (JPC_SIG |
-			  JPC_VISIT | JPC_OTHSIGMSK))) && (fp += frowstep, !((*fp) &
-			  (JPC_SIG | JPC_VISIT | JPC_OTHSIGMSK))) && (fp += frowstep,
-			  !((*fp) & (JPC_SIG | JPC_VISIT | JPC_OTHSIGMSK)))) {
-				dp = dvscanstart;
-				for (k = 0; k < vscanlen; ++k) {
-					v = (abs(*dp) & one) ? 1 : 0;
-					if (v) {
-						break;
-					}
-					dp += drowstep;
-				}
-				runlen = k;
-				if (runlen >= 4) {
-					jpc_mqenc_setcurctx(mqenc, JPC_AGGCTXNO);
-					jpc_mqenc_putbit(mqenc, 0);
-					continue;
-				}
-				jpc_mqenc_setcurctx(mqenc, JPC_AGGCTXNO);
-				jpc_mqenc_putbit(mqenc, 1);
-				jpc_mqenc_setcurctx(mqenc, JPC_UCTXNO);
-				jpc_mqenc_putbit(mqenc, runlen >> 1);
-				jpc_mqenc_putbit(mqenc, runlen & 1);
-				fp = fvscanstart + frowstep * runlen;
-				dp = dvscanstart + drowstep * runlen;
-				k = vscanlen - runlen;
-				switch (runlen) {
-				case 0:
-					goto clnpass_partial0;
-					break;
-				case 1:
-					goto clnpass_partial1;
-					break;
-				case 2:
-					goto clnpass_partial2;
-					break;
-				case 3:
-					goto clnpass_partial3;
-					break;
-				}
-			} else {
-				runlen = 0;
-				fp = fvscanstart;
-				dp = dvscanstart;
-				k = vscanlen;
-				goto clnpass_full0;
-			}
-			clnpass_step(fp, frowstep, dp, bitpos, one,
-			  orient, nmsedec, mqenc, clnpass_full0:, clnpass_partial0:, vcausalflag);
-			if (--k <= 0) {
-				continue;
-			}
-			fp += frowstep;
-			dp += drowstep;
-			clnpass_step(fp, frowstep, dp, bitpos, one,
-				orient, nmsedec, mqenc, ;, clnpass_partial1:, 0);
-			if (--k <= 0) {
-				continue;
-			}
-			fp += frowstep;
-			dp += drowstep;
-			clnpass_step(fp, frowstep, dp, bitpos, one,
-				orient, nmsedec, mqenc, ;, clnpass_partial2:, 0);
-			if (--k <= 0) {
-				continue;
-			}
-			fp += frowstep;
-			dp += drowstep;
-			clnpass_step(fp, frowstep, dp, bitpos, one,
-				orient, nmsedec, mqenc, ;, clnpass_partial3:, 0);
-		}
-	}
-
-	if (segsymflag) {
-		jpc_mqenc_setcurctx(mqenc, JPC_UCTXNO);
-		jpc_mqenc_putbit(mqenc, 1);
-		jpc_mqenc_putbit(mqenc, 0);
-		jpc_mqenc_putbit(mqenc, 1);
-		jpc_mqenc_putbit(mqenc, 0);
-	}
-
-	if (term) {
-		jpc_mqenc_flush(mqenc, term - 1);
-	}
-
-	return jpc_mqenc_error(mqenc) ? (-1) : 0;
+    int i;
+    int j;
+    int k;
+    int vscanlen;
+    int v;
+    int runlen;
+    jpc_fix_t *fp;
+    int width;
+    int height;
+    jpc_fix_t *dp;
+    int one;
+    int frowstep;
+    int drowstep;
+    int fstripestep;
+    int dstripestep;
+    jpc_fix_t *fstripestart;
+    jpc_fix_t *dstripestart;
+    jpc_fix_t *fvscanstart;
+    jpc_fix_t *dvscanstart;
+
+    *nmsedec = 0;
+    width = jas_matrix_numcols(data);
+    height = jas_matrix_numrows(data);
+    frowstep = jas_matrix_rowstep(flags);
+    drowstep = jas_matrix_rowstep(data);
+    fstripestep = frowstep << 2;
+    dstripestep = drowstep << 2;
+
+    one = 1 << (bitpos + JPC_NUMEXTRABITS);
+
+    fstripestart = jas_matrix_getref(flags, 1, 1);
+    dstripestart = jas_matrix_getref(data, 0, 0);
+    for (i = height; i > 0; i -= 4, fstripestart += fstripestep,
+      dstripestart += dstripestep) {
+        fvscanstart = fstripestart;
+        dvscanstart = dstripestart;
+        vscanlen = JAS_MIN(i, 4);
+        for (j = width; j > 0; --j, ++fvscanstart, ++dvscanstart) {
+
+            fp = fvscanstart;
+            if (vscanlen >= 4 && !((*fp) & (JPC_SIG | JPC_VISIT |
+              JPC_OTHSIGMSK)) && (fp += frowstep, !((*fp) & (JPC_SIG |
+              JPC_VISIT | JPC_OTHSIGMSK))) && (fp += frowstep, !((*fp) &
+              (JPC_SIG | JPC_VISIT | JPC_OTHSIGMSK))) && (fp += frowstep,
+              !((*fp) & (JPC_SIG | JPC_VISIT | JPC_OTHSIGMSK)))) {
+                dp = dvscanstart;
+                for (k = 0; k < vscanlen; ++k) {
+                    v = (abs(*dp) & one) ? 1 : 0;
+                    if (v) {
+                        break;
+                    }
+                    dp += drowstep;
+                }
+                runlen = k;
+                if (runlen >= 4) {
+                    jpc_mqenc_setcurctx(mqenc, JPC_AGGCTXNO);
+                    jpc_mqenc_putbit(mqenc, 0);
+                    continue;
+                }
+                jpc_mqenc_setcurctx(mqenc, JPC_AGGCTXNO);
+                jpc_mqenc_putbit(mqenc, 1);
+                jpc_mqenc_setcurctx(mqenc, JPC_UCTXNO);
+                jpc_mqenc_putbit(mqenc, runlen >> 1);
+                jpc_mqenc_putbit(mqenc, runlen & 1);
+                fp = fvscanstart + frowstep * runlen;
+                dp = dvscanstart + drowstep * runlen;
+                k = vscanlen - runlen;
+                switch (runlen) {
+                case 0:
+                    goto clnpass_partial0;
+                    break;
+                case 1:
+                    goto clnpass_partial1;
+                    break;
+                case 2:
+                    goto clnpass_partial2;
+                    break;
+                case 3:
+                    goto clnpass_partial3;
+                    break;
+                }
+            } else {
+                runlen = 0;
+                fp = fvscanstart;
+                dp = dvscanstart;
+                k = vscanlen;
+                goto clnpass_full0;
+            }
+            clnpass_step(fp, frowstep, dp, bitpos, one,
+              orient, nmsedec, mqenc, clnpass_full0:, clnpass_partial0:, vcausalflag);
+            if (--k <= 0) {
+                continue;
+            }
+            fp += frowstep;
+            dp += drowstep;
+            clnpass_step(fp, frowstep, dp, bitpos, one,
+                orient, nmsedec, mqenc, ;, clnpass_partial1:, 0);
+            if (--k <= 0) {
+                continue;
+            }
+            fp += frowstep;
+            dp += drowstep;
+            clnpass_step(fp, frowstep, dp, bitpos, one,
+                orient, nmsedec, mqenc, ;, clnpass_partial2:, 0);
+            if (--k <= 0) {
+                continue;
+            }
+            fp += frowstep;
+            dp += drowstep;
+            clnpass_step(fp, frowstep, dp, bitpos, one,
+                orient, nmsedec, mqenc, ;, clnpass_partial3:, 0);
+        }
+    }
+
+    if (segsymflag) {
+        jpc_mqenc_setcurctx(mqenc, JPC_UCTXNO);
+        jpc_mqenc_putbit(mqenc, 1);
+        jpc_mqenc_putbit(mqenc, 0);
+        jpc_mqenc_putbit(mqenc, 1);
+        jpc_mqenc_putbit(mqenc, 0);
+    }
+
+    if (term) {
+        jpc_mqenc_flush(mqenc, term - 1);
+    }
+
+    return jpc_mqenc_error(mqenc) ? (-1) : 0;
 }
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_util.c b/converter/other/jpeg2000/libjasper/jpc/jpc_util.c
index ecc4b914..fede2bef 100644
--- a/converter/other/jpeg2000/libjasper/jpc/jpc_util.c
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_util.c
@@ -6,14 +6,14 @@
  */
 
 /* __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
@@ -21,22 +21,22 @@
  * 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
@@ -56,17 +56,17 @@
  * 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
@@ -106,7 +106,7 @@
  * 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/pamtojpeg2k.c b/converter/other/jpeg2000/pamtojpeg2k.c
index 142e452f..4d73316a 100644
--- a/converter/other/jpeg2000/pamtojpeg2k.c
+++ b/converter/other/jpeg2000/pamtojpeg2k.c
@@ -8,6 +8,7 @@
 
 *****************************************************************************/
 
+#define _DEFAULT_SOURCE 1 /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE 1    /* Make sure strdup() is in string.h */
 #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
@@ -97,7 +98,7 @@ parseCommandLine(int argc, char ** argv,
     char * modeOpt;
 
     unsigned int option_def_index;
-    
+
     MALLOCARRAY_NOFAIL(option_def, 100);
 
     option_def_index = 0;   /* incremented by OPTENTRY */
@@ -133,25 +134,25 @@ parseCommandLine(int argc, char ** argv,
             &numrlvlsSpec,       0);
     OPTENT3(0, "numgbits",     OPT_UINT,   &cmdlineP->numgbits,
             &numgbitsSpec,       0);
-    OPTENT3(0, "nomct",        OPT_FLAG,   NULL, 
+    OPTENT3(0, "nomct",        OPT_FLAG,   NULL,
             &cmdlineP->nomct,    0);
-    OPTENT3(0, "sop",          OPT_FLAG,   NULL, 
+    OPTENT3(0, "sop",          OPT_FLAG,   NULL,
             &cmdlineP->sop,      0);
-    OPTENT3(0, "eph",          OPT_FLAG,   NULL, 
+    OPTENT3(0, "eph",          OPT_FLAG,   NULL,
             &cmdlineP->eph,      0);
-    OPTENT3(0, "lazy",         OPT_FLAG,   NULL, 
+    OPTENT3(0, "lazy",         OPT_FLAG,   NULL,
             &cmdlineP->lazy,     0);
-    OPTENT3(0, "termall",      OPT_FLAG,   NULL, 
+    OPTENT3(0, "termall",      OPT_FLAG,   NULL,
             &cmdlineP->termall,  0);
-    OPTENT3(0, "segsym",       OPT_FLAG,   NULL, 
+    OPTENT3(0, "segsym",       OPT_FLAG,   NULL,
             &cmdlineP->segsym,    0);
-    OPTENT3(0, "vcausal",      OPT_FLAG,   NULL, 
+    OPTENT3(0, "vcausal",      OPT_FLAG,   NULL,
             &cmdlineP->vcausal,   0);
-    OPTENT3(0, "pterm",        OPT_FLAG,   NULL, 
+    OPTENT3(0, "pterm",        OPT_FLAG,   NULL,
             &cmdlineP->pterm,     0);
-    OPTENT3(0, "resetprob",    OPT_FLAG,   NULL, 
+    OPTENT3(0, "resetprob",    OPT_FLAG,   NULL,
             &cmdlineP->resetprob, 0);
-    OPTENT3(0, "verbose",      OPT_FLAG,   NULL, 
+    OPTENT3(0, "verbose",      OPT_FLAG,   NULL,
             &cmdlineP->verbose,   0);
     OPTENT3(0, "debuglevel",   OPT_UINT,   &cmdlineP->debuglevel,
             &debuglevelSpec,      0);
@@ -222,7 +223,7 @@ parseCommandLine(int argc, char ** argv,
         cmdlineP->inputFilename = strdup("-");  /* he wants stdin */
     else if (argc - 1 == 1)
         cmdlineP->inputFilename = strdup(argv[1]);
-    else 
+    else
         pm_error("Too many arguments.  The only argument accepted\n"
                  "is the input file specification");
 
@@ -231,7 +232,7 @@ parseCommandLine(int argc, char ** argv,
 
 
 static void
-createJasperRaster(struct pam *  const inpamP, 
+createJasperRaster(struct pam *  const inpamP,
                    jas_image_t * const jasperP) {
 /*----------------------------------------------------------------------------
    Create the raster in the *jasperP object, reading the raster from the
@@ -253,7 +254,7 @@ createJasperRaster(struct pam *  const inpamP,
         if (matrix[plane] == NULL)
             pm_error("Unable to create matrix for plane %u.  "
                      "jas_matrix_create() failed.", plane);
-    }   
+    }
     tuplerow = pnm_allocpamrow(inpamP);
 
     jasperMaxval = pm_bitstomaxval(pm_maxvaltobits(inpamP->maxval));
@@ -270,7 +271,7 @@ createJasperRaster(struct pam *  const inpamP,
                 unsigned int jasperSample;
 
                 if (oddMaxval)
-                    jasperSample = tuplerow[col][plane] * 
+                    jasperSample = tuplerow[col][plane] *
                         jasperMaxval / inpamP->maxval;
                 else
                     jasperSample = tuplerow[col][plane];
@@ -278,16 +279,16 @@ createJasperRaster(struct pam *  const inpamP,
                 jas_matrix_set(matrix[plane], 0, col, jasperSample);
             }
         }
-        { 
+        {
             unsigned int plane;
 
             for (plane = 0; plane < inpamP->depth; ++plane) {
                 int rc;
-                rc = jas_image_writecmpt(jasperP, plane, 0, row, 
+                rc = jas_image_writecmpt(jasperP, plane, 0, row,
                                          inpamP->width, 1,
                                          matrix[plane]);
                 if (rc != 0)
-                    pm_error("jas_image_writecmpt() of plane %u failed.", 
+                    pm_error("jas_image_writecmpt() of plane %u failed.",
                              plane);
             }
         }
@@ -296,14 +297,14 @@ createJasperRaster(struct pam *  const inpamP,
     pnm_freepamrow(tuplerow);
     for (plane = 0; plane < inpamP->depth; ++plane)
         jas_matrix_destroy(matrix[plane]);
-    
+
     free(matrix);
 }
 
 
 
 static void
-createJasperImage(struct pam *   const inpamP, 
+createJasperImage(struct pam *   const inpamP,
                   jas_image_t ** const jasperPP) {
 
 	jas_image_cmptparm_t * cmptparms;
@@ -321,7 +322,7 @@ createJasperImage(struct pam *   const inpamP,
         cmptparms[plane].prec = pm_maxvaltobits(inpamP->maxval);
         cmptparms[plane].sgnd = 0;
     }
-    *jasperPP = 
+    *jasperPP =
         jas_image_create(inpamP->depth, cmptparms, JAS_CLRSPC_UNKNOWN);
     if (*jasperPP == NULL)
         pm_error("Unable to create jasper image structure.  "
@@ -340,7 +341,7 @@ convertToJasperImage(struct pam *   const inpamP,
 
     createJasperImage(inpamP, &jasperP);
 
-    if (strncmp(inpamP->tuple_type, "RGB", 3) == 0) {
+    if (strneq(inpamP->tuple_type, "RGB", 3)) {
         if (inpamP->depth < 3)
             pm_error("Input tuple type is RGB*, but depth is only %d.  "
                      "It should be at least 3.", inpamP->depth);
@@ -354,8 +355,8 @@ convertToJasperImage(struct pam *   const inpamP,
                                   JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_RGB_B));
         }
     } else {
-        if (strncmp(inpamP->tuple_type, "GRAYSCALE", 9) == 0 ||
-            strncmp(inpamP->tuple_type, "BLACKANDWHITE", 13) == 0) {
+        if (strneq(inpamP->tuple_type, "GRAYSCALE", 9) ||
+            strneq(inpamP->tuple_type, "BLACKANDWHITE", 13)) {
             jas_image_setclrspc(jasperP, JAS_CLRSPC_GENGRAY);
             jas_image_setcmpttype(jasperP, 0,
                                   JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_GRAY_Y));
@@ -370,7 +371,7 @@ convertToJasperImage(struct pam *   const inpamP,
 
 
 static void
-writeJpc(jas_image_t *      const jasperP, 
+writeJpc(jas_image_t *      const jasperP,
          struct cmdlineInfo const cmdline,
          FILE *             const ofP) {
 
@@ -382,8 +383,8 @@ writeJpc(jas_image_t *      const jasperP,
 
     /* Note: ilyrrates is a hack because we're too lazy to properly parse
        command line options to get the information and then compose
-       a proper input to Jasper.  So the user can screw things up by 
-       specifying garbage for the -ilyrrates option 
+       a proper input to Jasper.  So the user can screw things up by
+       specifying garbage for the -ilyrrates option
     */
     if (strlen(cmdline.ilyrrates) > 0)
         pm_asprintf(&ilyrratesOpt, "ilyrrates=%s", cmdline.ilyrrates);
@@ -409,7 +410,7 @@ writeJpc(jas_image_t *      const jasperP,
         */
         rateOpt[0] = '\0';
     }
-    pm_asprintf(&options, 
+    pm_asprintf(&options,
                 "imgareatlx=%u "
                 "imgareatly=%u "
                 "tilegrdtlx=%u "
@@ -427,7 +428,7 @@ writeJpc(jas_image_t *      const jasperP,
                 "numrlvls=%u "
                 "numgbits=%u "
                 "%s %s %s %s %s %s %s %s %s",
-                
+
                 cmdline.imgareatlx,
                 cmdline.imgareatly,
                 cmdline.tilegrdtlx,
@@ -470,8 +471,8 @@ writeJpc(jas_image_t *      const jasperP,
             pm_message("Using Jasper to encode to 'jpc' format with options "
                        "'%s'", options);
 
-        rc = jas_image_encode(jasperP, outStreamP, 
-                              jas_image_strtofmt((char*)"jpc"), 
+        rc = jas_image_encode(jasperP, outStreamP,
+                              jas_image_strtofmt((char*)"jpc"),
                               (char *)options);
         if (rc != 0)
             pm_error("jas_image_encode() failed to encode the JPEG 2000 "
@@ -483,11 +484,11 @@ writeJpc(jas_image_t *      const jasperP,
         int rc;
 
         rc = jas_stream_close(outStreamP);
-            
+
         if (rc != 0)
             pm_error("Failed to close output stream, "
                      "jas_stream_close() rc = %d", rc);
-    }                     
+    }
 
 	jas_image_clearfmts();
 
@@ -505,33 +506,33 @@ main(int argc, char **argv)
     jas_image_t * jasperP;
 
     pnm_init(&argc, argv);
-    
+
     parseCommandLine(argc, argv, &cmdline);
-    
-    { 
+
+    {
         int rc;
-        
+
         rc = jas_init();
         if ( rc != 0 )
             pm_error("Failed to initialize Jasper library.  "
                      "jas_init() returns rc %d", rc );
     }
-    
+
     jas_setdbglevel(cmdline.debuglevel);
-    
+
     ifP = pm_openr(cmdline.inputFilename);
-    
+
     pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
-    
+
     convertToJasperImage(&inpam, &jasperP);
-    
+
     writeJpc(jasperP, cmdline, stdout);
-    
+
 	jas_image_destroy(jasperP);
 
     pm_close(ifP);
 
     pm_close(stdout);
-    
+
     return 0;
 }
diff --git a/converter/other/jpegtopnm.c b/converter/other/jpegtopnm.c
index ab3b18e5..98552c00 100644
--- a/converter/other/jpegtopnm.c
+++ b/converter/other/jpegtopnm.c
@@ -48,6 +48,7 @@
     
 *****************************************************************************/
 
+#define _DEFAULT_SOURCE 1  /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
diff --git a/converter/other/pamtogif.c b/converter/other/pamtogif.c
index aabf7fc2..8d432da1 100644
--- a/converter/other/pamtogif.c
+++ b/converter/other/pamtogif.c
@@ -8,6 +8,7 @@
 
 #include <assert.h>
 #include <string.h>
+#include <stdbool.h>
 
 #include "pm_c_util.h"
 #include "mallocvar.h"
@@ -23,7 +24,7 @@ static unsigned int const gifMaxval = 255;
 static bool verbose;
 
 
-typedef int stringCode;
+typedef unsigned int StringCode;
     /* A code to be place in the GIF raster.  It represents
        a string of one or more pixels.  You interpret this in the context
        of a current code size.  The lower half of the values representable
@@ -34,15 +35,11 @@ typedef int stringCode;
        strings.  The mapping between value and the sequence of pixels
        changes throughout the image.
 
-       A variable of this type sometimes has the value -1 instead of
-       a string code because of cheesy programming.
-
-       Ergo, this data structure must be signed and at least BITS bits
-       wide plus sign bit.
+       Ergo, this data structure must be at least BITS bits wide.
     */
 
 
-struct cmap {
+struct Cmap {
     /* This is the information for the GIF colormap (aka palette). */
 
     struct pam pam;
@@ -67,7 +64,7 @@ struct cmap {
         /* A hash table to translate color to GIF colormap index. */
 };
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
@@ -79,6 +76,7 @@ struct cmdlineInfo {
     const char *transparent;    /* -transparent option value.  NULL if none. */
     const char *comment;        /* -comment option value; NULL if none */
     unsigned int nolzw;         /* -nolzw option */
+    unsigned int noclear;       /* -noclear option */
     float aspect;               /* -aspect option value (the ratio).  */
     unsigned int verbose;
 };
@@ -100,7 +98,7 @@ pamAlphaPlane(struct pam * const pamP) {
         alphaPlane = 2;
     else
         alphaPlane = 0;
-    
+
     if (alphaPlane >= pamP->depth)
         pm_error("Tuple type is '%s', but depth (%u) is less than %u",
                  pamP->tuple_type, pamP->depth, alphaPlane + 1);
@@ -112,7 +110,7 @@ pamAlphaPlane(struct pam * const pamP) {
 
 static void
 parseCommandLine(int argc, char ** argv,
-                 struct cmdlineInfo * const cmdlineP) {
+                 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.
@@ -131,30 +129,32 @@ parseCommandLine(int argc, char ** argv,
     MALLOCARRAY_NOFAIL(option_def, 100);
 
     option_def_index = 0;   /* incremented by OPTENT3 */
-    OPTENT3(0,   "interlace",   OPT_FLAG,   
+    OPTENT3(0,   "interlace",   OPT_FLAG,
             NULL,                       &cmdlineP->interlace, 0);
-    OPTENT3(0,   "sort",        OPT_FLAG,   
+    OPTENT3(0,   "sort",        OPT_FLAG,
             NULL,                       &cmdlineP->sort, 0);
-    OPTENT3(0,   "nolzw",       OPT_FLAG,   
+    OPTENT3(0,   "nolzw",       OPT_FLAG,
             NULL,                       &cmdlineP->nolzw, 0);
-    OPTENT3(0,   "mapfile",     OPT_STRING, 
+    OPTENT3(0,   "noclear",     OPT_FLAG,
+            NULL,                       &cmdlineP->noclear, 0);
+    OPTENT3(0,   "mapfile",     OPT_STRING,
             &cmdlineP->mapfile,        NULL, 0);
-    OPTENT3(0,   "transparent", OPT_STRING, 
+    OPTENT3(0,   "transparent", OPT_STRING,
             &cmdlineP->transparent,    NULL, 0);
-    OPTENT3(0,   "comment",     OPT_STRING, 
+    OPTENT3(0,   "comment",     OPT_STRING,
             &cmdlineP->comment,        NULL, 0);
-    OPTENT3(0,   "alphacolor",  OPT_STRING, 
+    OPTENT3(0,   "alphacolor",  OPT_STRING,
             &cmdlineP->alphacolor,     NULL, 0);
-    OPTENT3(0,   "aspect",      OPT_FLOAT, 
+    OPTENT3(0,   "aspect",      OPT_FLOAT,
             &cmdlineP->aspect,         &aspectSpec, 0);
-    OPTENT3(0,   "verbose",     OPT_FLAG, 
+    OPTENT3(0,   "verbose",     OPT_FLAG,
             NULL,                      &cmdlineP->verbose, 0);
-    
+
     /* Set the defaults */
     cmdlineP->mapfile = NULL;
     cmdlineP->transparent = NULL;  /* no transparency */
     cmdlineP->comment = NULL;      /* no comment */
-    cmdlineP->alphacolor = "rgb:0/0/0";      
+    cmdlineP->alphacolor = "rgb:0/0/0";
         /* We could say "black" here, but then we depend on the color names
            database existing.
         */
@@ -166,15 +166,15 @@ parseCommandLine(int argc, char ** argv,
     pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
-    if (argc-1 == 0) 
+    if (argc-1 == 0)
         cmdlineP->input_filespec = "-";
     else if (argc-1 != 1)
         pm_error("Program takes zero or one argument (filename).  You "
                  "specified %d", argc-1);
     else
         cmdlineP->input_filespec = argv[1];
-        
-    if (aspectSpec) { 
+
+    if (aspectSpec) {
         if (cmdlineP->aspect < 0.25  || cmdlineP->aspect > 4.21875)
             pm_error("Invalid -aspect value: %f.  "
                      "GIF allows only the range 0.25-4.0 .",
@@ -205,10 +205,10 @@ Putword(int const w, FILE * const fp) {
 static int
 closestColor(tuple         const color,
              struct pam *  const pamP,
-             struct cmap * const cmapP) {
+             struct Cmap * const cmapP) {
 /*----------------------------------------------------------------------------
    Return the colormap index of the color in the colormap *cmapP
-   that is closest to the color 'color', whose format is specified by 
+   that is closest to the color 'color', whose format is specified by
    *pamP.
 
    Also add 'color' to the colormap hash, with the colormap index we
@@ -217,7 +217,7 @@ closestColor(tuple         const color,
 -----------------------------------------------------------------------------*/
     unsigned int const nComp = pamP->depth >= 3 ? 3 : 1;
         /* Number of color components (not alpha) in 'color' */
-    
+
     unsigned int i;
     unsigned int imin, dmin;
     int fits;
@@ -234,8 +234,8 @@ closestColor(tuple         const color,
 
         if (distance < dmin) {
             dmin = distance;
-            imin = i; 
-        } 
+            imin = i;
+        }
     }
     pnm_addtotuplehash(pamP, cmapP->tuplehash, color, imin, &fits);
 
@@ -268,16 +268,16 @@ typedef struct {
         /* A bitbucket for rows we read in order to advance the file
            position.
         */
-} rowReader;
+} RowReader;
 
 
 
-static rowReader *
+static RowReader *
 rowReader_create(struct pam * const pamP,
                  pm_filepos   const rasterPos,
                  bool         const interlace) {
 
-    rowReader * rdrP;
+    RowReader * rdrP;
 
     MALLOCVAR_NOFAIL(rdrP);
 
@@ -298,7 +298,7 @@ rowReader_create(struct pam * const pamP,
 
 
 static void
-rowReader_destroy(rowReader * const rdrP) {
+rowReader_destroy(RowReader * const rdrP) {
 
     pnm_freepamrow(rdrP->discardBuffer);
     free(rdrP);
@@ -307,7 +307,7 @@ rowReader_destroy(rowReader * const rdrP) {
 
 
 static void
-rowReaderSkipRows(rowReader *  const rdrP,
+rowReaderSkipRows(RowReader *  const rdrP,
                   unsigned int const rowCount,
                   bool *       const eofP) {
 /*----------------------------------------------------------------------------
@@ -338,7 +338,7 @@ rowReaderSkipRows(rowReader *  const rdrP,
 
 
 static void
-rowReaderGotoNextInterlaceRow(rowReader * const rdrP) {
+rowReaderGotoNextInterlaceRow(RowReader * const rdrP) {
 /*----------------------------------------------------------------------------
   Position reader to the next row in the interlace pattern, assuming it
   is now positioned immediately after the current row.
@@ -351,7 +351,7 @@ rowReaderGotoNextInterlaceRow(rowReader * const rdrP) {
        MULT4PLUS2: Rows 2, 6, 10, 14, etc.
        MULT2PLUS1: Rows 1, 3, 5, 7, 9, etc.
     */
-    
+
     switch (rdrP->pass) {
     case MULT8PLUS0:
         rowReaderSkipRows(rdrP, 7, &endOfPass);
@@ -399,7 +399,7 @@ rowReaderGotoNextInterlaceRow(rowReader * const rdrP) {
 
 
 static void
-rowReaderGotoNextStraightRow(rowReader * const rdrP) {
+rowReaderGotoNextStraightRow(RowReader * const rdrP) {
 /*----------------------------------------------------------------------------
   Position reader to the next row in a straight, non-interlace
   pattern, assuming the file is now positioned immediately after the
@@ -415,7 +415,7 @@ rowReaderGotoNextStraightRow(rowReader * const rdrP) {
 
 
 static void
-rowReader_read(rowReader * const rdrP,
+rowReader_read(RowReader * const rdrP,
                tuple *     const tuplerow) {
 
     if (rdrP->eof)
@@ -424,7 +424,7 @@ rowReader_read(rowReader * const rdrP,
 
     pnm_readpamrow(&rdrP->pam, tuplerow);
     ++rdrP->nextRow;
-    
+
     if (rdrP->interlace)
         rowReaderGotoNextInterlaceRow(rdrP);
     else
@@ -437,8 +437,8 @@ static unsigned int
 gifPixel(struct pam *   const pamP,
          tuple          const tuple,
          unsigned int   const alphaPlane,
-         sample         const alphaThreshold, 
-         struct cmap *  const cmapP) {
+         sample         const alphaThreshold,
+         struct Cmap *  const cmapP) {
 /*----------------------------------------------------------------------------
    Return as *colorIndexP the colormap index of the tuple 'tuple',
    whose format is described by *pamP, using colormap *cmapP.
@@ -456,7 +456,7 @@ gifPixel(struct pam *   const pamP,
 
         pnm_lookuptuple(pamP, cmapP->tuplehash, tuple,
                         &found, &colorIndex);
-        
+
         if (!found)
             colorIndex = closestColor(tuple, pamP, cmapP);
     }
@@ -493,13 +493,13 @@ writeCommentExtension(FILE * const ofP,
     unsigned int const maxSegmentSize = 255;
 
     const char * segment;
-    
+
     fputc('!',  ofP);   /* Identifies an extension */
     fputc(0xfe, ofP);   /* Identifies a comment */
 
     /* Write it out in segments no longer than 255 characters */
-    for (segment = &comment[0]; 
-         segment < comment + strlen(comment); 
+    for (segment = &comment[0];
+         segment < comment + strlen(comment);
          segment += maxSegmentSize) {
 
         unsigned int const lengthThisSegment =
@@ -546,20 +546,30 @@ writeCommentExtension(FILE * const ofP,
  */
 
 
-static stringCode const maxCodeLimitLzw = (stringCode)1 << BITS;
-       /* One beyond the largest string code that can exist in GIF */ 
+static StringCode const maxCodeLimitLzw = (StringCode)1 << BITS;
+       /* One beyond the largest string code that can exist in GIF */
        /* Used only in assertions  */
 
 
-struct hashTableEntry {
-    stringCode fcode;   /* -1 means unused slot */
-    unsigned int ent;
-};    
+struct HashTableEntry {
+    /* This is an entry in the string table, which is a hash table.  It says
+       that the string code 'combinedString' represents the string which is
+       the single pixel 'additionalPixel' appended to 'baseString', where
+       'baseString' may represent a multi-pixel string.
+    */
+    bool present;
+        /* There is an entry here.  Following members are meaningless if
+           not.
+        */
+    StringCode baseString;
+    StringCode additionalPixel;
+    StringCode combinedString;
+};
 
 
 
 /***************************************************************************
-*                          BYTE OUTPUTTER                 
+*                          BYTE OUTPUTTER
 ***************************************************************************/
 
 typedef struct {
@@ -568,14 +578,14 @@ typedef struct {
         /* Number of bytes so far in the current data block */
     unsigned char buffer[256];
         /* The current data block, under construction */
-} byteBuffer;
+} ByteBuffer;
 
 
 
-static byteBuffer *
+static ByteBuffer *
 byteBuffer_create(FILE * const fileP) {
 
-    byteBuffer * byteBufferP;
+    ByteBuffer * byteBufferP;
 
     MALLOCVAR_NOFAIL(byteBufferP);
 
@@ -588,7 +598,7 @@ byteBuffer_create(FILE * const fileP) {
 
 
 static void
-byteBuffer_destroy(byteBuffer * const byteBufferP) {
+byteBuffer_destroy(ByteBuffer * const byteBufferP) {
 
     free(byteBufferP);
 }
@@ -596,9 +606,9 @@ byteBuffer_destroy(byteBuffer * const byteBufferP) {
 
 
 static void
-byteBuffer_flush(byteBuffer * const byteBufferP) {
+byteBuffer_flush(ByteBuffer * const byteBufferP) {
 /*----------------------------------------------------------------------------
-   Write the current data block to the output file, then reset the current 
+   Write the current data block to the output file, then reset the current
    data block to empty.
 -----------------------------------------------------------------------------*/
     if (byteBufferP->count > 0 ) {
@@ -613,10 +623,10 @@ byteBuffer_flush(byteBuffer * const byteBufferP) {
 
 
 static void
-byteBuffer_flushFile(byteBuffer * const byteBufferP) {
-    
+byteBuffer_flushFile(ByteBuffer * const byteBufferP) {
+
     fflush(byteBufferP->fileP);
-    
+
     if (ferror(byteBufferP->fileP))
         pm_error("error writing output file");
 }
@@ -624,7 +634,7 @@ byteBuffer_flushFile(byteBuffer * const byteBufferP) {
 
 
 static void
-byteBuffer_out(byteBuffer *  const byteBufferP,
+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 255
@@ -638,39 +648,40 @@ byteBuffer_out(byteBuffer *  const byteBufferP,
 
 
 /***************************************************************************
-*                          GIF CODE OUTPUTTER                 
+*                          GIF CODE OUTPUTTER
 ***************************************************************************/
 
 typedef struct {
-    byteBuffer * byteBufferP;
+    ByteBuffer * byteBufferP;
     unsigned int initBits;
     unsigned int nBits;
         /* Number of bits to put in output for each code */
-    stringCode maxCode;                  /* maximum code, given n_bits */
-    stringCode maxCodeLimit;
+    StringCode maxCode;                  /* maximum code, given n_bits */
+    StringCode maxCodeLimit;
         /* LZW: One beyond the largest string code that can exist in GIF.
            Uncompressed: a ceiling to prevent code size from ratcheting up.
            In either case, output code never reaches this value.
-        */  
+        */
     unsigned long curAccum;
-    int curBits;
-    unsigned int codeCount;
-        /* Number of codes that have been output to this buffer (doesn't
-           matter if they have gone out the other side yet or not) since
-           the last flush (or ever, if no last flush).  The main use of this
-           is debugging -- when something fails, you can see in a debugger
-           where in the image it was, then set a trap for there.
+    unsigned int curBits;
+    unsigned int stringCount;
+        /* Number of strings that have been output to this buffer (by writing
+           a string code) since the last flush (or ever, if no last flush).
+           Note that this counts only strings that go into the buffer; it
+           doesn't matter if they have gone out the other side yet.  The main
+           use of this is debugging -- when something fails, you can see in a
+           debugger where in the image it was, then set a trap for there.
         */
-} codeBuffer;
+} CodeBuffer;
 
 
 
-static codeBuffer *
+static CodeBuffer *
 codeBuffer_create(FILE *       const ofP,
                   unsigned int const initBits,
                   bool         const lzw) {
 
-    codeBuffer * codeBufferP;
+    CodeBuffer * codeBufferP;
 
     MALLOCVAR_NOFAIL(codeBufferP);
 
@@ -678,11 +689,11 @@ codeBuffer_create(FILE *       const ofP,
     codeBufferP->nBits       = codeBufferP->initBits;
     codeBufferP->maxCode     = (1 << codeBufferP->nBits) - 1;
     codeBufferP->maxCodeLimit = lzw ?
-        (stringCode)1 << BITS : (stringCode) (1 << codeBufferP->nBits) - 1; 
+        (StringCode)1 << BITS : (StringCode) (1 << codeBufferP->nBits) - 1;
     codeBufferP->byteBufferP = byteBuffer_create(ofP);
     codeBufferP->curAccum    = 0;
     codeBufferP->curBits     = 0;
-    codeBufferP->codeCount   = 0;
+    codeBufferP->stringCount = 0;
 
     return codeBufferP;
 }
@@ -690,7 +701,7 @@ codeBuffer_create(FILE *       const ofP,
 
 
 static void
-codeBuffer_destroy(codeBuffer * const codeBufferP) {
+codeBuffer_destroy(CodeBuffer * const codeBufferP) {
 
     byteBuffer_destroy(codeBufferP->byteBufferP);
 
@@ -700,7 +711,7 @@ codeBuffer_destroy(codeBuffer * const codeBufferP) {
 
 
 static void
-codeBuffer_resetCodeSize(codeBuffer * const codeBufferP) {
+codeBuffer_resetCodeSize(CodeBuffer * const codeBufferP) {
 
     codeBufferP->nBits = codeBufferP->initBits;
 
@@ -712,7 +723,7 @@ codeBuffer_resetCodeSize(codeBuffer * const codeBufferP) {
 
 
 static void
-codeBuffer_increaseCodeSize(codeBuffer * const codeBufferP) {
+codeBuffer_increaseCodeSize(CodeBuffer * const codeBufferP) {
 
     ++codeBufferP->nBits;
 
@@ -721,18 +732,16 @@ codeBuffer_increaseCodeSize(codeBuffer * const codeBufferP) {
     codeBufferP->maxCode = (1 << codeBufferP->nBits) - 1;
 }
 
+
+
 static void
-codeBuffer_output(codeBuffer * const codeBufferP,
-                  stringCode   const code) {
+codeBuffer_output(CodeBuffer * const codeBufferP,
+                  StringCode   const code) {
 /*----------------------------------------------------------------------------
    Output one GIF code to the file, through the code buffer.
 
    The code is represented as N bits in the file -- the lower
    N bits of 'code'.  N is a the current code size of *codeBufferP.
-   
-   Id 'code' is the maximum possible code for the current code size
-   for *codeBufferP, increase that code size (unless it's already 
-   maxed out).
 -----------------------------------------------------------------------------*/
     assert (code <= codeBufferP->maxCode);
 
@@ -752,13 +761,13 @@ codeBuffer_output(codeBuffer * const codeBufferP,
         codeBufferP->curBits -= 8;
     }
 
-    ++codeBufferP->codeCount;
+    ++codeBufferP->stringCount;
 }
 
 
 
 static void
-codeBuffer_flush(codeBuffer * const codeBufferP) {
+codeBuffer_flush(CodeBuffer * const codeBufferP) {
 
     /* Output the possible partial byte in the buffer */
 
@@ -768,19 +777,19 @@ codeBuffer_flush(codeBuffer * const codeBufferP) {
         codeBufferP->curBits = 0;
     }
     byteBuffer_flush(codeBufferP->byteBufferP);
-    
+
     byteBuffer_flushFile(codeBufferP->byteBufferP);
 
     if (verbose)
         pm_message("%u strings of pixels written to file",
-                   codeBufferP->codeCount);
-    codeBufferP->codeCount = 0;
+                   codeBufferP->stringCount);
+    codeBufferP->stringCount = 0;
 }
 
 
 
 typedef struct {
-    codeBuffer * codeBufferP;
+    CodeBuffer * codeBufferP;
         /* The place to which we write our string codes.
 
            Constant.
@@ -791,12 +800,16 @@ typedef struct {
            proper data, but always using one code per pixel, and therefore
            not effecting any compression and not using the LZW patent.
         */
+    bool noclear;
+        /* Never put a clear code in the output.  Ergo don't recompute the
+           string table from current input.  When the string table fills up,
+           continue using that table for the rest of the image.
+        */
     unsigned int hsize;
         /* The number of slots in the hash table.  This variable to
            enhance overall performance by reducing memory use when
-           encoding smaller gifs. 
+           encoding smaller gifs.
          */
-        
     unsigned int hshift;
         /* This is how many bits we shift left a string code in forming the
            primary hash of the concatenation of that string with another.
@@ -810,37 +823,32 @@ typedef struct {
        represents a string of pixels that is defined by the preceding
        stream.
     */
-    stringCode clearCode;
+    StringCode clearCode;
         /* The code in an LZW stream that means to clear the string
            dictionary and start fresh.
 
            Constant.
         */
-    stringCode eofCode;
+    StringCode eofCode;
         /* The code in an LZW stream that means there's no more coming
 
            Constant.
         */
-    stringCode initCodeLimit;
+    StringCode initCodeLimit;
         /* The value of 'codeLimit' at the start of a block.
 
            Constant.
         */
-
-    stringCode codeLimit;
+    StringCode codeLimit;
         /* One beyond the maximum code possible with the current code
            size.
         */
-
-    struct hashTableEntry * hashTable;
-    stringCode nextUnusedCode;
-        /* Numerically next code available to assign to a a multi-pixel
-           string.  Note that codes for multi-pixel strings are in the
-           upper half of the range of codes, always greater than
-           'clearCode'.
+    struct HashTableEntry * hashTable;
+    StringCode nextCodeToDefine;
+        /* The next string code the GIF protocol will define.  It will do this
+           the next time we emit a string code.
         */
-
-    stringCode stringSoFar;
+    StringCode stringSoFar;
         /* The code for the string we have built so far.  This code indicates
            one or more pixels that we have encoded but not yet output
            because we're hoping to match an even longer string.
@@ -853,16 +861,20 @@ typedef struct {
         /* We are in the middle of building a string; 'stringSoFar' describes
            the pixels in it so far.  The only time this is false is at the
            very beginning of the stream.
- 
-           Ignored in the non-lzw case. 
+
+           Ignored in the non-lzw case.
+        */
+    bool reportedNoclear;
+        /* We have reported to Standard Error that the string table filled up
+           and we elected not to clear it.
         */
-} lzwCompressor;
+} LzwCompressor;
 
 
 
 
 static unsigned int
-nSignificantBits( unsigned int const arg ){
+nSignificantBits(unsigned int const arg){
 
 #if HAVE_GCC_BITCOUNT
 
@@ -880,37 +892,39 @@ nSignificantBits( unsigned int const arg ){
 
 
 
-static lzwCompressor *
+static LzwCompressor *
 lzw_create(FILE *       const ofP,
            unsigned int const initBits,
            bool         const lzw,
+           bool         const noclear,
            unsigned int const pixelCount) {
 
     unsigned int const hsizeTable[] = {257, 521, 1031, 2053, 4099, 5003};
     /* If the image has 4096 or fewer pixels we use prime numbers slightly
        above powers of two between 8 and 12.  In this case the hash table
        never fills up; clear code is never emitted.
-    
+
        Above that we use a table with 4096 slots plus 20% extra.
        When this is not enough the clear code is emitted.
        Because of the extra 20% the table itself never fills up.
-       
+
        lzw.hsize and lzw.hshift stay constant through the image.
 
        Variable hsize is a performance enhancement based on the fact that
        the encoder never needs more codes than the number of pixels in
        the image.  Typically, the ratio of pixels to codes is around
        10:1 to 20:1.
-   
+
        Logic works with fixed values lzw.hsize=5003 and t=13.
     */
 
-    lzwCompressor * lzwP;
-       
+    LzwCompressor * lzwP;
+
     MALLOCVAR_NOFAIL(lzwP);
 
     /* Constants */
-    lzwP->lzw = lzw;
+    lzwP->lzw     = lzw;
+    lzwP->noclear = noclear;
 
     lzwP->clearCode     = 1 << (initBits - 1);
     lzwP->eofCode       = lzwP->clearCode + 1;
@@ -920,18 +934,18 @@ lzw_create(FILE *       const ofP,
         unsigned int const t =
             MIN(13, MAX(8, nSignificantBits(pixelCount +lzwP->eofCode - 2)));
             /* Index into hsizeTable */
-    
+
         lzwP->hsize = hsizeTable[t-8];
 
         lzwP->hshift = (t == 13 ? 12 : t) - nSignificantBits(MAXCMAPSIZE-1);
 
         MALLOCARRAY(lzwP->hashTable, lzwP->hsize);
-        
+
         if (lzwP->hashTable == NULL)
             pm_error("Couldn't get memory for %u-entry hash table.",
                      lzwP->hsize);
     } else {
-        /* No LZW compression.  We don't need a stringcode hash table */  
+        /* No LZW compression.  We don't need a stringcode hash table */
         lzwP->hashTable = NULL;
         lzwP->hsize     = 0;
     }
@@ -940,13 +954,15 @@ lzw_create(FILE *       const ofP,
 
     lzwP->codeBufferP = codeBuffer_create(ofP, initBits, lzw);
 
+    lzwP->reportedNoclear = false;
+
     return lzwP;
 }
 
 
 
 static void
-lzw_destroy(lzwCompressor * const lzwP) {
+lzw_destroy(LzwCompressor * const lzwP) {
 
     codeBuffer_destroy(lzwP->codeBufferP);
 
@@ -958,24 +974,38 @@ lzw_destroy(lzwCompressor * const lzwP) {
 
 
 static void
-lzwHashClear(lzwCompressor * const lzwP) {
+lzwHashClear(LzwCompressor * const lzwP) {
 
     /* Empty the code table */
 
     unsigned int i;
 
     for (i = 0; i < lzwP->hsize; ++i)
-        lzwP->hashTable[i].fcode = -1;
+        lzwP->hashTable[i].present = false;
+
+    lzwP->nextCodeToDefine = lzwP->clearCode + 2;
+}
+
+
 
-    lzwP->nextUnusedCode = lzwP->clearCode + 2;
+static void
+lzw_reportNoclear(LzwCompressor * const lzwP) {
+
+    if (verbose && !lzwP->reportedNoclear) {
+        pm_message("String table filled up.  Not starting a new one "
+                   "because of noclear mode");
+        lzwP->reportedNoclear = true;
+    }
 }
 
 
 
 static void
-lzw_clearBlock(lzwCompressor * const lzwP) {
+lzw_clearBlock(LzwCompressor * const lzwP) {
 /*----------------------------------------------------------------------------
-  
+  Insert a string table clear in the stream.  Clear our table and set it up to
+  start building again, and emit the code to tell the decoder we're doing it
+  so he can do the same.
 -----------------------------------------------------------------------------*/
     lzwHashClear(lzwP);
 
@@ -989,8 +1019,8 @@ lzw_clearBlock(lzwCompressor * const lzwP) {
 
 
 static void
-lzwAdjustCodeSize(lzwCompressor * const lzwP,
-                  stringCode      const newCode) {
+lzwAdjustCodeSize(LzwCompressor * const lzwP,
+                  StringCode      const newCode) {
 /*----------------------------------------------------------------------------
    Assuming we just defined code 'newCode', increase the code size as
    required so that this code fits.
@@ -1011,64 +1041,67 @@ lzwAdjustCodeSize(lzwCompressor * const lzwP,
 
 
 static void
-lzwOutputCurrentString(lzwCompressor * const lzwP) {
+lzwOutputCurrentString(LzwCompressor * const lzwP) {
 /*----------------------------------------------------------------------------
    Put a code for the currently built-up string in the output stream.
 
-   Doing this causes a new string code to be defined (code is
-   lzwP->nextUnusedCode), so Caller must add that to the hash.  If
-   that code's size is beyond the overall limit, we reset the hash
-   (which means future codes will start back at the minimum size) and
-   put a clear code in the stream to tell the decompressor to do the
-   same.  So Caller must add it to the hash _before_ calling us.
-
-   Note that in the non-compressing case, the overall limit is small
-   enough to prevent us from ever defining string codes; we'll always
-   reset the hash.
-
-   There's an odd case that always screws up any attempt to make this
-   code cleaner: At the end of the LZW stream, you have to output the
-   code for the final string even though you don't have a following
-   pixel that would make a longer string.  So there's nothing to add
-   to the hash table and no point in allocating a new string code.
-   But the decompressor doesn't know that we're done, so he allocates
-   the next string code and may therefore increase his code length.
-   If we don't do the same, we will write our one last code -- the EOF
-   code -- in a code length smaller than what the decompressor is
-   expecting, and he will have a premature end of stream.
-
-   So this subroutine does run for that final code flush and does some
-   of the motions of defining a new string code, but this subroutine
-   can't update the hash because in that particular case, there's
-   nothing to add.
+   Doing this causes the protocol to define a new string code, defined as the
+   string we put plus the first pixel of the next string we put.  (It almost
+   seems to violate causality, especially since the next string we put can
+   legally be the very string code that gets defined here, but it actually
+   works).
+
+   The code that gets defined is lzwP->nextCodeToDefine.  Caller is
+   responsible for figuring out the value for the code (i.e. what we just said
+   above) so it can use the code in the future.
+
+   BUT: if the string table has reached its maximum size, issue a clear code
+   instead to cause the protocol to forget all the defined string coes and
+   start its table over (so Caller must start its own table over too).
+   EXCEPT: if we're running in no-clear mode; then we skip the clear code (so
+   the protocol maintains all the string code definitions and caller will have
+   to do so as well).
+
+   Note that in the non-compressing case, the overall limit is small enough to
+   prevent us from ever defining string codes; we'll always issue the clear
+   code.
+
+   Note that there is a case where Caller can just ignore the fact that we
+   cause the protocol to define a new string code: where the string we're
+   outputting is the last one in the stream.  In that case, the new string
+   code we define is irrelevant; it will never be used.
 -----------------------------------------------------------------------------*/
     codeBuffer_output(lzwP->codeBufferP, lzwP->stringSoFar);
-    if (lzwP->nextUnusedCode < lzwP->codeBufferP->maxCodeLimit) {
-        /* Allocate the code for the extended string, which Caller
-           should have already put in the hash so he can use it in the
-           future.  Decompressor knows when it sees the code output
-           above to define a string on its end too, using the same
-           string code we do.
-        */
-        stringCode const newCode = lzwP->nextUnusedCode++;
 
-        /* This code may be too big to fit in the current code size, in
-           which case we have to increase the code size (and decompressor
-           will do the same).
+    if (lzwP->nextCodeToDefine < lzwP->codeBufferP->maxCodeLimit) {
+        /* Record that the protocol defined a new string code, to wit
+           the numerically next one, when we did the output to the stream
+           above, and adjust the code size if this code is too wide to
+           fit in the current size.
         */
+        StringCode const newCode = lzwP->nextCodeToDefine++;
+
         lzwAdjustCodeSize(lzwP, newCode);
     } else {
-        /* Forget all the strings so far; start building again; tell
-           decompressor to do the same.
-        */
-        lzw_clearBlock(lzwP);
+        if (lzwP->noclear)
+            lzw_reportNoclear(lzwP);
+        else {
+            /* Forget all the strings so far; start building again; tell
+               decompressor to do the same.
+            */
+            lzw_clearBlock(lzwP);
+
+            if (verbose)
+                pm_message("String table filled up.  "
+                           "Clearing and starting over");
+        }
     }
 }
 
 
 
 static void
-lzw_flush(lzwCompressor * const lzwP) {
+lzw_flush(LzwCompressor * const lzwP) {
 
     if (lzwP->lzw)
         lzwOutputCurrentString(lzwP);
@@ -1082,8 +1115,8 @@ lzw_flush(lzwCompressor * const lzwP) {
 
 
 static unsigned int
-primaryHash(stringCode   const baseString,
-            stringCode   const additionalPixel,
+primaryHash(StringCode   const baseString,
+            StringCode   const additionalPixel,
             unsigned int const hshift) {
 
     unsigned int hash;
@@ -1092,71 +1125,76 @@ primaryHash(stringCode   const baseString,
     assert(additionalPixel < MAXCMAPSIZE);
 
     hash = (additionalPixel << hshift) ^ baseString;
-    
+
     return hash;
 }
 
-    
+
 
 static void
-lookupInHash(lzwCompressor *  const lzwP,
+lookupInHash(LzwCompressor *  const lzwP,
              unsigned int     const gifPixel,
-             stringCode       const fcode,
              bool *           const foundP,
-             unsigned short * const codeP,
+             StringCode *     const codeP,
              unsigned int *   const hashP) {
 
-    int disp;
+    unsigned int disp;
         /* secondary hash stride (after G. Knott) */
-    int i;
+    unsigned int hash;
         /* Index into hash table */
 
-    i = primaryHash(lzwP->stringSoFar, gifPixel, lzwP->hshift);
-    disp = (i == 0) ? 1 : lzwP->hsize - i;
-
-    while (lzwP->hashTable[i].fcode != fcode &&
-           lzwP->hashTable[i].fcode >= 0) {
-        i -= disp;
-        if (i < 0)
-            i += lzwP->hsize;
+    hash = primaryHash(lzwP->stringSoFar, gifPixel, lzwP->hshift);
+    assert(hash < lzwP->hsize);
+    disp = (hash == 0) ? 1 : lzwP->hsize - hash;
+
+    while (lzwP->hashTable[hash].present &&
+           (lzwP->hashTable[hash].baseString != lzwP->stringSoFar ||
+            lzwP->hashTable[hash].additionalPixel != gifPixel)) {
+        if (hash < disp)
+            hash += lzwP->hsize;
+        assert(hash >= disp);
+        hash -= disp;
+        assert(hash < lzwP->hsize);
     }
 
-    if (lzwP->hashTable[i].fcode == fcode) {
+    if (lzwP->hashTable[hash].present) {
         /* Found fcode in hash table */
-        *foundP = TRUE;
-        *codeP = lzwP->hashTable[i].ent;
+        *foundP = true;
+        *codeP  = lzwP->hashTable[hash].combinedString;
     } else {
         /* Found where it _should_ be (but it's not) with primary hash */
-        *foundP = FALSE;
-        *hashP = i;
+        *foundP = false;
+        *hashP  = hash;
     }
 }
 
 
 
 static void
-lzw_encodePixel(lzwCompressor * const lzwP,
+lzw_encodePixel(LzwCompressor * const lzwP,
                 unsigned int    const gifPixel) {
 
-    bool found;
-    unsigned short code;
-    unsigned int hash;
-        /* Index into hash table where the value should go */
-    
     assert(gifPixel < 256);
 
     if (!lzwP->buildingString) {
         /* Start a new string with just this pixel */
         lzwP->stringSoFar = gifPixel;
-        lzwP->buildingString = TRUE;
+        lzwP->buildingString = true;
     } else {
-        stringCode const fcode =
-            ((stringCode) gifPixel << BITS) + lzwP->stringSoFar;
-            /* The encoding of the string we've already recognized plus the
-               instant pixel, to be looked up in the hash of known strings.
+        bool found;
+            /* There's a code for the current string in the string table */
+        StringCode code;
+            /* Existing code for the current string in the string table, if
+               any
+            */
+        unsigned int hash;
+            /* Index into hash table where the entry for the new string code
+               should go; meaningless if we don't need a new string code
+               (because there's already one in the hash table for the
+               current string)
             */
-    
-        lookupInHash(lzwP, gifPixel, fcode, &found, &code, &hash);
+
+        lookupInHash(lzwP, gifPixel, &found, &code, &hash);
 
         if (found)
             /* With this new pixel, it is still a known string; 'code' is
@@ -1164,14 +1202,22 @@ lzw_encodePixel(lzwCompressor * const lzwP,
             */
             lzwP->stringSoFar = code;
         else {
-            /* It's no longer a known string.  Output the code for the
-               known prefix of the string, thus defining a new string
-               code for possible later use.  Warning:
-               lzwOutputCurrentString() does more than you think. 
+            /* We've found the longest prefix of the rest of the image for
+               which we have a string code defined.  Output the code for that
+               prefix, thus defining a new string code in the protocol for
+               possible later use.  The new code is defined as this string
+               plus the first pixel of the next string.
+
+               But if there aren't any unused string codes left, outputting
+               our string doesn't define any new string code.
             */
 
-            lzwP->hashTable[hash].ent   = lzwP->nextUnusedCode;
-            lzwP->hashTable[hash].fcode = fcode;
+            if (lzwP->nextCodeToDefine < lzwP->codeBufferP->maxCodeLimit) {
+                lzwP->hashTable[hash].present         = true;
+                lzwP->hashTable[hash].baseString      = lzwP->stringSoFar;
+                lzwP->hashTable[hash].additionalPixel = gifPixel;
+                lzwP->hashTable[hash].combinedString  = lzwP->nextCodeToDefine;
+            }
 
             lzwOutputCurrentString(lzwP);
 
@@ -1184,8 +1230,6 @@ lzw_encodePixel(lzwCompressor * const lzwP,
 
 
 /*
- * compress stdin to stdout
- *
  * Algorithm:  use open addressing double hashing (no chaining) on the
  * prefix code / next character combination.  We do a variant of Knuth's
  * algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
@@ -1200,23 +1244,25 @@ lzw_encodePixel(lzwCompressor * const lzwP,
  */
 
 static void
-writePixelUncompressed(lzwCompressor * const lzwP,
+writePixelUncompressed(LzwCompressor * const lzwP,
                        unsigned int    const gifPixel) {
-                      
+
     lzwP->stringSoFar = gifPixel;
     lzwOutputCurrentString(lzwP);
 
-}    
+}
+
 
 static void
 writeRaster(struct pam *  const pamP,
-            rowReader *   const rowReaderP,
+            RowReader *   const rowReaderP,
             unsigned int  const alphaPlane,
             unsigned int  const alphaThreshold,
-            struct cmap * const cmapP, 
+            struct Cmap * const cmapP,
             unsigned int  const initBits,
             FILE *        const ofP,
-            bool          const lzw) {
+            bool          const lzw,
+            bool          const noclear) {
 /*----------------------------------------------------------------------------
    Write the raster to file 'ofP'.
 
@@ -1228,8 +1274,12 @@ writeRaster(struct pam *  const pamP,
 
    Write the raster using LZW compression, or uncompressed depending
    on 'lzw'.
+
+   If 'noclear', don't use any GIF clear codes in the output; i.e. don't
+   recompute the string table from current input.  Once the string table gets
+   to maximum size, just keep using that table for the rest of the image.
 -----------------------------------------------------------------------------*/
-    lzwCompressor * lzwP;
+    LzwCompressor * lzwP;
     tuple * tuplerow;
     unsigned int nRowsDone;
         /* Number of rows we have read so far from the the input (the
@@ -1237,15 +1287,15 @@ writeRaster(struct pam *  const pamP,
            in case of interlace, this is not the same thing as the row
            number of the current row.
         */
-    
-    lzwP = lzw_create(ofP, initBits, lzw, pamP->height * pamP->width);
+
+    lzwP = lzw_create(ofP, initBits, lzw, noclear, pamP->height * pamP->width);
 
     tuplerow = pnm_allocpamrow(pamP);
 
     lzw_clearBlock(lzwP);
 
     nRowsDone = 0;
-    
+
     while (nRowsDone < pamP->height) {
         unsigned int col;
 
@@ -1255,14 +1305,14 @@ writeRaster(struct pam *  const pamP,
             unsigned int const colorIndex =
                 gifPixel(pamP, tuplerow[col], alphaPlane, alphaThreshold,
                          cmapP);
-            
+
                 /* The value for the pixel in the GIF image.  I.e. the colormap
                    index.
                 */
             if (lzw)
                 lzw_encodePixel(lzwP, colorIndex);
             else
-                writePixelUncompressed(lzwP, colorIndex);    
+                writePixelUncompressed(lzwP, colorIndex);
         }
         ++nRowsDone;
     }
@@ -1272,7 +1322,7 @@ writeRaster(struct pam *  const pamP,
     lzw_flush(lzwP);
 
     pnm_freepamrow(tuplerow);
-    
+
     lzw_destroy(lzwP);
 }
 
@@ -1280,7 +1330,7 @@ writeRaster(struct pam *  const pamP,
 
 static void
 writeGlobalColorMap(FILE *              const ofP,
-                    const struct cmap * const cmapP,
+                    const struct Cmap * const cmapP,
                     unsigned int        const bitsPerPixel) {
 /*----------------------------------------------------------------------------
   Write out the Global Color Map
@@ -1326,16 +1376,16 @@ writeGlobalColorMap(FILE *              const ofP,
     }
     pnm_freepamtuple(tupleRgb255);
 }
-        
+
 
 
 static void
 writeGifHeader(FILE *              const ofP,
                unsigned int        const width,
-               unsigned int        const height, 
-               unsigned int        const background, 
+               unsigned int        const height,
+               unsigned int        const background,
                unsigned int        const bitsPerPixel,
-               const struct cmap * const cmapP,
+               const struct Cmap * const cmapP,
                char                const comment[],
                float               const aspect) {
 
@@ -1371,12 +1421,12 @@ writeGifHeader(FILE *              const ofP,
 
     {
         int const aspectValue = aspect == 1.0 ? 0 : ROUND(aspect * 64) - 15;
-        assert(0 <= aspectValue && aspectValue <= 255); 
+        assert(0 <= aspectValue && aspectValue <= 255);
         fputc(aspectValue, ofP);
     }
     writeGlobalColorMap(ofP, cmapP, bitsPerPixel);
 
-    if (cmapP->haveTransparent) 
+    if (cmapP->haveTransparent)
         writeTransparentColorIndexExtension(ofP, cmapP->transparent);
 
     if (comment)
@@ -1430,15 +1480,16 @@ reportImageInfo(bool         const interlace,
 
 static void
 gifEncode(struct pam *  const pamP,
-          FILE *        const ofP, 
+          FILE *        const ofP,
           pm_filepos    const rasterPos,
           bool          const gInterlace,
-          int           const background, 
+          int           const background,
           unsigned int  const bitsPerPixel,
-          struct cmap * const cmapP,
+          struct Cmap * const cmapP,
           char          const comment[],
           float         const aspect,
-          bool          const lzw) {
+          bool          const lzw,
+          bool          const noclear) {
 
     unsigned int const leftOffset = 0;
     unsigned int const topOffset  = 0;
@@ -1453,14 +1504,14 @@ gifEncode(struct pam *  const pamP,
 
     unsigned int const alphaPlane = pamAlphaPlane(pamP);
 
-    rowReader * rowReaderP;
+    RowReader * rowReaderP;
 
     reportImageInfo(gInterlace, background, bitsPerPixel);
 
     if (pamP->width > 65535)
         pm_error("Image width %u too large for GIF format.  (Max 65535)",
                  pamP->width);
-     
+
     if (pamP->height > 65535)
         pm_error("Image height %u too large for GIF format.  (Max 65535)",
                  pamP->height);
@@ -1479,7 +1530,7 @@ gifEncode(struct pam *  const pamP,
     /* Write the actual raster */
 
     writeRaster(pamP, rowReaderP, alphaPlane, alphaThreshold,
-                cmapP, initCodeSize + 1, ofP, lzw);
+                cmapP, initCodeSize + 1, ofP, lzw, noclear);
 
     rowReader_destroy(rowReaderP);
 
@@ -1493,7 +1544,7 @@ gifEncode(struct pam *  const pamP,
 
 
 static void
-reportTransparent(struct cmap * const cmapP) {
+reportTransparent(struct Cmap * const cmapP) {
 
     if (verbose) {
         if (cmapP->haveTransparent) {
@@ -1511,10 +1562,10 @@ reportTransparent(struct cmap * const cmapP) {
 
 
 static void
-computeTransparent(char          const colorarg[], 
+computeTransparent(char          const colorarg[],
                    bool          const usingFakeTrans,
                    unsigned int  const fakeTransparent,
-                   struct cmap * const cmapP) {
+                   struct Cmap * const cmapP) {
 /*----------------------------------------------------------------------------
    Figure out the color index (index into the colormap) of the color
    that is to be transparent in the GIF.
@@ -1546,7 +1597,7 @@ computeTransparent(char          const colorarg[],
         tuple transcolor;
         int found;
         int colorindex;
-        
+
         if (colorarg[0] == '=') {
             colorspec = &colorarg[1];
             exact = TRUE;
@@ -1558,7 +1609,7 @@ computeTransparent(char          const colorarg[],
         transcolor = pnm_parsecolor(colorspec, cmapP->pam.maxval);
         pnm_lookuptuple(&cmapP->pam, cmapP->tuplehash, transcolor, &found,
                         &colorindex);
-        
+
         if (found) {
             cmapP->haveTransparent = TRUE;
             cmapP->transparent = colorindex;
@@ -1602,7 +1653,7 @@ sortCompareColor(const void * const entry1P,
     struct tupleint * const * const tupleint1PP = entry1P;
     struct tupleint * const * const tupleint2PP = entry2P;
 
-    return (sortOrderColor((*tupleint1PP)->tuple) 
+    return (sortOrderColor((*tupleint1PP)->tuple)
             - sortOrderColor((*tupleint2PP)->tuple));
 }
 
@@ -1640,15 +1691,15 @@ sortTupletable(struct pam * const mapPamP,
     if (mapPamP->depth < 3)
         qsort(tuplefreq, colors, sizeof(tuplefreq[0]), sortCompareGray);
     else
-        qsort(tuplefreq, colors, sizeof(tuplefreq[0]), sortCompareColor); 
+        qsort(tuplefreq, colors, sizeof(tuplefreq[0]), sortCompareColor);
 
 }
 
 
 
 static void
-addToColormap(struct cmap *  const cmapP, 
-              const char *   const colorspec, 
+addToColormap(struct Cmap *  const cmapP,
+              const char *   const colorspec,
               unsigned int * const newIndexP) {
 /*----------------------------------------------------------------------------
   Add a new entry to the colormap.  Make the color that specified by
@@ -1685,7 +1736,7 @@ addToColormap(struct cmap *  const cmapP,
 static void
 colormapFromFile(char               const filespec[],
                  unsigned int       const maxcolors,
-                 tupletable *       const tupletableP, 
+                 tupletable *       const tupletableP,
                  struct pam *       const mapPamP,
                  unsigned int *     const colorCountP) {
 /*----------------------------------------------------------------------------
@@ -1703,13 +1754,13 @@ colormapFromFile(char               const filespec[],
     pm_close(mapfileP);
 
     pm_message("computing other colormap ...");
-    
-    *tupletableP = 
+
+    *tupletableP =
         pnm_computetuplefreqtable(mapPamP, colors, maxcolors, &colorCount);
 
     *colorCountP = colorCount;
 
-    pnm_freepamarray(colors, mapPamP); 
+    pnm_freepamarray(colors, mapPamP);
 }
 
 
@@ -1717,7 +1768,7 @@ colormapFromFile(char               const filespec[],
 static void
 readAndValidateColormapFromFile(char           const filename[],
                                 unsigned int   const maxcolors,
-                                tupletable *   const tuplefreqP, 
+                                tupletable *   const tuplefreqP,
                                 struct pam *   const mapPamP,
                                 unsigned int * const colorCountP,
                                 unsigned int   const nInputComp,
@@ -1753,7 +1804,7 @@ computeColormapBw(struct pam *   const pamP,
    $ pbmmake -w 600 400 | pamtogif -sort > canvas.gif
 -----------------------------------------------------------------------------*/
     tupletable const colormap = pnm_alloctupletable(pamP, 2);
-    
+
     *mapPamP = *pamP;
     mapPamP->depth = 1;
 
@@ -1761,12 +1812,12 @@ computeColormapBw(struct pam *   const pamP,
     colormap[0]->tuple[0] = PAM_BLACK;
     colormap[1]->value = 1;
     colormap[1]->tuple[0] = PAM_BW_WHITE;
-    
+
     *tuplefreqP  = colormap;
     *colorCountP = 2;
 }
-  
-    
+
+
 
 static void
 computeColormapFromInput(struct pam *   const pamP,
@@ -1775,7 +1826,7 @@ computeColormapFromInput(struct pam *   const pamP,
                          struct pam *   const mapPamP,
                          unsigned int * const colorCountP,
                          tupletable *   const tuplefreqP) {
-    
+
     tupletable tuplefreq;
 
     pm_message("computing colormap...");
@@ -1842,9 +1893,9 @@ computeLibnetpbmColormap(struct pam *   const pamP,
              pamP->height * pamP->width > 1)
         computeColormapBw(pamP, mapPamP, &colorCount, &tuplefreq);
     else
-        computeColormapFromInput(pamP, maxcolors, nInputComp, 
+        computeColormapFromInput(pamP, maxcolors, nInputComp,
                                  mapPamP, &colorCount, &tuplefreq);
-    
+
     if (tuplefreq == NULL)
         pm_error("too many colors - try doing a 'pnmquant %u'", maxcolors);
 
@@ -1870,10 +1921,10 @@ computeLibnetpbmColormap(struct pam *   const pamP,
 
 
 static void
-destroyCmap(struct cmap * const cmapP) {
+destroyCmap(struct Cmap * const cmapP) {
 
     unsigned int colorIndex;
-    
+
     for (colorIndex = 0; colorIndex < cmapP->cmapSize; ++colorIndex)
         pnm_freepamtuple(cmapP->color[colorIndex]);
 
@@ -1884,41 +1935,41 @@ destroyCmap(struct cmap * const cmapP) {
 
 int
 main(int argc, char *argv[]) {
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
     FILE * ifP;
     struct pam pam;
     unsigned int bitsPerPixel;
     pm_filepos rasterPos;
 
-    struct cmap cmap;
+    struct Cmap cmap;
         /* The colormap, with all its accessories */
     unsigned int fakeTransparent;
         /* colormap index of the fake transparency color we're using to
            implement the alpha mask.  Undefined if we're not doing an alpha
            mask.
         */
-    
+
     pnm_init(&argc, argv);
-    
+
     parseCommandLine(argc, argv, &cmdline);
-    
+
     verbose = cmdline.verbose;
-    
+
     ifP = pm_openr_seekable(cmdline.input_filespec);
-    
+
     pnm_readpaminit(ifP, &pam, PAM_STRUCT_SIZE(tuple_type));
-    
+
     pm_tell2(ifP, &rasterPos, sizeof(rasterPos));
-    
-    computeLibnetpbmColormap(&pam, !!pamAlphaPlane(&pam), cmdline.mapfile, 
+
+    computeLibnetpbmColormap(&pam, !!pamAlphaPlane(&pam), cmdline.mapfile,
                              cmap.color, &cmap.tuplehash,
                              &cmap.pam, &cmap.cmapSize, cmdline.sort);
-    
+
     assert(cmap.pam.maxval == pam.maxval);
 
     if (pamAlphaPlane(&pam)) {
-        /* Add a fake entry to the end of the colormap for transparency.  
-           Make its color black. 
+        /* Add a fake entry to the end of the colormap for transparency.
+           Make its color black.
         */
         addToColormap(&cmap, cmdline.alphacolor, &fakeTransparent);
     }
@@ -1931,13 +1982,13 @@ main(int argc, char *argv[]) {
     /* All set, let's do it. */
     gifEncode(&pam, stdout, rasterPos,
               cmdline.interlace, 0, bitsPerPixel, &cmap, cmdline.comment,
-              cmdline.aspect, !cmdline.nolzw);
-    
+              cmdline.aspect, !cmdline.nolzw, cmdline.noclear);
+
     destroyCmap(&cmap);
 
     pm_close(ifP);
     pm_close(stdout);
-    
+
     return 0;
 }
 
@@ -1955,21 +2006,21 @@ main(int argc, char *argv[]) {
   JPEG Group's djpeg on 2001.09.29.  In 2006.12 the output subroutines
   were rewritten; now no uncompressed output subroutines are derived from
   the Independent JPEG Group's source code.
-  
+
   2007.01  Changed sort routine to qsort.  (afu)
   2007.03  Implemented variable hash table size, PBM color table
            shortcut and "-aspect" command line option.   (afu)
 
- 
+
   Copyright (C) 1989 by Jef Poskanzer.
- 
+
   Permission to use, copy, modify, and distribute this software and its
   documentation for any purpose and without fee is hereby granted, provided
   that the above copyright notice appear in all copies and that both that
   copyright notice and this permission notice appear in supporting
   documentation.  This software is provided "as is" without express or
   implied warranty.
- 
+
   The Graphics Interchange Format(c) is the Copyright property of
   CompuServe Incorporated.  GIF(sm) is a Service Mark property of
   CompuServe Incorporated.
diff --git a/converter/other/pamtopdbimg.c b/converter/other/pamtopdbimg.c
index 6454292e..ce2f7659 100644
--- a/converter/other/pamtopdbimg.c
+++ b/converter/other/pamtopdbimg.c
@@ -38,6 +38,7 @@
 #include "mallocvar.h"
 #include "nstring.h"
 #include "shhopt.h"
+#include "runlength.h"
 #include "pam.h"
 
 #include "ipdb.h"
@@ -128,6 +129,8 @@ parseCommandLine(int argc, const char ** argv,
         cmdlineP->inputFileName = argv[1];
     else
         pm_error("Program takes at most one argument:  input file name");
+
+    free(option_def);
 }
 
 
@@ -238,91 +241,6 @@ textWrite(TEXT * const textP,
 
 
 
-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,
@@ -334,38 +252,29 @@ compressIfRequired(IPDB *     const pdbP,
         *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;
+        unsigned char * outbuf;
+        size_t          compressedSz;
 
-        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;
-            }
+        pm_rlenc_allocoutbuf(&outbuf, uncompressedSz, PM_RLE_PALMPDB);
+
+        pm_rlenc_compressbyte(pdbP->i->data, outbuf, PM_RLE_PALMPDB,
+                              uncompressedSz, &compressedSz);
+        if (comp == IPDB_COMPMAYBE && compressedSz >= uncompressedSz) {
+            /* Return the uncompressed data */
+            free(outbuf);
+            *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 = outbuf;
+            *compressedSizeP = compressedSz;
         }
     }
 }
@@ -742,6 +651,14 @@ readtxt(IPDB *       const pdbP,
         pm_error("stat of '%s' failed, errno = %d (%s)",
                  noteFileName, errno, strerror(errno));
 
+    /* The maximum size of a memory block that a Palm can allocate is 64K.
+       Abort with error if specified note file is any larger.
+    */
+
+    if (st.st_size + 1 >= 65535)
+        pm_error("Note file is too large: %lu bytes",
+                  (unsigned long) st.st_size);
+
     fP = pm_openr(noteFileName);
 
     MALLOCARRAY(fileContent, st.st_size + 1);
@@ -758,6 +675,8 @@ readtxt(IPDB *       const pdbP,
 
     pm_close(fP);
 
+    fileContent[st.st_size] = 0x00;  /* add terminating NUL char */
+
     /* Chop of trailing newlines */
     for (n = strlen(fileContent) - 1; n >= 0 && fileContent[n] == '\n'; --n)
         fileContent[n] = '\0';
@@ -787,6 +706,9 @@ main(int argc, const char **argv) {
     case MAYBE:        comp = IPDB_COMPMAYBE;  break;
     }
 
+    if (strlen(cmdline.title) > 31)
+        pm_error("Title too long.  Max length is 31 characters.");
+
     pdbP = ipdb_alloc(cmdline.title);
 
     if (pdbP == NULL)
diff --git a/converter/other/pamtopng.c b/converter/other/pamtopng.c
index fdeb6582..a323844f 100644
--- a/converter/other/pamtopng.c
+++ b/converter/other/pamtopng.c
@@ -1,21 +1,8 @@
-/*
-** 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.
-*/
+/*=============================================================================
+                                  pamtopng
+===============================================================================
+  Read a Netpbm image and produce a PNG (Portable Network Graphics) image.
+=============================================================================*/
 
 /*
   This Netpbm program pamtopng was derived in 2015 from the Netpbm program
@@ -29,18 +16,19 @@
     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
+  - 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 
+    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 <assert.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <time.h>
 #include <png.h>
 /* setjmp.h needs to be included after png.h */
 #include <setjmp.h>
@@ -61,6 +49,7 @@ static bool verbose;
 struct CmdlineInfo {
     const char * inputFileName;
     unsigned int verbose;
+    unsigned int interlace;
     unsigned int transparencySpec;
     const char * transparency;
     unsigned int chromaSpec;
@@ -88,7 +77,7 @@ 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,
@@ -107,7 +96,7 @@ parseChromaOpt(const char *         const chromaOpt,
 static void
 parseSrgbintentOpt(const char *      const srgbintentOpt,
                    pngx_srgbIntent * const srgbintentP) {
-    
+
     if (streq(srgbintentOpt, "perceptual"))
         *srgbintentP = PNGX_PERCEPTUAL;
     else if (streq(srgbintentOpt, "relativecolorimetric"))
@@ -145,7 +134,7 @@ parseTimeOpt(const char * const timeOpt,
     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)
@@ -192,7 +181,7 @@ 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 */
@@ -205,6 +194,8 @@ parseCommandLine (int                  argc,
 
     OPTENT3(0,  "verbose",      OPT_FLAG,       NULL,
             &cmdlineP->verbose,        0);
+    OPTENT3(0,  "interlace",    OPT_FLAG,       NULL,
+            &cmdlineP->interlace,      0);
     OPTENT3(0,  "transparency", OPT_STRING,     &cmdlineP->transparency,
             &cmdlineP->transparencySpec, 0);
     OPTENT3(0,  "chroma",       OPT_STRING,     &chroma,
@@ -239,7 +230,7 @@ parseCommandLine (int                  argc,
 
     if (cmdlineP->timeSpec)
         parseTimeOpt(time, &cmdlineP->time);
-    
+
     /* get the input-file or stdin pipe */
     if (argc-1 < 1)
         cmdlineP->inputFileName = "-";
@@ -256,7 +247,7 @@ parseCommandLine (int                  argc,
 static png_byte
 colorTypeFromInputType(const struct pam * const pamP) {
 /*----------------------------------------------------------------------------
-  Analyse the Netpbm image for color-type and bit-depth
+  Analyze the Netpbm image for color-type and bit-depth
 -----------------------------------------------------------------------------*/
     png_byte retval;
 
@@ -286,7 +277,7 @@ colorTypeFromInputType(const struct pam * const pamP) {
         if (pamP->depth == 2)
             retval = PNG_COLOR_TYPE_GRAY_ALPHA;
         else
-            pm_error("Input tupel type is GRAYSCALE_ALPHA, "
+            pm_error("Input tuple type is GRAYSCALE_ALPHA, "
                      "but number of planes is %u instread of 2",
                      pamP->depth);
     } else if (strneq(pamP->tuple_type, "GRAYSCALE", 9)) {
@@ -385,6 +376,8 @@ sigBitsFmImgType(unsigned int const pnmBitDepth,
             retval.gray  = pnmBitDepth;
             retval.alpha = pnmBitDepth;
             break;
+        default:
+            assert(false);
         }
     } else {
         /* PNG can (so presumably will) use original bit depth */
@@ -404,7 +397,7 @@ doTrnsChunk(const struct pam * const pamP,
             struct pngx *      const pngxP,
             const char *       const trans) {
 
-    if (pngx_colorType(pngxP) == PNG_COLOR_TYPE_GRAY_ALPHA || 
+    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 {
@@ -434,13 +427,13 @@ doTrnsChunk(const struct pam * const pamP,
 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 }", 
+                   "%4.2f, %4.2f, %4.2f, %4.2f }",
                    chroma.wx, chroma.wy,
                    chroma.rx, chroma.ry,
                    chroma.gx, chroma.gy,
@@ -450,7 +443,7 @@ doChrmChunk(struct pngx *      const pngxP,
 
 
 
-static void 
+static void
 doGamaChunk(struct pngx *  const pngxP,
             float          const gamma) {
 
@@ -500,9 +493,9 @@ doTextChunkSet(struct pngx * const pngxP,
     FILE * tfP;
 
     tfP = pm_openr(textFileName);
-    
+
     pngtxt_addChunk(pngxP, tfP, ztxt, itxt, verbose);
-    
+
     pm_close(tfP);
 }
 
@@ -518,9 +511,9 @@ doZtxtChunkSet(struct pngx * const pngxP,
     FILE * tfP;
 
     tfP = pm_openr(textFileName);
-    
+
     pngtxt_addChunk(pngxP, tfP, ztxt, itxt, verbose);
-    
+
     pm_close(tfP);
 }
 
@@ -556,11 +549,11 @@ doBkgdChunk (const struct pam * const pamP,
     pngx_setBkgdRgb(pngxP, pngColor);
 
     if (verbose) {
-        if (pngx_colorType(pngxP) == PNG_COLOR_TYPE_GRAY || 
+        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 || 
+        } 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}",
@@ -591,24 +584,10 @@ doTimeChunk(struct pngx * const pngxP,
 
 
 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) {
+convertRow(const struct pam * const pamP,
+           const tuple *      const tuplerow,
+           png_byte *         const pngRow,
+           unsigned int       const bitDepth) {
 
     unsigned int col;
 
@@ -637,9 +616,12 @@ convertRaster(const struct pam * const pamP,
 
 
 static void
-writeRaster(const struct pam * const pamP,
-            struct pngx *      const pngxP,
-            int                const bitDepth) {
+writeRasterRowByRow(const struct pam * const pamP,
+                    struct pngx *      const pngxP,
+                    int                const bitDepth) {
+
+    unsigned int const rowSz =
+        pamP->width * pamP->depth * (MAX(1, bitDepth/8));
 
     tuple * tupleRow;
     png_byte * pngRow;
@@ -649,8 +631,7 @@ writeRaster(const struct pam * const pamP,
 
     tupleRow = pnm_allocpamrow(pamP);
 
-    MALLOCARRAY(pngRow, pamP->width * 8);
-        /* sufficient to store a 16-bit RGB+A row */
+    MALLOCARRAY(pngRow, rowSz);
 
     if (pngRow == NULL)
         pm_error("Unable to allocate space for PNG pixel row for "
@@ -659,9 +640,9 @@ writeRaster(const struct pam * const pamP,
         for (row = 0; row < pamP->height; ++row) {
             pnm_readpamrow(pamP, tupleRow);
 
-            convertRaster(pamP, tupleRow, pngRow, bitDepth);
+            convertRow(pamP, tupleRow, pngRow, bitDepth);
 
-            png_write_row(pngxP->png_ptr, pngRow);
+            pngx_writeRow(pngxP, pngRow);
         }
         free(pngRow);
     }
@@ -670,40 +651,160 @@ writeRaster(const struct pam * const pamP,
 
 
 
+static png_bytep
+mallocPngImage(unsigned int const rowSize,
+               unsigned int const height) {
+
+    png_bytep pngImage;
+
+    if (UINT_MAX / rowSize < height)
+        pm_error("Image is uncomputably large at %u rows of %u bytes",
+                 height, rowSize);
+
+    MALLOCARRAY(pngImage, height * rowSize);
+
+    if (!pngImage)
+        pm_error("could not allocate %u bytes for a PNG image buffer",
+                 height * rowSize);
+
+    return pngImage;
+}
+
+
+
+static unsigned int
+pngLineSize(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:
+            samplesPerPixel = 1;
+            break;
+        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:
+            assert(false);
+    }
+
+    if (UINT_MAX / bytesPerSample / samplesPerPixel < pngx_imageWidth(pngxP)) {
+        pm_error("pngcopy: width %u of PNG is uncomputably large\n",
+                  pngx_imageWidth(pngxP));
+    }
+
+    return pngx_imageWidth(pngxP) * bytesPerSample * samplesPerPixel;
+}
+
+
+
 static void
-writePng(const struct pam * const pamP,
-         FILE *             const ofP,
-         struct CmdlineInfo const cmdline) {
+writeRasterWholeImg(struct pam *  const pamP,
+                    struct pngx * const pngxP,
+                    unsigned int  const bitDepth) {
 
-    unsigned int const pnmBitDepth = pm_maxvaltobits(pamP->maxval);
-    int const pngColorType = colorTypeFromInputType(pamP);
+    unsigned int const pngRowSize = pngLineSize(pngxP);
 
-    struct pngx * pngxP;
-    unsigned int pngBitDepth;
-    png_color_8 sBit;
+    tuple * tupleRow;
+    png_bytep pngImage;
+        /* A one-dimensional malloc'ed array of all pixels in image */
+    png_bytep * pngRowP;
+        /* A malloc'ed array of row pointers into pngImage[] */
+    unsigned int row;
 
-    pngx_create(&pngxP, PNGX_WRITE, NULL);
+    tupleRow = pnm_allocpamrow(pamP);
+
+    pngImage = mallocPngImage(pngRowSize, pamP->height);
+
+    MALLOCARRAY(pngRowP, pamP->height);
+
+    if (!pngRowP)
+        pm_error("Failed to allocate an array for %u PNG row pointers",
+                 pamP->height);
 
+    for (row = 0; row < pamP->height; ++row) {
+        png_bytep const thisPngRowP = &pngImage[row * pngRowSize];
 
+        pnm_readpamrow(pamP, tupleRow);
+
+        convertRow(pamP, tupleRow, thisPngRowP, bitDepth);
+
+        pngRowP[row] = thisPngRowP;
+    }
+
+    pngx_writeImage(pngxP, pngRowP);
+
+    free(pngRowP);
+    free(pngImage);
+    pnm_freepamrow(tupleRow);
+}
+
+
+
+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);
+}
+
+
+
+static unsigned int
+pngBitDepth(unsigned int const pnmBitDepth,
+            int          const pngColorType) {
+
+    unsigned int retval;
 
     if ((pngColorType == PNG_COLOR_TYPE_RGB ||
          pngColorType == PNG_COLOR_TYPE_RGB_ALPHA) &&
         pnmBitDepth < 8) {
 
-        pngBitDepth = 8;
+        retval = 8;
     } else
-        pngBitDepth = pnmBitDepth;
+        retval = pnmBitDepth;
 
-    png_init_io(pngxP->png_ptr, ofP);
+    return retval;
+}
 
-    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 */
+static void
+addAncillaryChunks(struct pam *       const pamP,
+                   struct pngx *      const pngxP,
+                   struct CmdlineInfo const cmdline,
+                   png_color_8        const sigBits) {
+/*----------------------------------------------------------------------------
+  Where requested, add ancillary chunks.
+-----------------------------------------------------------------------------*/
     if (cmdline.transparencySpec)
         doTrnsChunk(pamP, pngxP,cmdline.transparency);
 
@@ -715,7 +816,7 @@ writePng(const struct pam * const pamP,
 
     /* no iccp */
 
-    doSbitChunk(pamP, pngxP, sBit);
+    doSbitChunk(pamP, pngxP, sigBits);
 
     if (cmdline.srgbintentSpec)
         doSrgbChunk(pngxP, cmdline.srgbintent);
@@ -741,54 +842,96 @@ writePng(const struct pam * const pamP,
     if (cmdline.timeSpec)
         doTimeChunk(pngxP, cmdline.time);
 
-    setShift(pngxP, sBit);
-
-    /* Write the ancillary chunks to PNG file */
+    /* Write the ancillary chunks to PNG image */
     pngx_writeInfo(pngxP);
+}
+
+
+
+static void
+setShift(struct pngx * const pngxP,
+         png_color_8   const sigBits) {
+
+    if (sigBits.red + sigBits.green + sigBits.blue +
+        sigBits.gray + sigBits.alpha > 0) {
 
-    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);
+        pngx_setShift(pngxP, sigBits);
     }
+}
 
-    writeRaster(pamP, pngxP, pnmBitDepth);
 
-    pngx_writeEnd(pngxP);
-    pngx_destroy(pngxP);
-}
 
+static void
+doIhdrChunk(struct pngx * const pngxP,
+            unsigned int  const width,
+            unsigned int  const height,
+            unsigned int  const pnmBitDepth,
+            int           const pngColorType,
+            bool          const interlace) {
+
+    int const interlaceMethod =
+        interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE;
+
+    pngx_setIhdr(pngxP, width, height,
+                 pngBitDepth(pnmBitDepth, pngColorType), pngColorType,
+                 interlaceMethod, PNG_COMPRESSION_TYPE_BASE,
+                 PNG_FILTER_TYPE_BASE);
+}
 
 
 
 static void
-reportInputFormat(const struct pam * const pamP) {
+pamtopng(FILE *             const ifP,
+         FILE *             const ofP,
+         struct CmdlineInfo const cmdline) {
 
-    const char * formatDesc;
+    unsigned int  pnmBitDepth;
+    int           pngColorType;
+    struct pngx * pngxP;
+    png_color_8   sigBits;
+    struct pam    pam;
 
-    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;
+    pnm_readpaminit(ifP, &pam, PAM_STRUCT_SIZE(tuple_type));
 
-    if (formatDesc)
-        pm_message("Input format = %s", formatDesc);
-    else
-        pm_message("Unrecognized input format, format code = 0x%x",
-                   pamP->format);
+    if (verbose)
+        reportInputFormat(&pam);
 
-    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);
+    pnmBitDepth = pm_maxvaltobits(pam.maxval);
+
+    pngColorType = colorTypeFromInputType(&pam);
+
+    pngx_create(&pngxP, PNGX_WRITE, NULL);
+
+    png_init_io(pngxP->png_ptr, ofP);
+
+    doIhdrChunk(pngxP, pam.width, pam.height,
+                pnmBitDepth, pngColorType, cmdline.interlace > 0);
+
+    sigBits = sigBitsFmImgType(pnmBitDepth, pngColorType);
+
+    addAncillaryChunks(&pam, pngxP, cmdline, sigBits);
+
+    setShift(pngxP, sigBits);
+
+    if ((pngColorType == PNG_COLOR_TYPE_GRAY) && (pnmBitDepth < 8)) {
+        /* Pack multiple pixels in a byte */
+        pngx_setPacking(pngxP);
+    }
+
+    if (cmdline.interlace) {
+        /* Libpng will expect us to provide pixels in interlaced sequence
+           if we write row-by-row, and that is much to difficult, so we
+           do whole-image-at-once and let Libpng do the work.
+        */
+        writeRasterWholeImg(&pam, pngxP, pnmBitDepth);
+    } else {
+        /* We save memory by going row-by-row */
+        writeRasterRowByRow(&pam, pngxP, pnmBitDepth);
+    }
+
+    pngx_writeEnd(pngxP);
+    pngx_destroy(pngxP);
 }
 
 
@@ -799,7 +942,6 @@ main(int           argc,
 
     FILE * ifP;
     struct CmdlineInfo cmdline;
-    struct pam pam;
 
     pm_proginit(&argc, argv);
 
@@ -809,12 +951,7 @@ main(int           argc,
 
     ifP = pm_openr(cmdline.inputFileName);
 
-    pnm_readpaminit(ifP, &pam, PAM_STRUCT_SIZE(tuple_type));
-
-    if (verbose)
-        reportInputFormat(&pam);
-
-    writePng(&pam, stdout, cmdline);
+    pamtopng(ifP, stdout, cmdline);
 
     pm_close(ifP);
 
@@ -823,3 +960,18 @@ main(int           argc,
 
 
 
+/* 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.
+*/
diff --git a/converter/other/pamtopnm.c b/converter/other/pamtopnm.c
index f043d721..f0ad451a 100644
--- a/converter/other/pamtopnm.c
+++ b/converter/other/pamtopnm.c
@@ -96,7 +96,7 @@ validateTupleType(struct pam const inpam,
             /* It's a PPMable image */
         } else 
             pm_error("PAM image does not have a depth and tuple_type "
-                     "consistent with a PNM image."
+                     "consistent with a PNM image.  "
                      "According to its "
                      "header, depth is %d and tuple_type is '%s'.  "
                      "Use the -assume option to convert anyway.",
diff --git a/converter/other/pamtosvg/pxl-outline.c b/converter/other/pamtosvg/pxl-outline.c
index 456f41e1..19451c04 100644
--- a/converter/other/pamtosvg/pxl-outline.c
+++ b/converter/other/pamtosvg/pxl-outline.c
@@ -206,10 +206,8 @@ next_unmarked_pixel(unsigned int *   const row,
     direction_type const orig_dir = *dir;
 
     direction_type test_dir;
-    pixel color;
 
     test_dir = *dir;  /* initial value */
-    color = getBitmapColor(bitmap, *row, *col);
 
     do {
         if (is_valid_dir(orig_row, orig_col, test_dir, bitmap, *marked)) {
diff --git a/converter/other/pamtosvg/vector.c b/converter/other/pamtosvg/vector.c
index 0a5ef3a9..771e5f27 100644
--- a/converter/other/pamtosvg/vector.c
+++ b/converter/other/pamtosvg/vector.c
@@ -1,6 +1,6 @@
 /* vector.c: vector/point operations. */
 
-#define _XOPEN_SOURCE   /* Make sure M_PI is in <math.h> */
+#define _XOPEN_SOURCE 500  /* get M_PI 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 aca93015..1daa1e25 100644
--- a/converter/other/pamtotga.c
+++ b/converter/other/pamtotga.c
@@ -10,9 +10,11 @@
 ** implied warranty.
 */
 
+#define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE  /* Make sure string.h contains strdup() */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
+#include <assert.h>
 #include <string.h>
 
 #include "pm_c_util.h"
@@ -26,22 +28,24 @@
 /* Max number of colors allowed for colormapped output. */
 #define MAXCOLORS 256
 
-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 */
-    char *outName;
+    const char *          inputFileName;
+    const char *          outName;
     enum TGAbaseImageType imgType;
-    bool defaultFormat;
-    unsigned int norle;
+    enum TGAmapType       mapType;
+    bool                  defaultFormat;
+    unsigned int          norle;
+    unsigned int          verbose;
 };
 
 
 
 static void
-parseCommandLine(int argc, char ** argv,
-                 struct cmdlineInfo * const cmdlineP) {
+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.
@@ -56,38 +60,47 @@ parseCommandLine(int argc, char ** argv,
     unsigned int option_def_index;
 
     unsigned int outNameSpec;
-    unsigned int cmap, mono, rgb;
+    unsigned int cmap, cmap16, mono, rgb;
 
     option_def_index = 0;   /* incremented by OPTENT3 */
-    OPTENT3(0,   "name",       OPT_STRING, 
+    OPTENT3(0,   "name",       OPT_STRING,
             &cmdlineP->outName, &outNameSpec, 0);
-    OPTENT3(0,   "cmap",       OPT_FLAG, 
+    OPTENT3(0,   "cmap",       OPT_FLAG,
             NULL, &cmap, 0);
-    OPTENT3(0,   "mono",       OPT_FLAG, 
+    OPTENT3(0,   "cmap16",     OPT_FLAG,
+            NULL, &cmap16, 0);
+    OPTENT3(0,   "mono",       OPT_FLAG,
             NULL, &mono, 0);
-    OPTENT3(0,   "rgb",        OPT_FLAG, 
+    OPTENT3(0,   "rgb",        OPT_FLAG,
             NULL, &rgb, 0);
-    OPTENT3(0,   "norle",      OPT_FLAG, 
+    OPTENT3(0,   "norle",      OPT_FLAG,
             NULL, &cmdlineP->norle, 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, 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 (cmap + mono + rgb > 1)
-        pm_error("You may specify only one of -cmap, -mono, and -rgb.");
+    if (cmap + cmap16 + mono + rgb > 1)
+        pm_error("You may specify only one of -cmap, -cmap16, "
+                 "-mono, and -rgb.");
 
-    if (cmap + mono + rgb == 0)
+    if (cmap + cmap16 + mono + rgb == 0)
         cmdlineP->defaultFormat = TRUE;
     else {
         cmdlineP->defaultFormat = FALSE;
-    
-        if (cmap)
+
+        if (cmap) {
             cmdlineP->imgType = TGA_MAP_TYPE;
-        else if (mono)
+            cmdlineP->mapType = TGA_MAPTYPE_LONG;
+        } else if (cmap16) {
+            cmdlineP->imgType = TGA_MAP_TYPE;
+            cmdlineP->mapType = TGA_MAPTYPE_SHORT;
+        } else if (mono)
             cmdlineP->imgType = TGA_MONO_TYPE;
         else if (rgb)
             cmdlineP->imgType = TGA_RGB_TYPE;
@@ -95,56 +108,22 @@ parseCommandLine(int argc, char ** argv,
 
     if (!outNameSpec)
         cmdlineP->outName = NULL;
-    
-    if (argc-1 == 0) 
-        cmdlineP->inputFilespec = "-";
+
+    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->inputFilespec = argv[1];
-
-}
-
-
-static void
-writeTgaHeader(struct ImageHeader const tgaHeader) {
-
-    unsigned char flags;
-
-    putchar(tgaHeader.IdLength);
-    putchar(tgaHeader.CoMapType);
-    putchar(tgaHeader.ImgType);
-    putchar(tgaHeader.Index_lo);
-    putchar(tgaHeader.Index_hi);
-    putchar(tgaHeader.Length_lo);
-    putchar(tgaHeader.Length_hi);
-    putchar(tgaHeader.CoSize);
-    putchar(tgaHeader.X_org_lo);
-    putchar(tgaHeader.X_org_hi);
-    putchar(tgaHeader.Y_org_lo);
-    putchar(tgaHeader.Y_org_hi);
-    putchar(tgaHeader.Width_lo);
-    putchar(tgaHeader.Width_hi);
-    putchar(tgaHeader.Height_lo);
-    putchar(tgaHeader.Height_hi);
-    putchar(tgaHeader.PixelSize);
-    flags = (tgaHeader.AttBits & 0xf) | 
-        ((tgaHeader.Rsrvd & 0x1) << 4) |
-        ((tgaHeader.OrgBit & 0x1) << 5) | 
-        ((tgaHeader.OrgBit & 0x3) << 6);
-    putchar(flags);
+        cmdlineP->inputFileName = argv[1];
 
-    if (tgaHeader.IdLength > 0)
-        fwrite(tgaHeader.Id, 1, (int) tgaHeader.IdLength, stdout);
 }
-    
 
 
 static void
 putPixel(struct pam *          const pamP,
-         tuple                 const tuple, 
-         enum TGAbaseImageType const imgType, 
+         tuple                 const tuple,
+         enum TGAbaseImageType const imgType,
          bool                  const withAlpha,
          tuplehash             const cht) {
 /*----------------------------------------------------------------------------
@@ -166,22 +145,22 @@ putPixel(struct pam *          const pamP,
         if (imgType == TGA_RGB_TYPE && pamP->depth < 3) {
             /* Make RGB pixel out of a single input plane */
             unsigned int plane;
-            
-            for (plane = 0; plane < 3; ++plane) 
-                putchar(pnm_scalesample(tuple[0], 
+
+            for (plane = 0; plane < 3; ++plane)
+                putchar(pnm_scalesample(tuple[0],
                                         pamP->maxval, TGA_MAXVAL));
         } else if (imgType == TGA_MONO_TYPE)
-            putchar(pnm_scalesample(tuple[0], 
+            putchar(pnm_scalesample(tuple[0],
                                     pamP->maxval, TGA_MAXVAL));
         else {
-            putchar(pnm_scalesample(tuple[PAM_BLU_PLANE], 
+            putchar(pnm_scalesample(tuple[PAM_BLU_PLANE],
                                     pamP->maxval, TGA_MAXVAL));
-            putchar(pnm_scalesample(tuple[PAM_GRN_PLANE], 
+            putchar(pnm_scalesample(tuple[PAM_GRN_PLANE],
                                     pamP->maxval, TGA_MAXVAL));
-            putchar(pnm_scalesample(tuple[PAM_RED_PLANE], 
+            putchar(pnm_scalesample(tuple[PAM_RED_PLANE],
                                     pamP->maxval, TGA_MAXVAL));
             if (withAlpha)
-                putchar(pnm_scalesample(tuple[PAM_TRN_PLANE], 
+                putchar(pnm_scalesample(tuple[PAM_TRN_PLANE],
                                         pamP->maxval, TGA_MAXVAL));
         }
     }
@@ -190,39 +169,47 @@ putPixel(struct pam *          const pamP,
 
 
 static void
-putMapEntry(struct pam * const pamP, 
-            tuple        const value, 
+putMapEntry(struct pam * const pamP,
+            tuple        const value,
             int          const size) {
 
-    if (size == 15 || size == 16) {
-        /* 5 bits each of red, green, and blue.  Watch for byte order */
-
+    if (size == 8)
+        putchar(pnm_scalesample(value[0],
+                                pamP->maxval, TGA_MAXVAL));
+    else if (size == 15 || size == 16) {
         tuple const tuple31 = pnm_allocpamtuple(pamP);
 
+        assert(pamP->depth >= 3);
+
         pnm_scaletuple(pamP, tuple31, value, 31);
         {
-            int const mapentry = 
+            unsigned int const trn =
+                size == 16 && tuple31[PAM_TRN_PLANE] > 0 ? 1 : 0;
+
+            unsigned int const mapentry =
                 tuple31[PAM_BLU_PLANE] << 0 |
                 tuple31[PAM_GRN_PLANE] << 5 |
-                tuple31[PAM_RED_PLANE] << 10;
-            
+                tuple31[PAM_RED_PLANE] << 10 |
+                trn                    << 15;
+
+            /* Note little-endian byte swapping */
             putchar(mapentry % 256);
             putchar(mapentry / 256);
         }
         pnm_freepamtuple(tuple31);
-    } else if (size == 8)
-        putchar(pnm_scalesample(value[0], 
-                                pamP->maxval, TGA_MAXVAL));
-    else {
-        /* Must be 24 or 32 */
-        putchar(pnm_scalesample(value[PAM_BLU_PLANE], 
+    } else {
+        assert(size == 24 || size == 32);
+
+        assert(pamP->depth >= 3);
+
+        putchar(pnm_scalesample(value[PAM_BLU_PLANE],
                                 pamP->maxval, TGA_MAXVAL));
-        putchar(pnm_scalesample(value[PAM_GRN_PLANE], 
+        putchar(pnm_scalesample(value[PAM_GRN_PLANE],
                                 pamP->maxval, TGA_MAXVAL));
-        putchar(pnm_scalesample(value[PAM_RED_PLANE], 
+        putchar(pnm_scalesample(value[PAM_RED_PLANE],
                                     pamP->maxval, TGA_MAXVAL));
         if (size == 32)
-            putchar(pnm_scalesample(value[PAM_TRN_PLANE], 
+            putchar(pnm_scalesample(value[PAM_TRN_PLANE],
                                     pamP->maxval, TGA_MAXVAL));
     }
 }
@@ -230,8 +217,8 @@ putMapEntry(struct pam * const pamP,
 
 
 static void
-computeRunlengths(struct pam * const pamP, 
-                   tuple *      const tuplerow, 
+computeRunlengths(struct pam * const pamP,
+                   tuple *      const tuplerow,
                    int *        const runlength) {
 
     int col, start;
@@ -239,7 +226,7 @@ computeRunlengths(struct pam * const pamP,
     /* Initialize all run lengths to 0.  (This is just an error check.) */
     for (col = 0; col < pamP->width; ++col)
         runlength[col] = 0;
-    
+
     /* Find runs of identical pixels. */
     for ( col = 0; col < pamP->width; ) {
         start = col;
@@ -250,7 +237,7 @@ computeRunlengths(struct pam * const pamP,
                   pnm_tupleequal(pamP, tuplerow[col], tuplerow[start]));
         runlength[start] = col - start;
     }
-    
+
     /* Now look for runs of length-1 runs, and turn them into negative runs. */
     for (col = 0; col < pamP->width; ) {
         if (runlength[col] == 1) {
@@ -270,23 +257,23 @@ computeRunlengths(struct pam * const pamP,
 
 
 static void
-computeOutName(struct cmdlineInfo const cmdline, 
+computeOutName(struct CmdlineInfo const cmdline,
                const char **      const outNameP) {
-    
+
     char * workarea;
 
     if (cmdline.outName)
         workarea = strdup(cmdline.outName);
-    else if (streq(cmdline.inputFilespec, "-"))
+    else if (streq(cmdline.inputFileName, "-"))
         workarea = NULL;
     else {
         char * cp;
-        workarea = strdup(cmdline.inputFilespec);
+        workarea = strdup(cmdline.inputFileName);
         cp = strchr(workarea, '.');
         if (cp != NULL)
         	*cp = '\0';	/* remove extension */
     }
-    
+
     if (workarea == NULL)
         *outNameP = NULL;
     else {
@@ -305,16 +292,16 @@ validateTupleType(struct pam * const pamP) {
     if (streq(pamP->tuple_type, "RGB_ALPHA")) {
         if (pamP->depth < 4)
             pm_error("Invalid depth for tuple type RGB_ALPHA.  "
-                     "Should have at least 4 planes, but has %d.", 
+                     "Should have at least 4 planes, but has %d.",
                      pamP->depth);
     } else if (streq(pamP->tuple_type, "RGB")) {
         if (pamP->depth < 3)
             pm_error("Invalid depth for tuple type RGB.  "
-                     "Should have at least 3 planes, but has %d.", 
+                     "Should have at least 3 planes, but has %d.",
                      pamP->depth);
     } else if (streq(pamP->tuple_type, "GRAYSCALE")) {
     } else if (streq(pamP->tuple_type, "BLACKANDWHITE")) {
-    } else 
+    } else
         pm_error("Invalid type of input.  PAM tuple type is '%s'.  "
                  "This programs understands only RGB_ALPHA, RGB, GRAYSCALE, "
                  "and BLACKANDWHITE.", pamP->tuple_type);
@@ -324,69 +311,76 @@ validateTupleType(struct pam * const pamP) {
 
 static void
 computeImageType_cht(struct pam *            const pamP,
-                     struct cmdlineInfo      const cmdline, 
+                     struct CmdlineInfo      const cmdline,
                      tuple **                const tuples,
                      enum TGAbaseImageType * const baseImgTypeP,
+                     enum TGAmapType *       const mapTypeP,
                      bool *                  const withAlphaP,
                      tupletable *            const chvP,
-                     tuplehash *             const chtP, 
+                     tuplehash *             const chtP,
                      int *                   const ncolorsP) {
 
-    unsigned int ncolors;
+    unsigned int          ncolors;
     enum TGAbaseImageType baseImgType;
-    bool withAlpha;
+    enum TGAmapType       mapType;
+    bool                  withAlpha;
 
     validateTupleType(pamP);
 
-    withAlpha = (streq(pamP->tuple_type, "RGB_ALPHA"));
-
     if (cmdline.defaultFormat) {
         /* default the image type */
-        if (withAlpha) {
+        if (streq(pamP->tuple_type, "RGB_ALPHA")) {
             baseImgType = TGA_RGB_TYPE;
             *chvP = NULL;
-        } else if (pamP->depth > 1) {
+            withAlpha = true;
+        } else if (streq(pamP->tuple_type, "RGB")) {
             pm_message("computing colormap...");
-            *chvP = 
+            *chvP =
                 pnm_computetuplefreqtable(pamP, tuples, MAXCOLORS, &ncolors);
             if (*chvP == NULL) {
                 pm_message("Too many colors for colormapped TGA.  Doing RGB.");
                 baseImgType = TGA_RGB_TYPE;
-            } else 
+            } else {
                 baseImgType = TGA_MAP_TYPE;
+                mapType = TGA_MAPTYPE_LONG;
+            }
+            withAlpha = false;
         } else {
             baseImgType = TGA_MONO_TYPE;
             *chvP = NULL;
+            withAlpha = false;
         }
     } else {
+        withAlpha = (streq(pamP->tuple_type, "RGB_ALPHA"));
+
         baseImgType = cmdline.imgType;
 
         if (baseImgType == TGA_MAP_TYPE) {
+            mapType = cmdline.mapType;
+
             if (withAlpha)
                 pm_error("Can't do a colormap because image has transparency "
                          "information");
             pm_message("computing colormap...");
-            *chvP = 
+            *chvP =
                 pnm_computetuplefreqtable(pamP, tuples, MAXCOLORS, &ncolors);
-            if (*chvP == NULL) 
+            if (*chvP == NULL)
                 pm_error("Too many colors for colormapped TGA.  "
-                         "Use 'pnmquant %d' to reduce the number of colors.", 
+                         "Use 'pnmquant %d' to reduce the number of colors.",
                          MAXCOLORS);
         } else
             *chvP = NULL;
-        if (baseImgType == TGA_MONO_TYPE && pamP->depth > 1)
-            pm_error("For Mono TGA output, input must be "
-                     "GRAYSCALE or BLACKANDWHITE PAM or PBM or PGM");
     }
-    
+
     if (baseImgType == TGA_MAP_TYPE) {
-        pm_message("%d colors found.", ncolors);
+        pm_message("%u colors found.", ncolors);
         /* Make a hash table for fast color lookup. */
         *chtP = pnm_computetupletablehash(pamP, *chvP, ncolors);
     } else
         *chtP = NULL;
 
     *baseImgTypeP = baseImgType;
+    *mapTypeP     = mapType;
     *withAlphaP   = withAlpha;
     *ncolorsP     = ncolors;
 }
@@ -396,8 +390,9 @@ computeImageType_cht(struct pam *            const pamP,
 static void
 computeTgaHeader(struct pam *          const pamP,
                  enum TGAbaseImageType const baseImgType,
+                 enum TGAmapType       const mapType,
                  bool                  const withAlpha,
-                 bool                  const rle, 
+                 bool                  const rle,
                  int                   const ncolors,
                  unsigned char         const orgBit,
                  const char *          const id,
@@ -416,7 +411,7 @@ computeTgaHeader(struct pam *          const pamP,
         case TGA_RGB_TYPE:  tgaHeaderP->ImgType = TGA_RGB;           break;
         }
     }
-    
+
     if (id) {
         tgaHeaderP->IdLength = strlen(id);
         tgaHeaderP->Id = strdup(id);
@@ -428,7 +423,18 @@ computeTgaHeader(struct pam *          const pamP,
         tgaHeaderP->CoMapType = 1;
         tgaHeaderP->Length_lo = ncolors % 256;
         tgaHeaderP->Length_hi = ncolors / 256;
-        tgaHeaderP->CoSize = 8 * pamP->depth;
+        if (pamP->depth < 3)
+            tgaHeaderP->CoSize = 8;
+        else {
+            switch (mapType) {
+            case TGA_MAPTYPE_SHORT:
+                tgaHeaderP->CoSize = withAlpha ? 16 : 15;
+                break;
+            case TGA_MAPTYPE_LONG:
+                tgaHeaderP->CoSize = withAlpha ? 32 : 24;
+                break;
+            }
+        }
     } else {
         tgaHeaderP->CoMapType = 0;
         tgaHeaderP->Length_lo = 0;
@@ -440,7 +446,7 @@ computeTgaHeader(struct pam *          const pamP,
         tgaHeaderP->PixelSize = 8;
         break;
     case TGA_RGB_TYPE:
-        tgaHeaderP->PixelSize = 8 * MAX((withAlpha ? 4: 3), pamP->depth);
+        tgaHeaderP->PixelSize = 8 * (withAlpha ? 4: 3);
         break;
     case TGA_MONO_TYPE:
         tgaHeaderP->PixelSize = 8;
@@ -460,6 +466,70 @@ computeTgaHeader(struct pam *          const pamP,
 
 
 static void
+reportTgaHeader(struct ImageHeader const tgaHeader) {
+
+    switch (tgaHeader.ImgType) {
+    case TGA_RLEMono:
+        pm_message("Generating monochrome, run-length encoded");
+        break;
+    case TGA_RLEMap:
+        pm_message("Generating colormapped, run-length encoded");
+        pm_message("%u bits per colormap entry", tgaHeader.CoSize);
+        break;
+    case TGA_RLERGB:
+        pm_message("Generating RGB truecolor, run-length encoded");
+        break;
+    case TGA_Mono:
+        pm_message("Generating monochrome, uncompressed");
+        break;
+    case TGA_Map:
+        pm_message("Generating colormapped, uncompressed");
+        pm_message("%u bits per colormap entry", tgaHeader.CoSize);
+        break;
+    case TGA_RGB:
+        pm_message("Generating RGB truecolor, uncompressed");
+        break;
+    }
+    pm_message("%u bits per pixel", tgaHeader.PixelSize);
+}
+
+
+
+static void
+writeTgaHeader(struct ImageHeader const tgaHeader) {
+
+    unsigned char flags;
+
+    putchar(tgaHeader.IdLength);
+    putchar(tgaHeader.CoMapType);
+    putchar(tgaHeader.ImgType);
+    putchar(tgaHeader.Index_lo);
+    putchar(tgaHeader.Index_hi);
+    putchar(tgaHeader.Length_lo);
+    putchar(tgaHeader.Length_hi);
+    putchar(tgaHeader.CoSize);
+    putchar(tgaHeader.X_org_lo);
+    putchar(tgaHeader.X_org_hi);
+    putchar(tgaHeader.Y_org_lo);
+    putchar(tgaHeader.Y_org_hi);
+    putchar(tgaHeader.Width_lo);
+    putchar(tgaHeader.Width_hi);
+    putchar(tgaHeader.Height_lo);
+    putchar(tgaHeader.Height_hi);
+    putchar(tgaHeader.PixelSize);
+    flags = (tgaHeader.AttBits & 0xf) |
+        ((tgaHeader.Rsrvd & 0x1) << 4) |
+        ((tgaHeader.OrgBit & 0x1) << 5) |
+        ((tgaHeader.OrgBit & 0x3) << 6);
+    putchar(flags);
+
+    if (tgaHeader.IdLength > 0)
+        fwrite(tgaHeader.Id, 1, (int) tgaHeader.IdLength, stdout);
+}
+
+
+
+static void
 releaseTgaHeader(struct ImageHeader const tgaHeader) {
 
     if (tgaHeader.IdLength > 0)
@@ -468,9 +538,9 @@ releaseTgaHeader(struct ImageHeader const tgaHeader) {
 
 
 
-static void 
+static void
 writeTgaRaster(struct pam *          const pamP,
-               tuple **              const tuples, 
+               tuple **              const tuples,
                tuplehash             const cht,
                enum TGAbaseImageType const imgType,
                bool                  const withAlpha,
@@ -499,7 +569,7 @@ writeTgaRaster(struct pam *          const pamP,
                     int i;
                     putchar(-runlength[col] - 1);
                     for (i = 0; i < -runlength[col]; ++i)
-                        putPixel(pamP, tuples[realrow][col+i], 
+                        putPixel(pamP, tuples[realrow][col+i],
                                  imgType, withAlpha, cht);
                     col += -runlength[col];
                 } else
@@ -518,47 +588,53 @@ writeTgaRaster(struct pam *          const pamP,
 
 
 int
-main(int argc, char *argv[]) {
+main(int argc, const char **argv) {
 
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
     FILE * ifP;
     tuple ** tuples;
     struct pam pam;
-    int ncolors;
+    int colorCt;
     tupletable chv;
     tuplehash cht;
     struct ImageHeader tgaHeader;
     enum TGAbaseImageType baseImgType;
+    enum TGAmapType mapType;
     bool withAlpha;
-    const char *outName;
+    const char * outName;
 
-    pnm_init( &argc, argv );
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
-    ifP = pm_openr(cmdline.inputFilespec);
+    ifP = pm_openr(cmdline.inputFileName);
 
     computeOutName(cmdline, &outName);
 
     tuples = pnm_readpam(ifP, &pam, PAM_STRUCT_SIZE(tuple_type));
     pm_close(ifP);
 
-    computeImageType_cht(&pam, cmdline, tuples, 
-                         &baseImgType, &withAlpha, &chv, &cht, &ncolors);
+    computeImageType_cht(&pam, cmdline, tuples,
+                         &baseImgType, &mapType,
+                         &withAlpha, &chv, &cht, &colorCt);
 
     /* Do the Targa header */
-    computeTgaHeader(&pam, baseImgType, withAlpha, !cmdline.norle,
-                     ncolors, 0, outName, &tgaHeader);
+    computeTgaHeader(&pam, baseImgType, mapType, withAlpha, !cmdline.norle,
+                     colorCt, 0, outName, &tgaHeader);
+
+    if (cmdline.verbose)
+        reportTgaHeader(tgaHeader);
+
     writeTgaHeader(tgaHeader);
-    
+
     if (baseImgType == TGA_MAP_TYPE) {
         /* Write out the Targa colormap. */
-        int i;
-        for (i = 0; i < ncolors; ++i)
+        unsigned int i;
+        for (i = 0; i < colorCt; ++i)
             putMapEntry(&pam, chv[i]->tuple, tgaHeader.CoSize);
     }
 
-    writeTgaRaster(&pam, tuples, cht, baseImgType, withAlpha, 
+    writeTgaRaster(&pam, tuples, cht, baseImgType, withAlpha,
                    !cmdline.norle, 0);
 
     if (cht)
diff --git a/converter/other/pamtotiff.c b/converter/other/pamtotiff.c
index 7b645b23..115b8b4a 100644
--- a/converter/other/pamtotiff.c
+++ b/converter/other/pamtotiff.c
@@ -22,7 +22,6 @@
 */
 
 #define _XOPEN_SOURCE    /* Make sure stdio.h contains fileno() */
-#define _BSD_SOURCE      /* Make sure string.h contains strcasecmp() */
 
 #include <unistd.h>
 #include <stdio.h>
@@ -135,6 +134,44 @@ validateTagList(struct optNameValue const taglist[]) {
 
 
 static void
+parseIndexbits(bool          const indexbitsSpec,
+               char **       const indexbits,
+               CmdlineInfo * const cmdlineP) {
+
+    if (indexbitsSpec) {
+        unsigned int i;
+
+        /* Set initial values */
+        cmdlineP->indexsizeAllowed.b1 = false;
+        cmdlineP->indexsizeAllowed.b2 = false;
+        cmdlineP->indexsizeAllowed.b4 = false;
+        cmdlineP->indexsizeAllowed.b8 = false;
+
+        for (i = 0; indexbits[i]; ++i) {
+            const char * const thisItem = indexbits[i];
+            if (streq(thisItem, "1"))
+                cmdlineP->indexsizeAllowed.b1 = true;
+            else if (streq(thisItem, "2"))
+                cmdlineP->indexsizeAllowed.b2 = true;
+            else if (streq(thisItem, "4"))
+                cmdlineP->indexsizeAllowed.b4 = true;
+            else if (streq(thisItem, "8"))
+                cmdlineP->indexsizeAllowed.b8 = true;
+            else
+                pm_error("Invalid item in -indexbits list: '%s'.  "
+                         "We recognize only 1, 2, 4, and 8", thisItem);
+        }
+    } else {
+        cmdlineP->indexsizeAllowed.b1 = false;
+        cmdlineP->indexsizeAllowed.b2 = false;
+        cmdlineP->indexsizeAllowed.b4 = false;
+        cmdlineP->indexsizeAllowed.b8 = true;
+    }
+}
+
+
+
+static void
 parseCommandLine(int                 argc,
                  const char ** const argv,
                  CmdlineInfo * const cmdlineP) {
@@ -148,7 +185,7 @@ parseCommandLine(int                 argc,
 
     unsigned int none, packbits, lzw, g3, g4, msb2lsb, lsb2msb, opt_2d, fill;
     unsigned int flate, adobeflate;
-    char * indexbits;
+    char ** indexbits;
     char * resolutionunit;
 
     unsigned int appendSpec, outputSpec, predictorSpec, rowsperstripSpec,
@@ -191,13 +228,13 @@ parseCommandLine(int                 argc,
             &yresolutionSpec,  0);
     OPTENT3(0, "resolutionunit", OPT_STRING, &resolutionunit,
             &resolutionunitSpec,    0);
-    OPTENT3(0, "indexbits",    OPT_STRING,   &indexbits,
+    OPTENT3(0, "indexbits",    OPT_STRINGLIST, &indexbits,
             &indexbitsSpec,    0);
     OPTENT3(0, "tag",          OPT_NAMELIST, &cmdlineP->taglist, &tagSpec, 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 */
+    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. */
@@ -301,35 +338,16 @@ parseCommandLine(int                 argc,
     } else
         cmdlineP->resolutionunit = RESUNIT_INCH;
 
-    if (indexbitsSpec) {
-        if (strstr(indexbits, "1"))
-            cmdlineP->indexsizeAllowed.b1 = TRUE;
-        else
-            cmdlineP->indexsizeAllowed.b1 = FALSE;
-        if (strstr(indexbits, "2"))
-            cmdlineP->indexsizeAllowed.b2 = TRUE;
-        else
-            cmdlineP->indexsizeAllowed.b2 = FALSE;
-        if (strstr(indexbits, "4"))
-            cmdlineP->indexsizeAllowed.b4 = TRUE;
-        else
-            cmdlineP->indexsizeAllowed.b4 = FALSE;
-        if (strstr(indexbits, "8"))
-            cmdlineP->indexsizeAllowed.b8 = TRUE;
-        else
-            cmdlineP->indexsizeAllowed.b8 = FALSE;
-    } else {
-        cmdlineP->indexsizeAllowed.b1 = FALSE;
-        cmdlineP->indexsizeAllowed.b2 = FALSE;
-        cmdlineP->indexsizeAllowed.b4 = FALSE;
-        cmdlineP->indexsizeAllowed.b8 = TRUE;
-    }
+    parseIndexbits(indexbitsSpec, indexbits, cmdlineP);
+
+    if (indexbitsSpec)
+        free(indexbits);
 
     if (tagSpec)
         validateTagList(cmdlineP->taglist);
     else {
         MALLOCARRAY_NOFAIL(cmdlineP->taglist, 1);
-        cmdlineP->taglist[0].name = NULL;
+        cmdlineP->taglist[0].name  = NULL;
         cmdlineP->taglist[0].value = NULL;
     }
 
@@ -345,6 +363,14 @@ parseCommandLine(int                 argc,
 
 
 static void
+freeCmdline(CmdlineInfo const cmdline) {
+
+    pm_optDestroyNameValueList(cmdline.taglist);
+}
+
+
+
+static void
 fillRowOfSubBytePixels(struct pam *    const pamP,
                        const tuple *   const tuplerow,
                        unsigned char * const buf,
@@ -517,7 +543,7 @@ writeScanLines(struct pam *   const pamP,
 
        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.
+       grayscale, composed of one sample where a value of 0 means white.
     */
     MALLOCARRAY(buf, bytesperrow);
 
@@ -577,14 +603,14 @@ analyzeColorsInRgbInput(struct pam *   const pamP,
                         CmdlineInfo    const cmdline,
                         int            const maxcolors,
                         tupletable *   const chvP,
-                        unsigned int * const colorsP,
+                        unsigned int * const colorCtP,
                         bool *         const grayscaleP) {
 /*----------------------------------------------------------------------------
    Same as analyzeColors(), except assuming input image has R/G/B tuples.
 -----------------------------------------------------------------------------*/
     if (cmdline.color && cmdline.truecolor) {
         *chvP = NULL;
-        *grayscaleP = FALSE;
+        *grayscaleP = false;
     } else {
         tupletable chv;
         bool grayscale;
@@ -592,17 +618,17 @@ analyzeColorsInRgbInput(struct pam *   const pamP,
         pm_message("computing colormap...");
         chv = pnm_computetuplefreqtable2(pamP, NULL, maxcolors,
                                          pamP->maxval,
-                                         colorsP);
+                                         colorCtP);
         if (chv == NULL) {
-            grayscale = FALSE;
+            grayscale = false;
         } else {
             unsigned int i;
             pm_message("%u color%s found",
-                       *colorsP, *colorsP == 1 ? "" : "s");
-            grayscale = TRUE;  /* initial assumption */
-            for (i = 0; i < *colorsP && grayscale; ++i) {
+                       *colorCtP, *colorCtP == 1 ? "" : "s");
+            grayscale = true;  /* initial assumption */
+            for (i = 0; i < *colorCtP && grayscale; ++i) {
                 if (!pnm_rgbtupleisgray(chv[i]->tuple))
-                    grayscale = FALSE;
+                    grayscale = false;
             }
         }
         *grayscaleP = grayscale;
@@ -634,21 +660,22 @@ analyzeColors(struct pam *   const pamP,
               CmdlineInfo    const cmdline,
               int            const maxcolors,
               tupletable *   const chvP,
-              unsigned int * const colorsP,
+              unsigned int * const colorCtP,
               bool *         const grayscaleP) {
 /*----------------------------------------------------------------------------
    Analyze the colors in the input image described by 'pamP', whose file
    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
-   of a color map (in newly malloc'ed space).  If a colormapped TIFF is
-   not indicated, return *chvP == NULL.
-
    Return *grayscaleP == true iff the image should be stored as a grayscale
    image (which means the image is monochromatic and the user doesn't
    insist on color format).
 
+   If *grayscaleP is false and the colors, combined with command line options
+   'cmdline', indicate a colormapped TIFF should be generated, return as *chvP
+   the address of a color map (in newly malloc'ed space) and the number of
+   colors in it as *colorCtP.  If a colormapped color TIFF is not indicated,
+   return *chvP == NULL and nothing as *colorCtP.
+
    Leave the file position undefined.
 -----------------------------------------------------------------------------*/
     if (pamP->depth >= 3)
@@ -656,10 +683,28 @@ analyzeColors(struct pam *   const pamP,
            (tuple type RGB or RGB_ALPHA)
         */
         analyzeColorsInRgbInput(pamP, cmdline, maxcolors,
-                                chvP, colorsP, grayscaleP);
+                                chvP, colorCtP, grayscaleP);
     else {
         *chvP = NULL;
-        *grayscaleP = TRUE;
+        *grayscaleP = true;
+    }
+}
+
+
+
+static void
+reportTiffType(bool const grayscale,
+               bool const colormapped,
+               unsigned int const colorCt,
+               bool const verbose) {
+
+    if (verbose) {
+        pm_message("Generating %s TIFF", grayscale ? "grayscale" : "color");
+
+        if (colormapped)
+            pm_message("TIFF will have palette of %u colors", colorCt);
+        else
+            pm_message("TIFF will be truecolor (24 bit RGB)");
     }
 }
 
@@ -668,7 +713,7 @@ analyzeColors(struct pam *   const pamP,
 static void
 computeRasterParm(struct pam *     const pamP,
                   tupletable       const chv,
-                  int              const colors,
+                  int              const colorCt,
                   bool             const grayscale,
                   int              const compression,
                   bool             const minisblack,
@@ -682,7 +727,7 @@ computeRasterParm(struct pam *     const pamP,
    Compute the parameters of the raster portion of the TIFF image.
 
    'minisblack' and 'miniswhite' mean the user requests the corresponding
-   photometric.  Both FALSE means user has no explicit requirement.
+   photometric.  Both false means user has no explicit requirement.
 -----------------------------------------------------------------------------*/
     unsigned short defaultPhotometric;
     /* The photometric we use if the user specified no preference */
@@ -709,14 +754,14 @@ computeRasterParm(struct pam *     const pamP,
         if (chv) {
             *samplesperpixelP = 1;  /* Pixel is just the one index value */
             *bitspersampleP =
-                colors <=   2 && indexsizeAllowed.b1 ? 1 :
-                colors <=   4 && indexsizeAllowed.b2 ? 2 :
-                colors <=  16 && indexsizeAllowed.b4 ? 4 :
-                colors <= 256 && indexsizeAllowed.b8 ? 8 :
+                colorCt <=   2 && indexsizeAllowed.b1 ? 1 :
+                colorCt <=   4 && indexsizeAllowed.b2 ? 2 :
+                colorCt <=  16 && indexsizeAllowed.b4 ? 4 :
+                colorCt <= 256 && indexsizeAllowed.b8 ? 8 :
                 0;
             if (*bitspersampleP == 0)
                 pm_error("Your -indexbits option is insufficient for the "
-                         "%d colors in this image.", colors);
+                         "%d colors in this image.", colorCt);
 
             defaultPhotometric = PHOTOMETRIC_PALETTE;
         } else {
@@ -736,11 +781,15 @@ computeRasterParm(struct pam *     const pamP,
         }
     }
 
-    if (miniswhite)
+    if (miniswhite) {
+        if (!grayscale)
+            pm_error("Image is color, so -miniswhite is invalid");
         *photometricP = PHOTOMETRIC_MINISWHITE;
-    else if (minisblack)
+    } else if (minisblack) {
+        if (!grayscale)
+            pm_error("Image is color, so -minisblack is invalid");
         *photometricP = PHOTOMETRIC_MINISBLACK;
-    else
+    } else
         *photometricP = defaultPhotometric;
 
     {
@@ -764,9 +813,9 @@ computeRasterParm(struct pam *     const pamP,
 /*----------------------------------------------------------------------------
   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.
 
@@ -777,22 +826,22 @@ computeRasterParm(struct pam *     const pamP,
 
     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
@@ -972,9 +1021,9 @@ copyBufferToStdout(int const tmpfileFd) {
 
 
 static void
-destroyTiffGenerator(WriteMethod const writeMethod,
-                     TIFF *      const tifP,
-                     int         const ofd) {
+closeTiffGenerator(WriteMethod const writeMethod,
+                   TIFF *      const tifP,
+                   int         const ofd) {
 
     TIFFFlushData(tifP);
 
@@ -994,7 +1043,7 @@ static void
 createTiffColorMap(struct pam *       const pamP,
                    unsigned int       const bitspersample,
                    tupletable         const chv,
-                   unsigned int       const colors,
+                   unsigned int       const colorCt,
                    unsigned short *** const tiffColorMapP) {
 
     unsigned int const colorMapSize = 1 << bitspersample;
@@ -1009,7 +1058,7 @@ createTiffColorMap(struct pam *       const pamP,
     for (i = 0; i < colorMapSize; ++i) {
         unsigned int plane;
         for (plane = 0; plane < pamP->depth; ++plane) {
-            if (i < colors)
+            if (i < colorCt)
                 tiffColorMap[plane][i] =
                     chv[i]->tuple[plane] * 65535L / pamP->maxval;
             else
@@ -1136,7 +1185,7 @@ convertImage(FILE *       const ifP,
     tuplehash cht;
     unsigned short ** tiffColorMap;  /* malloc'ed */
     struct pam pam;
-    unsigned int colors;
+    unsigned int colorCt;
     bool grayscale;
     unsigned short photometric;
     unsigned short samplesperpixel;
@@ -1152,14 +1201,16 @@ convertImage(FILE *       const ifP,
 
     pm_tell2(ifP, &rasterPos, sizeof(rasterPos));
 
-    analyzeColors(&pam, cmdline, MAXCOLORS, &chv, &colors, &grayscale);
+    analyzeColors(&pam, cmdline, MAXCOLORS, &chv, &colorCt, &grayscale);
+
+    reportTiffType(grayscale, chv != NULL, colorCt, cmdline.verbose);
 
     /* Go back to beginning of raster */
     pm_seek2(ifP, &rasterPos, sizeof(rasterPos));
 
     /* Figure out TIFF parameters. */
 
-    computeRasterParm(&pam, chv, colors, grayscale,
+    computeRasterParm(&pam, chv, colorCt, grayscale,
                       cmdline.compression,
                       cmdline.minisblack, cmdline.miniswhite,
                       cmdline.indexsizeAllowed,
@@ -1172,10 +1223,10 @@ convertImage(FILE *       const ifP,
         cht = NULL;
         tiffColorMap = NULL;
     } else {
-        createTiffColorMap(&pam, bitspersample, chv, colors, &tiffColorMap);
+        createTiffColorMap(&pam, bitspersample, chv, colorCt, &tiffColorMap);
 
         /* Convert color vector to color hash table, for fast lookup. */
-        cht = pnm_computetupletablehash(&pam, chv, colors);
+        cht = pnm_computetupletablehash(&pam, chv, colorCt);
         pnm_freetupletable(&pam, chv);
     }
 
@@ -1193,9 +1244,6 @@ convertImage(FILE *       const ifP,
 
 
 
-
-
-
 int
 main(int argc, const char *argv[]) {
     CmdlineInfo cmdline;
@@ -1205,7 +1253,7 @@ main(int argc, const char *argv[]) {
     int ofd;
     int eof;
     unsigned int imageSeq;
-    
+
     pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
@@ -1229,7 +1277,7 @@ main(int argc, const char *argv[]) {
         break;
     }
 
-    eof = FALSE;  /* initial assumption */
+    eof = false;  /* initial assumption */
     imageSeq = 0;
 
     while (!eof) {
@@ -1254,9 +1302,11 @@ main(int argc, const char *argv[]) {
         }
     }
 
-    destroyTiffGenerator(cmdline.writeMethod, tifP, ofd);
+    closeTiffGenerator(cmdline.writeMethod, tifP, ofd);
     pm_close(ifP);
 
+    freeCmdline(cmdline);
+
     return 0;
 }
 
diff --git a/converter/other/pamtouil.c b/converter/other/pamtouil.c
index ee7f5ae6..01858cbc 100644
--- a/converter/other/pamtouil.c
+++ b/converter/other/pamtouil.c
@@ -13,6 +13,7 @@
 ** implied warranty.
 */
 
+#define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE  /* Make sure string.h contains strdup() */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #include <ctype.h>
diff --git a/converter/other/pamtowinicon.c b/converter/other/pamtowinicon.c
index 7e2c9e86..3d77d731 100644
--- a/converter/other/pamtowinicon.c
+++ b/converter/other/pamtowinicon.c
@@ -643,7 +643,7 @@ writePng(const struct pam * const pamP,
     
     if (haveAlpha || haveAnd) {
         const char * alphaFileName;
-        const char * command;
+        const char * alphaOpt;
 
         if (haveAlpha)
             makeAlphaFile(pamP, tuples, alphaPlane, &alphaFileName);
@@ -653,19 +653,19 @@ writePng(const struct pam * const pamP,
         strcpy (pam.tuple_type,
                 pam.depth == 3 ? PAM_PPM_TUPLETYPE: PAM_PGM_TUPLETYPE);
         
-        pm_asprintf(&command, "pnmtopng -alpha=\"%s\"", alphaFileName);
+        pm_asprintf(&alphaOpt, "-alpha=%s", alphaFileName);
 
-        pm_system(pm_feed_from_pamtuples, &pamTuples,
-                  acceptToFile, &acceptParm,
-                  command);
+        pm_system_lp("pnmtopng", pm_feed_from_pamtuples, &pamTuples,
+                     acceptToFile, &acceptParm,
+                     "pnmtopng", alphaOpt, NULL);
     
-        pm_strfree(command);
+        pm_strfree(alphaOpt);
     
         unlink(alphaFileName);
     } else {
-        pm_system(pm_feed_from_pamtuples, &pamTuples,
-                  acceptToFile, &acceptParm,
-                  "pnmtopng");
+        pm_system_lp("pnmtopng", pm_feed_from_pamtuples, &pamTuples,
+                     acceptToFile, &acceptParm,
+                     "pnmtopng", NULL);
     }
 
     *sizeP = pngSize;
diff --git a/converter/other/pclxl.h b/converter/other/pclxl.h
index b1d1c043..5b3eae4b 100644
--- a/converter/other/pclxl.h
+++ b/converter/other/pclxl.h
@@ -48,7 +48,7 @@ enum DataOrg {
 };
 
 enum DataSource {
-    eDefault = 0
+    eDefaultDataSource = 0
 };
 
 enum DataType {
@@ -58,9 +58,9 @@ enum DataType {
     eSint16 = 3
 };
 
-enum DitherMatrix {  
+enum DitherMatrix {
     eDeviceBest = 0
-}; 
+};
 
 
 enum DuplexPageMode {
@@ -109,7 +109,7 @@ enum Measure {
 };
 
 enum MediaSource {
-    eDefaultSource = 0,
+    eDefaultMediaSource = 0,
     eAutoSelect = 1,
     eManualFeed = 2,
     eMultiPurposeTray = 3,
@@ -246,7 +246,7 @@ enum Attribute {
     aYSpacingData        = 176,
     aCharBoldValue       = 177
 };
-                                          
+
 enum Operator {
     oBeginSession        = 0x41,
     oEndSession          = 0x42,
@@ -317,7 +317,7 @@ enum Operator {
     oPiePath             = 0x9f,
     oRectangle           = 0xa0,
     oRectanglePath       = 0xa1,
-    oRoundRectangle      = 0xa2,  
+    oRoundRectangle      = 0xa2,
     oRoundRectanglePath  = 0xa3,
     oText                = 0xa8,
     oTextPath            = 0xa9,
@@ -384,7 +384,7 @@ struct sPaperFormat {
 enum {
     eSTART,
     eRLE,
-    eLIT 
+    eLIT
 } RLEstates;
 
 #endif
diff --git a/converter/other/pdbimgtopam.c b/converter/other/pdbimgtopam.c
index 3cb4129a..67044109 100644
--- a/converter/other/pdbimgtopam.c
+++ b/converter/other/pdbimgtopam.c
@@ -97,6 +97,8 @@ parseCommandLine(int argc, const char ** argv,
         cmdlineP->inputFileName = argv[1];
     else
         pm_error("Program takes at most one argument:  input file name");
+
+    free(option_def);
 }
 
 
@@ -211,7 +213,8 @@ readCompressed(IMAGE *    const imgP,
 
     if (end_offset == UNKNOWN_OFFSET) {
         /*
-         * Read until EOF. Some of them have an extra zero byte
+         * Read sufficient amount for worst-case compressed image,
+         * or 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
@@ -221,12 +224,20 @@ readCompressed(IMAGE *    const imgP,
          * this extra byte and ignore it by paying attention to
          * the image dimensions.
          */
-        MALLOCARRAY(buffer, ipdb_img_size(imgP));
+       size_t const maxCompressedSizeWithBloat = ipdb_img_size(imgP) * 2;
+         /*
+          * Provide a buffer large enough for the worst case.
+          * See note in lib/util/runlength.c .
+          * We do not use pm_rlenc_allocoutbuf() because there is no
+          * guarantee that the encoder that produced the image was
+          * efficient.
+          */
+       MALLOCARRAY(buffer, maxCompressedSizeWithBloat);
 
         if (buffer == NULL)
             retval = ENOMEM;
         else {
-            dataSize = fread(buffer, 1, ipdb_img_size(imgP), fP);
+            dataSize = fread(buffer, 1,  maxCompressedSizeWithBloat, fP);
             if (dataSize <= 0)
                 retval = EIO;
             else
@@ -307,6 +318,8 @@ imageReadData(FILE *   const fileP,
               IMAGE *  const imgP,
               uint32_t const end_offset) {
 
+    size_t const imageSize = ipdb_img_size(imgP);  
+
     int retval;
     size_t dataSize;
     uint8_t * buffer;
@@ -318,14 +331,24 @@ imageReadData(FILE *   const fileP,
          * 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 (dataSize < imageSize || imgP->version == 1) {
+            if (imgP->version == 0)
+                pm_message("Image header says raster data is uncompressed.  "
+                           "Encountered only %u instead of the "
+                           "required %u bytes.  Assuming compressed mode.",
+                           (unsigned)dataSize, (unsigned)imageSize);
+            decompress(buffer, dataSize, imageSize, &imgP->data);
             if (imgP->data == NULL)
                 retval = ENOMEM;
             else
                 imgP->compressed = true;
             free(buffer);
         } else {
+            if (dataSize > imageSize)
+                pm_message("Image header says raster data is uncompressed. "
+                           "Encountered %u instead of the required %u bytes. "
+                           "Assuming uncompressed mode.",
+                           (unsigned)dataSize, (unsigned)imageSize);
             imgP->compressed = false;
             imgP->data       = buffer;
             /* Storage at 'buffer' now belongs to *imgP */
diff --git a/converter/other/pgmtoppm.c b/converter/other/pgmtoppm.c
index f8a69424..c3a26594 100644
--- a/converter/other/pgmtoppm.c
+++ b/converter/other/pgmtoppm.c
@@ -10,6 +10,7 @@
 ** implied warranty.
 */
 
+#define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
 #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/other/pngtopam.c b/converter/other/pngtopam.c
index 8743174e..1023c6c6 100644
--- a/converter/other/pngtopam.c
+++ b/converter/other/pngtopam.c
@@ -64,12 +64,12 @@ static bool verbose;
 
 
 static void
-parseCommandLine(int                  argc, 
+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.  
+   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.
@@ -90,13 +90,13 @@ parseCommandLine(int                  argc,
     MALLOCARRAY(option_def, 100);
 
     option_def_index = 0;   /* incremented by OPTENT3 */
-    OPTENT3(0, "verbose",     OPT_FLAG,   NULL,                  
+    OPTENT3(0, "verbose",     OPT_FLAG,   NULL,
             &cmdlineP->verbose,       0);
-    OPTENT3(0, "alpha",       OPT_FLAG,   NULL,                  
+    OPTENT3(0, "alpha",       OPT_FLAG,   NULL,
             &alphaSpec,               0);
-    OPTENT3(0, "alphapam",    OPT_FLAG,   NULL,                  
+    OPTENT3(0, "alphapam",    OPT_FLAG,   NULL,
             &alphapamSpec,            0);
-    OPTENT3(0, "mix",         OPT_FLAG,   NULL,                  
+    OPTENT3(0, "mix",         OPT_FLAG,   NULL,
             &mixSpec,                 0);
     OPTENT3(0, "background",  OPT_STRING, &cmdlineP->background,
             &backgroundSpec,          0);
@@ -104,9 +104,9 @@ parseCommandLine(int                  argc,
             &cmdlineP->gammaSpec,     0);
     OPTENT3(0, "text",        OPT_STRING, &cmdlineP->text,
             &textSpec,                0);
-    OPTENT3(0, "time",        OPT_FLAG,   NULL,                  
+    OPTENT3(0, "time",        OPT_FLAG,   NULL,
             &cmdlineP->time,          0);
-    OPTENT3(0, "byrow",       OPT_FLAG,   NULL,                  
+    OPTENT3(0, "byrow",       OPT_FLAG,   NULL,
             &cmdlineP->byrow,         0);
 
     opt.opt_table = option_def;
@@ -248,7 +248,7 @@ computePngLineSize(struct pngx * const pngxP) {
     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;
 }
 
@@ -370,7 +370,7 @@ reader_createRowByRow(struct pngx * const pngxP,
 
     readerP->pngRaster = NULL;
 
-    MALLOCARRAY(readerP->rowBuf, computePngLineSize(pngxP)); 
+    MALLOCARRAY(readerP->rowBuf, computePngLineSize(pngxP));
 
     if (!readerP->rowBuf)
         pm_error("Could not allocate %u bytes for a PNG row buffer",
@@ -393,7 +393,7 @@ reader_destroy(Reader * const readerP) {
 
     if (readerP->pngRaster)
         freePngRaster(readerP->pngRaster, readerP->pngxP);
-   
+
     if (readerP->rowBuf)
         free(readerP->rowBuf);
 
@@ -434,14 +434,14 @@ getPngVal(const png_byte ** const pp,
           int               const bitDepth) {
 
     png_uint_16 c;
-    
+
     if (bitDepth == 16)
         c = *(*pp)++ << 8;
     else
         c = 0;
 
     c |= *(*pp)++;
-    
+
     return c;
 }
 
@@ -498,7 +498,7 @@ setTuple(const struct pam *  const pamP,
             tuple[PAM_GRN_PLANE] = foreground.g;
             tuple[PAM_BLU_PLANE] = foreground.b;
             tuple[PAM_TRN_PLANE] = alpha;
-        }    
+        }
     } else {
         assert(alphaHandling == ALPHA_MIX);
 
@@ -540,7 +540,7 @@ saveText(struct pngx * const pngxP,
 
         while (text.line[i].key[j] != '\0' &&
                text.line[i].key[j] != ' ')
-            ++j;    
+            ++j;
 
         if (text.line[i].key[j] != ' ') {
             fprintf(tfP, "%s", text.line[i].key);
@@ -552,7 +552,7 @@ saveText(struct pngx * const pngxP,
                 putc(' ', tfP);
         }
         putc(' ', tfP); /* at least one space between key and text */
-    
+
         for (j = 0; j < text.line[i].text_length; ++j) {
             putc(text.line[i].text[j], tfP);
             if (text.line[i].text[j] == '\n') {
@@ -607,11 +607,11 @@ dumpTypeAndFilter(struct pngx * const pngxP) {
     case PNG_COLOR_TYPE_GRAY:
         typeString = "gray";
         break;
-        
+
     case PNG_COLOR_TYPE_GRAY_ALPHA:
         typeString = "gray+alpha";
         break;
-        
+
     case PNG_COLOR_TYPE_PALETTE:
         typeString = "palette";
         break;
@@ -630,13 +630,13 @@ dumpTypeAndFilter(struct pngx * const pngxP) {
         pm_asprintf(&filterString, "base filter");
         break;
     default:
-        pm_asprintf(&filterString, "unknown filter type %d", 
+        pm_asprintf(&filterString, "unknown filter type %d",
                     pngx_filterType(pngxP));
     }
 
     pm_message("%s, %s, %s",
                typeString,
-               pngx_interlaceType(pngxP) ? 
+               pngx_interlaceType(pngxP) ?
                "Adam7 interlaced" : "not interlaced",
                filterString);
 
@@ -679,7 +679,7 @@ dumpPngInfo(struct pngx * const pngxP) {
                    pngx_gama(pngxP));
     else
         pm_message("gAMA chunk (image gamma): not present");
-    
+
     if (pngx_chunkIsPresent(pngxP, PNG_INFO_sBIT))
         pm_message("sBIT chunk: present");
     else
@@ -739,7 +739,7 @@ transColor(struct pngx * const pngxP) {
     struct pngx_trns const trans = pngx_trns(pngxP);
 
     assert(pngx_chunkIsPresent(pngxP, PNG_INFO_tRNS));
-    
+
     return trans.transColor;
 }
 
@@ -775,7 +775,7 @@ isTransparentColor(pngcolor        const color,
            We could fix this by not letting libpng gamma-correct the
            pixels, and just do it ourselves.
         */
-    
+
         switch (pngx_colorType(pngxP)) {
         case PNG_COLOR_TYPE_GRAY:
             retval = color.r == gammaCorrect(transColor16.gray, gamma,
@@ -788,7 +788,7 @@ isTransparentColor(pngcolor        const color,
                                                      pngxP->maxval));
         }
         }
-    } else 
+    } else
         retval = FALSE;
 
     return retval;
@@ -810,7 +810,7 @@ setupGammaCorrection(struct pngx *     const pngxP,
    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 
+   '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,
@@ -845,7 +845,7 @@ setupGammaCorrection(struct pngx *     const pngxP,
             gammaCorrectionP->needCorrection = true;
             gammaCorrectionP->gamma = imageGamma * screenGamma;
             /* In case of gamma-corrections, sBIT's as in the
-               PNG-file are not valid anymore 
+               PNG-file are not valid anymore
             */
             pngx_removeChunk(pngxP, PNG_INFO_sBIT);
             if (verbose)
@@ -870,7 +870,7 @@ paletteHasPartialTransparency(struct pngx * const pngxP) {
 
             bool foundGray;
             unsigned int i;
-            
+
             for (i = 0, foundGray = FALSE;
                  i < trans.numTrans && !foundGray;
                  ++i) {
@@ -945,7 +945,7 @@ getComponentSbit(struct pngx *       const pngxP,
         /* We care about both the foreground and the alpha */
         bool fgNotUniform;
         png_byte fgSbit;
-        
+
         getComponentSbitFg(pngxP, &fgSbit, &fgNotUniform);
 
         if (fgNotUniform)
@@ -961,7 +961,7 @@ getComponentSbit(struct pngx *       const pngxP,
     }
 }
 
-                 
+
 
 static void
 shiftPalette(struct pngx * const pngxP,
@@ -977,9 +977,9 @@ shiftPalette(struct pngx * const pngxP,
                  shift);
     else {
         struct pngx_plte const palette = pngx_plte(pngxP);
-        
+
         unsigned int i;
-        
+
         for (i = 0; i < palette.size; ++i) {
             palette.palette[i].red   >>= (8 - shift);
             palette.palette[i].green >>= (8 - shift);
@@ -1004,7 +1004,7 @@ computeMaxvalFromSbit(struct pngx *       const pngxP,
        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 
+       is used
     */
 
     bool notUniform;
@@ -1043,7 +1043,7 @@ computeMaxvalFromSbit(struct pngx *       const pngxP,
                            "writing file with %u bits", componentSigBit);
                 *maxvalP = (1l << componentSigBit) - 1;
                 *succeededP = true;
-                
+
                 if (pngx_colorType(pngxP) == PNG_COLOR_TYPE_PALETTE)
                     shiftPalette(pngxP, componentSigBit);
                 else
@@ -1070,7 +1070,7 @@ setupSignificantBits(struct pngx *       const pngxP,
   Also set up *pngxP for the corresponding significant bits.
 -----------------------------------------------------------------------------*/
     bool gotItFromSbit;
-    
+
     if (pngx_chunkIsPresent(pngxP, PNG_INFO_sBIT))
         computeMaxvalFromSbit(pngxP, alphaHandling,
                               &pngxP->maxval, &gotItFromSbit, errorLevelP);
@@ -1119,7 +1119,7 @@ imageHasColor(struct pngx * const pngxP) {
 
         bool foundColor;
         unsigned int i;
-            
+
         for (i = 0, foundColor = FALSE;
              i < palette.size && !foundColor;
              ++i) {
@@ -1148,7 +1148,7 @@ determineOutputType(struct pngx *       const pngxP,
         /* The output is a old style pseudo-PNM transparency image */
         *depthP = 1;
         *formatP = maxval > 1 ? PGM_FORMAT : PBM_FORMAT;
-    } else {            
+    } else {
         /* The output is a normal Netpbm image */
         bool const outputIsColor =
             imageHasColor(pngxP) || !isGrayscale(bgColor);
@@ -1191,7 +1191,7 @@ getBackgroundColor(struct pngx *   const pngxP,
     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.  
+           which is a bit arbitrary.
         */
         pixel const backcolor = ppm_parsecolor(requestedColor, maxval);
 
@@ -1207,12 +1207,12 @@ getBackgroundColor(struct pngx *   const pngxP,
         switch (pngx_colorType(pngxP)) {
         case PNG_COLOR_TYPE_GRAY:
         case PNG_COLOR_TYPE_GRAY_ALPHA:
-            bgColorP->r = bgColorP->g = bgColorP->b = 
+            bgColorP->r = bgColorP->g = bgColorP->b =
                 gammaCorrect(background.gray, gamma, pngxP->maxval);
             break;
         case PNG_COLOR_TYPE_PALETTE: {
             struct pngx_plte const palette = pngx_plte(pngxP);
-            png_color const rawBgcolor = 
+            png_color const rawBgcolor =
                 palette.palette[background.index];
             *bgColorP = gammaCorrectColor(pngcolorFromByte(rawBgcolor),
                                           gamma, pngxP->maxval);
@@ -1221,13 +1221,13 @@ getBackgroundColor(struct pngx *   const pngxP,
         case PNG_COLOR_TYPE_RGB:
         case PNG_COLOR_TYPE_RGB_ALPHA: {
             png_color_16 const rawBgcolor = background;
-            
+
             *bgColorP = gammaCorrectColor(pngcolorFrom16(rawBgcolor),
                                           gamma, pngxP->maxval);
         }
         break;
         }
-    } else 
+    } else
         /* when no background given, we use white [from version 2.37] */
         bgColorP->r = bgColorP->g = bgColorP->b = maxval;
 }
@@ -1345,7 +1345,7 @@ makeTupleRow(const struct pam *  const pamP,
                      pngxP, paletteAlpha(pngxP, index, pngxP->maxval));
         }
         break;
-                
+
         case PNG_COLOR_TYPE_RGB: {
             pngcolor fgColor;
 
@@ -1403,7 +1403,7 @@ reportOutputFormat(const struct pam * const pamP) {
         assert(false); /* Every possible value handled above */
     }
 }
-    
+
 
 
 static void
@@ -1449,9 +1449,9 @@ writeNetpbm(struct pam *        const pamP,
 
 
 
-static void 
-convertpng(FILE *             const ifP, 
-           FILE *             const tfP, 
+static void
+convertpng(FILE *             const ifP,
+           FILE *             const tfP,
            struct CmdlineInfo const cmdline,
            int *              const errorLevelP) {
 
@@ -1487,7 +1487,7 @@ convertpng(FILE *             const ifP,
 
     getBackgroundColor(pngxP, cmdline.background, gamma, pngxP->maxval,
                        &bgColor);
-  
+
     pam.size        = sizeof(pam);
     pam.len         = PAM_STRUCT_SIZE(tuple_type);
     pam.file        = stdout;
@@ -1499,7 +1499,7 @@ convertpng(FILE *             const ifP,
     determineOutputType(pngxP, cmdline.alpha, bgColor, pngxP->maxval,
                         &pam.format, &pam.depth, pam.tuple_type);
 
-    rasterReaderP = cmdline.byrow ? 
+    rasterReaderP = cmdline.byrow ?
         reader_createRowByRow(pngxP, ifP) : reader_createAllAtOnce(pngxP, ifP);
 
     writeNetpbm(&pam, pngxP, rasterReaderP, bgColor,
@@ -1516,7 +1516,7 @@ convertpng(FILE *             const ifP,
 
 
 
-int 
+int
 main(int argc, const char *argv[]) {
 
     struct CmdlineInfo cmdline;
diff --git a/converter/other/pngx.c b/converter/other/pngx.c
index 185b4a27..4bb09421 100644
--- a/converter/other/pngx.c
+++ b/converter/other/pngx.c
@@ -1,3 +1,4 @@
+#include <stdbool.h>
 #include <assert.h>
 #include <png.h>
 #include "pm_c_util.h"
@@ -59,6 +60,7 @@ pngx_create(struct pngx ** const pngxPP,
     if (!pngxP)
         pm_error("Failed to allocate memory for PNG object");
     else {
+        pngxP->infoPrepared = false;
         pngxP->numPassesRequired = 1;
 
         switch(rw) {
@@ -347,7 +349,7 @@ void
 pngx_setChrm(struct pngx *      const pngxP,
              struct pngx_chroma const chroma) {
 
-    png_set_cHRM(pngxP->png_ptr, pngxP->info_ptr, 
+    png_set_cHRM(pngxP->png_ptr, pngxP->info_ptr,
                  chroma.wx, chroma.wy,
                  chroma.rx, chroma.ry,
                  chroma.gx, chroma.gy,
@@ -466,6 +468,37 @@ pngx_setIhdr(struct pngx * const pngxP,
 
 void
 pngx_setInterlaceHandling(struct pngx * const pngxP) {
+    /* The documentation is vague and contradictory on what this does, but
+       what it appears from reasoning and experimentation to do is the
+       following.
+
+       It applies to reading and writing by rows (png_write_row, png_read_row)
+       as opposed to whole image (png_write_image, png_read_image).  It has
+       no effect on whole image read and write.
+
+       This is not what makes an image interlaced or tells the decompressor
+       that it is interlaced.  All it does is control how you you read and
+       write the raster when the image is interlaced.  It has no effect if the
+       image is not interlaced.  (You make an image interlaced by setting the
+       IHDR; the decompressor finds out from the IHDR that it is interlaced).
+
+       In the write case, it controls whether you construct the subimages
+       yourself and feed them to libpng in sequence or you feed libpng the
+       entire image multiple times and libpng picks out the pixels appropriate
+       for each subimage in each pass.
+
+       In the read case, it controls whether you get the raw subimages and you
+       assemble them into the full image or you read the whole image multiple
+       times into the same buffer, with the pixels that belong to each
+       subimage being filled in on each pass.
+
+       Note that the only kind of interlacing that exists today is ADAM7 and
+       consequently, the number of passes is always 1 (for no interlacing) or
+       7 (for interlacing).
+    */
+    if (!pngxP->infoPrepared)
+        pm_error("pngx_setInterlaceHandling must not be called before "
+                 "pngx_writeInfo or pngx_readInfo");
 
     pngxP->numPassesRequired = png_set_interlace_handling(pngxP->png_ptr);
 }
@@ -475,6 +508,10 @@ pngx_setInterlaceHandling(struct pngx * const pngxP) {
 void
 pngx_setPacking(struct pngx * const pngxP) {
 
+    if (!pngxP->infoPrepared)
+        pm_error("pngx_setPacking must not be called before "
+                 "pngx_writeInfo or pngx_readInfo");
+
     png_set_packing(pngxP->png_ptr);
 }
 
@@ -484,7 +521,7 @@ void
 pngx_setPhys(struct pngx *    const pngxP,
              struct pngx_phys const phys) {
 
-    png_set_pHYs(pngxP->png_ptr, pngxP->info_ptr, 
+    png_set_pHYs(pngxP->png_ptr, pngxP->info_ptr,
                  phys.x, phys.y, phys.unit);
 }
 
@@ -516,7 +553,19 @@ pngx_setSbit(struct pngx * const pngxP,
 void
 pngx_setShift(struct pngx * const pngxP,
               png_color_8   const sigBitArg) {
-
+/*----------------------------------------------------------------------------
+   Tell the number of significant bits in the row buffers that will be given
+   to the compressor.  Those bits are the least significant of the 8 bits of
+   space in the row buffer for each sample.  For example, if red sample values
+   are in the range 0-7, only the lower 3 bits of the 8-bit byte for each
+   red sample will be used, so one would call this with sigBitArg.red == 3.
+
+   The name alludes to the fact that to normalize the sample to 8 bits, one
+   shifts it left, and this function tells how much shift has to happen.  In
+   the example above, each red sample has to be shifted left 5 bits (so that
+   the upper 3 bits are significant and the lower 5 bits are always zero) to
+   create an 8 bit sample out of the 3 bit samples.
+-----------------------------------------------------------------------------*/
     png_color_8 sigBit;
 
     sigBit = sigBitArg;
@@ -651,6 +700,8 @@ void
 pngx_readInfo(struct pngx * const pngxP) {
 
     png_read_info(pngxP->png_ptr, pngxP->info_ptr);
+
+    pngxP->infoPrepared = true;
 }
 
 
@@ -659,6 +710,8 @@ void
 pngx_writeInfo(struct pngx * const pngxP) {
 
     png_write_info(pngxP->png_ptr, pngxP->info_ptr);
+
+    pngxP->infoPrepared = true;
 }
 
 
@@ -688,7 +741,7 @@ pngx_readStart(struct pngx * const pngxP,
                FILE *        const ifP) {
 
     size_t sigByteCt;
-            
+
     verifyFileIsPng(ifP, &sigByteCt);
 
     /* Declare that we already read the signature bytes */
@@ -733,9 +786,18 @@ pngx_writeRow(struct pngx *    const pngxP,
 
 
 void
+pngx_writeImage(struct pngx * const pngxP,
+                png_byte **   const raster) {
+
+    png_write_image(pngxP->png_ptr, (png_byte **)raster);
+}
+
+
+
+void
 pngx_readEnd(struct pngx * const pngxP) {
 
-    /* Note that some of info_ptr is not defined until png_read_end() 
+    /* 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
diff --git a/converter/other/pngx.h b/converter/other/pngx.h
index 81e0dc55..f4701bb2 100644
--- a/converter/other/pngx.h
+++ b/converter/other/pngx.h
@@ -1,7 +1,12 @@
 #ifndef PNGX_H_INCLUDED
 #define PNGX_H_INCLUDED
 
+#include <stdbool.h>
 #include <png.h>
+    /* This includes the Zlib interface header file zlib.h because libpng uses
+       libz and some of the Zlib interface, e.g. the Z_DEFLATED constant,
+       is part of the libpng interface.
+    */
 #include "pm_c_util.h"
 
 /* pngx is designed to be an extension of the PNG library to make using
@@ -49,10 +54,16 @@ struct pngx {
     pngx_rw      rw;
     png_uint_16  maxval;
     unsigned int numPassesRequired;
-        /* The number of times we have write the complete image to the
+        /* The number of times we have to write the complete image to the
            compressor.  This is more than one when the compressor is set
            up to do an interlaced format.
         */
+    bool         infoPrepared;
+        /* png_write_info or png_read_info has been called, so libpng is in a
+           state in which things such as png_set_interlace_handling will work.
+           These functions use information in *png_ptr that is set by
+           png_XXX_info.
+        */
 };
 
 void
@@ -266,6 +277,10 @@ pngx_writeRow(struct pngx *    const pngxP,
               const png_byte * const line);
 
 void
+pngx_writeImage(struct pngx * const pngxP,
+                png_byte **   const raster);
+
+void
 pngx_readEnd(struct pngx * const pngxP);
 
 void
diff --git a/converter/other/pnmtoddif.c b/converter/other/pnmtoddif.c
index ae8c8524..ac02e425 100644
--- a/converter/other/pnmtoddif.c
+++ b/converter/other/pnmtoddif.c
@@ -538,15 +538,12 @@ convertRaster(FILE *       const ifP,
               unsigned int const bytesPerLine) {
 
     unsigned char * data;
-    unsigned char * p;
 
     MALLOCARRAY(data, bytesPerLine);
 
     if (data == NULL)
         pm_error("Couldn't allocate %u-byte line buffer", bytesPerLine);
 
-    p = data;  /* initial value */
-
     switch (PNM_FORMAT_TYPE(format)) {
     case PBM_TYPE:
         convertPbmRaster(ifP, format, cols, rows, ofP, bytesPerLine, data);
diff --git a/converter/other/pnmtojpeg.c b/converter/other/pnmtojpeg.c
index 7e7272a0..44826245 100644
--- a/converter/other/pnmtojpeg.c
+++ b/converter/other/pnmtojpeg.c
@@ -16,6 +16,7 @@
 
 *****************************************************************************/
 
+#define _DEFAULT_SOURCE 1  /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
diff --git a/converter/other/pnmtopalm/palm.h b/converter/other/pnmtopalm/palm.h
index 718a66cf..0edf9a28 100644
--- a/converter/other/pnmtopalm/palm.h
+++ b/converter/other/pnmtopalm/palm.h
@@ -28,9 +28,18 @@
 #define PALM_FORMAT_565LE       0x02    /* Palm says internal use only */
 #define PALM_FORMAT_INDEXEDLE   0x03    /* Palm says internal use only */
 
-typedef unsigned long Color_s;
+typedef unsigned long ColormapEntry;
+    /* A entry in a Colormap.  It is an encoding of 4 bytes as the integer
+       that those 4 bytes would represent in pure binary:
 
-typedef Color_s * Color;
+          MSB 0: the color index
+              1: red intensity
+              2: green intensity
+          LSB 3: blue intensity
+
+       The intensities are on a scale with a certain maxval (that must be
+       specified to interpret a ColormapEntry).
+    */
 
 typedef struct {
     unsigned int nentries;
@@ -39,24 +48,11 @@ typedef struct {
         /* number of colors actually in 'color_entries' -- entries are
            filled from 0 consecutively, one color per entry.
         */
-    Color_s * color_entries;  /* Array of colors */
-} Colormap_s;
-
-typedef Colormap_s * Colormap;
+    ColormapEntry * color_entries;  /* Array of colors */
+} Colormap;
 
 qsort_comparison_fn palmcolor_compare_indices;
 
 qsort_comparison_fn palmcolor_compare_colors;
 
-Colormap
-palmcolor_build_custom_8bit_colormap(unsigned int const rows,
-                                     unsigned int const cols,
-                                     pixel **     const pixels);
-
-Colormap
-palmcolor_build_default_8bit_colormap(void);
-
-Colormap
-palmcolor_read_colormap (FILE * const ifP);
-
 #endif
diff --git a/converter/other/pnmtopalm/palmcolormap.c b/converter/other/pnmtopalm/palmcolormap.c
index 0f47558c..1341ca2b 100644
--- a/converter/other/pnmtopalm/palmcolormap.c
+++ b/converter/other/pnmtopalm/palmcolormap.c
@@ -4,18 +4,46 @@
 #include "pm_c_util.h"
 #include "mallocvar.h"
 #include "pnm.h"
-
 #include "palm.h"
 
+#include "palmcolormap.h"
+
+
+
+static pixval
+scaleSample(pixval const arg,
+            pixval const oldMaxval,
+            pixval const newMaxval) {
+
+    return (arg * newMaxval + oldMaxval/2) / oldMaxval;
+}
+
+
+
+ColormapEntry
+palmcolor_mapEntryColorFmPixel(pixel  const color,
+                               pixval const maxval,
+                               pixval const newMaxval) {
+
+
+    return
+        0
+        | (scaleSample(PPM_GETR(color), maxval, newMaxval) << 16) 
+        | (scaleSample(PPM_GETG(color), maxval, newMaxval) <<  8)
+        | (scaleSample(PPM_GETB(color), maxval, newMaxval) <<  0);
+}
+
+
+
 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))
+    if ((*((ColormapEntry *) p1) & 0xFF000000) < (*((ColormapEntry *) p2) & 0xFF000000))
         return -1;
-    else if ((*((Color) p1) & 0xFF000000) > (*((Color) p2) & 0xFF000000))
+    else if ((*((ColormapEntry *) p1) & 0xFF000000) > (*((ColormapEntry *) p2) & 0xFF000000))
         return 1;
     else
         return 0;
@@ -165,86 +193,88 @@ static int PalmPalette8bpp[256][3] =
 
 
 
-Colormap
+Colormap *
 palmcolor_build_default_8bit_colormap(void) {
 
     unsigned int i;
 
-    Colormap cm;
+    Colormap * cmP;
 
-    MALLOCVAR_NOFAIL(cm);
-    cm->nentries = 232;
-    MALLOCARRAY_NOFAIL(cm->color_entries, cm->nentries);
+    MALLOCVAR_NOFAIL(cmP);
+    cmP->nentries = 232;
+    MALLOCARRAY_NOFAIL(cmP->color_entries, cmP->nentries);
 
     /* Fill in the colors */
     for (i = 0; i < 231; ++i) {
-        cm->color_entries[i] = ((i << 24) |
+        cmP->color_entries[i] = ((i << 24) |
                                 (PalmPalette8bpp[i][0] << 16) |
                                 (PalmPalette8bpp[i][1] << 8) |
                                 (PalmPalette8bpp[i][2]));
     }
-    cm->color_entries[231] = 0xFF000000;
-    cm->ncolors = 232;
+    cmP->color_entries[231] = 0xFF000000;
+    cmP->ncolors = 232;
 
     /* now sort the table */
-    qsort (cm->color_entries, cm->ncolors, sizeof(Color_s), 
-           palmcolor_compare_colors);
-    return cm;
+    qsort(cmP->color_entries, cmP->ncolors, sizeof(ColormapEntry), 
+          palmcolor_compare_colors);
+    return cmP;
 }
 
 
 
-Colormap
-palmcolor_build_custom_8bit_colormap(unsigned int const rows,
+Colormap *
+palmcolor_build_custom_8bit_colormap(pixel **     const pixels,
+                                     unsigned int const rows,
                                      unsigned int const cols,
-                                     pixel **     const pixels) {
+                                     pixval       const maxval) {
+
     unsigned int row;
-    Colormap colormap;
+    Colormap * colormapP;
 
-    MALLOCVAR_NOFAIL(colormap);
-    colormap->nentries = 256;
-    MALLOCARRAY_NOFAIL(colormap->color_entries, colormap->nentries);
-    colormap->ncolors = 0;  /* initial value */
+    MALLOCVAR_NOFAIL(colormapP);
+    colormapP->nentries = 256;
+    MALLOCARRAY_NOFAIL(colormapP->color_entries, colormapP->nentries);
+    colormapP->ncolors = 0;  /* initial value */
     
     for (row = 0; row < rows; ++row) {
         unsigned int col;
         for (col = 0; col < cols; ++col) {
-            Color found;
-            Color_s temp;
-
-            temp = ((PPM_GETR(pixels[row][col]) << 16) |
-                    (PPM_GETG(pixels[row][col]) << 8) |
-                    PPM_GETB(pixels[row][col]));
-            found = (bsearch (&temp,
-                              colormap->color_entries, colormap->ncolors,
-                              sizeof(Color_s), palmcolor_compare_colors));
-            if (!found) {
-                if (colormap->ncolors >= colormap->nentries)
+            ColormapEntry * foundEntryP;
+            ColormapEntry const searchTarget =
+                palmcolor_mapEntryColorFmPixel(pixels[row][col], maxval, 255);
+
+            foundEntryP =
+                bsearch(&searchTarget,
+                        colormapP->color_entries, colormapP->ncolors,
+                        sizeof(ColormapEntry), palmcolor_compare_colors);
+            if (!foundEntryP) {
+                if (colormapP->ncolors >= colormapP->nentries)
                     pm_error("Too many colors for custom colormap "
-                             "(max 256).  "
+                             "(max %u).  "
                              "Try using pnmquant to reduce the number "
-                             "of colors.");
+                             "of colors.", colormapP->nentries);
                 else {
                     /* add the new color, and re-sort */
-                    temp |= ((colormap->ncolors) << 24);
-                    colormap->color_entries[colormap->ncolors] = temp;
-                    colormap->ncolors += 1;
-                    qsort(colormap->color_entries, colormap->ncolors, 
-                          sizeof(Color_s), palmcolor_compare_colors);
+                    unsigned int const colorIndex = colormapP->ncolors++;
+                    ColormapEntry const newEntry =
+                        searchTarget | (colorIndex << 24);
+                    colormapP->color_entries[colorIndex] = newEntry;
+                    qsort(colormapP->color_entries, colormapP->ncolors, 
+                          sizeof(ColormapEntry), palmcolor_compare_colors);
                 }
             }
         }
     }
-    return colormap;
+    return colormapP;
 }
 
 
 
-Colormap
+Colormap *
 palmcolor_read_colormap (FILE * const ifP) {
 
     unsigned short ncolors;
-    Colormap retval;
+    Colormap * retval;
     int rc;
     
     rc = pm_readbigshort(ifP, (short *) &ncolors);
@@ -252,13 +282,13 @@ palmcolor_read_colormap (FILE * const ifP) {
         retval = NULL;
     else {
         long colorentry;
-        Colormap colormap;
+        Colormap * colormapP;
         unsigned int i;
         bool error;
 
-        MALLOCVAR_NOFAIL(colormap);
-        colormap->nentries = ncolors;
-        MALLOCARRAY_NOFAIL(colormap->color_entries, colormap->nentries);
+        MALLOCVAR_NOFAIL(colormapP);
+        colormapP->nentries = ncolors;
+        MALLOCARRAY_NOFAIL(colormapP->color_entries, colormapP->nentries);
         
         for (i = 0, error = FALSE;  i < ncolors && !error;  ++i) {
             int rc;
@@ -266,15 +296,15 @@ palmcolor_read_colormap (FILE * const ifP) {
             if (rc != 0)
                 error = TRUE;
             else
-                colormap->color_entries[i] = (colorentry & 0xFFFFFFFF);
+                colormapP->color_entries[i] = (colorentry & 0xFFFFFFFF);
         }
         if (error) {
-            free (colormap->color_entries);
-            free (colormap);
+            free (colormapP->color_entries);
+            free (colormapP);
             retval = NULL;
         } else {
-            colormap->ncolors = ncolors;
-            retval = colormap;
+            colormapP->ncolors = ncolors;
+            retval = colormapP;
         }
     }
     return retval;
diff --git a/converter/other/pnmtopalm/palmcolormap.h b/converter/other/pnmtopalm/palmcolormap.h
new file mode 100644
index 00000000..cbdb2031
--- /dev/null
+++ b/converter/other/pnmtopalm/palmcolormap.h
@@ -0,0 +1,25 @@
+#ifndef PALMCOLORMAP_H_INCLUDED
+#define PALMCOLORMAP_H_INCLUDED
+
+#include <stdio.h>
+#include "ppm.h"
+#include "palm.h"
+
+ColormapEntry
+palmcolor_mapEntryColorFmPixel(pixel    const color,
+                               pixval const maxval,
+                               pixval const newMaxval);
+
+Colormap *
+palmcolor_build_custom_8bit_colormap(pixel **     const pixels,
+                                     unsigned int const rows,
+                                     unsigned int const cols,
+                                     pixval       const maxval);
+
+Colormap *
+palmcolor_build_default_8bit_colormap(void);
+
+Colormap *
+palmcolor_read_colormap (FILE * const ifP);
+
+#endif
diff --git a/converter/other/pnmtopalm/palmtopnm.c b/converter/other/pnmtopalm/palmtopnm.c
index 0f76207d..b3c0321a 100644
--- a/converter/other/pnmtopalm/palmtopnm.c
+++ b/converter/other/pnmtopalm/palmtopnm.c
@@ -21,17 +21,18 @@
 #include "mallocvar.h"
 
 #include "palm.h"
+#include "palmcolormap.h"
 
 
 
-enum palmCompressionType {
-    COMPRESSION_NONE, 
-    COMPRESSION_RLE, 
+enum PalmCompressionType {
+    COMPRESSION_NONE,
+    COMPRESSION_RLE,
     COMPRESSION_SCANLINE,
     COMPRESSION_PACKBITS
 };
 
-struct palmHeader {
+struct PalmHeader {
     unsigned short cols;
     unsigned short rows;
     unsigned short bytesPerRow;
@@ -46,7 +47,7 @@ struct palmHeader {
     unsigned int   pixelSize;
     unsigned char  version;
     unsigned int   transparentIndex;
-    enum palmCompressionType compressionType;
+    enum PalmCompressionType compressionType;
     /* version 3 encoding specific */
     unsigned char  size;
     unsigned char  pixelFormat;
@@ -56,7 +57,7 @@ struct palmHeader {
 
 
 
-struct directPixelFormat {
+struct DirectPixelFormat {
     unsigned int redbits;
     unsigned int greenbits;
     unsigned int bluebits;
@@ -64,15 +65,15 @@ struct directPixelFormat {
 
 
 
-struct directColorInfo {
-    struct directPixelFormat pixelFormat;
-    Color_s                  transparentColor;
+struct DirectColorInfo {
+    struct DirectPixelFormat pixelFormat;
+    ColormapEntry            transparentColor;
 };
 
 
 
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
@@ -85,13 +86,13 @@ struct cmdlineInfo {
 
 
 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 = malloc( 100*sizeof( optEntry ) );
+    optEntry * option_def;
         /* Instructions to pm_optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
@@ -100,6 +101,8 @@ parseCommandLine(int argc, char ** argv,
 
     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);
@@ -107,23 +110,23 @@ parseCommandLine(int argc, char ** argv,
             &cmdlineP->showhist, 0);
     OPTENT3(0, "transparent",    OPT_FLAG, NULL,
             &cmdlineP->transparent, 0);
-    OPTENT3(0, "rendition",  OPT_UINT, &cmdlineP->rendition, 
+    OPTENT3(0, "rendition",  OPT_UINT, &cmdlineP->rendition,
             &renditionSpec, 0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
 
-    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
 
     if (renditionSpec) {
         if (cmdlineP->rendition < 1)
             pm_error("The -rendition value must be at least 1");
-    } else 
+    } else
         cmdlineP->rendition = 1;
-    
+
     if (cmdlineP->transparent && cmdlineP->showhist)
         pm_error("You can't specify -showhist with -transparent");
 
@@ -135,12 +138,13 @@ parseCommandLine(int argc, char ** argv,
             pm_error("Too many arguments (%d).  The only non-option "
                      "argument is the file name", argc-1);
     }
+    free(option_def);
 }
 
 
 
 static xelval *
-createGraymap(unsigned int const ncolors, 
+createGraymap(unsigned int const ncolors,
               xelval       const maxval) {
     int i;
     xelval *map;
@@ -154,8 +158,8 @@ createGraymap(unsigned int const ncolors,
 
 
 
-static void 
-skipbytes(FILE *       const ifP, 
+static void
+skipbytes(FILE *       const ifP,
           unsigned int const nbytes) {
 
     unsigned char buf[256];
@@ -176,15 +180,15 @@ skipbytes(FILE *       const ifP,
                pm_error("Error reading Palm file.  Short read.");
             n = 0;
         }
-    }    
+    }
 }
 
 
 
 static void
 interpretCompression(unsigned char              const compressionValue,
-                     enum palmCompressionType * const compressionTypeP) {
-    
+                     enum PalmCompressionType * const compressionTypeP) {
+
     switch (compressionValue) {
     case PALM_COMPRESSION_RLE:
         *compressionTypeP = COMPRESSION_RLE;
@@ -220,17 +224,18 @@ readRestOfHeaderVersion3(FILE *           const ifP,
                          short *          const nextDepthOffsetP) {
 
     unsigned char unused;
-    
+
     pm_readcharu(ifP, sizeP);
     /* should be 0x18, but I can't see why we should really care */
     if (*sizeP != 0x18)
-        pm_message("Strange value for Palm bitmap header size: %hu", *sizeP);
+        pm_message("Strange value for Palm bitmap header size: %u",
+                   (unsigned)*sizeP);
 
     pm_readcharu(ifP, pixelFormatP);
     if (*pixelFormatP != PALM_FORMAT_INDEXED &&
         *pixelFormatP != PALM_FORMAT_565)
         pm_error("Unrecognized pixelformat type: %u", *pixelFormatP);
-    
+
     pm_readcharu(ifP, &unused);
 
 
@@ -244,15 +249,15 @@ readRestOfHeaderVersion3(FILE *           const ifP,
         *densityP != PALM_DENSITY_TRIPLE &&
         *densityP != PALM_DENSITY_QUADRUPLE)
         pm_error("Invalid value for -density: %d.", *densityP);
- 
+
     pm_readbiglong(ifP, transparentValueP);
     if (pixelSize < 16)
         *transparentIndexP = *transparentValueP;
     else
         *transparentIndexP = 0;
- 
+
     pm_readbiglong(ifP, nextBitmapOffsetP);
-    
+
     /* version < 3 specific */
     *nextDepthOffsetP = 0;
 }
@@ -269,18 +274,18 @@ readRestOfHeaderOld(FILE *           const ifP,
                     long *           const transparentValueP,
                     long *           const nextBitmapOffsetP,
                     short *          const nextDepthOffsetP) {
-    
+
     short pad;
     unsigned char transparentIndex;
-    
+
     pm_readbigshort(ifP, nextDepthOffsetP);
     pm_readcharu(ifP, &transparentIndex);
     *transparentIndexP = transparentIndex;
- 
+
     pm_readcharu(ifP,compressionTypeP);
-    
+
     pm_readbigshort(ifP, &pad); /* reserved by Palm as of 8/9/00 */
-    
+
     /* version 3 specific */
     *sizeP = 0;
     *pixelFormatP = 0;
@@ -292,7 +297,7 @@ readRestOfHeaderOld(FILE *           const ifP,
 
 
 static void
-interpretHeader(struct palmHeader * const palmHeaderP,
+interpretHeader(struct PalmHeader * const palmHeaderP,
                 short               const cols,
                 short               const rows,
                 short               const bytesPerRow,
@@ -306,7 +311,7 @@ interpretHeader(struct palmHeader * const palmHeaderP,
                 long                const transparentValue,
                 unsigned int        const transparentIndex,
                 unsigned char       const compressionType) {
-    
+
     palmHeaderP->cols = cols;
     palmHeaderP->rows = rows;
     palmHeaderP->bytesPerRow = bytesPerRow;
@@ -329,11 +334,11 @@ interpretHeader(struct palmHeader * const palmHeaderP,
         */
         pm_error("PALM_DIRECT_COLOR_FLAG is set but pixelFormat is not"
                  "PALM_FORMAT_565.");
-        
-    palmHeaderP->directColor = ((flags & PALM_DIRECT_COLOR_FLAG) || 
+
+    palmHeaderP->directColor = ((flags & PALM_DIRECT_COLOR_FLAG) ||
                                 palmHeaderP->pixelFormat == PALM_FORMAT_565);
-    
-    if (flags & PALM_IS_COMPRESSED_FLAG) 
+
+    if (flags & PALM_IS_COMPRESSED_FLAG)
         interpretCompression(compressionType,
                              &palmHeaderP->compressionType);
     else
@@ -345,7 +350,7 @@ interpretHeader(struct palmHeader * const palmHeaderP,
 static void
 readHeader(FILE *              const ifP,
            unsigned int        const requestedRendition,
-           struct palmHeader * const palmHeaderP) {
+           struct PalmHeader * const palmHeaderP) {
 /*----------------------------------------------------------------------------
    Read the Palm Bitmap header from the file 'ifP'.  Read past all
    renditions up to 'requestedRendition' and read the header of that
@@ -358,7 +363,7 @@ readHeader(FILE *              const ifP,
     currentRendition = 1;
     while (!gotHeader) {
         short cols, rows, bytesPerRow, flags, nextDepthOffset, density;
-        unsigned char pixelSizeCode, version, compressionType, 
+        unsigned char pixelSizeCode, version, compressionType,
             size, pixelFormat;
         long transparentValue, nextBitmapOffset;
         unsigned int pixelSize, transparentIndex;
@@ -367,7 +372,7 @@ readHeader(FILE *              const ifP,
         pm_readbigshort(ifP, &rows);
         pm_readbigshort(ifP, &bytesPerRow);
         pm_readbigshort(ifP, &flags);
- 
+
         pm_readcharu(ifP, &pixelSizeCode);
         pixelSize = pixelSizeCode == 0 ? 1 : pixelSizeCode;
         if (pixelSizeCode != 0x00 &&
@@ -384,12 +389,12 @@ readHeader(FILE *              const ifP,
                      "bits per pixel.", bytesPerRow, cols, pixelSize);
 
         pm_readcharu(ifP, &version);
-        if (version > 3) 
+        if (version > 3)
             pm_error("Unknown encoding version type: %d", version);
         else if (version == 3)
             readRestOfHeaderVersion3(ifP, pixelSize,
                                      &size, &pixelFormat, &compressionType,
-                                     &density, &transparentIndex, 
+                                     &density, &transparentIndex,
                                      &transparentValue, &nextBitmapOffset,
                                      &nextDepthOffset);
         else
@@ -400,29 +405,29 @@ readHeader(FILE *              const ifP,
                                 &nextDepthOffset);
 
         if (currentRendition < requestedRendition) {
-             if (version < 3 && nextDepthOffset == 0 && pixelSizeCode != 0xFF) 
+             if (version < 3 && nextDepthOffset == 0 && pixelSizeCode != 0xFF)
                  pm_error("Not enough renditions in the input Palm Bitmap "
                           "to extract the %dth", requestedRendition);
-             if (version == 3 && nextBitmapOffset == 0) 
+             if (version == 3 && nextBitmapOffset == 0)
                  pm_error("Not enough renditions in the input Palm Bitmap "
                           "to extract the %dth", requestedRendition);
              /* nextDepthOffset is calculated in 4 byte words
-                from the beginning of this bitmap (so it equals its size) 
+                from the beginning of this bitmap (so it equals its size)
              */
              if (version < 3 && pixelSizeCode != 0xFF )
                  skipbytes(ifP, (nextDepthOffset*4)-16);
              else if (version == 3)
                  /* FIXME rewrite skipbytes to accept longs? */
-                 skipbytes(ifP, (short) nextBitmapOffset-24); 
+                 skipbytes(ifP, (short) nextBitmapOffset-24);
              if (pixelSizeCode != 0xFF)
                  ++currentRendition;
         } else if (pixelSizeCode != 0xFF) {
             gotHeader = TRUE;
-            
+
             interpretHeader(palmHeaderP,
                             cols, rows, bytesPerRow, flags, pixelSizeCode,
                             pixelSize, version, size, pixelFormat, density,
-                            transparentValue, transparentIndex, 
+                            transparentValue, transparentIndex,
                             compressionType);
         }
     }
@@ -441,8 +446,8 @@ yesno(bool const arg) {
 
 
 static void
-reportPalmHeader(struct palmHeader      const palmHeader,
-                 struct directColorInfo const directColorInfo) {
+reportPalmHeader(struct PalmHeader      const palmHeader,
+                 struct DirectColorInfo const directColorInfo) {
 
     const char *ctype;
 
@@ -462,9 +467,9 @@ reportPalmHeader(struct palmHeader      const palmHeader,
     }
     pm_message("Dimensions: %hu columns x %hu rows",
                palmHeader.cols, palmHeader.rows);
-    pm_message("Row layout: %hu bytes per row, %hu bits per pixel",
+    pm_message("Row layout: %hu bytes per row, %u bits per pixel",
                palmHeader.bytesPerRow, palmHeader.pixelSize);
-    pm_message("Pixel Size code: %hu", palmHeader.pixelSizeCode);
+    pm_message("Pixel Size code: %u", (unsigned)palmHeader.pixelSizeCode);
     pm_message("Flags: 0x%04hx", palmHeader.flags);
     pm_message("  Direct Color: %s", yesno(palmHeader.directColor));
     pm_message("  Colormap:     %s", yesno(palmHeader.hasColormap));
@@ -473,10 +478,10 @@ reportPalmHeader(struct palmHeader      const palmHeader,
     if (palmHeader.hasTransparency) {
         if (palmHeader.directColor) {
             /* Copied from doTransparent(...) */
-            Color_s const color = directColorInfo.transparentColor;
-            pm_message("Transparent value: #%02x%02x%02x", 
-                       (unsigned int)((color >> 16) & 0xFF), 
-                       (unsigned int)((color >>  8) & 0xFF), 
+            ColormapEntry const color = directColorInfo.transparentColor;
+            pm_message("Transparent value: #%02x%02x%02x",
+                       (unsigned int)((color >> 16) & 0xFF),
+                       (unsigned int)((color >>  8) & 0xFF),
                        (unsigned int)((color >>  0) & 0xFF));
         } else
             pm_message("Transparent index: %u", palmHeader.transparentIndex);
@@ -489,7 +494,7 @@ reportPalmHeader(struct palmHeader      const palmHeader,
 
 
 static void
-determineOutputFormat(struct palmHeader const palmHeader,
+determineOutputFormat(struct PalmHeader const palmHeader,
                       int *             const formatP,
                       xelval *          const maxvalP) {
 
@@ -515,14 +520,14 @@ determineOutputFormat(struct palmHeader const palmHeader,
 
 static void
 readRgbFormat(FILE *                     const ifP,
-              struct directPixelFormat * const pixelFormatP) {
+              struct DirectPixelFormat * const pixelFormatP) {
 
     unsigned char r, g, b;
 
     pm_readcharu(ifP, &r);
     pm_readcharu(ifP, &g);
     pm_readcharu(ifP, &b);
-    
+
     if (r != 5 || g != 6 || b != 5)
         pm_error("This image has a direct color pixel format of "
                  "%u red, %u green, %u blue bits.  This program "
@@ -537,8 +542,8 @@ readRgbFormat(FILE *                     const ifP,
 
 
 static void
-readDirectTransparentColor(FILE *    const ifP,
-                           Color_s * const colorP) {
+readDirectTransparentColor(FILE *          const ifP,
+                           ColormapEntry * const colorP) {
 
     unsigned char r, g, b;
 
@@ -553,8 +558,8 @@ readDirectTransparentColor(FILE *    const ifP,
 
 static void
 readDirectInfoType(FILE *                   const ifP,
-                   struct palmHeader        const palmHeader,
-                   struct directColorInfo * const directInfoTypeP) {
+                   struct PalmHeader        const palmHeader,
+                   struct DirectColorInfo * const directInfoTypeP) {
 /*----------------------------------------------------------------------------
    Read the Palm Bitmap Direct Info Type section, if any.
 
@@ -568,12 +573,12 @@ readDirectInfoType(FILE *                   const ifP,
     if ((palmHeader.directColor) && palmHeader.pixelSize != 16)
         pm_error("The image is of the direct color type, but has %u "
                  "bits per pixel.  The only kind of direct color images "
-                 "this program understands are 16 bit ones.", 
+                 "this program understands are 16 bit ones.",
                  palmHeader.pixelSize);
 
     if (palmHeader.version == 3) {
         /* All direct color info is in the header, because it'sversion
-           3 encoding.  No Direct Info Type section.  
+           3 encoding.  No Direct Info Type section.
         */
     } else {
         if (palmHeader.directColor) {
@@ -584,7 +589,7 @@ readDirectInfoType(FILE *                   const ifP,
             pm_readcharu(ifP, &padding);
             pm_readcharu(ifP, &padding);
 
-            readDirectTransparentColor(ifP, 
+            readDirectTransparentColor(ifP,
                                        &directInfoTypeP->transparentColor);
         } else {
             /* Not a direct color image; no Direct Info Type section. */
@@ -596,8 +601,8 @@ readDirectInfoType(FILE *                   const ifP,
 
 static void
 readColormap(FILE *            const ifP,
-             struct palmHeader const palmHeader,
-             Colormap *        const colormapP) {
+             struct PalmHeader const palmHeader,
+             Colormap **       const colormapPP) {
 /*----------------------------------------------------------------------------
    Read the colormap, if any from the Palm Bitmap.
 
@@ -605,18 +610,18 @@ readColormap(FILE *            const ifP,
    return an undefined value as *colormapP.
 -----------------------------------------------------------------------------*/
     if (palmHeader.hasColormap)
-        *colormapP = palmcolor_read_colormap(ifP);
+        *colormapPP = palmcolor_read_colormap(ifP);
 }
 
 
 
 static void
-getColorInfo(struct palmHeader        const palmHeader,
-             struct directColorInfo   const directInfoType,
-             Colormap                 const colormapFromImage,
-             Colormap *               const colormapP,
+getColorInfo(struct PalmHeader        const palmHeader,
+             struct DirectColorInfo   const directInfoType,
+             Colormap *               const colormapFromImageP,
+             Colormap **              const colormapPP,
              unsigned int *           const ncolorsP,
-             struct directColorInfo * const directColorInfoP) {
+             struct DirectColorInfo * const directColorInfoP) {
 /*----------------------------------------------------------------------------
    Gather color encoding information from the various sources.
 
@@ -626,14 +631,14 @@ getColorInfo(struct palmHeader        const palmHeader,
    If it's a version 3 direct color, the pixel format must be "565".
 -----------------------------------------------------------------------------*/
     if (palmHeader.version == 3 && palmHeader.directColor) {
-        *colormapP = NULL;
+        *colormapPP = NULL;
 
         assert(palmHeader.pixelFormat == PALM_FORMAT_565);
 
         directColorInfoP->pixelFormat.redbits   = 5;
         directColorInfoP->pixelFormat.greenbits = 6;
         directColorInfoP->pixelFormat.bluebits  = 5;
-        directColorInfoP->transparentColor = 
+        directColorInfoP->transparentColor =
             /* See convertRowToPnmDirect for this trick
 
                This will break once maxval isn't always set 255 for
@@ -646,18 +651,18 @@ getColorInfo(struct palmHeader        const palmHeader,
             ((((palmHeader.transparentValue >>  0) & 0x1F) * 255 / 0x1F)
              <<  0);
     } else if (palmHeader.directColor) {
-        *colormapP = NULL;
+        *colormapPP = NULL;
         *directColorInfoP = directInfoType;
     } else if (palmHeader.hasColormap)
-        *colormapP = colormapFromImage;
+        *colormapPP = colormapFromImageP;
     else if (palmHeader.pixelSize >= 8) {
-        Colormap colormap;
-        colormap = palmcolor_build_default_8bit_colormap();
-        qsort(colormap->color_entries, colormap->ncolors, 
-              sizeof(Color_s), palmcolor_compare_indices);
-        *colormapP = colormap;
+        Colormap * const colormapP =
+            palmcolor_build_default_8bit_colormap();
+        qsort(colormapP->color_entries, colormapP->ncolors,
+              sizeof(ColormapEntry), palmcolor_compare_indices);
+        *colormapPP = colormapP;
     } else
-        *colormapP = NULL;
+        *colormapPP = NULL;
 
     *ncolorsP = 1 << palmHeader.pixelSize;
 }
@@ -670,8 +675,8 @@ doTransparent(FILE *                 const ofP,
               bool                   const directColor,
               unsigned char          const transparentIndex,
               unsigned char          const pixelSize,
-              Colormap               const colormap,
-              struct directColorInfo const directColorInfo) {
+              Colormap *             const colormapP,
+              struct DirectColorInfo const directColorInfo) {
 /*----------------------------------------------------------------------------
    Generate a PNM comment on *ofP telling what color in the raster is
    supposed to be transparent.
@@ -681,31 +686,32 @@ doTransparent(FILE *                 const ofP,
    RGB_ALPHA and GRAYSCALE_ALPHA tuple types).
 -----------------------------------------------------------------------------*/
     if (hasTransparency) {
-        if (colormap) {
-            Color_s const color = transparentIndex << 24;
-            Color const actualColor = (bsearch(&color,
-                                               colormap->color_entries, 
-                                               colormap->ncolors,
-                                               sizeof(color), 
-                                               palmcolor_compare_indices));
-            if (!actualColor)
+        if (colormapP) {
+            ColormapEntry const searchTarget = transparentIndex << 24;
+            ColormapEntry * const foundEntryP =
+                bsearch(&searchTarget,
+                        colormapP->color_entries,
+                        colormapP->ncolors,
+                        sizeof(searchTarget),
+                        palmcolor_compare_indices);
+            if (!foundEntryP)
                 pm_error("Invalid input; transparent index %u "
                          "is not among the %u colors in the image's colormap",
-                         transparentIndex, colormap->ncolors);
+                         transparentIndex, colormapP->ncolors);
 
-            fprintf(ofP, "#%02x%02x%02x\n", 
-                   (unsigned int) ((*actualColor >> 16) & 0xFF),
-                   (unsigned int) ((*actualColor >>  8) & 0xFF), 
-                   (unsigned int) ((*actualColor >>  0) & 0xFF));
+            fprintf(ofP, "#%02x%02x%02x\n",
+                   (unsigned int) ((*foundEntryP >> 16) & 0xFF),
+                   (unsigned int) ((*foundEntryP >>  8) & 0xFF),
+                   (unsigned int) ((*foundEntryP >>  0) & 0xFF));
         } else if (directColor) {
-            Color_s const color = directColorInfo.transparentColor;
-            fprintf(ofP, "#%02x%02x%02x\n", 
-                   (unsigned int)((color >> 16) & 0xFF), 
-                   (unsigned int)((color >>  8) & 0xFF), 
+            ColormapEntry const color = directColorInfo.transparentColor;
+            fprintf(ofP, "#%02x%02x%02x\n",
+                   (unsigned int)((color >> 16) & 0xFF),
+                   (unsigned int)((color >>  8) & 0xFF),
                    (unsigned int)((color >>  0) & 0xFF));
         } else {
             unsigned int const maxval = pm_bitstomaxval(pixelSize);
-            unsigned int const grayval = 
+            unsigned int const grayval =
                 ((maxval - transparentIndex) * 256) / maxval;
             fprintf(ofP, "#%02x%02x%02x\n", grayval, grayval, grayval);
         }
@@ -723,10 +729,10 @@ createHistogram(unsigned int    const ncolors,
     MALLOCARRAY(seen, ncolors);
     if (!seen)
         pm_error("Can't allocate array for keeping track of "
-                 "how many pixels of each of %u colors are in the image.", 
+                 "how many pixels of each of %u colors are in the image.",
                  ncolors);
 
-    {    
+    {
         /* Initialize the counter for each color to zero */
         unsigned int i;
         for (i = 0; i < ncolors; ++i)
@@ -761,7 +767,7 @@ readScanlineRow(FILE *          const ifP,
 
         pm_readcharu(ifP, &diffmask);
         byteCount = MIN(bytesPerRow - j, 8);
-        
+
         for (k = 0; k < byteCount; ++k) {
             /* the first row cannot be compressed */
             if (firstRow || ((diffmask & (1 << (7 - k))) != 0)) {
@@ -800,7 +806,7 @@ readRleRow(FILE *          const ifP,
         memset(palmrow + j, inval, incount);
         j += incount;
     }
-} 
+}
 
 
 
@@ -810,7 +816,7 @@ readPackBitsRow16(FILE *          const ifP,
                   unsigned int    const bytesPerRow) {
 
     /*  From the Palm OS Programmer's API Reference:
-  
+
         Although the [...] spec is byte-oriented, the 16-bit algorithm is
         identical [to the 8-bit algorithm]: just substitute "word" for "byte".
     */
@@ -819,7 +825,7 @@ readPackBitsRow16(FILE *          const ifP,
     for (j = 0;  j < bytesPerRow; ) {
         char incount;
         pm_readchar(ifP, &incount);
-        if (incount < 0) { 
+        if (incount < 0) {
             /* How do we handle incount == -128 ? */
             unsigned int const runlength = (-incount + 1) * 2;
             unsigned int k;
@@ -841,13 +847,13 @@ readPackBitsRow16(FILE *          const ifP,
             }
             j += nonrunlength;
         }
-        if (j > bytesPerRow) 
+        if (j > bytesPerRow)
             pm_error("Bytes in PackBits compressed row exceed bytes per row.  "
                      "Bytes per row is %u.  "
                      "The bytes in this row were pushed up to %u bytes "
                      "(and then we gave up).", bytesPerRow, j);
     }
-} 
+}
 
 
 
@@ -861,7 +867,7 @@ readPackBitsRow(FILE *          const ifP,
     for (j = 0;  j < bytesPerRow; ) {
         char incount;
         pm_readchar(ifP, &incount);
-        if (incount < 0) { 
+        if (incount < 0) {
             /* How do we handle incount == -128 ? */
             unsigned int const runlength = -incount + 1;
             unsigned char inval;
@@ -879,13 +885,13 @@ readPackBitsRow(FILE *          const ifP,
             }
             j += nonrunlength;
         }
-        if (j > bytesPerRow) 
+        if (j > bytesPerRow)
             pm_error("Bytes in PackBits compressed row exceed bytes per row.  "
                      "Bytes per row is %u.  "
                      "The bytes in this row were pushed up to %u bytes "
                      "(and then we gave up).", bytesPerRow, j);
     }
-} 
+}
 
 
 
@@ -895,7 +901,7 @@ readUncompressedRow(FILE *          const ifP,
                     unsigned int    const bytesPerRow) {
 
     int bytesRead;
-    
+
     bytesRead = fread(palmrow, 1, bytesPerRow, ifP);
     if (bytesRead != bytesPerRow)
         pm_error("Error reading Palm file.  Short read.");
@@ -907,7 +913,7 @@ static void
 readDecompressedRow(FILE *                   const ifP,
                     unsigned char *          const palmrow,
                     unsigned char *          const lastrow,
-                    enum palmCompressionType const compressionType,
+                    enum PalmCompressionType const compressionType,
                     unsigned int             const bytesPerRow,
                     unsigned int             const pixelSize,
                     bool                     const firstRow) {
@@ -968,27 +974,27 @@ convertRowToPnmDirect(const unsigned char * const palmrow,
        blue=0x1F.  How do we promote those colors?  Simple
        shift would give us R=248,G=252,B=248; which is
        slightly green.  Hardly seems right.
-       
+
        So I've perverted the math a bit.  Each color value is
        multiplied by 255, then divided by either 31 (red or
        blue) or 63 (green).  That's the right way to do it
-       anyway.  
+       anyway.
     */
 
     const unsigned char *inbyte;
     unsigned int j;
-    
+
     for (inbyte = palmrow, j = 0;  j < cols;  ++j) {
         unsigned int inval;
         inval = *inbyte++ << 8;
         inval |= *inbyte++;
-        
+
         if (seen)
             ++seen[inval];
-        
-        PPM_ASSIGN(xelrow[j], 
-                   (((inval >> 11) & 0x1F) * maxval) / 0x1F, 
-                   (((inval >>  5) & 0x3F) * maxval) / 0x3F, 
+
+        PPM_ASSIGN(xelrow[j],
+                   (((inval >> 11) & 0x1F) * maxval) / 0x1F,
+                   (((inval >>  5) & 0x3F) * maxval) / 0x3F,
                    (((inval >>  0) & 0x1F) * maxval) / 0x1F
             );
     }
@@ -1000,46 +1006,50 @@ static void
 convertRowToPnmNotDirect(const unsigned char * const palmrow,
                          xel *                 const xelrow,
                          unsigned int          const cols,
-                         Colormap              const colormap,
+                         Colormap *            const colormapP,
                          xelval *              const graymap,
                          unsigned int *        const seen,
                          unsigned int          const pixelSize) {
 
     unsigned int const mask = (1 << pixelSize) - 1;
 
-    const unsigned char *inbyte;
+    const unsigned char * inbyteP;
     unsigned int inbit;
     unsigned int j;
-    
+
+    assert(pixelSize <= 8);
+
     inbit = 8 - pixelSize;
-    inbyte = palmrow;
+    inbyteP = &palmrow[0];
     for (j = 0; j < cols; ++j) {
-        short const color = ((*inbyte) & (mask << inbit)) >> inbit;
+        short const color = (*inbyteP & (mask << inbit)) >> inbit;
         if (seen)
             ++seen[color];
-        
-        if (colormap) {
-            Color_s const color2      = color << 24;
-            Color   const actualColor = (bsearch (&color2,
-                                                  colormap->color_entries, 
-                                                  colormap->ncolors,
-                                                  sizeof(color2), 
-                                                  palmcolor_compare_indices));
-            if (!actualColor)
+
+        if (colormapP) {
+            ColormapEntry const searchTarget = color << 24;
+            ColormapEntry * const foundEntryP =
+                bsearch(&searchTarget,
+                        colormapP->color_entries,
+                        colormapP->ncolors,
+                        sizeof(searchTarget),
+                        palmcolor_compare_indices);
+
+            if (!foundEntryP)
                 pm_error("Invalid input.  A color index in column %u "
                          "is %u, which is not among the %u colors "
                          "in the colormap",
-                         j, color, colormap->ncolors);
+                         j, color, colormapP->ncolors);
 
-            PPM_ASSIGN(xelrow[j], 
-                       (*actualColor >> 16) & 0xFF, 
-                       (*actualColor >>  8) & 0xFF, 
-                       (*actualColor >>  0) & 0xFF);
+            PPM_ASSIGN(xelrow[j],
+                       (*foundEntryP >> 16) & 0xFF,
+                       (*foundEntryP >>  8) & 0xFF,
+                       (*foundEntryP >>  0) & 0xFF);
         } else
             PNM_ASSIGN1(xelrow[j], graymap[color]);
-        
+
         if (!inbit) {
-            ++inbyte;
+            ++inbyteP;
             inbit = 8 - pixelSize;
         } else
             inbit -= pixelSize;
@@ -1050,9 +1060,9 @@ convertRowToPnmNotDirect(const unsigned char * const palmrow,
 
 static void
 writePnm(FILE *            const ofP,
-         struct palmHeader const palmHeader,
+         struct PalmHeader const palmHeader,
          FILE *            const ifP,
-         Colormap          const colormap,
+         Colormap *        const colormapP,
          xelval *          const graymap,
          unsigned int      const nColors,
          int               const format,
@@ -1067,22 +1077,22 @@ writePnm(FILE *            const ofP,
     xel *           xelrow;
     unsigned int *  seen;
     unsigned int    row;
-    
+
     pnm_writepnminit(ofP, cols, rows, maxval, format, 0);
     xelrow = pnm_allocrow(cols);
 
     /* Read the picture data, one row at a time */
     MALLOCARRAY_NOFAIL(palmrow, palmHeader.bytesPerRow);
-    MALLOCARRAY_NOFAIL(lastrow, palmHeader.bytesPerRow); 
-    
+    MALLOCARRAY_NOFAIL(lastrow, palmHeader.bytesPerRow);
+
     if (seenP) {
         createHistogram(nColors, &seen);
         *seenP = seen;
     } else
         seen = NULL;
 
-    /* We should actually use compressedDataSizeNN for checking the sanity 
-       of the data we're reading ... 
+    /* We should actually use compressedDataSizeNN for checking the sanity
+       of the data we're reading ...
     */
     if (palmHeader.compressionType != COMPRESSION_NONE) {
         if (palmHeader.version < 3) {
@@ -1095,8 +1105,8 @@ writePnm(FILE *            const ofP,
     }
 
     for (row = 0; row < rows; ++row) {
-        readDecompressedRow(ifP, palmrow, lastrow, 
-                            palmHeader.compressionType, 
+        readDecompressedRow(ifP, palmrow, lastrow,
+                            palmHeader.compressionType,
                             palmHeader.bytesPerRow,
                             palmHeader.pixelSize,
                             row == 0);
@@ -1105,7 +1115,7 @@ writePnm(FILE *            const ofP,
             assert(palmHeader.pixelSize == 16);
             convertRowToPnmDirect(palmrow, xelrow, cols, maxval, seen);
         } else
-            convertRowToPnmNotDirect(palmrow, xelrow, cols, colormap, graymap,
+            convertRowToPnmNotDirect(palmrow, xelrow, cols, colormapP, graymap,
                                      seen, palmHeader.pixelSize);
 
         pnm_writepnmrow(ofP, xelrow, cols, maxval, format, 0);
@@ -1119,28 +1129,29 @@ writePnm(FILE *            const ofP,
 
 static void
 showHistogram(unsigned int * const seen,
-              Colormap       const colormap,
+              Colormap *     const colormapP,
               const xelval * const graymap,
               unsigned int   const ncolors) {
 
     unsigned int colorIndex;
-    
+
     for (colorIndex = 0;  colorIndex < ncolors; ++colorIndex) {
-        if (!colormap)
-            pm_message("%.3d -> %.3d:  %d", 
+        if (!colormapP)
+            pm_message("%.3d -> %.3d:  %d",
                        colorIndex, graymap[colorIndex], seen[colorIndex]);
         else {
-            Color_s const color = colorIndex << 24;
-            Color const actualColor = (bsearch(&color,
-                                               colormap->color_entries, 
-                                               colormap->ncolors,
-                                               sizeof(color), 
-                                               palmcolor_compare_indices));
-            if (actualColor)
+            ColormapEntry const searchTarget = colorIndex << 24;
+            ColormapEntry * const foundEntryP =
+                bsearch(&searchTarget,
+                        colormapP->color_entries,
+                        colormapP->ncolors,
+                        sizeof(searchTarget),
+                        palmcolor_compare_indices);
+            if (foundEntryP)
                 pm_message("%.3d -> %ld,%ld,%ld:  %d", colorIndex,
-                           (*actualColor >> 16) & 0xFF,
-                           (*actualColor >> 8) & 0xFF,
-                           (*actualColor & 0xFF), seen[colorIndex]);
+                           (*foundEntryP >> 16) & 0xFF,
+                           (*foundEntryP >> 8) & 0xFF,
+                           (*foundEntryP & 0xFF), seen[colorIndex]);
         }
     }
 }
@@ -1148,22 +1159,21 @@ showHistogram(unsigned int * const seen,
 
 
 int
-main(int argc, char **argv) {
+main(int argc, const char **argv) {
 
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
 
-    FILE* ifP;
-    struct palmHeader palmHeader;
-    struct directColorInfo directInfoType;
-    Colormap colormapFromImage;
-    Colormap colormap;
-    struct directColorInfo directColorInfo;
+    FILE * ifP;
+    struct PalmHeader palmHeader;
+    struct DirectColorInfo directInfoType;
+    Colormap * colormapFromImageP;
+    Colormap * colormapP;
+    struct DirectColorInfo directColorInfo;
     int format;
     xelval maxval;
     unsigned int nColors;
 
-    /* Parse default params */
-    pnm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
@@ -1172,22 +1182,22 @@ main(int argc, char **argv) {
     readHeader(ifP, cmdline.rendition, &palmHeader);
 
     readDirectInfoType(ifP, palmHeader, &directInfoType);
-   
-    readColormap(ifP, palmHeader, &colormapFromImage);
-    
+
+    readColormap(ifP, palmHeader, &colormapFromImageP);
+
     determineOutputFormat(palmHeader, &format, &maxval);
-    
-    getColorInfo(palmHeader, directInfoType, colormapFromImage,
-                 &colormap, &nColors, &directColorInfo);
+
+    getColorInfo(palmHeader, directInfoType, colormapFromImageP,
+                 &colormapP, &nColors, &directColorInfo);
 
     if (cmdline.verbose)
         reportPalmHeader(palmHeader, directColorInfo);
 
     if (cmdline.transparent)
-        doTransparent(stdout, 
+        doTransparent(stdout,
                       palmHeader.hasTransparency, palmHeader.directColor,
-                      palmHeader.transparentIndex, 
-                      palmHeader.pixelSize, colormap, directColorInfo);
+                      palmHeader.transparentIndex,
+                      palmHeader.pixelSize, colormapP, directColorInfo);
     else {
         unsigned int * seen;
         xelval * graymap;
@@ -1195,12 +1205,12 @@ main(int argc, char **argv) {
         graymap = createGraymap(nColors, maxval);
 
         writePnm(stdout,
-                 palmHeader, ifP, colormap, graymap, nColors, format, maxval, 
+                 palmHeader, ifP, colormapP, graymap, nColors, format, maxval,
                  cmdline.showhist ? &seen : NULL);
-        
+
         if (cmdline.showhist)
-            showHistogram(seen, colormap, graymap, nColors);
-        
+            showHistogram(seen, colormapP, graymap, nColors);
+
         free(graymap);
     }
     pm_close(ifP);
diff --git a/converter/other/pnmtopalm/pnmtopalm.c b/converter/other/pnmtopalm/pnmtopalm.c
index a7d1fd46..25c8af2e 100644
--- a/converter/other/pnmtopalm/pnmtopalm.c
+++ b/converter/other/pnmtopalm/pnmtopalm.c
@@ -29,33 +29,37 @@
 
 #include "pm_c_util.h"
 #include "pnm.h"
-#include "palm.h"
 #include "shhopt.h"
 #include "mallocvar.h"
 #include "runlength.h"
 
-enum compressionType {COMP_NONE, COMP_SCANLINE, COMP_RLE, COMP_PACKBITS};
+#include "palm.h"
+#include "palmcolormap.h"
+
+enum CompressionType {COMP_NONE, COMP_SCANLINE, COMP_RLE, COMP_PACKBITS};
 
-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 * 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;
+    const char * transparent;
+    unsigned int depthSpec;
+    unsigned int depth;
+    unsigned int maxdepthSpec;
+    unsigned int maxdepth;
+    enum CompressionType compression;
     unsigned int verbose;
     unsigned int colormap;
-    unsigned int offset;        /* -offset specified */
-    unsigned int density;       /* screen density */
+    unsigned int offset;
+    unsigned int density;
     unsigned int withdummy;
 };
 
 
 
 static void
-parseCommandLine(int argc, char ** argv, struct cmdline_info *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.
@@ -64,7 +68,7 @@ parseCommandLine(int argc, char ** argv, struct cmdline_info *cmdlineP) {
     optEntry *option_def;
     unsigned int option_def_index;
 
-    unsigned int transSpec, depthSpec, maxdepthSpec, densitySpec;
+    unsigned int transSpec, densitySpec;
     unsigned int scanline_compression, rle_compression, packbits_compression;
 
     MALLOCARRAY_NOFAIL(option_def, 100);
@@ -73,9 +77,9 @@ parseCommandLine(int argc, char ** argv, struct cmdline_info *cmdlineP) {
     OPTENT3(0, "transparent",      OPT_STRING, 
             &cmdlineP->transparent, &transSpec, 0);
     OPTENT3(0, "depth",            OPT_UINT, 
-            &cmdlineP->depth,       &depthSpec, 0);
+            &cmdlineP->depth,       &cmdlineP->depthSpec, 0);
     OPTENT3(0, "maxdepth",         OPT_UINT, 
-            &cmdlineP->maxdepth,    &maxdepthSpec, 0);
+            &cmdlineP->maxdepth,    &cmdlineP->maxdepthSpec, 0);
     OPTENT3(0, "scanline_compression", OPT_FLAG, 
             NULL,                   &scanline_compression, 0);
     OPTENT3(0, "rle_compression",  OPT_FLAG, 
@@ -97,28 +101,26 @@ 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 */
 
-    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdline_p and others. */
 
-    if (depthSpec) {
+    if (cmdlineP->depthSpec) {
         if (cmdlineP->depth != 1 && cmdlineP->depth != 2 
             && cmdlineP->depth != 4 && cmdlineP->depth != 8
             && cmdlineP->depth != 16)
             pm_error("invalid value for -depth: %u.  Valid values are "
                      "1, 2, 4, 8, and 16", cmdlineP->depth);
-    } else
-        cmdlineP->depth = 0;
+    }
 
-    if (maxdepthSpec) {
+    if (cmdlineP->maxdepthSpec) {
         if (cmdlineP->maxdepth != 1 && cmdlineP->maxdepth != 2 
             && cmdlineP->maxdepth != 4 && cmdlineP->maxdepth != 8
             && cmdlineP->maxdepth != 16)
             pm_error("invalid value for -maxdepth: %u.  Valid values are "
                      "1, 2, 4, 8, and 16", cmdlineP->maxdepth);
-    } else
-        cmdlineP->maxdepth = 0;
+    }
 
-    if (depthSpec && maxdepthSpec && 
+    if (cmdlineP->depthSpec && cmdlineP->maxdepthSpec && 
         cmdlineP->depth > cmdlineP->maxdepth)
         pm_error("-depth value (%u) is greater than -maxdepth (%u) value.",
                  cmdlineP->depth, cmdlineP->maxdepth);
@@ -132,8 +134,8 @@ parseCommandLine(int argc, char ** argv, struct cmdline_info *cmdlineP) {
             cmdlineP->density != PALM_DENSITY_DOUBLE &&
             cmdlineP->density != PALM_DENSITY_TRIPLE &&
             cmdlineP->density != PALM_DENSITY_QUADRUPLE)
-            pm_error("Invalid value for -density: %d.  Valid values are "
-                     "%d, %d, %d, %d and %d.", cmdlineP->density, 
+            pm_error("Invalid value for -density: %u.  Valid values are "
+                     "%u, %u, %u, %u and %u.", cmdlineP->density, 
                      PALM_DENSITY_LOW, PALM_DENSITY_ONEANDAHALF,
                      PALM_DENSITY_DOUBLE, PALM_DENSITY_TRIPLE,
                      PALM_DENSITY_QUADRUPLE);
@@ -164,7 +166,7 @@ parseCommandLine(int argc, char ** argv, struct cmdline_info *cmdlineP) {
         
     if (argc-1 > 1)
         pm_error("This program takes at most 1 argument: the file name.  "
-                 "You specified %d", argc-1);
+                 "You specified %u", argc-1);
     else if (argc-1 > 0) 
         cmdlineP->inputFilespec = argv[1];
     else
@@ -173,108 +175,241 @@ parseCommandLine(int argc, char ** argv, struct cmdline_info *cmdlineP) {
 
 
 
+static xelval
+scaleSample(pixval const arg,
+            pixval const oldMaxval,
+            pixval const newMaxval) {
+
+    return (arg * newMaxval + oldMaxval/2) / oldMaxval;
+}
+
+
+
+static void
+determinePalmFormatPgm(xelval               const maxval, 
+                       bool                 const bppSpecified,
+                       unsigned int         const bpp,
+                       bool                 const maxBppSpecified,
+                       unsigned int         const maxBpp, 
+                       bool                 const wantCustomColormap,
+                       enum CompressionType const compression,
+                       bool                 const verbose,
+                       unsigned int *       const bppP) {
+        
+    /* We can usually handle this one, but may not have enough pixels.  So
+       check.
+    */
+
+    if (wantCustomColormap)
+        pm_error("You specified -colormap with a black and white input"
+                 "image.  -colormap is valid only with color.");
+    if (bppSpecified)
+        *bppP = bpp;
+    else if (maxBppSpecified && (maxval >= (1 << maxBpp)))
+        *bppP = maxBpp;
+    else if (compression != COMP_NONE && maxval > 255)
+        *bppP = 8;
+    else if (maxval > 16)
+        *bppP = 4;
+    else {
+        /* scale to minimum number of bpp needed */
+        unsigned int bpp;
+        for (bpp = 1;  (1 << bpp) < maxval;  bpp *= 2)
+            ;
+        *bppP = bpp;
+    }
+    if (verbose)
+        pm_message("output is grayscale %u bits-per-pixel", *bppP);
+}
+
+
+
+static void
+validateImageAgainstStandardColormap(const Colormap * const colormapP,
+                                     xel **           const xels,
+                                     unsigned int     const cols,
+                                     unsigned int     const rows,
+                                     xelval           const maxval) {
+/*----------------------------------------------------------------------------
+   Abort program if the image xels[][] (which is 'cols' x 'rows') contains a
+   color not in the colormap *colormapP, giving an error message assuming the
+   user chose the standard Palm colormap.
+-----------------------------------------------------------------------------*/
+    unsigned int row;
+
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+
+        for (col = 0; col < cols; ++col) {
+            ColormapEntry const searchTarget = 
+                palmcolor_mapEntryColorFmPixel(xels[row][col], maxval, 255);
+
+            ColormapEntry * const foundEntryP = 
+                (bsearch(&searchTarget,
+                         colormapP->color_entries, colormapP->ncolors,
+                         sizeof(ColormapEntry), palmcolor_compare_colors));
+            if (!foundEntryP)
+                pm_error(
+                    "A color in the input image is not in the standard Palm "
+                    "8-bit color palette.  Either adjust the colors in the "
+                    "input with 'pnmremap' and the 'palmcolor8.map' file "
+                    "(see manual) or specify -colormap or -depth=16");
+        }
+    }
+}
+
+
+
+static void
+determinePalmFormatPpm(unsigned int         const cols, 
+                       unsigned int         const rows, 
+                       xelval               const maxval, 
+                       xel **               const xels,
+                       bool                 const bppSpecified,
+                       unsigned int         const bpp,
+                       bool                 const maxBppSpecified,
+                       unsigned int         const maxBpp, 
+                       bool                 const wantCustomColormap,
+                       enum CompressionType const compression,
+                       bool                 const verbose,
+                       unsigned int *       const bppP, 
+                       bool *               const directColorP, 
+                       Colormap **          const colormapPP) {
+            
+    /* We don't attempt to identify PPM files that are actually
+       monochrome.  So there are two options here: either 8-bit with a
+       colormap, either the standard one or a custom one, or 16-bit direct
+       color.  In the colormap case, if 'wantCustomColormap' is true (not
+       recommended by Palm) we will put in our own colormap that has the
+       colors of the input image; otherwise we will select the default
+       Palm colormap and will fail if the input image has any colors that
+       are not in that map (user should use Pnmremap and the
+       palmcolor8.map file that comes with Netpbm to avoid this).  We try
+       for colormapped first, since it works on more PalmOS devices.
+    */
+    if ((bppSpecified && bpp == 16) || 
+        (!bppSpecified && maxBppSpecified && maxBpp == 16)) {
+        /* we do the 16-bit direct color */
+        *directColorP = TRUE;
+        *colormapPP = NULL;
+        *bppP = 16;
+    } else if (!wantCustomColormap) {
+        /* colormapped with the standard colormap */
+        Colormap * colormapP;
+            
+        if ((bppSpecified && bpp != 8) || (maxBppSpecified && maxBpp < 8))
+            pm_error("Must use depth of 8 for color Palm Bitmap without "
+                     "custom color table.");
+        colormapP = palmcolor_build_default_8bit_colormap();
+        validateImageAgainstStandardColormap(colormapP,
+                                             xels, cols, rows, maxval);
+
+        *colormapPP = colormapP;
+        *bppP = 8;
+        *directColorP = FALSE;
+        if (verbose)
+            pm_message("Output is color with default colormap at 8 bpp");
+    } else {
+        /* colormapped with a custom colormap */
+        *colormapPP = 
+            palmcolor_build_custom_8bit_colormap(xels, rows, cols, maxval);
+        for (*bppP = 1; (1 << *bppP) < (*colormapPP)->ncolors; *bppP *= 2);
+        if (bppSpecified) {
+            if (bpp >= *bppP)
+                *bppP = bpp;
+            else
+                pm_error("Too many colors for specified depth.  "
+                         "Specified depth is %u bits; would need %u to "
+                         "represent the %u colors in the image.  "
+                         "Use pnmquant to reduce.",
+                         maxBpp, *bppP, (*colormapPP)->ncolors);
+        } else if (maxBppSpecified && maxBpp < *bppP) {
+            pm_error("Too many colors for specified max depth.  "
+                     "Specified maximum is %u bits; would need %u to "
+                     "represent the %u colors in the image.  "
+                     "Use pnmquant to reduce.",
+                     maxBpp, *bppP, (*colormapPP)->ncolors);
+        } else if (compression != COMP_NONE && *bppP > 8) {
+            pm_error("Too many colors for a compressed image.  "
+                     "Maximum is 256; the image has %u",
+                     (*colormapPP)->ncolors);
+        }
+        *directColorP = FALSE;
+        if (verbose)
+            pm_message("Output is color with custom colormap "
+                       "with %u colors at %u bpp", 
+                       (*colormapPP)->ncolors, *bppP);
+    }
+}
+
+
+
 static void
-determinePalmFormat(unsigned int   const cols, 
-                    unsigned int   const rows, 
-                    xelval         const maxval, 
-                    int            const format, 
-                    xel **         const xels,
-                    unsigned int   const specified_bpp,
-                    unsigned int   const max_bpp, 
-                    bool           const custom_colormap,
-                    bool           const verbose,
-                    unsigned int * const bppP, 
-                    bool *         const directColorP, 
-                    Colormap *     const colormapP) {
+determinePalmFormat(unsigned int         const cols, 
+                    unsigned int         const rows, 
+                    xelval               const maxval, 
+                    int                  const format, 
+                    xel **               const xels,
+                    bool                 const bppSpecified,
+                    unsigned int         const bpp,
+                    bool                 const maxBppSpecified,
+                    unsigned int         const maxBpp, 
+                    bool                 const wantCustomColormap,
+                    enum CompressionType const compression,
+                    bool                 const verbose,
+                    unsigned int *       const bppP, 
+                    bool *               const directColorP, 
+                    Colormap **          const colormapPP) {
+/*----------------------------------------------------------------------------
+   Determine what kind of Palm output file to make.
 
+   Also compute the colormap, if there is to be one.  This could be either one
+   we make up, that needs to go into the image, or a standard one.
+-----------------------------------------------------------------------------*/
+    if (compression != COMP_NONE) {
+        if (bppSpecified && bpp > 8)
+            pm_error("You requested %u bits per pixel and compression.  "
+                     "This program does not know how to generate a "
+                     "compressed image with more than 8 bits per pixel",
+                     bpp);
+        if (maxBppSpecified && maxBpp > 8)
+            pm_error("You requested %u max bits per pixel and compression.  "
+                     "This program does not know how to generate a "
+                     "compressed image with more than 8 bits per pixel",
+                     maxBpp);
+    }
     if (PNM_FORMAT_TYPE(format) == PBM_TYPE) {
-        if (custom_colormap)
+        if (wantCustomColormap)
             pm_error("You specified -colormap with a black and white input "
                      "image.  -colormap is valid only with color.");
-        if (specified_bpp)
-            *bppP = specified_bpp;
+        if (bppSpecified)
+            *bppP = bpp;
         else
             *bppP = 1;    /* no point in wasting bits */
         *directColorP = FALSE;
-        *colormapP = NULL;
+        *colormapPP = NULL;
         if (verbose)
             pm_message("output is black and white");
     } else if (PNM_FORMAT_TYPE(format) == PGM_TYPE) {
-        /* we can usually handle this one, but may not have enough
-           pixels.  So check... */
-        if (custom_colormap)
-            pm_error("You specified -colormap with a black and white input"
-                     "image.  -colormap is valid only with color.");
-        if (specified_bpp)
-            *bppP = specified_bpp;
-        else if (max_bpp && (maxval >= (1 << max_bpp)))
-            *bppP = max_bpp;
-        else if (maxval > 16)
-            *bppP = 4;
-        else {
-            /* scale to minimum number of bpp needed */
-            for (*bppP = 1;  (1 << *bppP) < maxval;  *bppP *= 2)
-                ;
-        }
-        if (*bppP > 4)
-            *bppP = 4;
-        if (verbose)
-            pm_message("output is grayscale %d bits-per-pixel", *bppP);
+        determinePalmFormatPgm(maxval,
+                               bppSpecified, bpp, maxBppSpecified, maxBpp,
+                               wantCustomColormap, compression,
+                               verbose,
+                               bppP);
+
         *directColorP = FALSE;
-        *colormapP = NULL;
+        *colormapPP = NULL;
     } else if (PNM_FORMAT_TYPE(format) == PPM_TYPE) {
-
-        /* We assume that we only get a PPM if the image cannot be
-           represented as PBM or PGM.  There are two options here: either
-           8-bit with a colormap, either the standard one or a custom one,
-           or 16-bit direct color.  In the 8-bit case, if "custom_colormap"
-           is specified (not recommended by Palm) we will put in our own
-           colormap; otherwise we will assume that the colors have been
-           mapped to the default Palm colormap by appropriate use of
-           pnmquant.  We try for 8-bit color first, since it works on
-           more PalmOS devices. 
-        */
-        if ((specified_bpp == 16) || 
-            (specified_bpp == 0 && max_bpp == 16)) {
-            /* we do the 16-bit direct color */
-            *directColorP = TRUE;
-            *colormapP = NULL;
-            *bppP = 16;
-        } else if (!custom_colormap) {
-            /* standard indexed 8-bit color */
-            *colormapP = palmcolor_build_default_8bit_colormap();
-            *bppP = 8;
-            if (((specified_bpp != 0) && (specified_bpp != 8)) ||
-                ((max_bpp != 0) && (max_bpp < 8)))
-                pm_error("Must use depth of 8 for color Palm Bitmap without "
-                         "custom color table.");
-            *directColorP = FALSE;
-            if (verbose)
-                pm_message("Output is color with default colormap at 8 bpp");
-        } else {
-            /* indexed 8-bit color with a custom colormap */
-            *colormapP = 
-                palmcolor_build_custom_8bit_colormap(rows, cols, xels);
-            for (*bppP = 1; (1 << *bppP) < (*colormapP)->ncolors; *bppP *= 2);
-            if (specified_bpp != 0) {
-                if (specified_bpp >= *bppP)
-                    *bppP = specified_bpp;
-                else
-                    pm_error("Too many colors for specified depth.  "
-                             "Use pnmquant to reduce.");
-            } else if ((max_bpp != 0) && (max_bpp < *bppP)) {
-                pm_error("Too many colors for specified max depth.  "
-                         "Use pnmquant to reduce.");
-            }
-            *directColorP = FALSE;
-            if (verbose)
-                pm_message("Output is color with custom colormap "
-                           "with %d colors at %d bpp", 
-                           (*colormapP)->ncolors, *bppP);
-        }
+        determinePalmFormatPpm(cols, rows, maxval, xels, bppSpecified, bpp,
+                               maxBppSpecified, maxBpp,
+                               wantCustomColormap, compression, verbose,
+                               bppP, directColorP, colormapPP);
     } else {
         pm_error("unknown format 0x%x on input file", (unsigned) format);
     }
+
+    if (compression != COMP_NONE)
+        assert(*bppP <= 8);
 }
 
 
@@ -300,25 +435,23 @@ findTransparentColor(const char *   const colorSpec,
                      pixval         const newMaxval,
                      bool           const directColor, 
                      pixval         const maxval, 
-                     Colormap       const colormap,
+                     Colormap *     const colormapP,
                      xel *          const transcolorP, 
                      unsigned int * const transindexP) {
 
     *transcolorP = ppm_parsecolor(colorSpec, maxval);
     if (!directColor) {
-        Color_s const temp_color = 
-            ((((PPM_GETR(*transcolorP)*newMaxval) / maxval) << 16) 
-             | (((PPM_GETG(*transcolorP)*newMaxval) / maxval) << 8)
-             | ((PPM_GETB(*transcolorP)*newMaxval) / maxval));
-        Color const found = 
-            (bsearch(&temp_color,
-                     colormap->color_entries, colormap->ncolors,
-                     sizeof(Color_s), palmcolor_compare_colors));
-        if (!found) {
+        ColormapEntry const searchTarget = 
+            palmcolor_mapEntryColorFmPixel(*transcolorP, maxval, newMaxval);
+        ColormapEntry * const foundEntryP = 
+            (bsearch(&searchTarget,
+                     colormapP->color_entries, colormapP->ncolors,
+                     sizeof(ColormapEntry), palmcolor_compare_colors));
+        if (!foundEntryP) {
             pm_error("Specified transparent color %s not found "
                      "in colormap.", colorSpec);
         } else
-            *transindexP = (*found >> 24) & 0xFF;
+            *transindexP = (*foundEntryP >> 24) & 0xFF;
     }
 }
 
@@ -326,9 +459,9 @@ findTransparentColor(const char *   const colorSpec,
 
 static unsigned int
 bitmapVersion(unsigned int         const bpp,
-              bool                 const colormap,
+              bool                 const colormapped,
               bool                 const transparent,
-              enum compressionType const compression,
+              enum CompressionType const compression,
               unsigned int         const density) {
 /*----------------------------------------------------------------------------
    Return the version number of the oldest version that can represent
@@ -343,7 +476,7 @@ bitmapVersion(unsigned int         const bpp,
         version = 3;
     else if (transparent || compression != COMP_NONE)
         version = 2;
-    else if (bpp > 1 || colormap)
+    else if (bpp > 1 || colormapped)
         version = 1;
     else
         version = 0;
@@ -357,8 +490,8 @@ static void
 writeCommonHeader(unsigned int         const cols,
                   unsigned int         const rows,
                   unsigned int         const rowbytes,
-                  enum compressionType const compression,
-                  bool                 const colormap,
+                  enum CompressionType const compression,
+                  bool                 const colormapped,
                   bool                 const transparent,
                   bool                 const directColor,
                   unsigned int         const bpp,
@@ -382,7 +515,7 @@ writeCommonHeader(unsigned int         const cols,
     flags = 0;  /* initial value */
     if (compression != COMP_NONE)
         flags |= PALM_IS_COMPRESSED_FLAG;
-    if (colormap)
+    if (colormapped)
         flags |= PALM_HAS_COLORMAP_FLAG;
     if (transparent)
         flags |= PALM_HAS_TRANSPARENCY_FLAG;
@@ -398,7 +531,7 @@ writeCommonHeader(unsigned int         const cols,
 
 
 static unsigned char 
-compressionFieldValue(enum compressionType const compression) {
+compressionFieldValue(enum CompressionType const compression) {
 
     unsigned char retval;
 
@@ -424,7 +557,7 @@ compressionFieldValue(enum compressionType const compression) {
 static void
 writeRemainingHeaderLow(unsigned int         const nextDepthOffset,
                         unsigned int         const transindex,
-                        enum compressionType const compression,
+                        enum CompressionType const compression,
                         unsigned int         const bpp) {
 /*----------------------------------------------------------------------------
    Write last 6 bytes of a low density Palm Bitmap header. 
@@ -449,7 +582,7 @@ writeRemainingHeaderLow(unsigned int         const nextDepthOffset,
 
 static void
 writeRemainingHeaderHigh(unsigned int         const bpp,
-                         enum compressionType const compression,
+                         enum CompressionType const compression,
                          unsigned int         const density,
                          xelval               const maxval,
                          bool                 const transparent,
@@ -480,9 +613,9 @@ writeRemainingHeaderHigh(unsigned int         const bpp,
         if (bpp == 16) {
             /* Blind guess here */
             fputc(0, stdout);
-            fputc((PPM_GETR(transcolor) * 255) / maxval, stdout);
-            fputc((PPM_GETG(transcolor) * 255) / maxval, stdout);
-            fputc((PPM_GETB(transcolor) * 255) / maxval, stdout);
+            fputc(scaleSample(PPM_GETR(transcolor), maxval, 255), stdout);
+            fputc(scaleSample(PPM_GETG(transcolor), maxval, 255), stdout);
+            fputc(scaleSample(PPM_GETB(transcolor), maxval, 255), stdout);
         } else {
             assert(transindex <= UCHAR_MAX);
             fputc(0, stdout);
@@ -523,7 +656,7 @@ writeDummy() {
 
 static void
 writeColormap(bool         const explicitColormap,
-              Colormap     const colormap,
+              Colormap *   const colormapP,
               bool         const directColor,
               unsigned int const bpp,
               bool         const transparent,
@@ -534,14 +667,14 @@ writeColormap(bool         const explicitColormap,
     /* if there's a colormap, write it out */
     if (explicitColormap) {
         unsigned int row;
-        if (!colormap)
+        if (!colormapP)
             pm_error("Internal error: user specified -colormap, but we did "
                      "not generate a colormap.");
-        qsort (colormap->color_entries, colormap->ncolors,
-               sizeof(Color_s), palmcolor_compare_indices);
-        pm_writebigshort( stdout, colormap->ncolors );
-        for (row = 0;  row < colormap->ncolors; ++row)
-            pm_writebiglong (stdout, colormap->color_entries[row]);
+        qsort(colormapP->color_entries, colormapP->ncolors,
+              sizeof(ColormapEntry), palmcolor_compare_indices);
+        pm_writebigshort( stdout, colormapP->ncolors );
+        for (row = 0;  row < colormapP->ncolors; ++row)
+            pm_writebiglong (stdout, colormapP->color_entries[row]);
     }
 
     if (directColor && (version < 3)) {
@@ -552,13 +685,13 @@ writeColormap(bool         const explicitColormap,
             fputc(5, stdout);   /* # of bits of blue */
             fputc(0, stdout);   /* reserved by Palm */
         } else
-            pm_error("Don't know how to create %d bit DirectColor bitmaps.", 
+            pm_error("Don't know how to create %u bit DirectColor bitmaps.", 
                      bpp);
         if (transparent) {
             fputc(0, stdout);
-            fputc((PPM_GETR(transcolor) * 255) / maxval, stdout);
-            fputc((PPM_GETG(transcolor) * 255) / maxval, stdout);
-            fputc((PPM_GETB(transcolor) * 255) / maxval, stdout);
+            fputc(scaleSample(PPM_GETR(transcolor) , maxval, 255), stdout);
+            fputc(scaleSample(PPM_GETG(transcolor) , maxval, 255), stdout);
+            fputc(scaleSample(PPM_GETB(transcolor) , maxval, 255), stdout);
         } else
             pm_writebiglong(stdout, 0);     /* no transparent color */
     }
@@ -571,17 +704,29 @@ computeRawRowDirectColor(const xel *     const xelrow,
                          unsigned int    const cols,
                          xelval          const maxval,
                          unsigned char * const rowdata) {
+/*----------------------------------------------------------------------------
+  Compute a row of Palm data in raw (uncompressed) form for an image that
+  uses direct color (really, true color: each pixel contains RGB intensities
+  as distinct R, G, and B numbers).
 
+  In this format, each pixel is 16 bits: 5 red, 6 green, 5 blue.
+
+  'xelrow' is the image contents of row.  It is 'cols' columns wide and
+  samples are based on maxval 'maxval'.
+
+  Put the output data at 'rowdata'. 
+-----------------------------------------------------------------------------*/
     unsigned int col;
-    unsigned char *outptr;
+    unsigned char * outCursor;
     
-    for (col = 0, outptr = rowdata; col < cols; ++col) {
+    for (col = 0, outCursor = &rowdata[0]; col < cols; ++col) {
         unsigned int const color = 
-            ((((PPM_GETR(xelrow[col])*31)/maxval) << 11) |
-             (((PPM_GETG(xelrow[col])*63)/maxval) << 5) |
-             ((PPM_GETB(xelrow[col])*31)/maxval));
-        *outptr++ = (color >> 8) & 0xFF;
-        *outptr++ = color & 0xFF;
+            (scaleSample(PPM_GETR(xelrow[col]), maxval, 31) << 11) |
+            (scaleSample(PPM_GETG(xelrow[col]), maxval, 63) <<  5) |
+            (scaleSample(PPM_GETB(xelrow[col]), maxval, 31) <<  0);
+
+        *outCursor++ = (color >> 8) & 0xFF;
+        *outCursor++ = color & 0xFF;
     }
 }
 
@@ -592,23 +737,40 @@ computeRawRowNonDirect(const xel *     const xelrow,
                        unsigned int    const cols,
                        xelval          const maxval,
                        unsigned int    const bpp,
-                       Colormap        const colormap,
+                       Colormap *      const colormapP,
                        unsigned int    const newMaxval,
                        unsigned char * const rowdata) {
+/*----------------------------------------------------------------------------
+  Compute a row of Palm data in raw (uncompressed) form for an image that
+  does not have a raster whose elements are explicit R, G, and B
+  intensities.
 
+  If 'colormapP' is non-null, the pixel is an index into that colormap.
+  'newMaxval' is meaningless.
+
+  If 'colormapP' is null, the pixel is a grayscale intensity, on a scale with
+  maximum value 'newMaxval'.  (N.B. this is really direct color, but for some
+  reason it's historically lumped in with the paletted formats).
+
+  'xelrow' is the image contents of row.  It is 'cols' columns wide and
+  samples are based on maxval 'maxval'.
+
+  Put the output data at 'rowdata', using 'bpp' bits per pixel.
+-----------------------------------------------------------------------------*/
     unsigned int col;
-    unsigned char *outptr;
+    unsigned char * outCursor;
+        /* Points to next slot in 'rowdata' we will fill */
     unsigned char outbyte;
         /* Accumulated bits to be output */
     unsigned char outbit;
         /* The lowest bit number we want to access for this pixel */
 
-    outbyte = 0x00;
-    outptr = rowdata;
+    outbyte = 0x00;  /* initial value */
+    outCursor = &rowdata[0];  /* Start at the beginning of the row */
 
     for (outbit = 8 - bpp, col = 0; col < cols; ++col) {
         unsigned int color;
-        if (!colormap) {
+        if (!colormapP) {
             /* we assume grayscale, and use simple scaling */
             color = (PNM_GET1(xelrow[col]) * newMaxval)/maxval;
             if (color > newMaxval)
@@ -616,24 +778,23 @@ computeRawRowNonDirect(const xel *     const xelrow,
                          "color of %u.", color);
             color = newMaxval - color; /* note grayscale maps are inverted */
         } else {
-            Color_s const temp_color =
-                ((((PPM_GETR(xelrow[col])*newMaxval)/maxval)<<16) 
-                 | (((PPM_GETG(xelrow[col])*newMaxval)/maxval)<<8)
-                 | (((PPM_GETB(xelrow[col])*newMaxval)/maxval)));
-            Color const found = (bsearch (&temp_color,
-                                          colormap->color_entries, 
-                                          colormap->ncolors,
-                                          sizeof(Color_s), 
-                                          palmcolor_compare_colors));
-            if (!found) {
-                pm_error("Color %d:%d:%d not found in colormap.  "
-                         "Try using pnmquant to reduce the "
-                         "number of colors.",
+            ColormapEntry const searchTarget =
+                palmcolor_mapEntryColorFmPixel(xelrow[col], maxval, 255);
+            ColormapEntry * const foundEntryP =
+                bsearch(&searchTarget,
+                        colormapP->color_entries, 
+                        colormapP->ncolors,
+                        sizeof(ColormapEntry), 
+                        palmcolor_compare_colors);
+            if (!foundEntryP) {
+                pm_error("INERNAL ERROR: "
+                         "Color (%u,%u,%u) not found in colormap, "
+                         "though it was supposedly there before",
                          PPM_GETR(xelrow[col]), 
                          PPM_GETG(xelrow[col]), 
                          PPM_GETB(xelrow[col]));
             }
-            color = (*found >> 24) & 0xFF;
+            color = (*foundEntryP >> 24) & 0xFF;
         }
 
         if (color > newMaxval)
@@ -642,7 +803,7 @@ computeRawRowNonDirect(const xel *     const xelrow,
         outbyte |= (color << outbit);
         if (outbit == 0) {
             /* Bit buffer is full.  Flush to to rowdata. */
-            *outptr++ = outbyte;
+            *outCursor++ = outbyte;
             outbyte = 0x00;
             outbit = 8 - bpp;
         } else
@@ -650,7 +811,7 @@ computeRawRowNonDirect(const xel *     const xelrow,
     }
     if ((cols % (8 / bpp)) != 0) {
         /* Flush bits remaining in the bit buffer to rowdata */
-        *outptr++ = outbyte;
+        *outCursor++ = outbyte;
     }
 }
 
@@ -830,7 +991,7 @@ rleCompressAndBufferRow(const unsigned char * const rowdata,
         unsigned int repeatcount;
         for (repeatcount = 1;  
              repeatcount < (rowbytes - pos) && repeatcount  < 255;  
-             ++repeatcount)
+             ++repeatcount) 
             if (rowdata[pos + repeatcount] != rowdata[pos])
                 break;
 
@@ -870,7 +1031,7 @@ packbitsCompressAndBufferRow(const unsigned char * const rowdata,
 static void
 bufferRowFromRawRowdata(const unsigned char *  const rowdata,
                         unsigned int           const rowbytes,
-                        enum compressionType   const compression,
+                        enum CompressionType   const compression,
                         const unsigned char *  const lastrow,
                         struct seqBuffer *     const rasterBufferP) {
 /*----------------------------------------------------------------------------
@@ -909,16 +1070,17 @@ bufferRow(const xel *          const xelrow,
           unsigned int         const rowbytes,
           unsigned int         const bpp,
           unsigned int         const newMaxval,
-          enum compressionType const compression,
+          enum CompressionType const compression,
           bool                 const directColor,
-          Colormap             const colormap,
+          Colormap *           const colormapP,
           unsigned char *      const rowdata,
           unsigned char *      const lastrow,
           struct seqBuffer *   const rasterBufferP) {
 /*----------------------------------------------------------------------------
    Add a row of the Palm Bitmap raster to buffer 'rasterBufferP'.
    
-   'xelrow' is the image contents of row.  It is 'cols' columns wide.
+   'xelrow' is the image contents of row.  It is 'cols' columns wide and
+   samples are based on maxval 'maxval'.
 
    If 'compression' indicates scanline compression, 'lastrow' is the
    row immediately preceding this one in the image (and this function
@@ -930,7 +1092,7 @@ bufferRow(const xel *          const xelrow,
     if (directColor)
         computeRawRowDirectColor(xelrow, cols, maxval, rowdata);
     else 
-        computeRawRowNonDirect(xelrow, cols, maxval, bpp, colormap, newMaxval,
+        computeRawRowNonDirect(xelrow, cols, maxval, bpp, colormapP, newMaxval,
                                rowdata);
 
     bufferRowFromRawRowdata(rowdata, rowbytes, compression,
@@ -947,9 +1109,9 @@ bufferRaster(xel **               const xels,
              unsigned int         const rowbytes,
              unsigned int         const bpp,
              unsigned int         const newMaxval,
-             enum compressionType const compression,
+             enum CompressionType const compression,
              bool                 const directColor,
-             Colormap             const colormap,
+             Colormap *           const colormapP,
              struct seqBuffer **  const rasterBufferPP) {
     
     unsigned char * rowdata;
@@ -971,7 +1133,7 @@ bufferRaster(xel **               const xels,
     for (row = 0; row < rows; ++row) {
         bufferRow(xels[row], cols, maxval, rowbytes, bpp, newMaxval,
                   compression,
-                  directColor, colormap, rowdata, row > 0 ? lastrow : NULL,
+                  directColor, colormapP, rowdata, row > 0 ? lastrow : NULL,
                   *rasterBufferPP);
 
         if (compression == COMP_SCANLINE)
@@ -988,8 +1150,8 @@ static void
 computeOffsetStuff(bool                 const offsetWanted,
                    unsigned int         const version,
                    bool                 const directColor,
-                   enum compressionType const compression,
-                   bool                 const colormap,
+                   enum CompressionType const compression,
+                   bool                 const colormapped,
                    unsigned int         const colormapColorCount,
                    unsigned int         const sizePlusRasterSize,
                    unsigned int *       const nextDepthOffsetP,
@@ -1003,7 +1165,7 @@ computeOffsetStuff(bool                 const offsetWanted,
         */
         unsigned int const headerSize = ((version < 3) ? 16 : 24);
         unsigned int const colormapSize =
-            (colormap ? (2 + colormapColorCount * 4) : 0);
+            (colormapped ? (2 + colormapColorCount * 4) : 0);
         if (version < 3) {
             unsigned int const directSize = 
                 (directColor && version < 3) ? 8 : 0; 
@@ -1058,12 +1220,12 @@ writeBitmap(xel **               const xels,
             unsigned int         const rowbytes,
             unsigned int         const bpp,
             unsigned int         const newMaxval,
-            enum compressionType const compression,
+            enum CompressionType const compression,
             bool                 const transparent,
             bool                 const directColor,
             bool                 const offsetWanted,
-            bool                 const hasColormap,
-            Colormap             const colormap,
+            bool                 const colormapped,
+            Colormap *           const colormapP,
             unsigned int         const transindex,
             xel                  const transcolor,
             unsigned int         const version,
@@ -1086,11 +1248,11 @@ writeBitmap(xel **               const xels,
         */
     struct seqBuffer * rasterBufferP;
 
-    writeCommonHeader(cols, rows, rowbytes, compression, hasColormap, 
+    writeCommonHeader(cols, rows, rowbytes, compression, colormapped, 
                       transparent, directColor, bpp, version);
     
     bufferRaster(xels, cols, rows, maxval, rowbytes, bpp, newMaxval,
-                 compression, directColor, colormap, &rasterBufferP);
+                 compression, directColor, colormapP, &rasterBufferP);
 
     /* rasterSize itself takes 2 or 4 bytes */
     if (version < 3)
@@ -1099,7 +1261,7 @@ writeBitmap(xel **               const xels,
         sizePlusRasterSize = 4 + bufferLength(rasterBufferP);
     
     computeOffsetStuff(offsetWanted, version, directColor, compression,
-                       hasColormap, hasColormap ? colormap->ncolors : 0, 
+                       colormapped, colormapped ? colormapP->ncolors : 0, 
                        sizePlusRasterSize,
                        &nextDepthOffset, &nextBitmapOffset,
                        &padBytesRequired);
@@ -1111,7 +1273,7 @@ writeBitmap(xel **               const xels,
                                  maxval, transparent, transcolor,
                                  transindex, nextBitmapOffset);
 
-    writeColormap(hasColormap, colormap, directColor, bpp, 
+    writeColormap(colormapped, colormapP, directColor, bpp, 
                   transparent, transcolor, maxval, version);
 
     if (compression != COMP_NONE)
@@ -1134,8 +1296,8 @@ writeBitmap(xel **               const xels,
 
 
 int 
-main( int argc, char **argv ) {
-    struct cmdline_info cmdline;
+main( int argc, const char **argv ) {
+    struct CmdlineInfo cmdline;
     unsigned int version;
     FILE* ifP;
     xel** xels;
@@ -1148,10 +1310,9 @@ main( int argc, char **argv ) {
     unsigned int bpp;
     bool directColor;
     unsigned int newMaxval;
-    Colormap colormap;
+    Colormap * colormapP;
     
-    /* Parse default params */
-    pnm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
@@ -1161,18 +1322,20 @@ main( int argc, char **argv ) {
     pm_close(ifP);
 
     if (cmdline.verbose)
-        pm_message("Input is %dx%d %s, maxval %d", 
+        pm_message("Input is %ux%u %s, maxval %u", 
                    cols, rows, formatName(format), maxval);
     
-    determinePalmFormat(cols, rows, maxval, format, xels, cmdline.depth,
-                        cmdline.maxdepth, cmdline.colormap, cmdline.verbose,
-                        &bpp, &directColor, &colormap);
+    determinePalmFormat(cols, rows, maxval, format, xels,
+                        cmdline.depthSpec, cmdline.depth,
+                        cmdline.maxdepthSpec, cmdline.maxdepth,
+                        cmdline.colormap, cmdline.compression, cmdline.verbose,
+                        &bpp, &directColor, &colormapP);
 
     newMaxval = (1 << bpp) - 1;
 
     if (cmdline.transparent) 
         findTransparentColor(cmdline.transparent, newMaxval, directColor,
-                             maxval, colormap, &transcolor, &transindex);
+                             maxval, colormapP, &transcolor, &transindex);
     else 
         transindex = 0;
 
@@ -1185,7 +1348,7 @@ main( int argc, char **argv ) {
     writeBitmap(xels, cols, rows, maxval,
                 rowbytes, bpp, newMaxval, cmdline.compression,
                 !!cmdline.transparent, directColor, cmdline.offset, 
-                cmdline.colormap, colormap, transindex, transcolor,
+                cmdline.colormap, colormapP, transindex, transcolor,
                 version, cmdline.density, cmdline.withdummy);
     
     return 0;
diff --git a/converter/other/pnmtopclxl.c b/converter/other/pnmtopclxl.c
index 8cabb614..f7db62ee 100644
--- a/converter/other/pnmtopclxl.c
+++ b/converter/other/pnmtopclxl.c
@@ -3,7 +3,7 @@
  *
  *  (C) 2002 Jochen Karrer, Linuxdata GbR
  *
- *      convert a pnm to PCL-XL image 
+ *      convert a pnm to PCL-XL image
  *
  * -------------------------------------------------------------
  */
@@ -44,7 +44,7 @@
 
 
 typedef struct InputSource {
-    const char *         name; 
+    const char *         name;
     struct InputSource * next;
 } InputSource;
 
@@ -82,7 +82,7 @@ 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.  
+   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.
@@ -104,25 +104,25 @@ parseCommandLine(int argc, char ** argv,
     option_def_index = 0;   /* incremented by OPTENT3 */
     OPTENT3(0, "dpi",       OPT_UINT,    &cmdlineP->dpi,
             &dpiSpec,         0);
-    OPTENT3(0, "xoffs",     OPT_FLOAT,   &cmdlineP->xoffs, 
+    OPTENT3(0, "xoffs",     OPT_FLOAT,   &cmdlineP->xoffs,
             &xoffsSpec,        0);
-    OPTENT3(0, "yoffs",     OPT_FLOAT,   &cmdlineP->yoffs, 
+    OPTENT3(0, "yoffs",     OPT_FLOAT,   &cmdlineP->yoffs,
             &yoffsSpec,        0);
-    OPTENT3(0, "format",    OPT_STRING,  &formatOpt, 
+    OPTENT3(0, "format",    OPT_STRING,  &formatOpt,
             &formatSpec,        0);
-    OPTENT3(0, "duplex",    OPT_STRING,  &duplexOpt, 
+    OPTENT3(0, "duplex",    OPT_STRING,  &duplexOpt,
             &cmdlineP->duplexSpec,        0);
     OPTENT3(0, "copies",    OPT_UINT,    &cmdlineP->copies,
             &cmdlineP->copiesSpec,        0);
-    OPTENT3(0, "colorok",   OPT_FLAG,    NULL,                  
+    OPTENT3(0, "colorok",   OPT_FLAG,    NULL,
             &cmdlineP->colorok, 0);
-    OPTENT3(0, "center",    OPT_FLAG,    NULL,                  
+    OPTENT3(0, "center",    OPT_FLAG,    NULL,
             &cmdlineP->center, 0 );
     OPTENT3(0, "feeder",    OPT_UINT,    &cmdlineP->feeder,
             &cmdlineP->feederSpec,        0);
     OPTENT3(0, "outtray",   OPT_UINT,    &cmdlineP->outtray,
             &cmdlineP->outtraySpec,       0);
-    OPTENT3(0, "verbose",   OPT_FLAG,    NULL,                  
+    OPTENT3(0, "verbose",   OPT_FLAG,    NULL,
             &cmdlineP->verbose, 0);
     OPTENT3(0, "jobsetup",  OPT_STRING,  &cmdlineP->jobsetup,
             &jobsetupSpec,      0);
@@ -216,7 +216,7 @@ parseCommandLine(int argc, char ** argv,
 
 static void
 freeSource(InputSource * const firstSourceP) {
-    
+
     InputSource * sourceP;
 
     sourceP = firstSourceP;
@@ -284,7 +284,7 @@ pnmToPcllinePackbits(pclGenerator * const pclGeneratorP,
     unsigned int col, padCt;
     unsigned int const padsize =
         pclGeneratorP->paddedLinelen - pclGeneratorP->linelen;
-        
+
     tuplerow = pnm_allocpamrow(pamP);
 
     pnm_readpamrow(pamP, tuplerow);
@@ -297,7 +297,7 @@ pnmToPcllinePackbits(pclGenerator * const pclGeneratorP,
         if (bitmask == 0) {
             pclGeneratorP->data[pclGeneratorP->cursor++] = accum;
             bitmask = 0x80; accum = 0x0;
-        } 
+        }
     }
     if (bitmask != 0x80)
         pclGeneratorP->data[pclGeneratorP->cursor++] = accum;
@@ -351,7 +351,7 @@ createPclGeneratorPackbits(struct pam *    const pamP,
 
     pclGeneratorP->data =
         malloc(pclDatabuffSize(pclGeneratorP->paddedLinelen));
-    
+
     if (pclGeneratorP->data == NULL)
         pm_error("Unable to allocate row buffer.");
 
@@ -378,7 +378,7 @@ pnmToPcllineWholebytes(pclGenerator * const pclGeneratorP,
     for (col = 0; col < pamP->width; ++col) {
         unsigned int plane;
         for (plane = 0; plane < pamP->depth; ++plane) {
-            pclGeneratorP->data[pclGeneratorP->cursor++] = 
+            pclGeneratorP->data[pclGeneratorP->cursor++] =
                 pnm_scalesample(tuplerow[col][plane], pamP->maxval, 255);
         }
     }
@@ -399,7 +399,7 @@ createPclGeneratorWholebytes(struct pam *    const pamP,
     pclGenerator * pclGenP;
 
     MALLOCVAR_NOFAIL(pclGenP);
-    
+
     if (pamP->depth <  3)
         pclGenP->colorSpace = eGray;
     else
@@ -418,7 +418,7 @@ createPclGeneratorWholebytes(struct pam *    const pamP,
         if (pclGenP->data == NULL)
             pm_error("Unable to allocate row buffer.");
     }
-    
+
     pclGenP->getnextrow = pnmToPcllineWholebytes;
 
     *pclGenPP = pclGenP;
@@ -436,7 +436,7 @@ destroyPclGenerator(pclGenerator * const pclGenP) {
 
 
 
-static void 
+static void
 createPclGenerator(struct pam *        const pamP,
                    pclGenerator **     const pclGeneratorPP,
                    bool                const colorok) {
@@ -448,16 +448,16 @@ createPclGenerator(struct pam *        const pamP,
                    "through Ppmtopgm.  To suppress this warning, use the "
                    "-colorok option.");
 
-    if (pamP->depth == 1 && pamP->maxval == 1) 
+    if (pamP->depth == 1 && pamP->maxval == 1)
         createPclGeneratorPackbits(pamP, pclGeneratorPP);
-    else 
+    else
         createPclGeneratorWholebytes(pamP, pclGeneratorPP);
 }
 
 
 
 
-struct tPrinter { 
+struct tPrinter {
     const char *name;
     float topmargin;
     float bottommargin;
@@ -478,7 +478,7 @@ out_ubyte(int           const fd,
 
 
 
-static int 
+static int
 XL_Operator(int           const fd,
             enum Operator const data)  {
 
@@ -514,7 +514,7 @@ static int
 out_sint16(int          const fd,
            signed short const sdata ) {
 
-    unsigned short const data= (unsigned short)sdata;    
+    unsigned short const data= (unsigned short)sdata;
 
     unsigned char c[2];
 
@@ -559,7 +559,7 @@ xl_ubyte_array(int                   const fd,
 
     unsigned int i;
     unsigned char head[4];
-    
+
     head[0] = 0xc8;
     head[1] = 0xc1;
     head[2] = len & 0xff;
@@ -671,7 +671,7 @@ convertAndWriteRleBlock(int                  const outFd,
     pm_rlenc_compressbyte(pclGeneratorP->data, outbuf, PM_RLE_PACKBITS,
                           pclGeneratorP->paddedLinelen * lineCt, &rlelen);
 
-    xl_dataLength(outFd, rlelen); 
+    xl_dataLength(outFd, rlelen);
     XY_Write(outFd, outbuf, rlelen);
 }
 
@@ -680,10 +680,10 @@ convertAndWriteRleBlock(int                  const outFd,
 /*
  * ------------------------------------------------------------
  * XL_WriteImage
- *  Write a PCL-XL image to the datastream 
+ *  Write a PCL-XL image to the datastream
  * ------------------------------------------------------------
  */
-static void 
+static void
 convertAndWriteImage(int            const outFd,
                      pclGenerator * const pclGenP,
                      struct pam *   const pamP) {
@@ -695,10 +695,10 @@ convertAndWriteImage(int            const outFd,
 
     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_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);
@@ -707,13 +707,13 @@ convertAndWriteImage(int            const outFd,
         unsigned int const blockHeight =
             MIN(20, pclGenP->height-blockStartLine);
 
-        xl_uint16(outFd, blockStartLine); xl_attr_ubyte(outFd, aStartLine); 
+        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_ubyte(outFd, 1); xl_attr_ubyte(outFd, aPadBytesMultiple);
         */
         XL_Operator(outFd, oReadImage);
         convertAndWriteRleBlock(outFd, pclGenP, pamP,
@@ -735,16 +735,16 @@ printEmbeddedImage(int                 const outFd,
     struct pam pam;
     pclGenerator * pclGeneratorP;
 
-    openDataSource(outFd, eBinaryLowByteFirst, eDefaultSource);
+    openDataSource(outFd, eBinaryLowByteFirst, eDefaultDataSource);
 
     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);
@@ -772,9 +772,9 @@ copyFile(const char * const sourceFileName,
         if (ferror(sourceFileP))
             pm_error("Read from file failed.  errno=%d (%s)",
                      errno, strerror(errno));
-        
+
         totalBytesWritten = 0;
-        
+
         while (totalBytesWritten < bytesRead) {
             ssize_t rc;
 
@@ -804,16 +804,16 @@ jobHead(int          const outFd,
    as opposed to e.g. Postscript.
 -----------------------------------------------------------------------------*/
     /* Reset */
-    XY_Puts(outFd,"\033%-12345X");  
+    XY_Puts(outFd,"\033%-12345X");
 
     if (userJobSetupFileName)
         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");
 }
 
 
@@ -825,7 +825,7 @@ jobEnd(int const outFd) {
 
    Reset printer to quiescent mode.  Exit the printer language.
 -----------------------------------------------------------------------------*/
-    XY_Puts(outFd,"\033%-12345X");  
+    XY_Puts(outFd,"\033%-12345X");
 }
 
 
@@ -852,7 +852,7 @@ beginPage(int                 const outFd,
     }
 
     if (doMediaDestination) {
-        xl_ubyte(outFd, mediaDestination);  
+        xl_ubyte(outFd, mediaDestination);
         xl_attr_ubyte(outFd, aMediaDestination);
     }
 
@@ -898,11 +898,11 @@ setColorSpace(int                   const outFd,
    paletteDepth is not e8Bit.  Is each palette entry still a byte and only
    some of the byte gets used?  Or are there multiple entries per byte?
 -----------------------------------------------------------------------------*/
-    xl_ubyte(outFd, colorSpace); xl_attr_ubyte(outFd, aColorSpace);   
+    xl_ubyte(outFd, colorSpace); xl_attr_ubyte(outFd, aColorSpace);
     if (palette) {
-        xl_ubyte(outFd, paletteDepth); 
-        xl_attr_ubyte(outFd, aPaletteDepth);   
-        xl_ubyte_array(outFd, palette, paletteSize); 
+        xl_ubyte(outFd, paletteDepth);
+        xl_attr_ubyte(outFd, aPaletteDepth);
+        xl_ubyte_array(outFd, palette, paletteSize);
         xl_attr_ubyte(outFd, aPaletteData);
     }
     XL_Operator(outFd, oSetColorSpace);
@@ -925,8 +925,8 @@ positionCursor(int            const outFd,
     float xpos, ypos;
 
     if (center) {
-        float const width  = 1.0 * imageWidth/dpi;  
-        float const height = 1.0 * imageHeight/dpi;    
+        float const width  = 1.0 * imageWidth/dpi;
+        float const height = 1.0 * imageHeight/dpi;
         xpos = (PAPERWIDTH(format) - width)/2;
         ypos = (PAPERHEIGHT(format) - height)/2;
     } else {
@@ -985,11 +985,11 @@ convertAndPrintPage(int                  const outFd,
        an IllegalArraySize error from the printer on the SetColorSpace
        command.
 
-       So we don't use a palette at all now.  
+       So we don't use a palette at all now.
     */
     setColorSpace(outFd, pclGeneratorP->colorSpace, NULL, 0, 0);
 
-    positionCursor(outFd, center, xoffs, yoffs, 
+    positionCursor(outFd, center, xoffs, yoffs,
                    pclGeneratorP->width, pclGeneratorP->height, dpi, format);
 
     convertAndWriteImage(outFd, pclGeneratorP, pamP);
@@ -1007,7 +1007,7 @@ beginSession(int              const outFd,
              bool             const noReporting,
              enum ErrorReport const errorReport) {
 
-    xl_uint16_xy(outFd, xdpi, ydpi); xl_attr_ubyte(outFd, aUnitsPerMeasure); 
+    xl_uint16_xy(outFd, xdpi, ydpi); xl_attr_ubyte(outFd, aUnitsPerMeasure);
     xl_ubyte(outFd, measure);  xl_attr_ubyte(outFd, aMeasure);
     /* xl_ubyte(outFd,eNoReporting); xl_attr_ubyte(outFd,aErrorReport); */
     xl_ubyte(outFd,errorReport); xl_attr_ubyte(outFd,aErrorReport);
@@ -1015,8 +1015,8 @@ beginSession(int              const outFd,
 }
 
 
-             
-static void 
+
+static void
 endSession(int outFd) {
     XL_Operator(outFd,oEndSession);
 }
@@ -1046,9 +1046,9 @@ printPages(int                 const outFd,
     InputSource * sourceP;
     unsigned int sourceNum;
 
-    sourceP = firstSourceP;    
+    sourceP = firstSourceP;
 
-    openDataSource(outFd, eBinaryLowByteFirst, eDefaultSource);
+    openDataSource(outFd, eBinaryLowByteFirst, eDefaultDataSource);
 
     sourceNum = 0;   /* initial value */
 
@@ -1074,9 +1074,9 @@ printPages(int                 const outFd,
                 pm_message("Processing File %u, Page %u", sourceNum, pageNum);
 
                 pnm_readpaminit(ifP, &pam, PAM_STRUCT_SIZE(tuple_type));
-                
+
                 createPclGenerator(&pam, &pclGeneratorP, colorok);
-                
+
                 convertAndPrintPage(
                     outFd, pclGeneratorP, &pam,
                     format, dpi, center, xoffs, yoffs, doDuplex, duplex,
@@ -1087,7 +1087,7 @@ printPages(int                 const outFd,
             }
         }
         pm_close(ifP);
-        sourceP = sourceP->next; 
+        sourceP = sourceP->next;
     }
     closeDataSource(outFd);
 }
@@ -1100,9 +1100,9 @@ main(int argc, char *argv[]) {
     int const outFd = STDOUT_FILENO;
 
     struct cmdlineInfo cmdline;
-    
+
     /* In case you're wondering why we do direct file descriptor I/O
-       instead of stream (FILE *), it's because Jochen originally 
+       instead of stream (FILE *), it's because Jochen originally
        wrote this code for an embedded system with diet-libc.  Without
        the stream library, the statically linked binary was only about
        5K big.
@@ -1116,7 +1116,7 @@ main(int argc, char *argv[]) {
     else {
         jobHead(outFd, cmdline.rendergray, cmdline.jobsetup);
 
-        beginSession(outFd, cmdline.dpi, cmdline.dpi, eInch, 
+        beginSession(outFd, cmdline.dpi, cmdline.dpi, eInch,
                      FALSE, eBackChAndErrPage);
 
         printPages(outFd, cmdline.sourceP,
diff --git a/converter/other/pnmtoplainpnm b/converter/other/pnmtoplainpnm
index 87c58597..4f5378b2 100755
--- a/converter/other/pnmtoplainpnm
+++ b/converter/other/pnmtoplainpnm
@@ -1,3 +1,3 @@
 #! /bin/sh
 
-pnmtopnm -plain $@
+pamtopnm -plain $@
diff --git a/converter/other/pnmtopng.c b/converter/other/pnmtopng.c
index 3899a9d2..60b8276b 100644
--- a/converter/other/pnmtopng.c
+++ b/converter/other/pnmtopng.c
@@ -26,7 +26,7 @@
    it's easy to see that an ordinary fax could deplete your virtual
    memory and even if it didn't, it might deplete your real memory and
    iterating through the array would cause thrashing.  This program
-   iterates through the image multiple times.  
+   iterates through the image multiple times.
 
    So instead, we read the image into memory one row at a time, into a
    single row buffer.  We use Netpbm's pm_openr_seekable() facility to
@@ -38,19 +38,19 @@
    in the system's I/O cache (remember that the file is a lot smaller than
    the xel array you'd get by doing a pnm_readpnm() of it).
 
-   However, it does introduce some delay because of all the system calls 
+   However, it does introduce some delay because of all the system calls
    required to read the file.  A future enhancement might read the entire
-   file into an xel array in some cases, and read one row at a time in 
+   file into an xel array in some cases, and read one row at a time in
    others, depending on the needs of the particular use.
 
    We do still read the entire alpha mask (if there is one) into a
-   'gray' array, rather than access it one row at a time.  
+   'gray' array, rather than access it one row at a time.
 
    Before May 2001, we did in fact read the whole image into an xel array,
    and we got complaints.  Before April 2000, it wasn't as big a problem
    because xels were only 24 bits.  Now they're 96.
 */
-   
+
 #ifndef PNMTOPNG_WARNING_LEVEL
 #  define PNMTOPNG_WARNING_LEVEL 0   /* use 0 for backward compatibility, */
 #endif                               /*  2 for warnings (1 == error) */
@@ -62,7 +62,7 @@
 /* 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 <setjmp.h>
 #include <zlib.h>
 
 #include "pm_c_util.h"
@@ -170,7 +170,7 @@ parseSizeOpt(const char *       const sizeOpt,
              struct pngx_phys * const sizeP) {
 
     int count;
-    
+
     count = sscanf(sizeOpt, "%d %d %d", &sizeP->x, &sizeP->y, &sizeP->unit);
 
     if (count != 3)
@@ -185,7 +185,7 @@ parseRgbOpt(const char *         const rgbOpt,
             struct pngx_chroma * const rgbP) {
 
     int count;
-    
+
     count = sscanf(rgbOpt, "%f %f %f %f %f %f %f %f",
                    &rgbP->wx, &rgbP->wy,
                    &rgbP->rx, &rgbP->ry,
@@ -204,7 +204,7 @@ 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"))
@@ -242,7 +242,7 @@ parseModtimeOpt(const char * const modtimeOpt,
     if (count != 6)
         pm_error("Invalid value for -modtime '%s'.   It should have "
                  "the form [yy]yy-mm-dd hh:mm:ss.", modtimeOpt);
-    
+
     if (year < 0)
         pm_error("Year is negative in -modtime value '%s'", modtimeOpt);
     if (year > 9999)
@@ -290,7 +290,7 @@ 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.  
+   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.
@@ -422,11 +422,11 @@ parseCommandLine(int argc, const char ** argv,
         cmdlineP->ztxt = NULL;
     if (!paletteSpec)
         cmdlineP->palette = NULL;
-    
+
     if (filterSpec && (nofilter + sub + up + avg + paeth > 0))
         pm_error("You may mot specify -filter with "
                  "-nofilter, -sub, -up, -avg, or -paeth");
-    
+
     if (filterSpec) {
         if (filter < 0 || filter > 4)
             pm_error("-filter is obsolete.  Use -nofilter, -sub, -up, -avg, "
@@ -460,13 +460,12 @@ parseCommandLine(int argc, const char ** argv,
                 cmdlineP->filterSet |= PNG_FILTER_PAETH;
         }
     }
-
     if (cmdlineP->sizeSpec)
         parseSizeOpt(size, &cmdlineP->size);
 
     if (cmdlineP->rgbSpec)
         parseRgbOpt(rgb, &cmdlineP->rgb);
-    
+
     if (cmdlineP->srgbintentSpec)
         parseSrgbintentOpt(srgbintent, &cmdlineP->srgbintent);
 
@@ -538,14 +537,14 @@ reportInputType(int    const format,
 
 
 static png_color_16
-xelToPngColor_16(xel    const input, 
-                 xelval const maxval, 
+xelToPngColor_16(xel    const input,
+                 xelval const maxval,
                  xelval const pngMaxval) {
 
     png_color_16 retval;
 
     xel scaled;
-    
+
     PPM_DEPTH(scaled, input, maxval, pngMaxval);
 
     retval.red   = PPM_GETR(scaled);
@@ -559,12 +558,12 @@ xelToPngColor_16(xel    const input,
 
 
 static void
-closestColorInPalette(pixel          const targetColor, 
+closestColorInPalette(pixel          const targetColor,
                       pixel                palette_pnm[],
                       unsigned int   const paletteSize,
                       unsigned int * const bestIndexP,
                       unsigned int * const bestMatchP) {
-    
+
     unsigned int paletteIndex;
     unsigned int bestIndex;
     unsigned int bestMatch;
@@ -573,7 +572,7 @@ closestColorInPalette(pixel          const targetColor,
 
     bestMatch = UINT_MAX;
     for (paletteIndex = 0; paletteIndex < paletteSize; ++paletteIndex) {
-        unsigned int const dist = 
+        unsigned int const dist =
             PPM_DISTANCE(palette_pnm[paletteIndex], targetColor);
 
         if (dist < bestMatch) {
@@ -678,13 +677,13 @@ static colorhist_vector getChv_chv;
 
 
 static void
-getChv(FILE *             const ifP, 
+getChv(FILE *             const ifP,
        pm_filepos         const rasterPos,
-       int                const cols, 
-       int                const rows, 
+       int                const cols,
+       int                const rows,
        xelval             const maxval,
-       int                const format, 
-       int                const maxColors, 
+       int                const format,
+       int                const maxColors,
        colorhist_vector * const chvP,
        unsigned int *     const colorsP) {
 /*----------------------------------------------------------------------------
@@ -693,7 +692,7 @@ getChv(FILE *             const ifP,
    raster starts at position 'rasterPos' of the file.  The image's properties
    are 'cols', 'rows', 'maxval', and 'format'.
 
-   Return the number of colors as *colorsP.  Return the details of the 
+   Return the number of colors as *colorsP.  Return the details of the
    colors in newly malloc'ed storage, and its address as *chvP.  If
    there are more than 'maxColors' colors, though, just return NULL as
    *chvP and leave *colorsP undefined.
@@ -708,13 +707,13 @@ getChv(FILE *             const ifP,
 
     if (!getChv_computed) {
         int colorCount;
-        if (verbose) 
+        if (verbose)
             pm_message ("Finding colors in input image...");
 
         pm_seek2(ifP, &rasterPos, sizeof(rasterPos));
-        getChv_chv = ppm_computecolorhist2(ifP, cols, rows, maxval, format, 
+        getChv_chv = ppm_computecolorhist2(ifP, cols, rows, maxval, format,
                                            maxColors, &colorCount);
-        
+
         getChv_colors = colorCount;
 
         if (verbose) {
@@ -745,7 +744,7 @@ static void freeChv(void) {
 static bool
 pgmBitsAreRepeated(unsigned int const repeatedSize,
                    FILE *       const ifP,
-                   pm_filepos   const rasterPos, 
+                   pm_filepos   const rasterPos,
                    int          const cols,
                    int          const rows,
                    xelval       const maxval,
@@ -772,7 +771,7 @@ pgmBitsAreRepeated(unsigned int const repeatedSize,
     xel * xelrow;
 
     xelrow = pnm_allocrow(cols);
-    
+
     pm_seek2(ifP, &rasterPos, sizeof(rasterPos));
 
     mayscale = TRUE;  /* initial assumption */
@@ -797,8 +796,8 @@ pgmBitsAreRepeated(unsigned int const repeatedSize,
 
 
 static void
-meaningful_bits_pgm(FILE *         const ifP, 
-                    pm_filepos     const rasterPos, 
+meaningful_bits_pgm(FILE *         const ifP,
+                    pm_filepos     const rasterPos,
                     int            const cols,
                     int            const rows,
                     xelval         const maxval,
@@ -849,8 +848,8 @@ meaningful_bits_pgm(FILE *         const ifP,
 
 
 static void
-meaningful_bits_ppm(FILE *         const ifp, 
-                    pm_filepos     const rasterPos, 
+meaningful_bits_ppm(FILE *         const ifp,
+                    pm_filepos     const rasterPos,
                     int            const cols,
                     int            const rows,
                     xelval         const maxval,
@@ -903,12 +902,12 @@ meaningful_bits_ppm(FILE *         const ifp,
 
 
 static void
-tryTransparentColor(FILE *     const ifp, 
-                    pm_filepos const rasterPos, 
-                    int        const cols, 
-                    int        const rows, 
+tryTransparentColor(FILE *     const ifp,
+                    pm_filepos const rasterPos,
+                    int        const cols,
+                    int        const rows,
                     xelval     const maxval,
-                    int        const format, 
+                    int        const format,
                     gray **    const alphaMask,
                     gray       const alphaMaxval,
                     pixel      const transcolor,
@@ -926,7 +925,7 @@ tryTransparentColor(FILE *     const ifp,
     pm_seek2(ifp, &rasterPos, sizeof(rasterPos));
 
     singleColorIsTrans = TRUE;  /* initial assumption */
-        
+
     for (row = 0; row < rows && singleColorIsTrans; ++row) {
         int col;
         pnm_readpnmrow(ifp, xelrow, cols, maxval, format);
@@ -961,7 +960,7 @@ tryTransparentColor(FILE *     const ifp,
                 }
             }
         }
-    }  
+    }
     pnm_freerow(xelrow);
 }
 
@@ -969,15 +968,15 @@ tryTransparentColor(FILE *     const ifp,
 
 static void
 analyzeAlpha(FILE *       const ifP,
-             pm_filepos   const rasterPos, 
-             unsigned int const cols, 
-             unsigned int const rows, 
+             pm_filepos   const rasterPos,
+             unsigned int const cols,
+             unsigned int const rows,
              xelval       const maxval,
-             int          const format, 
+             int          const format,
              gray **      const alphaMask,
              gray         const alphaMaxval,
              bool *       const allOpaqueP,
-             bool *       const singleColorIsTransP, 
+             bool *       const singleColorIsTransP,
              pixel *      const alphaTranscolorP) {
 /*----------------------------------------------------------------------------
   Get information about the alpha mask, in combination with the masked
@@ -1003,7 +1002,7 @@ analyzeAlpha(FILE *       const ifP,
         /* We found a pixel in the image where the alpha mask says it is
            not fully opaque.
         */
-    
+
     xelrow = pnm_allocrow(cols);
 
     {
@@ -1083,7 +1082,7 @@ determineTransparency(struct cmdlineInfo const cmdline,
    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
@@ -1107,7 +1106,7 @@ determineTransparency(struct cmdlineInfo const cmdline,
         if (alphaCols != cols || alphaRows != rows) {
             pm_error("dimensions for image and alpha mask do not agree");
         }
-        analyzeAlpha(ifP, rasterPos, cols, rows, maxval, format, 
+        analyzeAlpha(ifP, rasterPos, cols, rows, maxval, format,
                      alphaMask, alphaMaxval, &allOpaque,
                      &alphaCanBeTransparencyIndex, &alphaTranscolor);
 
@@ -1137,7 +1136,7 @@ determineTransparency(struct cmdlineInfo const cmdline,
         *alphaMaxvalP = 255;
 
         if (cmdline.transparent) {
-            const char * transstring2;  
+            const char * transstring2;
             /* The -transparent value, but with possible leading '=' removed */
             if (cmdline.transparent[0] == '=') {
                 *transExactP = TRUE;
@@ -1145,12 +1144,12 @@ determineTransparency(struct cmdlineInfo const cmdline,
             } 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_DEPTH(*transColorP,
                       ppm_parsecolor(transstring2, PNM_OVERALLMAXVAL),
                       PNM_OVERALLMAXVAL, maxval);
 
@@ -1167,9 +1166,9 @@ determineBackground(struct cmdlineInfo const cmdline,
                     xelval             const maxval,
                     xel *              const backColorP) {
 
-  if (cmdline.background) 
+  if (cmdline.background)
       PPM_DEPTH(*backColorP,
-                ppm_parsecolor(cmdline.background, PNM_OVERALLMAXVAL), 
+                ppm_parsecolor(cmdline.background, PNM_OVERALLMAXVAL),
                 PNM_OVERALLMAXVAL, maxval);;
 }
 
@@ -1221,8 +1220,8 @@ hasColor(FILE *       const ifP,
 
 
 static void
-findRedundantBits(FILE *         const ifp, 
-                  int            const rasterPos, 
+findRedundantBits(FILE *         const ifp,
+                  int            const rasterPos,
                   int            const cols,
                   int            const rows,
                   xelval         const maxval,
@@ -1239,13 +1238,13 @@ findRedundantBits(FILE *         const ifp,
    of bits, starting from the least significant end, that contain
    original information.
 -----------------------------------------------------------------------------*/
-  if (!alpha && PNM_FORMAT_TYPE(format) == PGM_TYPE && !force) 
+  if (!alpha && PNM_FORMAT_TYPE(format) == PGM_TYPE && !force)
       meaningful_bits_pgm(ifp, rasterPos, cols, rows, maxval, format,
                           meaningfulBitsP);
   else if (PNM_FORMAT_TYPE(format) == PPM_TYPE && !force)
       meaningful_bits_ppm(ifp, rasterPos, cols, rows, maxval, format,
                           meaningfulBitsP);
-  else 
+  else
       *meaningfulBitsP = pm_maxvaltobits(maxval);
 
   if (verbose && *meaningfulBitsP != pm_maxvaltobits(maxval))
@@ -1257,24 +1256,24 @@ findRedundantBits(FILE *         const ifp,
 
 static void
 readOrderedPalette(FILE *         const pfp,
-                   xel                  ordered_palette[], 
+                   xel                  ordered_palette[],
                    unsigned int * const ordered_palette_size_p) {
 
     xel ** xels;
     int cols, rows;
     xelval maxval;
     int format;
-    
+
     if (verbose)
         pm_message("reading ordered palette (colormap)...");
 
     xels = pnm_readpnm(pfp, &cols, &rows, &maxval, &format);
-    
-    if (PNM_FORMAT_TYPE(format) != PPM_TYPE) 
+
+    if (PNM_FORMAT_TYPE(format) != PPM_TYPE)
         pm_error("ordered palette must be a PPM file, not type %d", format);
 
     *ordered_palette_size_p = rows * cols;
-    if (*ordered_palette_size_p > MAXCOLORS) 
+    if (*ordered_palette_size_p > MAXCOLORS)
         pm_error("ordered-palette image contains %d pixels.  Maximum is %d",
                  *ordered_palette_size_p, MAXCOLORS);
     if (verbose)
@@ -1286,12 +1285,12 @@ readOrderedPalette(FILE *         const pfp,
         j = 0;  /* initial value */
         for (row = 0; row < rows; ++row) {
             int col;
-            for (col = 0; col < cols; ++col) 
+            for (col = 0; col < cols; ++col)
                 ordered_palette[j++] = xels[row][col];
         }
     }
     pnm_freearray(xels, rows);
-}        
+}
 
 
 
@@ -1315,31 +1314,31 @@ compute_nonalpha_palette(colorhist_vector const chv,
    wants the colors in a particular order in the palette.
 -----------------------------------------------------------------------------*/
     unsigned int colorIndex;
-    
+
     xel ordered_palette[MAXCOLORS];
     unsigned int ordered_palette_size;
 
     if (pfp) {
         readOrderedPalette(pfp, ordered_palette, &ordered_palette_size);
 
-        if (colors != ordered_palette_size) 
+        if (colors != ordered_palette_size)
             pm_error("sizes of ordered palette (%d) "
                      "and existing palette (%d) differ",
                      ordered_palette_size, colors);
-        
+
         /* Make sure the ordered palette contains all the colors in
-           the image 
+           the image
         */
         for (colorIndex = 0; colorIndex < colors; colorIndex++) {
             int j;
             bool found;
-            
+
             found = FALSE;
             for (j = 0; j < ordered_palette_size && !found; ++j) {
-                if (PNM_EQUAL(ordered_palette[j], chv[colorIndex].color)) 
+                if (PNM_EQUAL(ordered_palette[j], chv[colorIndex].color))
                     found = TRUE;
             }
-            if (!found) 
+            if (!found)
                 pm_error("failed to find color (%d, %d, %d), which is in the "
                          "input image, in the ordered palette",
                          PPM_GETR(chv[colorIndex].color),
@@ -1352,7 +1351,7 @@ compute_nonalpha_palette(colorhist_vector const chv,
         for (colorIndex = 0; colorIndex < colors; ++colorIndex)
             palette_pnm[colorIndex] = ordered_palette[colorIndex];
     } else {
-        for (colorIndex = 0; colorIndex < colors; ++colorIndex) 
+        for (colorIndex = 0; colorIndex < colors; ++colorIndex)
             palette_pnm[colorIndex] = chv[colorIndex].color;
     }
     *paletteSizeP = colors;
@@ -1442,7 +1441,7 @@ computeUnsortedAlphaPalette(FILE *           const ifP,
    described by 'cols', 'rows', 'maxval', and 'format'.
 
    Using the alpha mask 'alpha_mask' and color map 'chv' (of size 'colors')
-   for the image, construct a palette of (color index, alpha) ordered pairs 
+   for the image, construct a palette of (color index, alpha) ordered pairs
    for the image, as follows.
 
    The alpha/color palette is the set of all ordered pairs of
@@ -1476,7 +1475,7 @@ computeUnsortedAlphaPalette(FILE *           const ifP,
         alphasOfColor[colorIndex] = NULL;
         alphasOfColorCnt[colorIndex] = 0;
     }
- 
+
     pm_seek2(ifP, &rasterPos, sizeof(rasterPos));
 
     xelrow = pnm_allocrow(cols);
@@ -1562,22 +1561,22 @@ sortAlphaPalette(gray *         const alphasOfColor[],
         unsigned int bot_idx;
         unsigned int top_idx;
         unsigned int colorIndex;
-    
+
         /* We start one index at the bottom of the palette index range
            and another at the top.  We run through the unsorted palette,
            and when we see an opaque entry, we map it to the current top
-           cursor and bump it down.  When we see a non-opaque entry, we map 
+           cursor and bump it down.  When we see a non-opaque entry, we map
            it to the current bottom cursor and bump it up.  Because the input
            and output palettes are the same size, the two cursors should meet
            right when we process the last entry of the unsorted palette.
-        */    
+        */
         bot_idx = 0;
         top_idx = alphasFirstIndex[colors-1] + alphasOfColorCnt[colors-1] - 1;
-    
+
         for (colorIndex = 0;  colorIndex < colors;  ++colorIndex) {
             unsigned int j;
             for (j = 0; j < alphasOfColorCnt[colorIndex]; ++j) {
-                unsigned int const paletteIndex = 
+                unsigned int const paletteIndex =
                     alphasFirstIndex[colorIndex] + j;
                 if (alphasOfColor[colorIndex][j] == alphaMaxval)
                     mapping[paletteIndex] = top_idx--;
@@ -1598,7 +1597,7 @@ sortAlphaPalette(gray *         const alphasOfColor[],
 
 
 static void
-compute_alpha_palette(FILE *         const ifP, 
+compute_alpha_palette(FILE *         const ifP,
                       int            const cols,
                       int            const rows,
                       xelval         const maxval,
@@ -1626,10 +1625,10 @@ compute_alpha_palette(FILE *         const ifP,
    The palette is sorted so that the opaque entries are last, and we return
    *transSizeP as the number of non-opaque entries.
 
-   palette[] and trans[] are allocated by the caller to at least 
+   palette[] and trans[] are allocated by the caller to at least
    MAXPALETTEENTRIES elements.
 
-   If there are more than MAXPALETTEENTRIES color/alpha pairs in the image, 
+   If there are more than MAXPALETTEENTRIES color/alpha pairs in the image,
    don't return any palette information -- just return *tooBigP == TRUE.
 -----------------------------------------------------------------------------*/
     colorhist_vector chv;
@@ -1638,8 +1637,8 @@ compute_alpha_palette(FILE *         const ifP,
     gray * alphas_of_color[MAXPALETTEENTRIES];
     unsigned int alphas_first_index[MAXPALETTEENTRIES];
     unsigned int alphas_of_color_cnt[MAXPALETTEENTRIES];
- 
-    getChv(ifP, rasterPos, cols, rows, maxval, format, MAXCOLORS, 
+
+    getChv(ifP, rasterPos, cols, rows, maxval, format, MAXCOLORS,
            &chv, &colors);
 
     assert(colors <= ARRAY_SIZE(alphas_of_color));
@@ -1658,7 +1657,7 @@ compute_alpha_palette(FILE *         const ifP,
                index into the sorted PNG palette of the alpha/color
                pair whose index is x in the unsorted PNG palette.
                This mapping sorts the palette so that opaque entries
-               are last.  
+               are last.
             */
 
         *paletteSizeP = colors == 0 ?
@@ -1677,22 +1676,22 @@ compute_alpha_palette(FILE *         const ifP,
             for (colorIndex = 0; colorIndex < colors; ++colorIndex) {
                 unsigned int j;
                 for (j = 0; j < alphas_of_color_cnt[colorIndex]; ++j) {
-                    unsigned int const paletteIndex = 
+                    unsigned int const paletteIndex =
                         alphas_first_index[colorIndex] + j;
                     palette_pnm[mapping[paletteIndex]] = chv[colorIndex].color;
-                    trans_pnm[mapping[paletteIndex]] = 
+                    trans_pnm[mapping[paletteIndex]] =
                     alphas_of_color[colorIndex][j];
                 }
             }
         }
         freeAlphasOfColor(alphas_of_color, colors);
     }
-} 
+}
 
 
 
 static void
-makeOneColorTransparentInPalette(xel            const transColor, 
+makeOneColorTransparentInPalette(xel            const transColor,
                                  bool           const exact,
                                  pixel                palette_pnm[],
                                  unsigned int   const paletteSize,
@@ -1700,12 +1699,12 @@ makeOneColorTransparentInPalette(xel            const transColor,
                                  unsigned int * const transSizeP) {
 /*----------------------------------------------------------------------------
    Find the color 'transColor' in the color/alpha palette defined by
-   palette_pnm[], paletteSize, trans_pnm[] and *transSizeP.  
+   palette_pnm[], paletteSize, trans_pnm[] and *transSizeP.
 
    Make that entry fully transparent.
 
    Rearrange the palette so that that entry is first.  (The PNG compressor
-   can do a better job when the opaque entries are all last in the 
+   can do a better job when the opaque entries are all last in the
    color/alpha palette).
 
    If the specified color is not there and exact == TRUE, return
@@ -1720,26 +1719,26 @@ makeOneColorTransparentInPalette(xel            const transColor,
     unsigned int distance;
 
     assert(paletteSize > 0);
-    
+
     if (*transSizeP != 0)
         pm_error("Internal error: trying to make a color in the palette "
                  "transparent where there already is one.");
 
-    closestColorInPalette(transColor, palette_pnm, paletteSize, 
+    closestColorInPalette(transColor, palette_pnm, paletteSize,
                           &transparentIndex, &distance);
 
     if (distance != 0 && exact) {
         pm_message("specified transparent color not present in palette; "
                    "ignoring -transparent");
         errorlevel = PNMTOPNG_WARNING_LEVEL;
-    } else {        
+    } else {
         /* Swap this with the first entry in the palette */
         pixel tmp;
-    
+
         tmp = palette_pnm[transparentIndex];
         palette_pnm[transparentIndex] = palette_pnm[0];
         palette_pnm[0] = tmp;
-        
+
         /* Make it transparent */
         trans_pnm[0] = PGM_TRANSPARENT;
         *transSizeP = 1;
@@ -1755,8 +1754,8 @@ makeOneColorTransparentInPalette(xel            const transColor,
 
 
 static void
-findOrAddBackgroundInPalette(pixel          const backColor, 
-                             pixel                palette_pnm[], 
+findOrAddBackgroundInPalette(pixel          const backColor,
+                             pixel                palette_pnm[],
                              unsigned int * const paletteSizeP,
                              unsigned int * const backgroundIndexP) {
 /*----------------------------------------------------------------------------
@@ -1765,11 +1764,11 @@ findOrAddBackgroundInPalette(pixel          const backColor,
   add it, choose a background color that's already in the palette,
   as close to 'backColor' as possible.
 
-  If we add an entry to the palette, make it opaque.  But in searching the 
+  If we add an entry to the palette, make it opaque.  But in searching the
   existing palette, ignore transparency.
 
   Note that PNG specs say that transparency of the background is meaningless;
-  i.e. a viewer must ignore the transparency of the palette entry when 
+  i.e. a viewer must ignore the transparency of the palette entry when
   using the background color.
 
   Return the palette index of the background color as *backgroundIndexP.
@@ -1778,9 +1777,9 @@ findOrAddBackgroundInPalette(pixel          const backColor,
     unsigned int paletteIndex;
 
     backgroundIndex = -1;
-    for (paletteIndex = 0; 
-         paletteIndex < *paletteSizeP; 
-         ++paletteIndex) 
+    for (paletteIndex = 0;
+         paletteIndex < *paletteSizeP;
+         ++paletteIndex)
         if (PPM_EQUAL(palette_pnm[paletteIndex], backColor))
             backgroundIndex = paletteIndex;
 
@@ -1823,8 +1822,8 @@ findOrAddBackgroundInPalette(pixel          const backColor,
 
 
 
-static void 
-buildColorLookup(pixel                   palette_pnm[], 
+static void
+buildColorLookup(pixel                   palette_pnm[],
                  unsigned int      const paletteSize,
                  colorhash_table * const chtP) {
 /*----------------------------------------------------------------------------
@@ -1849,14 +1848,14 @@ buildColorLookup(pixel                   palette_pnm[],
 
 
 
-static void 
-buildColorAlphaLookup(pixel              palette_pnm[], 
+static void
+buildColorAlphaLookup(pixel              palettePnm[],
                       unsigned int const paletteSize,
-                      gray               trans_pnm[], 
+                      gray               transPnm[],
                       unsigned int const transSize,
                       gray         const alphaMaxval,
                       coloralphahash_table * const cahtP) {
-    
+
     coloralphahash_table const caht = alloccoloralphahash();
 
     unsigned int paletteIndex;
@@ -1865,12 +1864,12 @@ buildColorAlphaLookup(pixel              palette_pnm[],
         gray paletteTrans;
 
         if (paletteIndex < transSize)
-            paletteTrans = alphaMaxval;
+            paletteTrans = transPnm[paletteIndex];
         else
-            paletteTrans = trans_pnm[paletteIndex];
+            paletteTrans = alphaMaxval;
 
-        addtocoloralphahash(caht, &palette_pnm[paletteIndex],
-                            &trans_pnm[paletteIndex], paletteIndex);
+        addtocoloralphahash(caht, &palettePnm[paletteIndex],
+                            &paletteTrans, paletteIndex);
     }
     *cahtP = caht;
 }
@@ -1905,18 +1904,18 @@ tryAlphaPalette(FILE *         const ifP,
                  "a PNG with transparency when you specify "
                  "the palette with -palette.");
 
-    compute_alpha_palette(ifP, cols, rows, maxval, format, 
+    compute_alpha_palette(ifP, cols, rows, maxval, format,
                           rasterPos,  alpha_mask, alphaMaxval,
-                          palette_pnm, trans_pnm, 
+                          palette_pnm, trans_pnm,
                           paletteSizeP, transSizeP, &tooBig);
     if (tooBig) {
         pm_asprintf(impossibleReasonP,
                     "too many color/transparency pairs "
-                    "(more than the PNG maximum of %u", 
+                    "(more than the PNG maximum of %u",
                     MAXPALETTEENTRIES);
     } else
         *impossibleReasonP = NULL;
-} 
+}
 
 
 
@@ -1930,15 +1929,15 @@ computePixelWidth(bool           const colorPng,
     unsigned int bitsPerSample, bitsPerPixel;
 
     if (colorPng || alpha) {
-        /* PNG allows only depths of 8 and 16 for a truecolor image 
+        /* PNG allows only depths of 8 and 16 for a truecolor image
            and for a grayscale image with an alpha channel.
           */
         if (pnmMeaningfulBitCt > 8)
             bitsPerSample = 16;
-        else 
+        else
             bitsPerSample = 8;
     } else {
-        /* A grayscale, non-colormapped, no-alpha PNG may have any 
+        /* A grayscale, non-colormapped, no-alpha PNG may have any
              bit depth from 1 to 16
           */
         if (pnmMeaningfulBitCt > 8)
@@ -1977,7 +1976,7 @@ paletteIndexBits(unsigned int const nColors) {
   Return the number of bits that a palette index in the PNG will
   occupy given that the palette has 'nColors' colors in it.  It is 1,
   2, 4, or 8 bits.
-  
+
   If 'nColors' is not a valid PNG palette size, return 0.
 -----------------------------------------------------------------------------*/
     unsigned int retval;
@@ -2056,7 +2055,7 @@ computeColorMap(FILE *         const ifP,
         pm_asprintf(noColormapReasonP, "You requested no color map");
     else if (maxval > PALETTEMAXVAL)
         pm_asprintf(noColormapReasonP, "The maxval of the input image (%u) "
-                    "exceeds the PNG palette maxval (%u)", 
+                    "exceeds the PNG palette maxval (%u)",
                     maxval, PALETTEMAXVAL);
     else {
         unsigned int bitsPerPixel;
@@ -2076,20 +2075,20 @@ computeColorMap(FILE *         const ifP,
             */
             colorhist_vector chv;
             unsigned int colors;
-            
-            getChv(ifP, rasterPos, cols, rows, maxval, format, MAXCOLORS, 
+
+            getChv(ifP, rasterPos, cols, rows, maxval, format, MAXCOLORS,
                    &chv, &colors);
 
             if (chv == NULL) {
-                pm_asprintf(noColormapReasonP, 
+                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)
-                    pm_asprintf(noColormapReasonP, 
+                    pm_asprintf(noColormapReasonP,
                                 "palette index for %u colors would be "
-                                "no smaller than the indexed value (%u bits)", 
+                                "no smaller than the indexed value (%u bits)",
                                 colors, bitsPerPixel);
                 else {
                     unsigned int paletteSize;
@@ -2098,7 +2097,7 @@ computeColorMap(FILE *         const ifP,
                         tryAlphaPalette(ifP, cols, rows, maxval, format,
                                         rasterPos, alpha_mask, alphaMaxval,
                                         pfP,
-                                        palette_pnm, &paletteSize, 
+                                        palette_pnm, &paletteSize,
                                         trans_pnm, &transSize,
                                         noColormapReasonP);
 
@@ -2106,13 +2105,13 @@ computeColorMap(FILE *         const ifP,
                         *noColormapReasonP = NULL;
 
                         compute_nonalpha_palette(chv, colors, maxval, pfP,
-                                                 palette_pnm, &paletteSize, 
+                                                 palette_pnm, &paletteSize,
                                                  trans_pnm, &transSize);
-    
+
                         if (transparent)
                             makeOneColorTransparentInPalette(
-                                transcolor, transexact, 
-                                palette_pnm, paletteSize, trans_pnm, 
+                                transcolor, transexact,
+                                palette_pnm, paletteSize, trans_pnm,
                                 &transSize);
                     }
                     if (!*noColormapReasonP) {
@@ -2145,7 +2144,7 @@ static void computeColorMapLookupTable(
 /*----------------------------------------------------------------------------
    Compute applicable lookup tables for the palette index.  If there's no
    alpha mask, this is just a standard Netpbm colorhash_table.  If there's
-   an alpha mask, it is the slower Pnmtopng-specific 
+   an alpha mask, it is the slower Pnmtopng-specific
    coloralphahash_table.
 
    If a lookup table is not applicable to the image, return NULL as
@@ -2153,10 +2152,10 @@ static void computeColorMapLookupTable(
 -----------------------------------------------------------------------------*/
     if (colorMapped) {
         if (alpha) {
-            buildColorAlphaLookup(palette_pnm, palette_size, 
+            buildColorAlphaLookup(palette_pnm, palette_size,
                                   trans_pnm, trans_size, alpha_maxval, cahtP);
             *chtP = NULL;
-        } else { 
+        } else {
             buildColorLookup(palette_pnm, palette_size, chtP);
             *cahtP = NULL;
         }
@@ -2203,15 +2202,15 @@ computeRasterWidth(bool           const colorMapped,
                           bitsPerSampleP, bitsPerPixelP);
 
         if (verbose)
-            pm_message("Writing %u bits per component per pixel", 
+            pm_message("Writing %u bits per component per pixel",
                        *bitsPerSampleP);
     }
 }
 
 
 static void
-createPngPalette(pixel              palette_pnm[], 
-                 unsigned int const paletteSize, 
+createPngPalette(pixel              palette_pnm[],
+                 unsigned int const paletteSize,
                  pixval       const maxval,
                  gray               trans_pnm[],
                  unsigned int const transSize,
@@ -2269,7 +2268,7 @@ setZlibCompression(struct pngx *          const pngxP,
         pngx_setCompressionSize(pngxP, zlibCompression.buffer_size);
     }
 }
-                  
+
 
 
 static void
@@ -2284,7 +2283,7 @@ makePngLine(png_byte *           const line,
             struct pngx *        const pngxP,
             xelval               const png_maxval,
             unsigned int         const depth) {
-            
+
     unsigned int col;
     png_byte *pp;
 
@@ -2318,7 +2317,7 @@ makePngLine(png_byte *           const line,
             *pp++ = PPM_GETB(p_png) & 0xff;
         } else
             pm_error("INTERNAL ERROR: undefined color_type");
-                
+
         if (pngx_colorType(pngxP) & PNG_COLOR_MASK_ALPHA) {
             int const png_alphaval = (int)
                 alpha_mask[col] * (float) png_maxval / maxval + 0.5;
@@ -2372,7 +2371,7 @@ writeRaster(struct pngx *        const pngxP,
             pnm_readpnmrow(ifP, xelrow, cols, maxval, format);
             pnm_promoteformatrow(xelrow, cols, maxval, format, maxval,
                                  PPM_TYPE);
-            
+
             makePngLine(line, xelrow, cols, maxval,
                         alpha, alpha ? alpha_mask[row] : NULL,
                         cht, caht, pngxP, png_maxval, depth);
@@ -2401,15 +2400,15 @@ doHistChunk(struct pngx * const pngxP,
         colorhist_vector chv;
         unsigned int colorCt;
         colorhash_table cht;
-        
-        getChv(ifP, rasterPos, cols, rows, maxval, format, MAXCOLORS, 
+
+        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)
@@ -2424,7 +2423,7 @@ doHistChunk(struct pngx * const pngxP,
                     else
                         histogram[i] = chv[chvIndex].value;
                 }
-            
+
                 pngx_setHist(pngxP, histogram);
 
                 if (verbose)
@@ -2471,7 +2470,7 @@ doIhdrChunk(struct pngx * const pngxP,
 static void
 doGamaChunk(struct cmdlineInfo const cmdline,
             struct pngx *      const pngxP) {
-            
+
     if (cmdline.gammaSpec)
         pngx_setGama(pngxP, cmdline.gamma);
 }
@@ -2541,7 +2540,7 @@ reportTrans(struct pngx * const pngxP) {
         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,
@@ -2594,21 +2593,21 @@ doBkgdChunk(struct pngx * const pngxP,
             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 = 
+            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 ); 
+                           pngBackground.gray,
+                           pngBackground.red,
+                           pngBackground.green,
+                           pngBackground.blue );
         }
     }
 }
@@ -2625,20 +2624,19 @@ doSbitChunk(struct pngx * const pngxP,
     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
-           of the input image and the alpha mask.  So we write an sBIT
-           chunk to tell what the original image's maxval was.  The
-           sBit chunk doesn't let us specify any maxval -- only powers
-           of two minus one.  So we pick the power of two minus one
-           which is greater than or equal to the actual input maxval.
-           
-           PNG also doesn't let an sBIT chunk indicate a maxval
-           _greater_ than the the PNG maxval.  The designers probably
-           did not conceive of the case where that would happen.  The
-           case is this: We detected redundancy in the bits so were
-           able to store fewer bits than the user provided.  But since
-           PNG doesn't allow it, we don't attempt to create such an
-           sBIT chunk.
+        /* We're writing in a bit depth that doesn't match the maxval of the
+           input image and the alpha mask.  So we write an sBIT chunk to tell
+           what the original image's maxval was.  The sBit chunk doesn't let
+           us specify any maxval -- only powers of two minus one.  So we pick
+           the power of two minus one which is greater than or equal to the
+           actual input maxval.
+
+           PNG also doesn't let an sBIT chunk indicate a maxval _greater_ than
+           the PNG maxval.  The designers probably did not conceive of the
+           case where that would happen.  The case is this: We detected
+           redundancy in the bits so were able to store fewer bits than the
+           user provided.  But since PNG doesn't allow it, we don't attempt to
+           create such an sBIT chunk.
         */
 
         {
@@ -2652,7 +2650,7 @@ doSbitChunk(struct pngx * const pngxP,
                 sbit.blue  = sbitval;
             } else
                 sbit.gray  = sbitval;
-            
+
             if (verbose)
                 pm_message("Writing sBIT chunk with bits = %d", sbitval);
 
@@ -2683,7 +2681,7 @@ addSrgbChunk(struct pngx *   const pngxP,
 
 
 
-static void 
+static void
 convertpnm(struct cmdlineInfo const cmdline,
            FILE *             const ifP,
            FILE *             const ofP,
@@ -2702,14 +2700,14 @@ convertpnm(struct cmdlineInfo const cmdline,
     xelval maxval;
     /* The maxval of the input image */
     xelval pngMaxval;
-        /* The maxval of the samples in the PNG output 
+        /* 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;  
+    bool transExact;
         /* boolean: the user wants only the exact color he specified to be
            transparent; not just something close to it.
         */
@@ -2728,14 +2726,14 @@ convertpnm(struct cmdlineInfo const cmdline,
     pixel palettePnm[MAXCOLORS];
     png_color palette[MAXCOLORS];
         /* The color part of the color/alpha palette passed to the PNG
-           compressor 
+           compressor
         */
     unsigned int paletteSize;
 
     gray transPnm[MAXCOLORS];
     png_byte  trans[MAXCOLORS];
         /* The alpha part of the color/alpha palette passed to the PNG
-           compressor 
+           compressor
         */
     unsigned int transSize;
 
@@ -2751,7 +2749,7 @@ convertpnm(struct cmdlineInfo const cmdline,
            we should.  malloc'ed null-terminated string.
         */
     unsigned int depth;
-        /* The number of bits per sample in the (uncompressed) png 
+        /* 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.
         */
@@ -2759,7 +2757,7 @@ convertpnm(struct cmdlineInfo const cmdline,
         /* The total number of bits per pixel in the (uncompressed) png
            raster, including all channels.
         */
-    pm_filepos rasterPos;  
+    pm_filepos rasterPos;
         /* file position in input image file of start of image (i.e. after
            the header)
         */
@@ -2800,7 +2798,7 @@ convertpnm(struct cmdlineInfo const cmdline,
         colorPng = (PNM_FORMAT_TYPE(format) == PPM_TYPE);
     else {
         if (PNM_FORMAT_TYPE(format) == PPM_TYPE) {
-            colorPng = hasColor(ifP, cols, rows, maxval, format, rasterPos); 
+            colorPng = hasColor(ifP, cols, rows, maxval, format, rasterPos);
         } else
             colorPng = false;
     }
@@ -2815,10 +2813,10 @@ convertpnm(struct cmdlineInfo const cmdline,
 
     findRedundantBits(ifP, rasterPos, cols, rows, maxval, format, alpha,
                       cmdline.force, &pnmMeaningfulBitCt);
-  
+
     computeColorMap(ifP, rasterPos, cols, rows, maxval, colorPng, format,
                     cmdline.force, pfP,
-                    alpha, transparent >= 0, transcolor, transExact, 
+                    alpha, transparent >= 0, transcolor, transExact,
                     !!cmdline.background, backColor,
                     alpha_mask, alphaMaxval, pnmMeaningfulBitCt,
                     palettePnm, &paletteSize, transPnm, &transSize,
@@ -2835,7 +2833,7 @@ convertpnm(struct cmdlineInfo const cmdline,
         colorMapped = FALSE;
     } else
         colorMapped = TRUE;
-  
+
     computeColorMapLookupTable(colorMapped, palettePnm, paletteSize,
                                transPnm, transSize, alpha, alphaMaxval,
                                &cht, &caht);
@@ -2846,7 +2844,7 @@ convertpnm(struct cmdlineInfo const cmdline,
     if (verbose)
         pm_message ("writing a%s %d-bit %s%s file%s",
                     fulldepth == 8 ? "n" : "", fulldepth,
-                    colorMapped ? "palette": 
+                    colorMapped ? "palette":
                     colorPng ? "RGB" : "gray",
                     alpha ? (colorMapped ? "+transparency" : "+alpha") : "",
                     cmdline.interlace ? " (interlaced)" : "");
@@ -2873,7 +2871,7 @@ convertpnm(struct cmdlineInfo const cmdline,
         /* creating PNG palette (Not counting the transparency palette) */
 
         createPngPalette(palettePnm, paletteSize, maxval,
-                         transPnm, transSize, alphaMaxval, 
+                         transPnm, transSize, alphaMaxval,
                          palette, trans);
         pngx_setPlte(pngxP, palette, paletteSize);
 
@@ -2973,7 +2971,7 @@ displayVersion() {
 
 
 
-int 
+int
 main(int argc, const char * argv[]) {
 
     struct cmdlineInfo cmdline;
@@ -2983,29 +2981,29 @@ main(int argc, const char * argv[]) {
     FILE * tfP;
 
     int errorlevel;
-    
+
     pm_proginit(&argc, argv);
-    
+
     parseCommandLine(argc, argv, &cmdline);
-    
+
     if (cmdline.libversion) {
         displayVersion();
         return 0;
     }
     verbose = cmdline.verbose;
-    
+
     ifP = pm_openr_seekable(cmdline.inputFileName);
-    
+
     if (cmdline.alpha)
         afP = pm_openr(cmdline.alpha);
     else
         afP = NULL;
-    
+
     if (cmdline.palette)
         pfP = pm_openr(cmdline.palette);
     else
         pfP = NULL;
-    
+
     if (cmdline.text)
         tfP = pm_openr(cmdline.text);
     else if (cmdline.ztxt)
@@ -3014,7 +3012,7 @@ main(int argc, const char * argv[]) {
         tfP = NULL;
 
     convertpnm(cmdline, ifP, stdout, afP, pfP, tfP, &errorlevel);
-    
+
     if (afP)
         pm_close(afP);
     if (pfP)
diff --git a/converter/other/pnmtops.c b/converter/other/pnmtops.c
index c1dadc3e..de0dfd8d 100644
--- a/converter/other/pnmtops.c
+++ b/converter/other/pnmtops.c
@@ -33,6 +33,7 @@
    goes in separate from the rest of the raster.
 */
 
+#define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE  /* Make sure string.h contains strdup() */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #include <stdlib.h>
@@ -86,7 +87,7 @@ setSignals() {
 
 
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
@@ -208,7 +209,7 @@ validateCompDimension(unsigned int const value,
 
 static void
 parseCommandLine(int argc, const char ** argv,
-                 struct cmdlineInfo * const cmdlineP) {
+                 struct CmdlineInfo * const cmdlineP) {
 
     unsigned int imagewidthSpec, imageheightSpec;
     float imagewidth, imageheight;
@@ -2034,7 +2035,7 @@ main(int argc, const char * argv[]) {
 
     FILE * ifP;
     const char * name;  /* malloc'ed */
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
 
     pm_proginit(&argc, argv);
 
diff --git a/converter/other/pnmtosir.c b/converter/other/pnmtosir.c
index c8dec5b6..20bb6178 100644
--- a/converter/other/pnmtosir.c
+++ b/converter/other/pnmtosir.c
@@ -1,5 +1,5 @@
 /* pnmtosir.c - read a portable anymap and produce a Solitaire Image Recorder
-**		file (MGI TYPE 11 or MGI TYPE 17)
+**      file (MGI TYPE 11 or MGI TYPE 17)
 **
 ** Copyright (C) 1991 by Marvin Landis
 **
@@ -11,67 +11,63 @@
 ** implied warranty.
 */
 
+#include <stdbool.h>
 #include "pnm.h"
 
 #define MAXCOLORS 256
 
-int main(int argc, char * argv[]) {
-    FILE* ifp;
-    xel** xels;
-    register xel* xP;
-    const char* dumpname;
-    int rows, cols, format, row, col;
-    int m, n;
-    int grayscale;
+
+
+int
+main(int argc, const char * argv[]) {
+    
+    FILE * ifP;
+    xel ** xels;
+    int rows, cols, format;
+    unsigned int n;
+    bool isGrayscale;
     xelval maxval;
-    const char* const usage = "[pnmfile]";
-    unsigned char ub;
     unsigned short Header[16];
     unsigned short LutHeader[16];
     unsigned short Lut[2048];
 
-    pnm_init( &argc, argv );
+    pm_proginit(&argc, argv);
 
-    if ( argc > 2 )
-        pm_usage( usage );
+    if (argc-1 > 1)
+        pm_error("There is only one possible argument: the input file.  "
+                 "You specified %d", argc-1);
 
-    if ( argc == 2 )
-	{
-        dumpname = argv[1];
-        ifp = pm_openr( argv[1] );
-	}
-    else
-	{
-        dumpname = "Standard Input";
-        ifp = stdin;
-	}
+    if (argc-1 > 0) {
+        const char * const inputFileName = argv[1];
+        ifP = pm_openr(inputFileName);
+    }  else {
+        ifP = stdin;
+    }
     
-    xels = pnm_readpnm( ifp, &cols, &rows, &maxval, &format );
-    pm_close( ifp );
+    xels = pnm_readpnm(ifP, &cols, &rows, &maxval, &format);
     
     /* Figure out the colormap. */
-    switch ( PNM_FORMAT_TYPE(format) )
-	{
-	case PPM_TYPE:
-        grayscale = 0;
-        pm_message( "Writing a 24-bit SIR format (MGI TYPE 11)" );
+    switch (PNM_FORMAT_TYPE(format) ) {
+    case PPM_TYPE:
+        isGrayscale = false;
+        pm_message("Writing a 24-bit SIR format (MGI TYPE 11)");
         break;
 
     case PGM_TYPE:
-        grayscale = 1;
-        pm_message( "Writing a grayscale SIR format (MGI TYPE 17)" );
+        isGrayscale = true;
+        pm_message("Writing a grayscale SIR format (MGI TYPE 17)");
         break;
 
-	default:
-        grayscale = 1;
-        pm_message( "Writing a monochrome SIR format (MGI TYPE 17)" );
+    default:
+        isGrayscale = true;
+        pm_message("Writing a monochrome SIR format (MGI TYPE 17)");
         break;
-	}
+    }
 
     /* Set up the header. */
     Header[0] = 0x3a4f;
     Header[1] = 0;
-    if (grayscale)
+    if (isGrayscale)
         Header[2] = 17;
     else
         Header[2] = 11;
@@ -93,54 +89,65 @@ int main(int argc, char * argv[]) {
     LutHeader[2] = 5;
     LutHeader[3] = 256;
     LutHeader[4] = 256;
-    for (n = 0; n < 5; n++)
+    for (n = 0; n < 5; ++n)
         pm_writelittleshort(stdout,LutHeader[n]);
-    for (n = 5; n < 256; n++)
+    for (n = 5; n < 256; ++n)
         pm_writelittleshort(stdout,0);
  
-    for(n = 0; n < 3; n ++)
-        for (m = 0; m < 256; m++)
+    for (n = 0; n < 3; ++n) {
+        unsigned int m;
+        for (m = 0; m < 256; ++m)
             Lut[m * 4 + n] = m << 8;
-    for (n = 0; n < 1024; n++)
+    }
+    for (n = 0; n < 1024; ++n)
         pm_writelittleshort(stdout,Lut[n]);
  
     /* Finally, write out the data. */
-    switch ( PNM_FORMAT_TYPE(format) )
-    {
-	case PPM_TYPE:
-	    for ( row = 0; row < rows; ++row )
-            for ( col = 0, xP = xels[row]; col < cols; ++col, ++xP )
-            {
-                ub = (char) ( PPM_GETR( *xP ) * ( 255 / maxval ) ); 
-                fputc( ub, stdout );
+    switch (PNM_FORMAT_TYPE(format)) {
+    case PPM_TYPE: {
+        unsigned int row;
+        for (row = 0; row < rows; ++row) {
+            unsigned int col;
+            for (col = 0; col < cols; ++col) {
+                unsigned char const ub =
+                    (char) (PPM_GETR(xels[row][col]) * (255 / maxval)); 
+                fputc(ub, stdout);
             }
-        for ( row = 0; row < rows; ++row )
-            for ( col = 0, xP = xels[row]; col < cols; ++col, ++xP )
-            {  
-                ub = (char) ( PPM_GETG( *xP ) * ( 255 / maxval ) );
-                fputc( ub, stdout );
+        }
+        for (row = 0; row < rows; ++row) {
+            unsigned int col;
+            for (col = 0; col < cols; ++col) {  
+                unsigned const char ub =
+                    (char) (PPM_GETG(xels[row][col]) * (255 / maxval));
+                fputc(ub, stdout);
             }
-        for ( row = 0; row < rows; ++row )
-            for ( col = 0, xP = xels[row]; col < cols; ++col, ++xP )
-            {  
-                ub = (char) ( PPM_GETB( *xP ) * ( 255 / maxval ) );
-                fputc( ub, stdout );
+        }
+        for (row = 0; row < rows; ++row) {
+            unsigned int col;
+            for (col = 0; col < cols; ++col) {  
+                unsigned const char ub =
+                    (char) (PPM_GETB(xels[row][col]) * (255 / maxval));
+                fputc(ub, stdout);
             }
-	    break;
+        }
+    } break;
 
-    default:
-        for ( row = 0; row < rows; ++row )
-            for ( col = 0, xP = xels[row]; col < cols; ++col, ++xP )
-            {
-                register unsigned long val;
-
-                val = PNM_GET1( *xP );
-                ub = (char) ( val * ( 255 / maxval ) );
-                fputc( ub, stdout );
+    default: {
+        unsigned int row;
+        for (row = 0; row < rows; ++row) {
+            unsigned int col;
+            for (col = 0; col < cols; ++col) {
+                unsigned long const val = PNM_GET1(xels[row][col]);
+                unsigned const char ub = (char) (val * (255 / maxval));
+                fputc(ub, stdout);
             }
-        break;
+        }
+    } break;
     }
+    
+    pm_close(ifP);
 
-    exit( 0 );
+    return 0;
 }
 
+
diff --git a/converter/other/pstopnm.c b/converter/other/pstopnm.c
index 19b1630a..f5342655 100644
--- a/converter/other/pstopnm.c
+++ b/converter/other/pstopnm.c
@@ -14,8 +14,9 @@
 
 -----------------------------------------------------------------------------*/
 
+#define _DEFAULT_SOURCE 1 /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE 1   /* Make sure strdup() is in string.h */
-#define _XOPEN_SOURCE 500  
+#define _XOPEN_SOURCE 500
     /* Make sure fdopen() is in stdio.h and strdup() is in string.h */
 
 #include <assert.h>
@@ -24,7 +25,7 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <signal.h>
-#include <sys/wait.h>  
+#include <sys/wait.h>
 #include <sys/stat.h>
 
 #include "pm_c_util.h"
@@ -37,17 +38,32 @@ 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.
+    /* Description of a rectangle within an image; all coordinates
+       measured in points (1/72") with lower left corner of page being the
+       origin.  Negative values are OK.
     */
+    bool isDefined;
+
+    /* Nothing below is meaningful unless 'isDefined' is true */
     int llx;  /* lower left X coord */
-        /* -1 for llx means whole box is undefined. */
     int lly;  /* lower left Y coord */
     int urx;  /* upper right X coord */
     int ury;  /* upper right Y coord */
 };
 
+
+
+static void
+assertValidBox(struct Box const box) {
+
+    if (box.isDefined) {
+        assert(box.urx >= box.llx);
+        assert(box.ury >= box.lly);
+    }
+}
+
+
+
 struct Dimensions {
 /*----------------------------------------------------------------------------
   Horizontal and vertical dimensions of something, both in pixels and
@@ -106,7 +122,7 @@ parseCommandLine(int argc, char ** argv,
     unsigned int textalphabitsSpec;
 
     MALLOCARRAY_NOFAIL(option_def, 100);
-    
+
     option_def_index = 0;   /* incremented by OPTENTRY */
     OPTENT3(0, "forceplain", OPT_FLAG,  NULL, &cmdlineP->forceplain,     0);
     OPTENT3(0, "llx",        OPT_FLOAT, &llx, &llxSpec,                  0);
@@ -150,7 +166,7 @@ parseCommandLine(int argc, char ** argv,
     if (ymaxSpec) {
         if (cmdlineP->ymax == 0)
             pm_error("zero is not a valid value for -ymax");
-    } else 
+    } else
         cmdlineP->ymax = 792;
 
     if (xsizeSpec) {
@@ -162,7 +178,7 @@ parseCommandLine(int argc, char ** argv,
     if (ysizeSpec) {
         if (cmdlineP->ysize == 0)
             pm_error("zero is not a valid value for -ysize");
-    } else 
+    } else
         cmdlineP->ysize = 0;
 
     if (portraitOpt && !landscapeOpt)
@@ -184,9 +200,11 @@ parseCommandLine(int argc, char ** argv,
         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.  
+       command line, we default any of the 4 that aren't.
     */
     if (llxSpec || llySpec || urxSpec || urySpec) {
+        cmdlineP->extractBox.isDefined = true;
+
         if (!llxSpec) cmdlineP->extractBox.llx = 72;
         else cmdlineP->extractBox.llx = llx * 72;
         if (!llySpec) cmdlineP->extractBox.lly = 72;
@@ -196,7 +214,7 @@ parseCommandLine(int argc, char ** argv,
         if (!urySpec) cmdlineP->extractBox.ury = 720;
         else cmdlineP->extractBox.ury = ury * 72;
     } else {
-        cmdlineP->extractBox.llx = -1;
+        cmdlineP->extractBox.isDefined = false;
     }
 
     if (dpiSpec) {
@@ -224,7 +242,7 @@ parseCommandLine(int argc, char ** argv,
         cmdlineP->inputFileName = "-";  /* stdin */
     else if (argc-1 == 1)
         cmdlineP->inputFileName = argv[1];
-    else 
+    else
         pm_error("Too many arguments (%d).  "
                  "Only need one: the Postscript file name", argc-1);
 
@@ -248,7 +266,7 @@ addPsToFileName(char          const origFileName[],
     int statRc;
 
     statRc = lstat(origFileName, &statbuf);
-    
+
     if (statRc == 0)
         *newFileNameP = strdup(origFileName);
     else {
@@ -269,34 +287,62 @@ addPsToFileName(char          const origFileName[],
 
 
 
+static unsigned int
+resolution(unsigned int const dotCt,
+           unsigned int const pointCt) {
+/*----------------------------------------------------------------------------
+   The resolution in dots per inch when 'dotCt' dots print 'pointCt' points
+   long.
+
+   When this would round to zero, we return 1 dot per inch instead so it
+   doesn't play havoc with arithmetic - it's never going to happen unless
+   something is broken anyway.
+-----------------------------------------------------------------------------*/
+    return MAX(1, (unsigned int)((float)dotCt * 72 / pointCt + 0.5));
+}
+
+
+
 static void
 computeSizeResFromSizeSpec(unsigned int        const requestedXsize,
                            unsigned int        const requestedYsize,
                            unsigned int        const imageWidth,
                            unsigned int        const imageHeight,
                            struct Dimensions * const imageDimP) {
+/*----------------------------------------------------------------------------
+   Compute output image size and assumed Postscript input resolution, assuming
+   user requested a specific size for at least one of the dimensions and the
+   input is 'imageWidth' x 'imageHeight' points.
+
+   'requestedXsize' is what the user requested for output image width in
+   pixels, or zero if he made no request.  'requestedYsize' is analogous
+   for the height.
+-----------------------------------------------------------------------------*/
+    assert(requestedXsize || requestedYsize);
+
+    assert(imageWidth > 0);
 
     if (requestedXsize) {
         imageDimP->xsize = requestedXsize;
-        imageDimP->xres = (unsigned int)
-            (requestedXsize * 72 / imageWidth + 0.5);
+        imageDimP->xres = resolution(requestedXsize, imageWidth);
         if (!requestedYsize) {
             imageDimP->yres = imageDimP->xres;
             imageDimP->ysize = (unsigned int)
                 (imageHeight * (float)imageDimP->yres/72 + 0.5);
-            }
         }
+    }
+
+    assert(imageHeight > 0);
 
     if (requestedYsize) {
         imageDimP->ysize = requestedYsize;
-        imageDimP->yres = (unsigned int)
-            (requestedYsize * 72 / imageHeight + 0.5);
+        imageDimP->yres = resolution(requestedYsize, imageHeight);
         if (!requestedXsize) {
             imageDimP->xres = imageDimP->yres;
             imageDimP->xsize = (unsigned int)
                 (imageWidth * (float)imageDimP->xres/72 + 0.5);
         }
-    } 
+    }
 }
 
 
@@ -312,8 +358,9 @@ computeSizeResBlind(unsigned int        const xmax,
     if (imageWidth == 0 || imageHeight == 0) {
         imageDimP->xres = imageDimP->yres = 72;
     } else {
-        imageDimP->xres = imageDimP->yres = MIN(xmax * 72 / imageWidth,
-                                                ymax * 72 / imageHeight);
+        imageDimP->xres = imageDimP->yres =
+            MIN(resolution(xmax, imageWidth),
+                resolution(ymax, imageHeight));
     }
 
     if (nocrop) {
@@ -330,17 +377,17 @@ computeSizeResBlind(unsigned int        const xmax,
 
 
 static void
-computeSizeRes(struct CmdlineInfo  const cmdline, 
+computeSizeRes(struct CmdlineInfo  const cmdline,
                struct Box          const borderedBox,
                struct Dimensions * const imageDimP) {
 /*----------------------------------------------------------------------------
   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
+  A resolution number is the number of pixels per inch that the
   printer prints.  Since we're emulating a printed page with a PNM
   image, and a PNM image has no spatial dimension (you can't say how
-  many inches wide a PNM image is), it's kind of confusing.  
+  many inches wide a PNM image is), it's kind of confusing.
 
   If the user doesn't select a resolution, we choose the resolution
   that causes the image to be a certain number of pixels, knowing how
@@ -349,8 +396,8 @@ computeSizeRes(struct CmdlineInfo  const cmdline,
   inches wide.  We want the PNM image to be 1000 pixels wide.  So we
   tell Ghostscript that our horizontal output device resolution is 500
   pixels per inch.
-  
-  X and Y in all returned values is with respect to the image, not the
+
+  X and Y in all returned values are 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.
 -----------------------------------------------------------------------------*/
@@ -360,6 +407,8 @@ computeSizeRes(struct CmdlineInfo  const cmdline,
     unsigned int const sx = borderedBox.urx - borderedBox.llx;
     unsigned int const sy = borderedBox.ury - borderedBox.lly;
 
+    assertValidBox(borderedBox); assert(borderedBox.isDefined);
+
     if (cmdline.dpi) {
         /* User gave resolution; we figure out output image size */
         imageDimP->xres = imageDimP->yres = cmdline.dpi;
@@ -369,6 +418,7 @@ computeSizeRes(struct CmdlineInfo  const cmdline,
         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,
                                    imageDimP);
     } else
@@ -397,7 +447,7 @@ languageDeclaration(char const inputFileName[]) {
     enum PostscriptLanguage language;
 
     if (streq(inputFileName, "-"))
-        /* Can't read stdin, because we need it to remain positioned for the 
+        /* Can't read stdin, because we need it to remain positioned for the
            Ghostscript interpreter to read it.
         */
         language = COMMON_POSTSCRIPT;
@@ -430,12 +480,57 @@ languageDeclaration(char const inputFileName[]) {
 
 
 static struct Box
+boundingBoxFmPostscriptFile(FILE * const ifP) {
+
+    struct Box retval;
+    bool eof;
+
+    for (retval.isDefined = false, eof = false; !retval.isDefined && !eof; ) {
+        char line[200];
+        char * fgetsRc;
+
+        fgetsRc = fgets(line, sizeof(line), ifP);
+
+        if (fgetsRc == NULL)
+            eof = true;
+        else {
+            int rc;
+            int llx, lly, urx, ury;
+
+            rc = sscanf(line, "%%%%BoundingBox: %d %d %d %d",
+                        &llx, &lly, &urx, &ury);
+            if (rc == 4) {
+                /* We found a BoundingBox statement */
+
+                if (llx > urx)
+                    pm_error("%%%%BoundingBox statement in input file has "
+                             "lower left corner to the right of the "
+                             "upper right corner");
+                if (lly > ury)
+                    pm_error("%%%%BoundingBox statement in input file has "
+                             "lower left corner above the "
+                             "upper right corner");
+
+                retval.llx = llx; retval.lly = lly;
+                retval.urx = urx; retval.ury = ury;
+                retval.isDefined = true;
+            }
+        }
+    }
+    fclose(ifP);
+
+    return retval;
+}
+
+
+
+static struct Box
 computeBoxToExtract(struct Box const cmdlineExtractBox,
                     char       const inputFileName[]) {
 
     struct Box retval;
 
-    if (cmdlineExtractBox.llx != -1)
+    if (cmdlineExtractBox.isDefined)
         /* User told us what box to extract, so that's what we'll do */
         retval = cmdlineExtractBox;
     else {
@@ -446,54 +541,37 @@ computeBoxToExtract(struct Box const cmdlineExtractBox,
 
         if (streq(inputFileName, "-"))
             /* Can't read stdin, because we need it to remain
-               positioned for the Ghostscript interpreter to read it.  
+               positioned for the Ghostscript interpreter to read it.
             */
-            psBb.llx = -1;
+            psBb.isDefined = false;
         else {
             FILE * ifP;
-            bool foundBb;
-            bool eof;
 
             ifP = pm_openr(inputFileName);
-            
-            for (foundBb = FALSE, eof = FALSE; !foundBb && !eof; ) {
-                char line[200];
-                char * fgetsRc;
-
-                fgetsRc = fgets(line, sizeof(line), ifP);
-
-                if (fgetsRc == NULL)
-                    eof = TRUE;
-                else {
-                    int rc;
-                    rc = sscanf(line, "%%%%BoundingBox: %d %d %d %d",
-                                &psBb.llx, &psBb.lly, 
-                                &psBb.urx, &psBb.ury);
-                    if (rc == 4) 
-                        foundBb = TRUE;
-                }
-            }
-            fclose(ifP);
 
-            if (!foundBb) {
-                psBb.llx = -1;
+            psBb = boundingBoxFmPostscriptFile(ifP);
+
+            if (!psBb.isDefined)
                 pm_message("Warning: no %%%%BoundingBox statement "
                            "in the input or command line.  "
                            "Will use defaults");
-            }
         }
-        if (psBb.llx != -1) {
+        if (psBb.isDefined) {
             if (verbose)
                 pm_message("Using %%%%BoundingBox statement from input.");
             retval = psBb;
-        } else { 
+        } else {
             /* Use the center of an 8.5" x 11" page with 1" border all around*/
+            retval.isDefined = true;
             retval.llx = 72;
             retval.lly = 72;
             retval.urx = 540;
             retval.ury = 720;
         }
     }
+
+    assert(retval.isDefined);
+
     if (verbose)
         pm_message("Extracting the box ((%d,%d),(%d,%d))",
                    retval.llx, retval.lly, retval.urx, retval.ury);
@@ -503,7 +581,7 @@ computeBoxToExtract(struct Box const cmdlineExtractBox,
 
 
 static enum Orientation
-computeOrientation(struct CmdlineInfo const cmdline, 
+computeOrientation(struct CmdlineInfo const cmdline,
                    struct Box         const extractBox) {
 /*----------------------------------------------------------------------------
    The proper orientation of the image on the page, given the user's
@@ -531,7 +609,7 @@ computeOrientation(struct CmdlineInfo const cmdline,
         /* 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)
         */
@@ -579,31 +657,30 @@ computeOrientation(struct CmdlineInfo const cmdline,
 
 
 static struct Box
-addBorders(struct Box const inputBox, 
+addBorders(struct Box const inputBox,
            float      const xborderScale,
            float      const yborderScale) {
 /*----------------------------------------------------------------------------
    Return a box which is 'inputBox' plus some borders.
 
    Add left and right borders that are the fraction 'xborderScale' of the
-   width of the input box; likewise for top and bottom borders with 
+   width of the input box; likewise for top and bottom borders with
    'yborderScale'.
 -----------------------------------------------------------------------------*/
-    unsigned int const leftRightBorderSize = 
+    unsigned int const leftRightBorderSize =
         ROUNDU((inputBox.urx - inputBox.llx) * xborderScale);
-    unsigned int const topBottomBorderSize = 
+    unsigned int const topBottomBorderSize =
         ROUNDU((inputBox.ury - inputBox.lly) * yborderScale);
 
     struct Box retval;
 
-
-    assert(inputBox.urx >= inputBox.llx);
-    assert(inputBox.ury >= inputBox.lly);
+    assertValidBox(inputBox); assert(inputBox.isDefined);
 
     retval.llx = inputBox.llx - (int)leftRightBorderSize;
     retval.lly = inputBox.lly - (int)topBottomBorderSize;
     retval.urx = inputBox.urx + (int)leftRightBorderSize;
     retval.ury = inputBox.ury + (int)topBottomBorderSize;
+    retval.isDefined = true;
 
     if (verbose)
         pm_message("With borders, extracted box is ((%d,%d),(%d,%d))",
@@ -627,6 +704,8 @@ writePstrans(struct Box        const box,
 
     const char * pstrans;
 
+    assert(xres > 0); assert(yres > 0);
+
     switch (orientation) {
     case PORTRAIT: {
         int llx, lly;
@@ -647,7 +726,7 @@ writePstrans(struct Box        const box,
     if (pstrans == pm_strsol)
         pm_error("Unable to allocate memory for pstrans");
 
-    if (verbose) 
+    if (verbose)
         pm_message("Postscript prefix command: '%s'", pstrans);
 
     fprintf(pipeToGsP, "%s\n", pstrans);
@@ -678,10 +757,10 @@ computeOutfileArg(struct CmdlineInfo const cmdline) {
     else {
         char * basename;
         const char * suffix;
-        
+
         basename  = strdup(cmdline.inputFileName);
-        if (strlen(basename) > 3 && 
-            streq(basename+strlen(basename)-3, ".ps")) 
+        if (strlen(basename) > 3 &&
+            streq(basename+strlen(basename)-3, ".ps"))
             /* The input file name ends in ".ps".  Chop it off. */
             basename[strlen(basename)-3] = '\0';
 
@@ -729,7 +808,7 @@ computeGsDevice(int  const formatType,
 
 static void
 findGhostscriptProg(const char ** const retvalP) {
-    
+
     *retvalP = NULL;  /* initial assumption */
     if (getenv("GHOSTSCRIPT"))
         *retvalP = strdup(getenv("GHOSTSCRIPT"));
@@ -739,7 +818,7 @@ findGhostscriptProg(const char ** const retvalP) {
             const char * candidate;
 
             pathwork = strdup(getenv("PATH"));
-            
+
             candidate = strtok(pathwork, ":");
 
             *retvalP = NULL;
@@ -773,7 +852,7 @@ findGhostscriptProg(const char ** const retvalP) {
 static void
 execGhostscript(int               const inputPipeFd,
                 char              const ghostscriptDevice[],
-                char              const outfileArg[], 
+                char              const outfileArg[],
                 struct Dimensions const pageDim,
                 unsigned int      const textalphabits) {
 /*----------------------------------------------------------------------------
@@ -791,12 +870,11 @@ execGhostscript(int               const inputPipeFd,
     const char * gopt;
     const char * ropt;
     const char * textalphabitsopt;
-    int rc;
 
     findGhostscriptProg(&ghostscriptProg);
 
     /* Put the input pipe on Standard Input */
-    rc = dup2(inputPipeFd, STDIN_FILENO);
+    dup2(inputPipeFd, STDIN_FILENO);
     close(inputPipeFd);
 
     pm_asprintf(&arg0, "gs");
@@ -817,13 +895,13 @@ execGhostscript(int               const inputPipeFd,
                    "'%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s'",
                    ghostscriptProg, arg0,
                    deviceopt, outfileopt, gopt, ropt, textalphabitsopt,
-                   "-q", "-dNOPAUSE", 
+                   "-q", "-dNOPAUSE",
                    "-dSAFER", "-");
     }
 
     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));
 }
@@ -852,26 +930,26 @@ feedPsToGhostScript(const char *            const inputFileName,
     bool eof;  /* End of file on input */
 
     pipeToGsP = fdopen(pipeToGhostscriptFd, "w");
-    if (pipeToGsP == NULL) 
+    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 
+      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 
+      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 
+      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,
@@ -884,11 +962,11 @@ feedPsToGhostScript(const char *            const inputFileName,
     while (!eof) {
         char buffer[4096];
         size_t readCt;
-            
+
         readCt = fread(buffer, 1, sizeof(buffer), ifP);
-        if (readCt == 0) 
+        if (readCt == 0)
             eof = TRUE;
-        else 
+        else
             fwrite(buffer, 1, readCt, pipeToGsP);
     }
     pm_close(ifP);
@@ -897,7 +975,7 @@ feedPsToGhostScript(const char *            const inputFileName,
         fprintf(pipeToGsP, "\nb4_Inc_state restore showpage\n");
 
     fclose(pipeToGsP);
-}        
+}
 
 
 
@@ -940,7 +1018,7 @@ executeGhostscript(char                    const inputFileName[],
                    struct Dimensions       const imageDim,
                    enum Orientation        const orientation,
                    char                    const ghostscriptDevice[],
-                   char                    const outfileArg[], 
+                   char                    const outfileArg[],
                    unsigned int            const textalphabits,
                    enum PostscriptLanguage const language) {
 
@@ -949,12 +1027,12 @@ executeGhostscript(char                    const inputFileName[],
 
     if (strlen(outfileArg) > 80)
         pm_error("output file spec too long.");
-    
+
     rc = pm_pipe(pipefd);
     if (rc < 0)
         pm_error("Unable to create pipe to talk to Ghostscript process.  "
                  "errno = %d (%s)", errno, strerror(errno));
-    
+
     rc = fork();
     if (rc < 0)
         pm_error("Unable to fork a Ghostscript process.  errno=%d (%s)",
@@ -986,13 +1064,13 @@ executeGhostscript(char                    const inputFileName[],
 
         if (gsTermStatus != 0) {
             if (WIFEXITED(gsTermStatus))
-                pm_error("Ghostscript failed.  Exit code=%d\n", 
+                pm_error("Ghostscript failed.  Exit code=%d\n",
                          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", 
+            else
+                pm_error("Ghostscript process died with exit code %d",
                          gsTermStatus);
         }
     }
@@ -1010,7 +1088,7 @@ main(int argc, char ** argv) {
         /* 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. 
+           that will become the output.
            */
     struct Box borderedBox;
         /* Same as above, but expanded to include borders */
@@ -1031,32 +1109,31 @@ main(int argc, char ** argv) {
     extractBox = computeBoxToExtract(cmdline.extractBox, inputFileName);
 
     language = languageDeclaration(inputFileName);
-    
+
     orientation = computeOrientation(cmdline, extractBox);
 
     borderedBox = addBorders(extractBox, cmdline.xborder, cmdline.yborder);
 
+    assertValidBox(borderedBox); assert(borderedBox.isDefined);
+
     computeSizeRes(cmdline, borderedBox, &imageDim);
 
-    if (imageDim.xres == 0)
-        imageDim.xres = 1;
-    if (imageDim.yres == 0)
-        imageDim.yres = 1;
-    
+    assert(imageDim.xres > 0); assert(imageDim.yres > 0);
+
     outfileArg = computeOutfileArg(cmdline);
 
-    ghostscriptDevice = 
+    ghostscriptDevice =
         computeGsDevice(cmdline.formatType, cmdline.forceplain);
-    
+
     pm_message("Writing %s format", ghostscriptDevice);
-    
+
     executeGhostscript(inputFileName, borderedBox, imageDim, orientation,
                        ghostscriptDevice, outfileArg, cmdline.textalphabits,
                        language);
 
     pm_strfree(ghostscriptDevice);
     pm_strfree(outfileArg);
-    
+
     return 0;
 }
 
diff --git a/converter/other/rletopnm.c b/converter/other/rletopnm.c
index 99959141..018456c8 100644
--- a/converter/other/rletopnm.c
+++ b/converter/other/rletopnm.c
@@ -35,6 +35,7 @@
  *
  */
 
+#define _DEFAULT_SOURCE 1  /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
@@ -264,7 +265,8 @@ writePpmRaster(FILE * const imageoutFileP,
     pixel *pixelrow;
     gray *alpharow;
    
-    int scan, x, y;
+    int scan;
+    int x;
     /*
      *  Allocate some stuff.
      */
@@ -280,9 +282,9 @@ writePpmRaster(FILE * const imageoutFileP,
     /*
      * Loop through those scan lines.
      */
-    for (scan = 0; scan < height; scan++)
-        y = rle_getrow(&hdr, scanlines[height - scan - 1]);
-    for (scan = 0; scan < height; scan++) {
+    for (scan = 0; scan < height; ++scan)
+        rle_getrow(&hdr, scanlines[height - scan - 1]);
+    for (scan = 0; scan < height; ++scan) {
         scanline = scanlines[scan];
         switch (visual) {
         case GRAYSCALE:    /* 8 bits without colormap */
@@ -366,7 +368,6 @@ writePgmRaster(FILE * const imageoutFileP,
     gray * pixelrow;
     gray * alpharow;
     int scan;
-    int y;
     /*
      *  Allocate some stuff.
      */
@@ -383,7 +384,7 @@ writePgmRaster(FILE * const imageoutFileP,
      * Loop through those scan lines.
      */
     for (scan = 0; scan < height; ++scan)
-        y = rle_getrow(&hdr, scanlines[height - scan - 1]);
+        rle_getrow(&hdr, scanlines[height - scan - 1]);
 
     for (scan = 0; scan < height; ++scan) {
         int x;
diff --git a/converter/other/svgtopam.c b/converter/other/svgtopam.c
index 58e7928f..ca6f4dc7 100644
--- a/converter/other/svgtopam.c
+++ b/converter/other/svgtopam.c
@@ -26,6 +26,7 @@
    
 ============================================================================*/
 
+#define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
 #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> */
@@ -146,36 +147,36 @@ typedef struct {
     unsigned int height;
     pixel ** pixels;
     pixval maxval;
-} canvas;
+} Canvas;
 
 typedef struct {
     pixel fillColor;
-} style;
+} Style;
 
 
 
 typedef struct {
     const char * pathText;
         /* This is e.g. "M0 0 L1 1 L9 8 Z" */
-    style        style;
+    Style        style;
         /* This is the style as given by a 'style' attribute of <path> */
     unsigned int pathTextLength;
         /* This is the length in characters of 'pathText'.  It's redundant
            with 'pathText' and exists for convenience.
         */
-} path;
+} Path;
 
 static void
 createPath(const char * const pathText,
-           style        const style,
-           path **      const pathPP) {
+           Style        const style,
+           Path **      const pathPP) {
 /*----------------------------------------------------------------------------
    Create a path as described by a <path> element whose "style" attribute
    indicates style 'style' and whose "d" attribute indicates path data
    'pathText'.
 -----------------------------------------------------------------------------*/
     bool error;
-    path * pathP;
+    Path * pathP;
     
     MALLOCVAR(pathP);
     if (pathP == NULL)
@@ -203,7 +204,7 @@ createPath(const char * const pathText,
 
 
 static void
-destroyPath(path * const pathP) {
+destroyPath(Path * const pathP) {
     
     assert(pathP->pathTextLength == strlen(pathP->pathText));
 
@@ -217,13 +218,13 @@ destroyPath(path * const pathP) {
 typedef struct {
     unsigned int x;
     unsigned int y;
-} point;
+} Point;
 
-static point
+static Point
 makePoint(unsigned int const x,
           unsigned int const y) {
 
-    point p;
+    Point p;
     
     p.x = x;
     p.y = y;
@@ -232,7 +233,7 @@ makePoint(unsigned int const x,
 }
 
 static ppmd_point
-makePpmdPoint(point const arg) {
+makePpmdPoint(Point const arg) {
 
     ppmd_point p;
 
@@ -247,16 +248,16 @@ typedef enum {
     PATH_LINETO,
     PATH_CLOSEPATH,
     PATH_CUBIC
-} pathCommandVerb;
+} PathCommandVerb;
 
 typedef struct {
-    point dest;
-} pathMovetoArgs;
+    Point dest;
+} PathMovetoArgs;
 
 typedef struct {
     /* Draw a line segment from current point to 'dest' */
-    point dest;
-} pathLinetoArgs;
+    Point dest;
+} PathLinetoArgs;
 
 typedef struct {
     /* Draw a cubic spline from current point to 'dest' with control points
@@ -271,19 +272,19 @@ typedef struct {
        A cubic curve is a plot of a polynomial equation of degree 3
        (or less, for our purposes).
     */
-    point dest;
-    point ctl1;
-    point ctl2;
-} pathCubicArgs;
+    Point dest;
+    Point ctl1;
+    Point ctl2;
+} PathCubicArgs;
 
 typedef struct {
-    pathCommandVerb verb;
+    PathCommandVerb verb;
     union {
-        pathMovetoArgs moveto;
-        pathLinetoArgs lineto;
-        pathCubicArgs  cubic;
+        PathMovetoArgs moveto;
+        PathLinetoArgs lineto;
+        PathCubicArgs  cubic;
     } args;
-} pathCommand;
+} PathCommand;
 
 
 
@@ -291,15 +292,15 @@ typedef struct {
 /*----------------------------------------------------------------------------
    This is an object for reading through a path from beginning to end.
 -----------------------------------------------------------------------------*/
-    path *       pathP;
+    Path *       pathP;
     unsigned int cursor;
-} pathReader;
+} PathReader;
 
 static void
-createPathReader(path *        const pathP,
-                 pathReader ** const pathReaderPP) {
+pathReader_create(Path *        const pathP,
+                  PathReader ** const pathReaderPP) {
 
-    pathReader * pathReaderP;
+    PathReader * pathReaderP;
 
     MALLOCVAR_NOFAIL(pathReaderP);
 
@@ -310,18 +311,31 @@ createPathReader(path *        const pathP,
 }
 
 static void
-destroyPathReader(pathReader * const pathReaderP) {
+pathReader_destroy(PathReader * const pathReaderP) {
     free(pathReaderP);
 }
 
 
 
+static const char *
+pathReader_context(PathReader * const pathReaderP) {
+
+    const char * retval;
+
+    pm_asprintf(&retval, "Character position %u (starting at 0) in '%s'",
+                pathReaderP->cursor, pathReaderP->pathP->pathText);
+
+    return retval;
+}
+
+
+
 static void
-skipWhiteSpace(pathReader * const pathReaderP) {
+pathReader_skipWhiteSpace(PathReader * const pathReaderP) {
 /*----------------------------------------------------------------------------
    Move the cursor over any white space where it now points.
 -----------------------------------------------------------------------------*/
-    const path * const pathP = pathReaderP->pathP;
+    const Path * const pathP = pathReaderP->pathP;
 
     while (isspace(pathP->pathText[pathReaderP->cursor]) &&
            pathReaderP->cursor < pathP->pathTextLength)
@@ -331,10 +345,10 @@ skipWhiteSpace(pathReader * const pathReaderP) {
 
 
 static void
-getNumber(pathReader *   const pathReaderP,
-          unsigned int * const numberP) {
+pathReader_getNumber(PathReader *   const pathReaderP,
+                     unsigned int * const numberP) {
 
-    const path * const pathP          = pathReaderP->pathP;
+    const Path * const pathP          = pathReaderP->pathP;
     const char * const pathText       = pathP->pathText;
     size_t       const pathTextLength = pathP->pathTextLength;
 
@@ -342,7 +356,10 @@ getNumber(pathReader *   const pathReaderP,
 
     if (pathReaderP->cursor >= pathTextLength)
         pm_error("Path description ends where a number was expected.");
-    else {
+    else if (!isdigit(pathText[pathReaderP->cursor])) {
+        pm_error("Character '%c' instead of a digit where number expected",
+                 pathText[pathReaderP->cursor]);
+    } else {
         unsigned int number;
 
         number = 0;  /* initial value */
@@ -352,6 +369,10 @@ getNumber(pathReader *   const pathReaderP,
             number = 10 * number + (pathText[pathReaderP->cursor] - '0');
             ++pathReaderP->cursor;
         }
+        if (pathText[pathReaderP->cursor] == '.')
+            pm_error("Number contains decimal point.  This program does not "
+                     "know how to deal with fractional positions");
+
         *numberP = number;
     }
 }
@@ -359,15 +380,15 @@ getNumber(pathReader *   const pathReaderP,
 
 
 static void
-getNextCommand(pathReader *  const pathReaderP,
-               pathCommand * const pathCommandP,
-               bool *        const endOfPathP) {
+pathReader_getNextCommand(PathReader *  const pathReaderP,
+                          PathCommand * const pathCommandP,
+                          bool *        const endOfPathP) {
 
-    const path * const pathP          = pathReaderP->pathP;
+    const Path * const pathP          = pathReaderP->pathP;
     const char * const pathText       = pathP->pathText;
     size_t       const pathTextLength = pathP->pathTextLength;
 
-    skipWhiteSpace(pathReaderP);
+    pathReader_skipWhiteSpace(pathReaderP);
 
     if (pathReaderP->cursor >= pathTextLength)
         *endOfPathP = true;
@@ -375,67 +396,79 @@ getNextCommand(pathReader *  const pathReaderP,
         switch (pathText[pathReaderP->cursor++]) {
         case 'M':
             pathCommandP->verb = PATH_MOVETO;
-            skipWhiteSpace(pathReaderP);
-            getNumber(pathReaderP, &pathCommandP->args.moveto.dest.x);
-            skipWhiteSpace(pathReaderP);
-            getNumber(pathReaderP, &pathCommandP->args.moveto.dest.y);
+            pathReader_skipWhiteSpace(pathReaderP);
+            pathReader_getNumber(pathReaderP,
+                                 &pathCommandP->args.moveto.dest.x);
+            pathReader_skipWhiteSpace(pathReaderP);
+            pathReader_getNumber(pathReaderP,
+                                 &pathCommandP->args.moveto.dest.y);
             break;
         case 'L':
             pathCommandP->verb = PATH_LINETO;
-            skipWhiteSpace(pathReaderP);
-            getNumber(pathReaderP, &pathCommandP->args.lineto.dest.x);
-            skipWhiteSpace(pathReaderP);
-            getNumber(pathReaderP, &pathCommandP->args.lineto.dest.y);
+            pathReader_skipWhiteSpace(pathReaderP);
+            pathReader_getNumber(pathReaderP,
+                                 &pathCommandP->args.lineto.dest.x);
+            pathReader_skipWhiteSpace(pathReaderP);
+            pathReader_getNumber(pathReaderP,
+                                 &pathCommandP->args.lineto.dest.y);
             break;
         case 'C':
             pathCommandP->verb = PATH_CUBIC;
-            skipWhiteSpace(pathReaderP);
-            getNumber(pathReaderP, &pathCommandP->args.cubic.ctl1.x);
-            skipWhiteSpace(pathReaderP);
-            getNumber(pathReaderP, &pathCommandP->args.cubic.ctl1.y);
-            skipWhiteSpace(pathReaderP);
-            getNumber(pathReaderP, &pathCommandP->args.cubic.ctl2.x);
-            skipWhiteSpace(pathReaderP);
-            getNumber(pathReaderP, &pathCommandP->args.cubic.ctl2.y);
-            skipWhiteSpace(pathReaderP);
-            getNumber(pathReaderP, &pathCommandP->args.cubic.dest.x);
-            skipWhiteSpace(pathReaderP);
-            getNumber(pathReaderP, &pathCommandP->args.cubic.dest.y);
+            pathReader_skipWhiteSpace(pathReaderP);
+            pathReader_getNumber(pathReaderP, &pathCommandP->args.cubic.ctl1.x);
+            pathReader_skipWhiteSpace(pathReaderP);
+            pathReader_getNumber(pathReaderP, &pathCommandP->args.cubic.ctl1.y);
+            pathReader_skipWhiteSpace(pathReaderP);
+            pathReader_getNumber(pathReaderP, &pathCommandP->args.cubic.ctl2.x);
+            pathReader_skipWhiteSpace(pathReaderP);
+            pathReader_getNumber(pathReaderP, &pathCommandP->args.cubic.ctl2.y);
+            pathReader_skipWhiteSpace(pathReaderP);
+            pathReader_getNumber(pathReaderP, &pathCommandP->args.cubic.dest.x);
+            pathReader_skipWhiteSpace(pathReaderP);
+            pathReader_getNumber(pathReaderP, &pathCommandP->args.cubic.dest.y);
             break;
         case 'z':
             pathCommandP->verb = PATH_CLOSEPATH;
             break;
-        default:
-            pm_error("Unrecognized command in <path>: '%c'",
-                     pathText[pathReaderP->cursor++]);
+        default: {
+            const char * const context = pathReader_context(pathReaderP);
+            
+            pm_errormsg("Unrecognized command in <path>: '%c'.  %s",
+                        pathText[pathReaderP->cursor++], context);
+
+            pm_strfree(context);
+
+            pm_longjmp();
+        }
         }
     }
 }
 
 
+
 static void
-outlineObject(path *           const pathP,
+outlineObject(Path *           const pathP,
               struct fillobj * const fillObjP) {
 /*----------------------------------------------------------------------------
   Create a fill object, which contains and outline of the object and
   can be used with ppmd_fill() to fill the figure.  The outline is as
   described by *pathP.
 -----------------------------------------------------------------------------*/
-    pathReader * pathReaderP;
+    PathReader * pathReaderP;
     bool endOfPath;
-    point currentPos;
-    point subpathStart;
+    Point currentPos;
+    Point subpathStart;
         /* Point at which the current subpath starts */
 
     endOfPath = false;
     subpathStart = makePoint(0,0);
     currentPos = subpathStart;
 
-    createPathReader(pathP, &pathReaderP);
+    pathReader_create(pathP, &pathReaderP);
 
     while (!endOfPath) {
-        pathCommand pathCommand;
-        getNextCommand(pathReaderP, &pathCommand, &endOfPath);
+        PathCommand pathCommand;
+        pathReader_getNextCommand(pathReaderP, &pathCommand, &endOfPath);
         if (!endOfPath) {
             switch (pathCommand.verb) {
             case PATH_MOVETO:
@@ -447,7 +480,7 @@ outlineObject(path *           const pathP,
                 currentPos = subpathStart;
                 break;
             case PATH_LINETO: {
-                point const dest = pathCommand.args.lineto.dest;
+                Point const dest = pathCommand.args.lineto.dest;
                 if (traceDraw)
                     pm_message("Lining to (%u, %u)", dest.x, dest.y);
                 ppmd_line(NULL, 0, 0, 0,
@@ -465,12 +498,14 @@ outlineObject(path *           const pathP,
                 currentPos = subpathStart;
                 break;
             case PATH_CUBIC: {
-                point const dest = pathCommand.args.cubic.dest;
-                point const ctl1 = pathCommand.args.cubic.ctl1;
-                point const ctl2 = pathCommand.args.cubic.ctl2;
+                Point const dest = pathCommand.args.cubic.dest;
+                Point const ctl1 = pathCommand.args.cubic.ctl1;
+                Point const ctl2 = pathCommand.args.cubic.ctl2;
                 if (traceDraw)
                     pm_message("Doing cubic spline to (%u, %u)",
                                dest.x, dest.y);
+                pm_error("SVG image contains a cubic spline path.  "
+                         "This program cannot process cubic splines.");
                 /* We need to write ppmd_spline4() */
                 ppmd_spline4p(NULL, 0, 0, 0,
                               makePpmdPoint(currentPos),
@@ -483,14 +518,14 @@ outlineObject(path *           const pathP,
             }
         }
     }
-    destroyPathReader(pathReaderP);
+    pathReader_destroy(pathReaderP);
 }
 
 
 
 static void
-drawPath(canvas * const canvasP,
-         path *   const pathP) {
+drawPath(Canvas * const canvasP,
+         Path *   const pathP) {
 /*----------------------------------------------------------------------------
    Draw the path 'pathP' on the canvas 'canvasP'.
 -----------------------------------------------------------------------------*/
@@ -517,10 +552,10 @@ drawPath(canvas * const canvasP,
 
 
 
-static style
+static Style
 interpretStyle(const char * const styleAttr) {
 
-    style style;
+    Style style;
 
     char * buffer;
 
@@ -581,7 +616,7 @@ interpretStyle(const char * const styleAttr) {
 
 static void
 getPathAttributes(xmlTextReaderPtr const xmlReaderP,
-                  style *          const styleP,
+                  Style *          const styleP,
                   const char **    const pathP) {
 
     const char * const style = getAttribute(xmlReaderP, "style");
@@ -627,11 +662,11 @@ processSubPathNode(xmlTextReaderPtr const xmlReaderP,
 
 static void
 processPathElement(xmlTextReaderPtr const xmlReaderP,
-                   canvas *         const canvasP) {
+                   Canvas *         const canvasP) {
 
-    style style;
+    Style style;
     const char * pathData;
-    path * pathP;
+    Path * pathP;
     bool endOfPath;
 
     assert(xmlTextReaderNodeType(xmlReaderP) == XML_READER_TYPE_ELEMENT);
@@ -669,29 +704,6 @@ processPathElement(xmlTextReaderPtr const xmlReaderP,
 
 
 static void
-stringToUint(const char *   const string,
-             unsigned int * const uintP,
-             const char **  const errorP) {
-
-    /* TODO: move this to nstring.c */
-
-    if (strlen(string) == 0)
-        pm_asprintf(errorP, "Value is a null string");
-    else {
-        char * tailptr;
-
-        *uintP = strtoul(string, &tailptr, 10);
-
-        if (*tailptr != '\0')
-            pm_asprintf(errorP, "Non-numeric crap in string: '%s'", tailptr);
-        else
-            *errorP = NULL;
-    }
-}
-
-
-
-static void
 getSvgAttributes(xmlTextReaderPtr const xmlReaderP,
                  unsigned int *   const colsP,
                  unsigned int *   const rowsP) {
@@ -701,14 +713,16 @@ getSvgAttributes(xmlTextReaderPtr const xmlReaderP,
 
     const char * error;
 
-    stringToUint(width, colsP, &error);
+    pm_string_to_uint(width, colsP, &error);
     if (error) {
-        pm_error("'width' attribute of <svg> has invalid value.  %s", error);
+        pm_error("'width' attribute of <svg> has invalid value '%s'.  %s",
+                 width, error);
         pm_strfree(error);
     }
-    stringToUint(height, rowsP, &error);
+    pm_string_to_uint(height, rowsP, &error);
     if (error) {
-        pm_error("'height' attribute of <svg> has invalid value.  %s", error);
+        pm_error("'height' attribute of <svg> has invalid value '%s'.  %s",
+                 height, error);
         pm_strfree(error);
     }
 }
@@ -717,7 +731,7 @@ getSvgAttributes(xmlTextReaderPtr const xmlReaderP,
 
 static void
 processSubSvgElement(xmlTextReaderPtr const xmlReaderP,
-                     canvas *         const canvasP) {
+                     Canvas *         const canvasP) {
 
     const char * const nodeName = currentNodeName(xmlReaderP);
 
@@ -734,7 +748,7 @@ processSubSvgElement(xmlTextReaderPtr const xmlReaderP,
 
 static void
 processSubSvgNode(xmlTextReaderPtr const xmlReaderP,
-                  canvas *         const canvasP,
+                  Canvas *         const canvasP,
                   bool *           const endOfSvgP) {
 
     xmlReaderTypes const nodeType = xmlTextReaderNodeType(xmlReaderP);
@@ -764,9 +778,9 @@ static void
 createCanvas(unsigned int const width,
              unsigned int const height,
              pixval       const maxval,
-             canvas **    const canvasPP) {
+             Canvas **    const canvasPP) {
 
-    canvas * canvasP;
+    Canvas * canvasP;
 
     MALLOCVAR_NOFAIL(canvasP);
 
@@ -781,7 +795,7 @@ createCanvas(unsigned int const width,
 
 
 static void
-destroyCanvas(canvas * const canvasP) {
+destroyCanvas(Canvas * const canvasP) {
 
     ppm_freearray(canvasP->pixels, canvasP->height);
 
@@ -792,7 +806,7 @@ destroyCanvas(canvas * const canvasP) {
 
 static void
 writePam(FILE *   const ofP,
-         canvas * const canvasP) {
+         Canvas * const canvasP) {
 
     unsigned int row;
     struct pam pam;
@@ -838,7 +852,7 @@ processSvgElement(xmlTextReaderPtr const xmlReaderP,
 
     unsigned int width, height;
     bool endOfSvg;
-    canvas * canvasP;
+    Canvas * canvasP;
 
     assert(xmlTextReaderNodeType(xmlReaderP) == XML_READER_TYPE_ELEMENT);
     assert(streq(currentNodeName(xmlReaderP), "svg"));
diff --git a/converter/other/tiff.c b/converter/other/tiff.c
index d0cbbd74..39e3b0ce 100644
--- a/converter/other/tiff.c
+++ b/converter/other/tiff.c
@@ -6,6 +6,7 @@
 
 ============================================================================*/
 
+#define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE    /* Make sure strcaseeq() is in nstring.h */
 
 #include <string.h>
diff --git a/converter/other/tifftopnm.c b/converter/other/tifftopnm.c
index 4a8cf902..c1e7af85 100644
--- a/converter/other/tifftopnm.c
+++ b/converter/other/tifftopnm.c
@@ -46,12 +46,14 @@
    give the user the -byrow option to order (2) only.
 */
 
+#define _DEFAULT_SOURCE 1  /* New name for SVID & BSD source defines */
 #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 <stdio.h>
+#include <sys/wait.h>  /* WIFSIGNALED, etc. */
 
 #include "pm_c_util.h"
 #include "shhopt.h"
@@ -182,14 +184,14 @@ getBps(TIFF *           const tif,
 
     unsigned short tiffBps;
     unsigned short bps;
-    int rc;
+    int fldPresent;
 
-    rc = TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &tiffBps);
-    bps = (rc == 0) ? 1 : tiffBps;
+    fldPresent = TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &tiffBps);
+    bps = fldPresent ? tiffBps : 1;
 
     if (bps < 1 || (bps > 8 && bps != 16 && bps != 32))
         pm_error("This program can process Tiff images with only "
-                 "1-8 or 16 bits per sample.  The input Tiff image "
+                 "1-8 or 16 or 32 bits per sample.  The input Tiff image "
                  "has %hu bits per sample.", bps);
     else
         *bpsP = bps;
@@ -217,26 +219,30 @@ struct tiffDirInfo {
 
 
 static void
-tiffToImageDim(unsigned int   const tiffCols,
-               unsigned int   const tiffRows,
+tiffToImageDim(unsigned int   const tiffWidth,
+               unsigned int   const tiffHeight,
                unsigned short const orientation,
                unsigned int * const imageColsP,
                unsigned int * const imageRowsP) {
-
+/*----------------------------------------------------------------------------
+   Determine the image dimensions (as *imageColsP and *imageRowsP) from the
+   width, height, and orientation of the TIFF raster ('tiffWidth',
+   'tiffHeight', and 'orientation', respectively.
+-----------------------------------------------------------------------------*/
     switch (orientation) {
     case ORIENTATION_TOPLEFT:
     case ORIENTATION_TOPRIGHT:
     case ORIENTATION_BOTRIGHT:
     case ORIENTATION_BOTLEFT:
-        *imageColsP = tiffCols;
-        *imageRowsP = tiffRows;
+        *imageColsP = tiffWidth;
+        *imageRowsP = tiffHeight;
         break;
     case ORIENTATION_LEFTTOP:
     case ORIENTATION_RIGHTTOP:
     case ORIENTATION_RIGHTBOT:
     case ORIENTATION_LEFTBOT:
-        *imageColsP = tiffRows;
-        *imageRowsP = tiffCols;
+        *imageColsP = tiffHeight;
+        *imageRowsP = tiffWidth;
         break;
     default:
         pm_error("Invalid value for orientation tag in TIFF directory: %u",
@@ -255,28 +261,96 @@ getTiffDimensions(TIFF *         const tiffP,
    dimensions of the internal raster matrix -- the dimensions of the
    actual visual image.
 -----------------------------------------------------------------------------*/
-    int ok;
+    int fldPresent;
 
     unsigned int width, length;
     unsigned short tiffOrientation;
     unsigned short orientation;
-    int present;
 
-    ok = TIFFGetField(tiffP, TIFFTAG_IMAGEWIDTH, &width);
-    if (!ok)
+    fldPresent = TIFFGetField(tiffP, TIFFTAG_IMAGEWIDTH, &width);
+    if (!fldPresent)
         pm_error("Input Tiff file is invalid.  It has no IMAGEWIDTH tag.");
-    ok = TIFFGetField(tiffP, TIFFTAG_IMAGELENGTH, &length);
-    if (!ok)
+    fldPresent = TIFFGetField(tiffP, TIFFTAG_IMAGELENGTH, &length);
+    if (!fldPresent)
         pm_error("Input Tiff file is invalid.  It has no IMAGELENGTH tag.");
 
-    present = TIFFGetField(tiffP, TIFFTAG_ORIENTATION, &tiffOrientation);
-    orientation = present ? tiffOrientation : ORIENTATION_TOPLEFT;
+    fldPresent = TIFFGetField(tiffP, TIFFTAG_ORIENTATION, &tiffOrientation);
+    orientation = fldPresent ? tiffOrientation : ORIENTATION_TOPLEFT;
 
     tiffToImageDim(width, length, orientation, colsP, rowsP);
 }
 
 
 
+static unsigned short
+planarConfigFmTiff(TIFF * const tiffP) {
+
+    int fldPresent;
+    unsigned short retval;
+
+    fldPresent = TIFFGetField(tiffP, TIFFTAG_PLANARCONFIG, &retval);
+
+    if (!fldPresent)
+        pm_error("PLANARCONFIG tag is not in Tiff file, though it "
+                 "has more than one sample per pixel.  "
+                 "TIFFGetField() of it failed.  This means the input "
+                 "is not valid Tiff.");
+
+    return retval;
+}
+
+
+
+static void
+validatePlanarConfig(unsigned short const planarconfig,
+                     unsigned short const photomet) {
+
+    switch (planarconfig) {
+    case PLANARCONFIG_CONTIG:
+        break;
+    case PLANARCONFIG_SEPARATE:
+        if (photomet != PHOTOMETRIC_RGB && 
+            photomet != PHOTOMETRIC_SEPARATED)
+            pm_error("This program can handle separate planes only "
+                     "with RGB (PHOTOMETRIC tag = %u) or SEPARATED "
+                     "(PHOTOMETRIC tag = %u) data.  The input Tiff file " 
+                     "has PHOTOMETRIC tag = %hu.",
+                     PHOTOMETRIC_RGB, PHOTOMETRIC_SEPARATED,
+                     photomet);
+        break;
+    default:
+        pm_error("Unrecognized PLANARCONFIG tag value in Tiff input: %u",
+                 planarconfig);
+    }
+}
+
+
+
+static unsigned short
+orientationFmTiff(TIFF * const tiffP) {
+
+    unsigned short tiffOrientation;
+    int fldPresent;
+
+    fldPresent = TIFFGetField(tiffP, TIFFTAG_ORIENTATION, &tiffOrientation);
+
+    return fldPresent ? tiffOrientation : ORIENTATION_TOPLEFT;
+}
+
+
+
+static void
+dumpHeader(const struct tiffDirInfo * const headerP) {
+
+    pm_message("%ux%ux%u raster matrix, oriented %u",
+               headerP->width, headerP->height,
+               headerP->bps * headerP->spp, headerP->orientation);
+    pm_message("%hu bits/sample, %hu samples/pixel",
+               headerP->bps, headerP->spp);
+}
+
+
+
 static void 
 readDirectory(TIFF *               const tiffP,
               bool                 const headerdump,
@@ -285,7 +359,7 @@ readDirectory(TIFF *               const tiffP,
    Read various values of TIFF tags from the TIFF directory, and
    default them if not in there and make guesses where values are
    invalid.  Exit program with error message if required tags aren't
-   there or values are inconsistent or beyond our capabilities.  if
+   there or values are inconsistent or beyond our capabilities.  If
    'headerdump' is true, issue informational messages about what we
    find.
 
@@ -293,7 +367,7 @@ readDirectory(TIFF *               const tiffP,
    input file contains invalid values).  We generally return those
    invalid values to our caller.
 -----------------------------------------------------------------------------*/
-    int rc;
+    int fldPresent;
     unsigned short tiffSpp;
 
     if (headerdump)
@@ -301,65 +375,35 @@ readDirectory(TIFF *               const tiffP,
 
     getBps(tiffP, &headerP->bps);
 
-    rc = TIFFGetFieldDefaulted(tiffP, TIFFTAG_FILLORDER, &headerP->fillorder);
-    rc = TIFFGetField(tiffP, TIFFTAG_SAMPLESPERPIXEL, &tiffSpp);
-    headerP->spp = (rc == 0) ? 1 : tiffSpp;
+    fldPresent =
+        TIFFGetFieldDefaulted(tiffP, TIFFTAG_FILLORDER, &headerP->fillorder);
+    fldPresent = TIFFGetField(tiffP, TIFFTAG_SAMPLESPERPIXEL, &tiffSpp);
+    headerP->spp = fldPresent ? tiffSpp: 1;
 
-    rc = TIFFGetField(tiffP, TIFFTAG_PHOTOMETRIC, &headerP->photomet);
-    if (rc == 0)
+    fldPresent = TIFFGetField(tiffP, TIFFTAG_PHOTOMETRIC, &headerP->photomet);
+    if (!fldPresent)
         pm_error("PHOTOMETRIC tag is not in Tiff file.  "
                  "TIFFGetField() of it failed.\n"
                  "This means the input is not valid Tiff.");
 
-    if (headerP->spp > 1) {
-        rc = TIFFGetField(tiffP, TIFFTAG_PLANARCONFIG, &headerP->planarconfig);
-        if (rc == 0)
-            pm_error("PLANARCONFIG tag is not in Tiff file, though it "
-                     "has more than one sample per pixel.  "
-                     "TIFFGetField() of it failed.  This means the input "
-                     "is not valid Tiff.");
-    } else
+    if (headerP->spp > 1)
+        headerP->planarconfig = planarConfigFmTiff(tiffP);
+    else
         headerP->planarconfig = PLANARCONFIG_CONTIG;
 
-    switch (headerP->planarconfig) {
-    case PLANARCONFIG_CONTIG:
-        break;
-    case PLANARCONFIG_SEPARATE:
-        if (headerP->photomet != PHOTOMETRIC_RGB && 
-            headerP->photomet != PHOTOMETRIC_SEPARATED)
-            pm_error("This program can handle separate planes only "
-                     "with RGB (PHOTOMETRIC tag = %u) or SEPARATED "
-                     "(PHOTOMETRIC tag = %u) data.  The input Tiff file " 
-                     "has PHOTOMETRIC tag = %hu.",
-                     PHOTOMETRIC_RGB, PHOTOMETRIC_SEPARATED,
-                     headerP->photomet);
-        break;
-    default:
-        pm_error("Unrecognized PLANARCONFIG tag value in Tiff input: %u.\n",
-                 headerP->planarconfig);
-    }
+    validatePlanarConfig(headerP->planarconfig, headerP->photomet);
 
-    rc = TIFFGetField(tiffP, TIFFTAG_IMAGEWIDTH, &headerP->width);
-    if (rc == 0)
+    fldPresent = TIFFGetField(tiffP, TIFFTAG_IMAGEWIDTH, &headerP->width);
+    if (!fldPresent)
         pm_error("Input Tiff file is invalid.  It has no IMAGEWIDTH tag.");
-    rc = TIFFGetField(tiffP, TIFFTAG_IMAGELENGTH, &headerP->height);
-    if (rc == 0)
+    fldPresent = TIFFGetField(tiffP, TIFFTAG_IMAGELENGTH, &headerP->height);
+    if (!fldPresent)
         pm_error("Input Tiff file is invalid.  It has no IMAGELENGTH tag.");
 
-    {
-        unsigned short tiffOrientation;
-        int present;
-        present = TIFFGetField(tiffP, TIFFTAG_ORIENTATION, &tiffOrientation);
-        headerP->orientation =
-            present ? tiffOrientation : ORIENTATION_TOPLEFT;
-    }
-    if (headerdump) {
-        pm_message("%ux%ux%u raster matrix, oriented %u",
-                   headerP->width, headerP->height,
-                   headerP->bps * headerP->spp, headerP->orientation);
-        pm_message("%hu bits/sample, %hu samples/pixel",
-                   headerP->bps, headerP->spp);
-    }
+    headerP->orientation = orientationFmTiff(tiffP);
+
+    if (headerdump)
+        dumpHeader(headerP);
 }
 
 
@@ -533,7 +577,7 @@ computeFillorder(unsigned short   const fillorderTag,
 
 
 static void
-analyzeImageType(TIFF *             const tif, 
+analyzeImageType(TIFF *             const tiffP, 
                  unsigned short     const bps, 
                  unsigned short     const spp, 
                  unsigned short     const photomet,
@@ -542,129 +586,147 @@ analyzeImageType(TIFF *             const tif,
                  xel *              const colormap,
                  bool               const headerdump,
                  struct CmdlineInfo const cmdline) {
+/*----------------------------------------------------------------------------
+   Determine from the TIFF header in *tif certain properties of the image
+   as well as the proper format of PNM image for the conversion.
 
-    bool grayscale; 
-
-        /* How come we don't deal with the photometric for the monochrome 
-           case (make sure it's one we know)?  -Bryan 00.03.04
-        */
-        switch (photomet) {
-        case PHOTOMETRIC_MINISBLACK:
-        case PHOTOMETRIC_MINISWHITE:
-            if (spp != 1)
-                pm_error("This grayscale image has %d samples per pixel.  "
-                         "We understand only 1.", spp);
-            grayscale = TRUE;
-            *maxvalP = pm_bitstomaxval(MIN(bps,16));
-            if (headerdump)
-                pm_message("grayscale image, (min=%s) output maxval %u ", 
-                           photomet == PHOTOMETRIC_MINISBLACK ? 
-                           "black" : "white",
-                           *maxvalP
-                           );
-            break;
+   *formatP and *maxvalP are the basic PNM parameters.
+-----------------------------------------------------------------------------*/
+    switch (photomet) {
+    case PHOTOMETRIC_MINISBLACK:
+    case PHOTOMETRIC_MINISWHITE:
+        if (spp != 1)
+            pm_error("This grayscale image has %d samples per pixel.  "
+                     "We understand only 1.", spp);
+
+        *formatP = bps == 1 ? PBM_TYPE : PGM_TYPE;
+
+        *maxvalP = pm_bitstomaxval(MIN(bps, 16));
+
+        if (headerdump)
+            pm_message("grayscale image, (min=%s) output maxval %u ", 
+                       photomet == PHOTOMETRIC_MINISBLACK ? 
+                       "black" : "white",
+                       *maxvalP
+                );
+        break;
             
-        case PHOTOMETRIC_PALETTE: {
-            int i;
-            int numcolors;
-            unsigned short* redcolormap;
-            unsigned short* greencolormap;
-            unsigned short* bluecolormap;
-
-            if (headerdump)
-                pm_message("colormapped");
-
-            if (spp != 1)
-                pm_error("This paletted image has %d samples per pixel.  "
-                         "We understand only 1.", spp);
-
-            if (!TIFFGetField(tif, TIFFTAG_COLORMAP, 
-                              &redcolormap, &greencolormap, &bluecolormap))
-                pm_error("error getting colormaps");
-
-            numcolors = 1 << bps;
-            if (numcolors > MAXCOLORS)
-                pm_error("too many colors");
-            *maxvalP = PNM_MAXMAXVAL;
-            grayscale = FALSE;
-            for (i = 0; i < numcolors; ++i) {
-                xelval r, g, b;
-                r = (long) redcolormap[i] * PNM_MAXMAXVAL / 65535L;
-                g = (long) greencolormap[i] * PNM_MAXMAXVAL / 65535L;
-                b = (long) bluecolormap[i] * PNM_MAXMAXVAL / 65535L;
-                PPM_ASSIGN(colormap[i], r, g, b);
-            }
+    case PHOTOMETRIC_PALETTE: {
+        int fldPresent;
+        int i;
+        int numcolors;
+        unsigned short* redcolormap;
+        unsigned short* greencolormap;
+        unsigned short* bluecolormap;
+
+        if (headerdump)
+            pm_message("colormapped");
+
+        if (spp != 1)
+            pm_error("This paletted image has %d samples per pixel.  "
+                     "We understand only 1.", spp);
+
+        fldPresent = TIFFGetField(
+            tiffP, TIFFTAG_COLORMAP, 
+            &redcolormap, &greencolormap, &bluecolormap);
+
+        if (!fldPresent)
+            pm_error("error getting colormaps");
+
+        numcolors = 1 << bps;
+        if (numcolors > MAXCOLORS)
+            pm_error("too many colors");
+
+        *formatP = PPM_TYPE;
+
+        *maxvalP = PNM_MAXMAXVAL;
+
+        for (i = 0; i < numcolors; ++i) {
+            xelval r, g, b;
+            r = (long) redcolormap[i] * PNM_MAXMAXVAL / 65535L;
+            g = (long) greencolormap[i] * PNM_MAXMAXVAL / 65535L;
+            b = (long) bluecolormap[i] * PNM_MAXMAXVAL / 65535L;
+            PPM_ASSIGN(colormap[i], r, g, b);
         }
+    }
         break;
 
-        case PHOTOMETRIC_SEPARATED: {
-            unsigned short inkset;
-
-            if (headerdump)
-                pm_message("color separation");
-            if (TIFFGetField(tif, TIFFTAG_INKNAMES, &inkset) == 1
-                && inkset != INKSET_CMYK)
-            if (inkset != INKSET_CMYK) 
-                pm_error("This color separation file uses an inkset (%d) "
-                         "we can't handle.  We handle only CMYK.", inkset);
-            if (spp != 4) 
-                pm_error("This CMYK color separation file is %d samples per "
-                         "pixel.  "
-                         "We need 4 samples, though: C, M, Y, and K.  ",
-                         spp);
-            grayscale = FALSE;
-            *maxvalP = (1 << bps) - 1;
-        }
+    case PHOTOMETRIC_SEPARATED: {
+        unsigned short inkset;
+        int fldPresent;
+
+        if (headerdump)
+            pm_message("color separation");
+
+        fldPresent = TIFFGetField(tiffP, TIFFTAG_INKNAMES, &inkset);
+        if (fldPresent && inkset != INKSET_CMYK)
+            pm_error("This color separation file uses an inkset (%d) "
+                     "we can't handle.  We handle only CMYK.", inkset);
+        if (spp != 4) 
+            pm_error("This CMYK color separation file is %d samples per "
+                     "pixel.  "
+                     "We need 4 samples, though: C, M, Y, and K.  ",
+                     spp);
+
+        *formatP = PPM_TYPE;
+
+        *maxvalP = (1 << bps) - 1;
+    }
         break;
             
-        case PHOTOMETRIC_RGB:
-            if (headerdump)
-                pm_message("RGB truecolor");
-            grayscale = FALSE;
+    case PHOTOMETRIC_RGB:
+        if (headerdump)
+            pm_message("RGB truecolor");
 
-            if (spp != 3 && spp != 4)
-                pm_error("This RGB image has %d samples per pixel.  "
-                         "We understand only 3 or 4.", spp);
+        if (spp != 3 && spp != 4)
+            pm_error("This RGB image has %d samples per pixel.  "
+                     "We understand only 3 or 4.", spp);
 
-            *maxvalP = (1 << bps) - 1;
-            break;
+        *formatP = PPM_TYPE;
 
-        case PHOTOMETRIC_MASK:
-            pm_error("don't know how to handle PHOTOMETRIC_MASK");
+        *maxvalP = (1 << bps) - 1;
+        break;
 
-        case PHOTOMETRIC_DEPTH:
-            pm_error("don't know how to handle PHOTOMETRIC_DEPTH");
+    case PHOTOMETRIC_MASK:
+        pm_error("don't know how to handle PHOTOMETRIC_MASK");
 
-        case PHOTOMETRIC_YCBCR:
-            pm_error("don't know how to handle PHOTOMETRIC_YCBCR");
+    case PHOTOMETRIC_DEPTH:
+        pm_error("don't know how to handle PHOTOMETRIC_DEPTH");
 
-        case PHOTOMETRIC_CIELAB:
-            pm_error("don't know how to handle PHOTOMETRIC_CIELAB");
+    case PHOTOMETRIC_YCBCR:
+        pm_error("don't know how to handle PHOTOMETRIC_YCBCR");
 
-        case PHOTOMETRIC_LOGL:
-            pm_error("don't know how to handle PHOTOMETRIC_LOGL");
+    case PHOTOMETRIC_CIELAB:
+        pm_error("don't know how to handle PHOTOMETRIC_CIELAB");
 
-        case PHOTOMETRIC_LOGLUV:
-            pm_error("don't know how to handle PHOTOMETRIC_LOGLUV");
+    case PHOTOMETRIC_LOGL:
+        pm_error("don't know how to handle PHOTOMETRIC_LOGL");
+
+    case PHOTOMETRIC_LOGLUV:
+        pm_error("don't know how to handle PHOTOMETRIC_LOGLUV");
             
-        default:
-            pm_error("unknown photometric: %d", photomet);
-        }
+    default:
+        pm_error("unknown photometric: %d", photomet);
+    }
     if (*maxvalP > PNM_OVERALLMAXVAL)
-        pm_error("bits/sample (%d) in the input image is too large.",
-                 bps);
-    if (grayscale) {
-        if (*maxvalP == 1) {
-            *formatP = PBM_TYPE;
-            pm_message("writing PBM file");
-        } else {
-            *formatP = PGM_TYPE;
-            pm_message("writing PGM file");
-        }
-    } else {
-        *formatP = PPM_TYPE;
-        pm_message("writing PPM file");
+        pm_error("bits/sample (%u) in the input image is too large.", bps);
+}
+
+
+
+static void
+reportOutputFormat(int const format) {
+
+    const char * formatDesc;
+
+    switch (format) {
+    case PBM_TYPE: formatDesc = "PBM"; break;
+    case PGM_TYPE: formatDesc = "PGM"; break;
+    case PPM_TYPE: formatDesc = "PPM"; break;
+    default: assert(false);
     }
+
+    pm_message("writing %s file", formatDesc);
 }
 
 
@@ -775,14 +837,22 @@ spawnWithInputPipe(const char *  const shellCmd,
                 else
                     *errorP = NULL;
             } else {
-                int rc;
+                int terminationStatus;
                 close(fd[PIPE_WRITE]);
                 close(STDIN_FILENO);
                 dup2(fd[PIPE_READ], STDIN_FILENO);
 
-                rc = system(shellCmd);
+                terminationStatus = system(shellCmd);
 
-                exit(rc);
+                if (WIFSIGNALED(terminationStatus))
+                    pm_error("Shell process was killed "
+                             "by a Class %u signal.",
+                             WTERMSIG(terminationStatus));
+                else if (!WIFEXITED(terminationStatus))
+                    pm_error("Shell process died, but its termination status "
+                             "0x%x doesn't make sense", terminationStatus);
+                else
+                    exit(WEXITSTATUS(terminationStatus));
             }
         }
     }
@@ -1364,9 +1434,9 @@ warnBrokenTiffLibrary(TIFF * const tiffP) {
 */
 
     unsigned short tiffOrientation;
-    int present;
-    present = TIFFGetField(tiffP, TIFFTAG_ORIENTATION, &tiffOrientation);
-    if (present) {
+    int fldPresent;
+    fldPresent = TIFFGetField(tiffP, TIFFTAG_ORIENTATION, &tiffOrientation);
+    if (fldPresent) {
         switch (tiffOrientation) {
         case ORIENTATION_LEFTTOP:
         case ORIENTATION_RIGHTTOP:
@@ -1438,16 +1508,47 @@ enum convertDisp {CONV_DONE,
                   CONV_FAILED, 
                   CONV_NOTATTEMPTED};
 
+
+static void
+convertRasterIntoProvidedMemory(pnmOut *           const pnmOutP,
+                                unsigned int       const cols,
+                                unsigned int       const rows,
+                                xelval             const maxval,
+                                TIFF *             const tif,
+                                bool               const verbose,
+                                uint32 *           const raster,
+                                enum convertDisp * const statusP) {
+
+    int const stopOnErrorFalse = false;
+
+    TIFFRGBAImage img;
+    char emsg[1024];
+    int ok;
+                
+    ok = TIFFRGBAImageBegin(&img, tif, stopOnErrorFalse, emsg);
+    if (!ok) {
+        pm_message("%s", emsg);
+        *statusP = CONV_FAILED;
+    } else {
+        int ok;
+        ok = TIFFRGBAImageGet(&img, raster, cols, rows);
+        TIFFRGBAImageEnd(&img) ;
+        if (!ok) {
+            pm_message("%s", emsg);
+            *statusP = CONV_FAILED;
+        } else {
+            *statusP = CONV_DONE;
+            convertTiffRaster(raster, cols, rows, maxval, pnmOutP);
+        }
+    } 
+}
+
+
+
 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,
                       enum convertDisp * const statusP) {
 /*----------------------------------------------------------------------------
@@ -1467,64 +1568,49 @@ convertRasterInMemory(pnmOut *           const pnmOutP,
    programs, we simply abort the program if we are unable to allocate
    memory for other things.
 -----------------------------------------------------------------------------*/
-    unsigned int cols, rows;  /* Dimensions of output image */
+    char emsg[1024];
+    int ok;
 
     if (verbose)
         pm_message("Converting in memory ...");
 
     warnBrokenTiffLibrary(tif);
 
-    getTiffDimensions(tif, &cols, &rows);
-
-    if (rows == 0 || cols == 0) 
-        *statusP = CONV_DONE;
-    else {
-        char emsg[1024];
-        int ok;
-        ok = TIFFRGBAImageOK(tif, emsg);
-        if (!ok) {
-            pm_message("%s", emsg);
-            *statusP = CONV_UNABLE;
-        } else {
-            uint32 * raster;
+    ok = TIFFRGBAImageOK(tif, emsg);
+    if (!ok) {
+        pm_message("%s", emsg);
+        *statusP = CONV_UNABLE;
+    } else {
+        unsigned int cols, rows;  /* Dimensions of output image */
+        getTiffDimensions(tif, &cols, &rows);
 
-            /* Note that TIFFRGBAImageGet() converts any bits per sample
-               to 8.  Maxval of the raster it returns is always 255.
-            */
+        if (rows == 0 || cols == 0) 
+            *statusP = CONV_DONE;
+        else {
             if (cols > UINT_MAX/rows) {
                 pm_message("%u rows of %u columns is too large to compute",
                            rows, cols);
                 *statusP = CONV_OOM;
-                return;
-            }
-
-            MALLOCARRAY(raster, cols * rows);
-            if (raster == NULL) {
-                pm_message("Unable to allocate space for a raster of %u "
-                           "pixels.", cols * rows);
-                *statusP = CONV_OOM;
             } else {
-                int const stopOnErrorFalse = FALSE;
-                TIFFRGBAImage img;
-                int ok;
-                
-                ok = TIFFRGBAImageBegin(&img, tif, stopOnErrorFalse, emsg);
-                if (!ok) {
-                    pm_message("%s", emsg);
-                    *statusP = CONV_FAILED;
+                unsigned int const pixelCt = rows * cols;
+
+                uint32 * raster;
+
+                /* Note that TIFFRGBAImageGet() converts any bits per sample
+                   to 8.  Maxval of the raster it returns is always 255.
+                */
+                MALLOCARRAY(raster, pixelCt);
+                if (raster == NULL) {
+                    pm_message("Unable to allocate space for a raster of %u "
+                               "pixels.", pixelCt);
+                    *statusP = CONV_OOM;
                 } else {
-                    int ok;
-                    ok = TIFFRGBAImageGet(&img, raster, cols, rows);
-                    TIFFRGBAImageEnd(&img) ;
-                    if (!ok) {
-                        pm_message("%s", emsg);
-                        *statusP = CONV_FAILED;
-                    } else {
-                        *statusP = CONV_DONE;
-                        convertTiffRaster(raster, cols, rows, maxval, pnmOutP);
-                    }
-                } 
-                free(raster);
+                    convertRasterIntoProvidedMemory(
+                        pnmOutP, cols, rows, maxval, tif, verbose,
+                        raster, statusP);
+                    
+                    free(raster);
+                }
             }
         }
     }
@@ -1549,11 +1635,7 @@ convertRaster(pnmOut *           const pnmOutP,
     if (byrow || !flipOk)
         status = CONV_NOTATTEMPTED;
     else {
-        convertRasterInMemory(
-            pnmOutP, maxval,
-            tifP, tiffDir.photomet, tiffDir.planarconfig, 
-            tiffDir.bps, tiffDir.spp, fillorder,
-            colormap, verbose, &status);
+        convertRasterInMemory(pnmOutP, maxval, tifP, verbose, &status);
     }
     if (status == CONV_DONE) {
         if (tiffDir.bps > 8)
@@ -1602,6 +1684,8 @@ convertImage(TIFF *             const tifP,
     analyzeImageType(tifP, tiffDir.bps, tiffDir.spp, tiffDir.photomet, 
                      &maxval, &format, colormap, cmdline.headerdump, cmdline);
 
+    reportOutputFormat(format);
+
     pnmOut_init(imageoutFileP, alphaFileP, tiffDir.width, tiffDir.height,
                 tiffDir.orientation, maxval, format, maxval,
                 cmdline.byrow, cmdline.orientraw,
diff --git a/converter/other/winicontopam.c b/converter/other/winicontopam.c
index 664b4ef9..69ce7f05 100644
--- a/converter/other/winicontopam.c
+++ b/converter/other/winicontopam.c
@@ -1156,9 +1156,9 @@ convertPng(const unsigned char * const image,
     imageBuffer.buffer = (unsigned char *)image;
 
     fflush (stdout);
-    pm_system(pm_feed_from_memory, &imageBuffer,
-              NULL /* stdout accepter */, NULL,
-              "pngtopam -alphapam");
+    pm_system_lp("pngtopam", pm_feed_from_memory, &imageBuffer,
+                 NULL /* stdout accepter */, NULL,
+                 "pngtopam", "-alphapam", NULL);
 }
 
 
diff --git a/converter/other/xwdtopnm.c b/converter/other/xwdtopnm.c
index d49a2b09..df3c7375 100644
--- a/converter/other/xwdtopnm.c
+++ b/converter/other/xwdtopnm.c
@@ -23,6 +23,7 @@
 */
 
 
+#define _DEFAULT_SOURCE 1  /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
diff --git a/converter/pbm/Makefile b/converter/pbm/Makefile
index 602cb156..352b73de 100644
--- a/converter/pbm/Makefile
+++ b/converter/pbm/Makefile
@@ -31,11 +31,14 @@ endif
 MATHBINARIES =	pbmtopk
 BINARIES =	$(PORTBINARIES) $(MATHBINARIES)
 SCRIPTS =       pbmtox10bm
+EXTRA_OBJECTS = g3ttable.o g3prefab.o
 
-OBJECTS = $(BINARIES:%=%.o)
+OBJECTS = $(BINARIES:%=%.o) $(EXTRA_OBJECTS)
 
 MERGEBINARIES = $(BINARIES)
-MERGE_OBJECTS = $(MERGEBINARIES:%=%.o2)
+MERGE_OBJECTS = $(MERGEBINARIES:%=%.o2) $(EXTRA_OBJECTS)
+
+HAVE_MERGE_COMPAT=YES
 
 SUBDIRS=pbmtoppa
 
@@ -73,7 +76,7 @@ thinkjettopbm.c:%.c:%.c1 $(SRCDIR)/lib/util/lexheader
 	  grep -v "^[[:space:]]*int yywrap(void);" \
 	  >$@
 
-install.bin: install.bin.local
+install.bin install.merge: install.bin.local
 .PHONY: install.bin.local
 install.bin.local: $(PKGDIR)/bin
 # Remember that $(SYMLINK) might just be a copy command.
@@ -81,7 +84,17 @@ install.bin.local: $(PKGDIR)/bin
 	cd $(PKGDIR)/bin ; \
 	$(SYMLINK) pbmtosunicon$(EXE) pbmtoicon$(EXE)
 
+mergecomptrylist:
+	cat /dev/null >$@
+	echo "TRY(\"pbmtoicon\",   main_pbmtosunicon);"     >>$@
+
 thisdirclean: localclean
 .PHONY: localclean
 localclean:
 	-rm -f thinkjettopbm.c
+
+pbmtog3: g3ttable.o g3prefab.o
+pbmtog3: ADDL_OBJECTS = g3ttable.o g3prefab.o
+
+g3topbm: g3ttable.o
+g3topbm: ADDL_OBJECTS = g3ttable.o
diff --git a/converter/pbm/cmuwmtopbm.c b/converter/pbm/cmuwmtopbm.c
index ccf8cfc9..be8a7fc1 100644
--- a/converter/pbm/cmuwmtopbm.c
+++ b/converter/pbm/cmuwmtopbm.c
@@ -68,8 +68,8 @@ readCmuwmHeader(FILE *         const ifP,
 
 
 int
-main(int     argc,
-     char * argv[]) {
+main(int           argc,
+     const char ** argv) {
 
     FILE * ifP;
     unsigned char * bitrow;
@@ -78,7 +78,7 @@ main(int     argc,
 
     const char * inputFileName;
 
-    pbm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     if (argc-1 > 1)
         pm_error("Too many arguments (%u).  "
@@ -117,6 +117,7 @@ main(int     argc,
         pbm_writepbmrow_packed(stdout, bitrow, cols, 0);
     }
 
+    pbm_freerow_packed(bitrow);
     pm_close(ifP);
     pm_close(stdout);
 
diff --git a/converter/pbm/g3.h b/converter/pbm/g3.h
index e982f2da..3e216a78 100644
--- a/converter/pbm/g3.h
+++ b/converter/pbm/g3.h
@@ -21,126 +21,24 @@
    the subject of this header file.  It also specifies layers
    underneath the bit stream.
 
-   There is also the newer G4.  
+   There is also the newer G4.
 */
-
-typedef struct g3TableEntry {
-    short int code;
-    short int length;
-} g3TableEntry;
-
-static struct g3TableEntry ttable[] = {
-/*    TERMWHITE           TERMBLACK   */
-    { 0x35, 8 },    { 0x37, 10 },       /* white 0 , black 0 */
-    { 0x07, 6 },    { 0x02,  3 },
-    { 0x07, 4 },    { 0x03,  2 },
-    { 0x08, 4 },    { 0x02,  2 },
-    { 0x0b, 4 },    { 0x03,  3 },
-    { 0x0c, 4 },    { 0x03,  4 },
-    { 0x0e, 4 },    { 0x02,  4 },
-    { 0x0f, 4 },    { 0x03,  5 },
-    { 0x13, 5 },    { 0x05,  6 },
-    { 0x14, 5 },    { 0x04,  6 },
-    { 0x07, 5 },    { 0x04,  7 },
-    { 0x08, 5 },    { 0x05,  7 },
-    { 0x08, 6 },    { 0x07,  7 },
-    { 0x03, 6 },    { 0x04,  8 },
-    { 0x34, 6 },    { 0x07,  8 },
-    { 0x35, 6 },    { 0x18,  9 },
-    { 0x2a, 6 },    { 0x17, 10 },
-    { 0x2b, 6 },    { 0x18, 10 },
-    { 0x27, 7 },    { 0x08, 10 },
-    { 0x0c, 7 },    { 0x67, 11 },
-    { 0x08, 7 },    { 0x68, 11 },
-    { 0x17, 7 },    { 0x6c, 11 },
-    { 0x03, 7 },    { 0x37, 11 },
-    { 0x04, 7 },    { 0x28, 11 },
-    { 0x28, 7 },    { 0x17, 11 },
-    { 0x2b, 7 },    { 0x18, 11 },
-    { 0x13, 7 },    { 0xca, 12 },
-    { 0x24, 7 },    { 0xcb, 12 },
-    { 0x18, 7 },    { 0xcc, 12 },
-    { 0x02, 8 },    { 0xcd, 12 },
-    { 0x03, 8 },    { 0x68, 12 },
-    { 0x1a, 8 },    { 0x69, 12 },
-    { 0x1b, 8 },    { 0x6a, 12 },
-    { 0x12, 8 },    { 0x6b, 12 },
-    { 0x13, 8 },    { 0xd2, 12 },
-    { 0x14, 8 },    { 0xd3, 12 },
-    { 0x15, 8 },    { 0xd4, 12 },
-    { 0x16, 8 },    { 0xd5, 12 },
-    { 0x17, 8 },    { 0xd6, 12 },
-    { 0x28, 8 },    { 0xd7, 12 },
-    { 0x29, 8 },    { 0x6c, 12 },
-    { 0x2a, 8 },    { 0x6d, 12 },
-    { 0x2b, 8 },    { 0xda, 12 },
-    { 0x2c, 8 },    { 0xdb, 12 },
-    { 0x2d, 8 },    { 0x54, 12 },
-    { 0x04, 8 },    { 0x55, 12 },
-    { 0x05, 8 },    { 0x56, 12 },
-    { 0x0a, 8 },    { 0x57, 12 },
-    { 0x0b, 8 },    { 0x64, 12 },
-    { 0x52, 8 },    { 0x65, 12 },
-    { 0x53, 8 },    { 0x52, 12 },
-    { 0x54, 8 },    { 0x53, 12 },
-    { 0x55, 8 },    { 0x24, 12 },
-    { 0x24, 8 },    { 0x37, 12 },
-    { 0x25, 8 },    { 0x38, 12 },
-    { 0x58, 8 },    { 0x27, 12 },
-    { 0x59, 8 },    { 0x28, 12 },
-    { 0x5a, 8 },    { 0x58, 12 },
-    { 0x5b, 8 },    { 0x59, 12 },
-    { 0x4a, 8 },    { 0x2b, 12 },
-    { 0x4b, 8 },    { 0x2c, 12 },
-    { 0x32, 8 },    { 0x5a, 12 },
-    { 0x33, 8 },    { 0x66, 12 },
-    { 0x34, 8 },    { 0x67, 12 },       /* white 63 , black 63 */
-
-/* mtable */    
-/*    MKUPWHITE           MKUPBLACK   */
-    { 0x00, 0 },    { 0x00,  0 },   /* dummy to simplify pointer math */
-    { 0x1b, 5 },    { 0x0f, 10 },   /* white 64 , black 64 */
-    { 0x12, 5 },    { 0xc8, 12 },
-    { 0x17, 6 },    { 0xc9, 12 },
-    { 0x37, 7 },    { 0x5b, 12 },
-    { 0x36, 8 },    { 0x33, 12 },
-    { 0x37, 8 },    { 0x34, 12 },
-    { 0x64, 8 },    { 0x35, 12 },
-    { 0x65, 8 },    { 0x6c, 13 },
-    { 0x68, 8 },    { 0x6d, 13 },
-    { 0x67, 8 },    { 0x4a, 13 },
-    { 0xcc, 9 },    { 0x4b, 13 },
-    { 0xcd, 9 },    { 0x4c, 13 },
-    { 0xd2, 9 },    { 0x4d, 13 },
-    { 0xd3, 9 },    { 0x72, 13 },
-    { 0xd4, 9 },    { 0x73, 13 },
-    { 0xd5, 9 },    { 0x74, 13 },
-    { 0xd6, 9 },    { 0x75, 13 },
-    { 0xd7, 9 },    { 0x76, 13 },
-    { 0xd8, 9 },    { 0x77, 13 },
-    { 0xd9, 9 },    { 0x52, 13 },
-    { 0xda, 9 },    { 0x53, 13 },
-    { 0xdb, 9 },    { 0x54, 13 },
-    { 0x98, 9 },    { 0x55, 13 },
-    { 0x99, 9 },    { 0x5a, 13 },
-    { 0x9a, 9 },    { 0x5b, 13 },
-    { 0x18, 6 },    { 0x64, 13 },
-    { 0x9b, 9 },    { 0x65, 13 },
-    { 0x08, 11 },   { 0x08, 11 },        /* extable len = 1792 */
-    { 0x0c, 11 },   { 0x0c, 11 },
-    { 0x0d, 11 },   { 0x0d, 11 },
-    { 0x12, 12 },   { 0x12, 12 },
-    { 0x13, 12 },   { 0x13, 12 },
-    { 0x14, 12 },   { 0x14, 12 },
-    { 0x15, 12 },   { 0x15, 12 },
-    { 0x16, 12 },   { 0x16, 12 },
-    { 0x17, 12 },   { 0x17, 12 },
-    { 0x1c, 12 },   { 0x1c, 12 },
-    { 0x1d, 12 },   { 0x1d, 12 },
-    { 0x1e, 12 },   { 0x1e, 12 },
-    { 0x1f, 12 },   { 0x1f, 12 },
+#include "pm_config.h"  /* uint32_t */
+
+struct BitString {
+    /* A string of bits, up to as many fit in 32 bits. */
+    uint32_t     intBuffer;
+        /* The bits are in the 'bitCount' least significant bit positions
+           of this number.  The rest of the bits of this number are always
+           zero.
+        */
+    unsigned int bitCount;
+        /* The length of the bit string */
+
+    /* Example:  The bit string 010100 would be represented by
+       bitCount = 6, intBuffer = 20
+       (N.B. 20 = 00000000 00000000 00000000 00010100 in binary)
+    */
 };
 
-#define mtable ((ttable)+64*2)
-
 #endif
diff --git a/converter/pbm/g3prefab.c b/converter/pbm/g3prefab.c
new file mode 100644
index 00000000..4c45b6a2
--- /dev/null
+++ b/converter/pbm/g3prefab.c
@@ -0,0 +1,286 @@
+/* Prefabricated G3 Huffman code
+
+   Most g3 fax encoders scan the input data bitwise, count run-lengths
+   and emit the corresponding Huffman codes.  This is a different approach.
+
+   8 bits of raw input data is read and Huffman codes are emitted directly,
+   reading from a look-up table.
+
+   For example if the input is WWBBWWWB then the Huffman codes for
+   White(2), Black(2), White(3), Black(1) are combined and emitted.
+
+   In reality things are a somewhat more complicated for the runs on
+   both ends of the input byte are likely to be parts of longer runs
+   encompassing multiple bytes.
+
+   This method has one serious drawback: It does not work well
+   with adaptive Huffman coding schemes.  This is, of course, never
+   a problem with CCITT G3 fax images for which the Huffman code table
+   is fixed by standard.  For adaptive Huffman coding schemes, one
+   should consider preparing a code table like the one below which
+   encodes mixed black-white sequences up to a certain length.
+
+*/
+#include "g3.h"
+
+#include "g3prefab.h"
+
+ struct PrefabCode const g3prefab_code[256] =
+{
+{ 0, 8, {   0x0000,   0}},   /* 000 */
+{ 7, 1, {   0x0000,   0}},   /* 001 */
+{ 6, 1, {   0x0002,   3}},   /* 002 */
+{ 6, 2, {   0x0000,   0}},   /* 003 */
+{ 5, 2, {   0x0002,   3}},   /* 004 */
+{ 5, 1, {   0x0087,   9}},   /* 005 */
+{ 5, 1, {   0x0003,   2}},   /* 006 */
+{ 5, 3, {   0x0000,   0}},   /* 007 */
+{ 4, 3, {   0x0002,   3}},   /* 008 */
+{ 4, 1, {   0x0027,   7}},   /* 009 */
+{ 4, 1, {   0x043a,  12}},   /* 010 */
+{ 4, 2, {   0x0087,   9}},   /* 011 */
+{ 4, 2, {   0x0003,   2}},   /* 012 */
+{ 4, 1, {   0x00c7,   8}},   /* 013 */
+{ 4, 1, {   0x0002,   2}},   /* 014 */
+{ 4, 4, {   0x0000,   0}},   /* 015 */
+{ 3, 4, {   0x0002,   3}},   /* 016 */
+{ 3, 1, {   0x0028,   7}},   /* 017 */
+{ 3, 1, {   0x013a,  10}},   /* 018 */
+{ 3, 2, {   0x0027,   7}},   /* 019 */
+{ 3, 2, {   0x043a,  12}},   /* 020 */
+{ 3, 1, {  0x10e87,  18}},   /* 021 */
+{ 3, 1, {   0x021f,  11}},   /* 022 */
+{ 3, 3, {   0x0087,   9}},   /* 023 */
+{ 3, 3, {   0x0003,   2}},   /* 024 */
+{ 3, 1, {   0x0037,   6}},   /* 025 */
+{ 3, 1, {   0x063a,  11}},   /* 026 */
+{ 3, 2, {   0x00c7,   8}},   /* 027 */
+{ 3, 2, {   0x0002,   2}},   /* 028 */
+{ 3, 1, {   0x0087,   8}},   /* 029 */
+{ 3, 1, {   0x0003,   3}},   /* 030 */
+{ 3, 5, {   0x0000,   0}},   /* 031 */
+{ 2, 5, {   0x0002,   3}},   /* 032 */
+{ 2, 1, {   0x002b,   7}},   /* 033 */
+{ 2, 1, {   0x0142,  10}},   /* 034 */
+{ 2, 2, {   0x0028,   7}},   /* 035 */
+{ 2, 2, {   0x013a,  10}},   /* 036 */
+{ 2, 1, {   0x4e87,  16}},   /* 037 */
+{ 2, 1, {   0x009f,   9}},   /* 038 */
+{ 2, 3, {   0x0027,   7}},   /* 039 */
+{ 2, 3, {   0x043a,  12}},   /* 040 */
+{ 2, 1, {   0x43a7,  16}},   /* 041 */
+{ 2, 1, {  0x8743a,  21}},   /* 042 */
+{ 2, 2, {  0x10e87,  18}},   /* 043 */
+{ 2, 2, {   0x021f,  11}},   /* 044 */
+{ 2, 1, {   0x87c7,  17}},   /* 045 */
+{ 2, 1, {   0x021e,  11}},   /* 046 */
+{ 2, 4, {   0x0087,   9}},   /* 047 */
+{ 2, 4, {   0x0003,   2}},   /* 048 */
+{ 2, 1, {   0x0038,   6}},   /* 049 */
+{ 2, 1, {   0x01ba,   9}},   /* 050 */
+{ 2, 2, {   0x0037,   6}},   /* 051 */
+{ 2, 2, {   0x063a,  11}},   /* 052 */
+{ 2, 1, {  0x18e87,  17}},   /* 053 */
+{ 2, 1, {   0x031f,  10}},   /* 054 */
+{ 2, 3, {   0x00c7,   8}},   /* 055 */
+{ 2, 3, {   0x0002,   2}},   /* 056 */
+{ 2, 1, {   0x0027,   6}},   /* 057 */
+{ 2, 1, {   0x043a,  11}},   /* 058 */
+{ 2, 2, {   0x0087,   8}},   /* 059 */
+{ 2, 2, {   0x0003,   3}},   /* 060 */
+{ 2, 1, {   0x00c7,   9}},   /* 061 */
+{ 2, 1, {   0x0003,   4}},   /* 062 */
+{ 2, 6, {   0x0000,   0}},   /* 063 */
+{ 1, 6, {   0x0002,   3}},   /* 064 */
+{ 1, 1, {   0x002c,   7}},   /* 065 */
+{ 1, 1, {   0x015a,  10}},   /* 066 */
+{ 1, 2, {   0x002b,   7}},   /* 067 */
+{ 1, 2, {   0x0142,  10}},   /* 068 */
+{ 1, 1, {   0x5087,  16}},   /* 069 */
+{ 1, 1, {   0x00a3,   9}},   /* 070 */
+{ 1, 3, {   0x0028,   7}},   /* 071 */
+{ 1, 3, {   0x013a,  10}},   /* 072 */
+{ 1, 1, {   0x13a7,  14}},   /* 073 */
+{ 1, 1, {  0x2743a,  19}},   /* 074 */
+{ 1, 2, {   0x4e87,  16}},   /* 075 */
+{ 1, 2, {   0x009f,   9}},   /* 076 */
+{ 1, 1, {   0x27c7,  15}},   /* 077 */
+{ 1, 1, {   0x009e,   9}},   /* 078 */
+{ 1, 4, {   0x0027,   7}},   /* 079 */
+{ 1, 4, {   0x043a,  12}},   /* 080 */
+{ 1, 1, {   0x43a8,  16}},   /* 081 */
+{ 1, 1, {  0x21d3a,  19}},   /* 082 */
+{ 1, 2, {   0x43a7,  16}},   /* 083 */
+{ 1, 2, {  0x8743a,  21}},   /* 084 */
+{ 1, 1, {0x21d0e87,  27}},   /* 085 */
+{ 1, 1, {  0x43a1f,  20}},   /* 086 */
+{ 1, 3, {  0x10e87,  18}},   /* 087 */
+{ 1, 3, {   0x021f,  11}},   /* 088 */
+{ 1, 1, {   0x21f7,  15}},   /* 089 */
+{ 1, 1, {  0x43e3a,  20}},   /* 090 */
+{ 1, 2, {   0x87c7,  17}},   /* 091 */
+{ 1, 2, {   0x021e,  11}},   /* 092 */
+{ 1, 1, {   0x8787,  17}},   /* 093 */
+{ 1, 1, {   0x043b,  12}},   /* 094 */
+{ 1, 5, {   0x0087,   9}},   /* 095 */
+{ 1, 5, {   0x0003,   2}},   /* 096 */
+{ 1, 1, {   0x003b,   6}},   /* 097 */
+{ 1, 1, {   0x01c2,   9}},   /* 098 */
+{ 1, 2, {   0x0038,   6}},   /* 099 */
+{ 1, 2, {   0x01ba,   9}},   /* 100 */
+{ 1, 1, {   0x6e87,  15}},   /* 101 */
+{ 1, 1, {   0x00df,   8}},   /* 102 */
+{ 1, 3, {   0x0037,   6}},   /* 103 */
+{ 1, 3, {   0x063a,  11}},   /* 104 */
+{ 1, 1, {   0x63a7,  15}},   /* 105 */
+{ 1, 1, {  0xc743a,  20}},   /* 106 */
+{ 1, 2, {  0x18e87,  17}},   /* 107 */
+{ 1, 2, {   0x031f,  10}},   /* 108 */
+{ 1, 1, {   0xc7c7,  16}},   /* 109 */
+{ 1, 1, {   0x031e,  10}},   /* 110 */
+{ 1, 4, {   0x00c7,   8}},   /* 111 */
+{ 1, 4, {   0x0002,   2}},   /* 112 */
+{ 1, 1, {   0x0028,   6}},   /* 113 */
+{ 1, 1, {   0x013a,   9}},   /* 114 */
+{ 1, 2, {   0x0027,   6}},   /* 115 */
+{ 1, 2, {   0x043a,  11}},   /* 116 */
+{ 1, 1, {  0x10e87,  17}},   /* 117 */
+{ 1, 1, {   0x021f,  10}},   /* 118 */
+{ 1, 3, {   0x0087,   8}},   /* 119 */
+{ 1, 3, {   0x0003,   3}},   /* 120 */
+{ 1, 1, {   0x0037,   7}},   /* 121 */
+{ 1, 1, {   0x063a,  12}},   /* 122 */
+{ 1, 2, {   0x00c7,   9}},   /* 123 */
+{ 1, 2, {   0x0003,   4}},   /* 124 */
+{ 1, 1, {   0x00c7,  10}},   /* 125 */
+{ 1, 1, {   0x0002,   4}},   /* 126 */
+{ 1, 7, {   0x0000,   0}},   /* 127 */
+{ 1, 7, {   0x0000,   0}},   /* 128 */
+{ 1, 1, {   0x000e,   4}},   /* 129 */
+{ 1, 1, {   0x0062,   7}},   /* 130 */
+{ 1, 2, {   0x000c,   4}},   /* 131 */
+{ 1, 2, {   0x005a,   7}},   /* 132 */
+{ 1, 1, {   0x1687,  13}},   /* 133 */
+{ 1, 1, {   0x002f,   6}},   /* 134 */
+{ 1, 3, {   0x000b,   4}},   /* 135 */
+{ 1, 3, {   0x0042,   7}},   /* 136 */
+{ 1, 1, {   0x0427,  11}},   /* 137 */
+{ 1, 1, {   0x843a,  16}},   /* 138 */
+{ 1, 2, {   0x1087,  13}},   /* 139 */
+{ 1, 2, {   0x0023,   6}},   /* 140 */
+{ 1, 1, {   0x08c7,  12}},   /* 141 */
+{ 1, 1, {   0x0022,   6}},   /* 142 */
+{ 1, 4, {   0x0008,   4}},   /* 143 */
+{ 1, 4, {   0x003a,   7}},   /* 144 */
+{ 1, 1, {   0x03a8,  11}},   /* 145 */
+{ 1, 1, {   0x1d3a,  14}},   /* 146 */
+{ 1, 2, {   0x03a7,  11}},   /* 147 */
+{ 1, 2, {   0x743a,  16}},   /* 148 */
+{ 1, 1, { 0x1d0e87,  22}},   /* 149 */
+{ 1, 1, {   0x3a1f,  15}},   /* 150 */
+{ 1, 3, {   0x0e87,  13}},   /* 151 */
+{ 1, 3, {   0x001f,   6}},   /* 152 */
+{ 1, 1, {   0x01f7,  10}},   /* 153 */
+{ 1, 1, {   0x3e3a,  15}},   /* 154 */
+{ 1, 2, {   0x07c7,  12}},   /* 155 */
+{ 1, 2, {   0x001e,   6}},   /* 156 */
+{ 1, 1, {   0x0787,  12}},   /* 157 */
+{ 1, 1, {   0x003b,   7}},   /* 158 */
+{ 1, 5, {   0x0007,   4}},   /* 159 */
+{ 1, 5, {   0x003a,   9}},   /* 160 */
+{ 1, 1, {   0x03ab,  13}},   /* 161 */
+{ 1, 1, {   0x1d42,  16}},   /* 162 */
+{ 1, 2, {   0x03a8,  13}},   /* 163 */
+{ 1, 2, {   0x1d3a,  16}},   /* 164 */
+{ 1, 1, {  0x74e87,  22}},   /* 165 */
+{ 1, 1, {   0x0e9f,  15}},   /* 166 */
+{ 1, 3, {   0x03a7,  13}},   /* 167 */
+{ 1, 3, {   0x743a,  18}},   /* 168 */
+{ 1, 1, {  0x743a7,  22}},   /* 169 */
+{ 1, 1, { 0xe8743a,  27}},   /* 170 */
+{ 1, 2, { 0x1d0e87,  24}},   /* 171 */
+{ 1, 2, {   0x3a1f,  17}},   /* 172 */
+{ 1, 1, {  0xe87c7,  23}},   /* 173 */
+{ 1, 1, {   0x3a1e,  17}},   /* 174 */
+{ 1, 4, {   0x0e87,  15}},   /* 175 */
+{ 1, 4, {   0x001f,   8}},   /* 176 */
+{ 1, 1, {   0x01f8,  12}},   /* 177 */
+{ 1, 1, {   0x0fba,  15}},   /* 178 */
+{ 1, 2, {   0x01f7,  12}},   /* 179 */
+{ 1, 2, {   0x3e3a,  17}},   /* 180 */
+{ 1, 1, {  0xf8e87,  23}},   /* 181 */
+{ 1, 1, {   0x1f1f,  16}},   /* 182 */
+{ 1, 3, {   0x07c7,  14}},   /* 183 */
+{ 1, 3, {   0x001e,   8}},   /* 184 */
+{ 1, 1, {   0x01e7,  12}},   /* 185 */
+{ 1, 1, {   0x3c3a,  17}},   /* 186 */
+{ 1, 2, {   0x0787,  14}},   /* 187 */
+{ 1, 2, {   0x003b,   9}},   /* 188 */
+{ 1, 1, {   0x0ec7,  15}},   /* 189 */
+{ 1, 1, {   0x0073,  10}},   /* 190 */
+{ 1, 6, {   0x0007,   6}},   /* 191 */
+{ 2, 6, {   0x0000,   0}},   /* 192 */
+{ 2, 1, {   0x000c,   4}},   /* 193 */
+{ 2, 1, {   0x005a,   7}},   /* 194 */
+{ 2, 2, {   0x000b,   4}},   /* 195 */
+{ 2, 2, {   0x0042,   7}},   /* 196 */
+{ 2, 1, {   0x1087,  13}},   /* 197 */
+{ 2, 1, {   0x0023,   6}},   /* 198 */
+{ 2, 3, {   0x0008,   4}},   /* 199 */
+{ 2, 3, {   0x003a,   7}},   /* 200 */
+{ 2, 1, {   0x03a7,  11}},   /* 201 */
+{ 2, 1, {   0x743a,  16}},   /* 202 */
+{ 2, 2, {   0x0e87,  13}},   /* 203 */
+{ 2, 2, {   0x001f,   6}},   /* 204 */
+{ 2, 1, {   0x07c7,  12}},   /* 205 */
+{ 2, 1, {   0x001e,   6}},   /* 206 */
+{ 2, 4, {   0x0007,   4}},   /* 207 */
+{ 2, 4, {   0x003a,   9}},   /* 208 */
+{ 2, 1, {   0x03a8,  13}},   /* 209 */
+{ 2, 1, {   0x1d3a,  16}},   /* 210 */
+{ 2, 2, {   0x03a7,  13}},   /* 211 */
+{ 2, 2, {   0x743a,  18}},   /* 212 */
+{ 2, 1, { 0x1d0e87,  24}},   /* 213 */
+{ 2, 1, {   0x3a1f,  17}},   /* 214 */
+{ 2, 3, {   0x0e87,  15}},   /* 215 */
+{ 2, 3, {   0x001f,   8}},   /* 216 */
+{ 2, 1, {   0x01f7,  12}},   /* 217 */
+{ 2, 1, {   0x3e3a,  17}},   /* 218 */
+{ 2, 2, {   0x07c7,  14}},   /* 219 */
+{ 2, 2, {   0x001e,   8}},   /* 220 */
+{ 2, 1, {   0x0787,  14}},   /* 221 */
+{ 2, 1, {   0x003b,   9}},   /* 222 */
+{ 2, 5, {   0x0007,   6}},   /* 223 */
+{ 3, 5, {   0x0000,   0}},   /* 224 */
+{ 3, 1, {   0x000b,   4}},   /* 225 */
+{ 3, 1, {   0x0042,   7}},   /* 226 */
+{ 3, 2, {   0x0008,   4}},   /* 227 */
+{ 3, 2, {   0x003a,   7}},   /* 228 */
+{ 3, 1, {   0x0e87,  13}},   /* 229 */
+{ 3, 1, {   0x001f,   6}},   /* 230 */
+{ 3, 3, {   0x0007,   4}},   /* 231 */
+{ 3, 3, {   0x003a,   9}},   /* 232 */
+{ 3, 1, {   0x03a7,  13}},   /* 233 */
+{ 3, 1, {   0x743a,  18}},   /* 234 */
+{ 3, 2, {   0x0e87,  15}},   /* 235 */
+{ 3, 2, {   0x001f,   8}},   /* 236 */
+{ 3, 1, {   0x07c7,  14}},   /* 237 */
+{ 3, 1, {   0x001e,   8}},   /* 238 */
+{ 3, 4, {   0x0007,   6}},   /* 239 */
+{ 4, 4, {   0x0000,   0}},   /* 240 */
+{ 4, 1, {   0x0008,   4}},   /* 241 */
+{ 4, 1, {   0x003a,   7}},   /* 242 */
+{ 4, 2, {   0x0007,   4}},   /* 243 */
+{ 4, 2, {   0x003a,   9}},   /* 244 */
+{ 4, 1, {   0x0e87,  15}},   /* 245 */
+{ 4, 1, {   0x001f,   8}},   /* 246 */
+{ 4, 3, {   0x0007,   6}},   /* 247 */
+{ 5, 3, {   0x0000,   0}},   /* 248 */
+{ 5, 1, {   0x0007,   4}},   /* 249 */
+{ 5, 1, {   0x003a,   9}},   /* 250 */
+{ 5, 2, {   0x0007,   6}},   /* 251 */
+{ 6, 2, {   0x0000,   0}},   /* 252 */
+{ 6, 1, {   0x0007,   6}},   /* 253 */
+{ 7, 1, {   0x0000,   0}},   /* 254 */
+{ 0, 8, {   0x0000,   0}},   /* 255 */
+  };
diff --git a/converter/pbm/g3prefab.h b/converter/pbm/g3prefab.h
new file mode 100644
index 00000000..4fa4751a
--- /dev/null
+++ b/converter/pbm/g3prefab.h
@@ -0,0 +1,14 @@
+#ifndef G3_PREFAB_H_INCLUDED
+#define G3_PREFAB_H_INCLUDED
+
+struct PrefabCode {
+    unsigned int leadBits;
+    unsigned int trailBits;
+    struct BitString activeBits;
+};
+
+
+extern struct PrefabCode const g3prefab_code[256];
+
+#endif
+
diff --git a/converter/pbm/g3topbm.c b/converter/pbm/g3topbm.c
index fcac1981..5d98fcb2 100644
--- a/converter/pbm/g3topbm.c
+++ b/converter/pbm/g3topbm.c
@@ -4,7 +4,7 @@
 
   This program reads a Group 3 FAX file and produces a PBM image.
 
-  Bryan Henderson wrote this on August 5, 2004 and contributed it to 
+  Bryan Henderson wrote this on August 5, 2004 and contributed it to
   the public domain.
 
   This program is designed to be a drop-in replacement for the program
@@ -18,6 +18,7 @@
   contributing their work to the public domain.
 ===========================================================================*/
 
+#define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE   /* Make nstring.h define strcaseeq() */
 
 #include "pm_c_util.h"
@@ -26,6 +27,7 @@
 #include "nstring.h"
 #include "mallocvar.h"
 #include "g3.h"
+#include "g3ttable.h"
 #include "bitreverse.h"
 #include "bitarith.h"
 
@@ -43,11 +45,37 @@
 
 #define HASHSIZE 1021
 
-static g3TableEntry * whash[HASHSIZE];
-static g3TableEntry * bhash[HASHSIZE];
+#define MAXFILLBITS (5 * 9600)
 
+/*
+Fill bits are for flow control.  This was important when DRAM was
+expensive and fax machines came with small buffers.
 
-struct cmdlineInfo {
+If data arrives too quickly it may overflow the buffer of the
+receiving device.  On sending devices transmission time of compressed
+data representing a single row can be shorter than the time required
+to scan and encode.  The CCITT standard allows sending devices to
+insert fill bits to put communication on hold in these cases.
+
+By the CCITT standard, the maximum transmission time for one row is:
+
+100 - 400 pixels/inch 13 seconds
+(standard mode: 200 pixels/inch)
+600 pixels/inch 19 seconds
+1200 pixels/inch 37 seconds
+
+If one row is not received within the above limits, the receiving
+machine must disconnect the line.
+
+The receiver may be less patient.  It may opt to disconnect if one row
+is not received within 5 seconds.
+*/
+
+static G3TableEntry * whash[HASHSIZE];
+static G3TableEntry * bhash[HASHSIZE];
+
+
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
@@ -62,8 +90,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.
@@ -84,20 +112,20 @@ parseCommandLine(int argc, char ** const argv,
             0);
     OPTENT3(0, "kludge",           OPT_FLAG,  NULL, &cmdlineP->kludge,
             0);
-    OPTENT3(0, "stretch",          OPT_FLAG,  NULL, &cmdlineP->stretch, 
+    OPTENT3(0, "stretch",          OPT_FLAG,  NULL, &cmdlineP->stretch,
             0);
-    OPTENT3(0, "stop_error",       OPT_FLAG,  NULL, &cmdlineP->stop_error, 
+    OPTENT3(0, "stop_error",       OPT_FLAG,  NULL, &cmdlineP->stop_error,
             0);
     OPTENT3(0, "width",            OPT_UINT,  &cmdlineP->expectedLineSize,
             &widthSpec,                0);
     OPTENT3(0, "paper_size",       OPT_STRING, &paperSize,
             &paper_sizeSpec,           0);
-    
+
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
 
-    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char**)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (widthSpec && paper_sizeSpec)
@@ -124,7 +152,7 @@ parseCommandLine(int argc, char ** const argv,
     } else
         cmdlineP->expectedLineSize = 0;
 
-    if (argc-1 == 0) 
+    if (argc-1 == 0)
         cmdlineP->inputFilespec = "-";
     else if (argc-1 != 1)
         pm_error("Program takes zero or one argument (filename).  You "
@@ -135,7 +163,7 @@ parseCommandLine(int argc, char ** const argv,
 
 
 
-struct bitStream {
+struct BitStream {
 
     FILE * fileP;
     bool reversebits;
@@ -155,7 +183,7 @@ struct bitStream {
 
 
 static void
-readBit(struct bitStream * const bitStreamP,
+readBit(struct BitStream * const bitStreamP,
         unsigned int *     const bitP,
         const char **      const errorP) {
 /*----------------------------------------------------------------------------
@@ -188,7 +216,7 @@ readBit(struct bitStream * const bitStreamP,
 
 
 static void
-readBitAndDetectEol(struct bitStream * const bitStreamP,
+readBitAndDetectEol(struct BitStream * const bitStreamP,
                     unsigned int *     const bitP,
                     bool *             const eolP,
                     const char **      const errorP) {
@@ -216,10 +244,10 @@ readBitAndDetectEol(struct bitStream * const bitStreamP,
 
 
 static void
-initBitStream(struct bitStream * const bitStreamP,
+initBitStream(struct BitStream * const bitStreamP,
               FILE *             const fileP,
               bool               const reversebits) {
-    
+
     bitStreamP->fileP        = fileP;
     bitStreamP->reversebits  = reversebits;
     bitStreamP->shbit        = 0x00;
@@ -229,17 +257,17 @@ initBitStream(struct bitStream * const bitStreamP,
 
 
 static void
-skipToNextLine(struct bitStream * const bitStreamP) {
+skipToNextLine(struct BitStream * const bitStreamP) {
 
     bool eol;
     const char * error;
 
     eol = FALSE;
     error = NULL;
-    
+
     while (!eol && !error) {
         unsigned int bit;
-        
+
         readBitAndDetectEol(bitStreamP, &bit, &eol, &error);
     }
 }
@@ -247,16 +275,16 @@ skipToNextLine(struct bitStream * const bitStreamP) {
 
 
 static void
-addtohash(g3TableEntry *     hash[], 
-          g3TableEntry       table[], 
-          unsigned int const n, 
-          int          const a, 
+addtohash(G3TableEntry *     hash[],
+          G3TableEntry       table[],
+          unsigned int const n,
+          int          const a,
           int          const b) {
-    
+
     unsigned int i;
 
     for (i = 0; i < n; ++i) {
-        g3TableEntry * const teP = &table[i*2];
+        G3TableEntry * const teP = &table[i*2];
         unsigned int const pos =
             ((teP->length + a) * (teP->code + b)) % HASHSIZE;
         if (hash[pos])
@@ -267,15 +295,15 @@ addtohash(g3TableEntry *     hash[],
 
 
 
-static g3TableEntry *
-hashfind(g3TableEntry *       hash[], 
-         int            const length, 
-         int            const code, 
-         int            const a, 
+static G3TableEntry *
+hashfind(G3TableEntry *       hash[],
+         int            const length,
+         int            const code,
+         int            const a,
          int            const b) {
 
     unsigned int pos;
-    g3TableEntry * te;
+    G3TableEntry * te;
 
     pos = ((length + a) * (code + b)) % HASHSIZE;
     te = hash[pos];
@@ -285,19 +313,19 @@ hashfind(g3TableEntry *       hash[],
 
 
 static void
-buildHashes(g3TableEntry * (*whashP)[HASHSIZE],
-            g3TableEntry * (*bhashP)[HASHSIZE]) {
+buildHashes(G3TableEntry * (*whashP)[HASHSIZE],
+            G3TableEntry * (*bhashP)[HASHSIZE]) {
 
     unsigned int i;
 
     for (i = 0; i < HASHSIZE; ++i)
         (*whashP)[i] = (*bhashP)[i] = NULL;
 
-    addtohash(*whashP, &ttable[0], 64, WHASHA, WHASHB);
-    addtohash(*whashP, &mtable[2], 40, WHASHA, WHASHB);
+    addtohash(*whashP, &g3ttable_table[0], 64, WHASHA, WHASHB);
+    addtohash(*whashP, &g3ttable_mtable[2], 40, WHASHA, WHASHB);
 
-    addtohash(*bhashP, &ttable[1], 64, BHASHA, BHASHB);
-    addtohash(*bhashP, &mtable[3], 40, BHASHA, BHASHB);
+    addtohash(*bhashP, &g3ttable_table[1], 64, BHASHA, BHASHB);
+    addtohash(*bhashP, &g3ttable_mtable[3], 40, BHASHA, BHASHB);
 
 }
 
@@ -314,7 +342,7 @@ makeRowWhite(unsigned char * const packedBitrow,
 
 
 
-static g3TableEntry *
+static G3TableEntry *
 g3code(unsigned int const curcode,
        unsigned int const curlen,
        bit          const color) {
@@ -325,7 +353,7 @@ g3code(unsigned int const 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;
+    G3TableEntry * retval;
 
     switch (color) {
     case PBM_WHITE:
@@ -382,7 +410,7 @@ writeBlackBitSpan(unsigned char * const packedBitrow,
 enum g3tableId {TERMWHITE, TERMBLACK, MKUPWHITE, MKUPBLACK};
 
 static void
-processG3Code(const g3TableEntry * const teP,
+processG3Code(const G3TableEntry * const teP,
               unsigned char *      const packedBitrow,
               unsigned int *       const colP,
               bit *                const colorP,
@@ -393,15 +421,15 @@ processG3Code(const g3TableEntry * const teP,
    matters.
 -----------------------------------------------------------------------------*/
     enum g3tableId const teId =
-        (teP > mtable ? 2 : 0) + (teP - ttable) % 2;
+        (teP > g3ttable_mtable ? 2 : 0) + (teP - g3ttable_table) % 2;
 
     unsigned int teCount;
-    
+
     switch(teId) {
-    case TERMWHITE: teCount = (teP - ttable    ) / 2;      break;
-    case TERMBLACK: teCount = (teP - ttable - 1) / 2;      break;
-    case MKUPWHITE: teCount = (teP - mtable    ) / 2 * 64; break;
-    case MKUPBLACK: teCount = (teP - mtable - 1) / 2 * 64; break;
+    case TERMWHITE: teCount = (teP - g3ttable_table    ) / 2;      break;
+    case TERMBLACK: teCount = (teP - g3ttable_table - 1) / 2;      break;
+    case MKUPWHITE: teCount = (teP - g3ttable_mtable    ) / 2 * 64; break;
+    case MKUPBLACK: teCount = (teP - g3ttable_mtable - 1) / 2 * 64; break;
     }
 
     switch (teId) {
@@ -409,7 +437,7 @@ processG3Code(const g3TableEntry * const teP,
     case TERMBLACK: {
         unsigned int totalRunLength;
         unsigned int col;
-        
+
         col = *colP;
         totalRunLength = MIN(*countP + teCount, MAXCOLS - col);
 
@@ -451,13 +479,13 @@ formatBadCodeException(const char ** const exceptionP,
 
 
 static void
-readFaxRow(struct bitStream * const bitStreamP,
+readFaxRow(struct BitStream * const bitStreamP,
            unsigned char *    const packedBitrow,
            unsigned int *     const lineLengthP,
            const char **      const exceptionP,
            const char **      const errorP) {
 /*----------------------------------------------------------------------------
-  Read one line of G3 fax from the bit stream *bitStreamP into 
+  Read one line of G3 fax from the bit stream *bitStreamP into
   packedBitrow[].  Return the length of the line, in pixels, as *lineLengthP.
 
   If there's a problem with the line, return as much of it as we can,
@@ -468,15 +496,17 @@ readFaxRow(struct bitStream * const bitStreamP,
   We guarantee that we make progress through the input stream.
 
   Iff there is an error, return a text description of it in newly
-  malloc'ed storage at *errorP and all other specified behavior 
+  malloc'ed storage at *errorP and all other specified behavior
   (including return values) is unspecified.
 -----------------------------------------------------------------------------*/
     unsigned int col;
-    unsigned int curlen;  
-        /* Number of bits we've read so far for the code we're currently 
+    unsigned int curlen;
+        /* Number of bits we've read so far for the code we're currently
            reading
         */
-    unsigned int curcode; 
+    unsigned int fillbits;
+        /* Number of consecutive 0 bits.  Can precede EOL codes */
+    unsigned int curcode;
         /* What we've assembled so far of the code we're currently reading */
     unsigned int count;
         /* Number of consecutive pixels of the same color */
@@ -489,6 +519,7 @@ readFaxRow(struct bitStream * const bitStreamP,
     col = 0;
     curlen = 0;
     curcode = 0;
+    fillbits = 0;
     currentColor = PBM_WHITE;
     count = 0;
     *exceptionP = NULL;
@@ -520,21 +551,26 @@ readFaxRow(struct bitStream * const bitStreamP,
             else {
                 curcode = (curcode << 1) | bit;
                 ++curlen;
-            
-                if (curlen > 13) {
+
+		if (curlen > 11 && curcode == 0x00) {
+		    if (++fillbits > MAXFILLBITS)
+                pm_error("Encountered %u consecutive fill bits.  "
+                       "Aborting", fillbits);
+		}
+		else if (curlen - fillbits > 13) {
                     formatBadCodeException(exceptionP, col, curlen, curcode);
                     done = TRUE;
                 } else if (curcode != 0) {
-                    const g3TableEntry * const teP =
+                    const G3TableEntry * const teP =
                         g3code(curcode, curlen, currentColor);
-                        /* Address of structure that describes the 
+                        /* Address of structure that describes the
                            current G3 code.  Null means 'curcode' isn't
                            a G3 code yet (probably just the beginning of one)
                         */
                     if (teP) {
                         processG3Code(teP, packedBitrow,
                                       &col, &currentColor, &count);
-                        
+
                         curcode = 0;
                         curlen = 0;
                     }
@@ -562,7 +598,7 @@ freeBits(unsigned char ** const packedBits,
             /* This is just a pointer to the previous row; don't want to
                free it twice.
             */
-        } else 
+        } else
             pbm_freerow_packed(packedBits[row]);
     }
     free(packedBits);
@@ -675,11 +711,11 @@ analyzeLineSize(lineSizeAnalyzer * const analyzerP,
    line.  Starting in Netpbm 10.24 (August 2004), we assume there is
    no valid reason to have an empty line and recognize EOF as any
    empty line.  Alternatively, we could read off and ignore two empty
-   lines without a 3rd.  
+   lines without a 3rd.
 */
 
 static void
-readFax(struct bitStream * const bitStreamP,
+readFax(struct BitStream * const bitStreamP,
         bool               const stretch,
         unsigned int       const expectedLineSize,
         bool               const tolerateErrors,
@@ -692,7 +728,7 @@ readFax(struct bitStream * const bitStreamP,
     const char * error;
     bool eof;
     unsigned int row;
-    
+
     MALLOCARRAY_NOFAIL(packedBits, MAXROWS);
 
     initializeLineSizeAnalyzer(&lineSizeAnalyzer,
@@ -723,7 +759,7 @@ readFax(struct bitStream * const bitStreamP,
                     eof = TRUE;
                 } else {
                     analyzeLineSize(&lineSizeAnalyzer, lineSize);
-                    
+
                     if (stretch) {
                         ++row;
                         if (row >= MAXROWS)
@@ -746,15 +782,15 @@ readFax(struct bitStream * const bitStreamP,
 
 
 int
-main(int argc, char * argv[]) {
+main(int argc, const char * argv[]) {
 
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
     FILE * ifP;
-    struct bitStream bitStream;
+    struct BitStream bitStream;
     unsigned int rows, cols;
     unsigned char ** packedBits;
 
-    pbm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
@@ -773,7 +809,7 @@ main(int argc, char * argv[]) {
     buildHashes(&whash, &bhash);
 
     readFax(&bitStream, cmdline.stretch, cmdline.expectedLineSize,
-            !cmdline.stop_error, 
+            !cmdline.stop_error,
             &packedBits, &cols, &rows);
 
     pm_close(ifP);
diff --git a/converter/pbm/g3ttable.c b/converter/pbm/g3ttable.c
new file mode 100644
index 00000000..6f9eb94f
--- /dev/null
+++ b/converter/pbm/g3ttable.c
@@ -0,0 +1,116 @@
+#include "g3ttable.h"
+
+struct G3TableEntry g3ttable_table[] = {
+/*    TERMWHITE           TERMBLACK   */
+    { 0x35, 8 },    { 0x37, 10 },       /* white 0 , black 0 */
+    { 0x07, 6 },    { 0x02,  3 },
+    { 0x07, 4 },    { 0x03,  2 },
+    { 0x08, 4 },    { 0x02,  2 },
+    { 0x0b, 4 },    { 0x03,  3 },
+    { 0x0c, 4 },    { 0x03,  4 },
+    { 0x0e, 4 },    { 0x02,  4 },
+    { 0x0f, 4 },    { 0x03,  5 },
+    { 0x13, 5 },    { 0x05,  6 },
+    { 0x14, 5 },    { 0x04,  6 },
+    { 0x07, 5 },    { 0x04,  7 },
+    { 0x08, 5 },    { 0x05,  7 },
+    { 0x08, 6 },    { 0x07,  7 },
+    { 0x03, 6 },    { 0x04,  8 },
+    { 0x34, 6 },    { 0x07,  8 },
+    { 0x35, 6 },    { 0x18,  9 },
+    { 0x2a, 6 },    { 0x17, 10 },
+    { 0x2b, 6 },    { 0x18, 10 },
+    { 0x27, 7 },    { 0x08, 10 },
+    { 0x0c, 7 },    { 0x67, 11 },
+    { 0x08, 7 },    { 0x68, 11 },
+    { 0x17, 7 },    { 0x6c, 11 },
+    { 0x03, 7 },    { 0x37, 11 },
+    { 0x04, 7 },    { 0x28, 11 },
+    { 0x28, 7 },    { 0x17, 11 },
+    { 0x2b, 7 },    { 0x18, 11 },
+    { 0x13, 7 },    { 0xca, 12 },
+    { 0x24, 7 },    { 0xcb, 12 },
+    { 0x18, 7 },    { 0xcc, 12 },
+    { 0x02, 8 },    { 0xcd, 12 },
+    { 0x03, 8 },    { 0x68, 12 },
+    { 0x1a, 8 },    { 0x69, 12 },
+    { 0x1b, 8 },    { 0x6a, 12 },
+    { 0x12, 8 },    { 0x6b, 12 },
+    { 0x13, 8 },    { 0xd2, 12 },
+    { 0x14, 8 },    { 0xd3, 12 },
+    { 0x15, 8 },    { 0xd4, 12 },
+    { 0x16, 8 },    { 0xd5, 12 },
+    { 0x17, 8 },    { 0xd6, 12 },
+    { 0x28, 8 },    { 0xd7, 12 },
+    { 0x29, 8 },    { 0x6c, 12 },
+    { 0x2a, 8 },    { 0x6d, 12 },
+    { 0x2b, 8 },    { 0xda, 12 },
+    { 0x2c, 8 },    { 0xdb, 12 },
+    { 0x2d, 8 },    { 0x54, 12 },
+    { 0x04, 8 },    { 0x55, 12 },
+    { 0x05, 8 },    { 0x56, 12 },
+    { 0x0a, 8 },    { 0x57, 12 },
+    { 0x0b, 8 },    { 0x64, 12 },
+    { 0x52, 8 },    { 0x65, 12 },
+    { 0x53, 8 },    { 0x52, 12 },
+    { 0x54, 8 },    { 0x53, 12 },
+    { 0x55, 8 },    { 0x24, 12 },
+    { 0x24, 8 },    { 0x37, 12 },
+    { 0x25, 8 },    { 0x38, 12 },
+    { 0x58, 8 },    { 0x27, 12 },
+    { 0x59, 8 },    { 0x28, 12 },
+    { 0x5a, 8 },    { 0x58, 12 },
+    { 0x5b, 8 },    { 0x59, 12 },
+    { 0x4a, 8 },    { 0x2b, 12 },
+    { 0x4b, 8 },    { 0x2c, 12 },
+    { 0x32, 8 },    { 0x5a, 12 },
+    { 0x33, 8 },    { 0x66, 12 },
+    { 0x34, 8 },    { 0x67, 12 },       /* white 63 , black 63 */
+
+/* mtable */
+/*    MKUPWHITE           MKUPBLACK   */
+    { 0x00, 0 },    { 0x00,  0 },   /* dummy to simplify pointer math */
+    { 0x1b, 5 },    { 0x0f, 10 },   /* white 64 , black 64 */
+    { 0x12, 5 },    { 0xc8, 12 },
+    { 0x17, 6 },    { 0xc9, 12 },
+    { 0x37, 7 },    { 0x5b, 12 },
+    { 0x36, 8 },    { 0x33, 12 },
+    { 0x37, 8 },    { 0x34, 12 },
+    { 0x64, 8 },    { 0x35, 12 },
+    { 0x65, 8 },    { 0x6c, 13 },
+    { 0x68, 8 },    { 0x6d, 13 },
+    { 0x67, 8 },    { 0x4a, 13 },
+    { 0xcc, 9 },    { 0x4b, 13 },
+    { 0xcd, 9 },    { 0x4c, 13 },
+    { 0xd2, 9 },    { 0x4d, 13 },
+    { 0xd3, 9 },    { 0x72, 13 },
+    { 0xd4, 9 },    { 0x73, 13 },
+    { 0xd5, 9 },    { 0x74, 13 },
+    { 0xd6, 9 },    { 0x75, 13 },
+    { 0xd7, 9 },    { 0x76, 13 },
+    { 0xd8, 9 },    { 0x77, 13 },
+    { 0xd9, 9 },    { 0x52, 13 },
+    { 0xda, 9 },    { 0x53, 13 },
+    { 0xdb, 9 },    { 0x54, 13 },
+    { 0x98, 9 },    { 0x55, 13 },
+    { 0x99, 9 },    { 0x5a, 13 },
+    { 0x9a, 9 },    { 0x5b, 13 },
+    { 0x18, 6 },    { 0x64, 13 },
+    { 0x9b, 9 },    { 0x65, 13 },
+    { 0x08, 11 },   { 0x08, 11 },        /* extable len = 1792 */
+    { 0x0c, 11 },   { 0x0c, 11 },
+    { 0x0d, 11 },   { 0x0d, 11 },
+    { 0x12, 12 },   { 0x12, 12 },
+    { 0x13, 12 },   { 0x13, 12 },
+    { 0x14, 12 },   { 0x14, 12 },
+    { 0x15, 12 },   { 0x15, 12 },
+    { 0x16, 12 },   { 0x16, 12 },
+    { 0x17, 12 },   { 0x17, 12 },
+    { 0x1c, 12 },   { 0x1c, 12 },
+    { 0x1d, 12 },   { 0x1d, 12 },
+    { 0x1e, 12 },   { 0x1e, 12 },
+    { 0x1f, 12 },   { 0x1f, 12 },
+};
+
+
+
diff --git a/converter/pbm/g3ttable.h b/converter/pbm/g3ttable.h
new file mode 100644
index 00000000..2ae2bda4
--- /dev/null
+++ b/converter/pbm/g3ttable.h
@@ -0,0 +1,13 @@
+#ifndef G3TTABLE_H_INCLUDED
+#define G3TTABLE_H_INCLUDED
+
+typedef struct G3TableEntry {
+    unsigned short int code;
+    unsigned short int length;
+} G3TableEntry;
+
+extern struct G3TableEntry g3ttable_table[];
+
+#define g3ttable_mtable ((g3ttable_table)+64*2)
+
+#endif
diff --git a/converter/pbm/pbmtoascii.c b/converter/pbm/pbmtoascii.c
index 976b188f..0305a47b 100644
--- a/converter/pbm/pbmtoascii.c
+++ b/converter/pbm/pbmtoascii.c
@@ -83,15 +83,15 @@ BSQ,D04, D04,D05, D04,'L', D05,'[', '<','Z', '/','Z', 'c','k',D06,'R',/*D0-DF*/
 
 
 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) {
+makeRowOfSignatures(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.
 
@@ -183,23 +183,30 @@ pbmtoascii(FILE *       const ifP,
            unsigned int const cellHeight,
            const char * const carr) {
 
-    int cols, rows, format;
+    int format;
+    int cols, rows;
+        /* Dimensions of the input in pixels */
     unsigned int ccols;
+        /* Width of the output in characters */
     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.
+    unsigned int * signatureRow;  /* malloc'ed array */
+        /* This is the cell signatures of a row of cells.
+           signatureRow[0] is the signature for the first (leftmost) cell
+           in the row, signatureRow[1] is the signature for the next one,
+           etc.  A signature is an encoding of the pixels of a cell as
+           an integer, as described above.
         */
-    assert(cellWidth * cellHeight <= sizeof(sig[0])*8);
+    assert(cellWidth * cellHeight <= sizeof(signatureRow[0])*8);
 
     pbm_readpbminit(ifP, &cols, &rows, &format);
 
     ccols = (cols + cellWidth - 1) / cellWidth;
 
-    MALLOCARRAY(sig, ccols);
-    if (sig == NULL)
+    MALLOCARRAY(signatureRow, ccols);
+    if (signatureRow == 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);
@@ -207,16 +214,16 @@ pbmtoascii(FILE *       const ifP,
     for (row = 0; row < rows; row += cellHeight) {
         unsigned int endCol;
 
-        makeRowOfSigs(ifP, cols, rows, format, cellWidth, cellHeight,
-                      row, sig, ccols);
+        makeRowOfSignatures(ifP, cols, rows, format, cellWidth, cellHeight,
+                            row, signatureRow, ccols);
 
-        findRightMargin(sig, ccols, carr, &endCol);
+        findRightMargin(signatureRow, ccols, carr, &endCol);
 
-        assembleCellRow(sig, endCol, carr, line);
+        assembleCellRow(signatureRow, endCol, carr, line);
 
         puts(line);
     }
-    free(sig);
+    free(signatureRow);
     free(line);
 }
 
diff --git a/converter/pbm/pbmtoepson.c b/converter/pbm/pbmtoepson.c
index bb36791d..122a438f 100644
--- a/converter/pbm/pbmtoepson.c
+++ b/converter/pbm/pbmtoepson.c
@@ -11,6 +11,7 @@
 ** implied warranty.
 */
 
+#define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE    /* Make sure strcaseeq() is in nstring.h */
 #include <stdio.h>
 
diff --git a/converter/pbm/pbmtog3.c b/converter/pbm/pbmtog3.c
index f0fd1252..f034b466 100644
--- a/converter/pbm/pbmtog3.c
+++ b/converter/pbm/pbmtog3.c
@@ -1,84 +1,54 @@
 /* pbmtog3.c - read a PBM image and produce a Group 3 FAX file
-**
-** Copyright (C) 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.
-*/
 
-/*
-   For specifications for Group 3 (G3) fax MH coding see ITU-T T.4
-   This program generates only MH.  It is coded with future expansion for
-   MR and MMR in mind.
+   For specifications for Group 3 (G3) fax MH coding see ITU-T T.4:
+   Standardization of Group 3 facsimile terminals for document transmission
+   https://www.itu.int/rec/T-REC-T.4/en
+
+   This program generates only MH.
 */
 
+
 #include <assert.h>
 
 #include "pm_c_util.h"
 #include "shhopt.h"
 #include "mallocvar.h"
 #include "bitreverse.h"
-#include "wordaccess.h"
-#include "wordintclz.h"
+#include "intcode.h"
 #include "g3.h"
+#include "g3ttable.h"
+#include "g3prefab.h"
 #include "pbm.h"
 
-#define TC_MC 64
-
-static bool const pbmtorl = 
-#ifdef PBMTORL
-    TRUE;
-#else
-    FALSE;
-#endif
-
+enum G3eol {EOL, ALIGN8, ALIGN16, NO_EOL, NO_RTC, NO_EOLRTC};
 
-struct bitString {
-    /* A string of bits, up to as many fit in a word. */
-    unsigned int bitCount;
-        /* The length of the bit string */
-    wordint intBuffer;
-        /* The bits are in the 'bitCount' least significant bit positions 
-           of this number.  The rest of the bits of this number are always 
-           zero.
-        */
+struct OutStream;
 
-    /* Example:  The bit string 010100, on a machine with a 32 bit word,
-       would be represented by bitCount = 6, intBuffer = 20
-       (N.B. 20 = 00000000 00000000 00000000 00010100 in binary)
-    */
+struct OutStream {
+    FILE * fp;
+    struct BitString buffer;
+    bool reverseBits;    /* Reverse bit order */
+    enum G3eol eolAlign; /* Omit EOL and/or RTC; align EOL to 8/16 bits */
+    void * data;         /* Reserved for future expansion */
 };
 
 
 
-struct outStream {
-    struct bitString buffer;
-
-    bool reverseBits;
-};
-
-/* This is a global variable for speed. */
-static struct outStream out;
-
-
 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 reversebits;
-    unsigned int nofixedwidth;
+    enum G3eol   align;
+    unsigned int desiredWidth;
     unsigned int verbose;
 };
 
 
 
 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
@@ -87,6 +57,8 @@ parseCommandLine(int argc, char ** const argv,
     optEntry * option_def;
         /* Instructions to OptParseOptions2 on how to parse our options.  */
     optStruct3 opt;
+    unsigned int nofixedwidth;
+    unsigned int align8, align16;
 
     unsigned int option_def_index;
 
@@ -95,9 +67,13 @@ parseCommandLine(int argc, char ** const argv,
     option_def_index = 0;   /* incremented by OPTENTRY */
     OPTENT3(0,   "reversebits",      OPT_FLAG,  NULL, &cmdlineP->reversebits,
             0);
-    OPTENT3(0,   "nofixedwidth",     OPT_FLAG,  NULL, &cmdlineP->nofixedwidth,
+    OPTENT3(0,   "nofixedwidth",     OPT_FLAG,  NULL, &nofixedwidth,
             0);
-    OPTENT3(0,   "verbose",          OPT_FLAG,  NULL, &cmdlineP->verbose, 
+    OPTENT3(0,   "align8",           OPT_FLAG,  NULL, &align8,
+            0);
+    OPTENT3(0,   "align16",          OPT_FLAG,  NULL, &align16,
+            0);
+    OPTENT3(0,   "verbose",          OPT_FLAG,  NULL, &cmdlineP->verbose,
             0);
 
     /* TODO
@@ -105,15 +81,30 @@ parseCommandLine(int argc, char ** const argv,
     */
 
     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 */
+    opt.short_allowed = false;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = true;  /* We may have parms that are negative numbers */
 
-    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     free(option_def);
 
-    if (argc-1 == 0) 
+    if (align8) {
+        if (align16)
+            pm_error("You can't specify both -align8 and -align16");
+        else
+            cmdlineP->align = ALIGN8;
+    } else if (align16)
+        cmdlineP->align = ALIGN16;
+    else
+        cmdlineP->align = EOL;
+
+    if (nofixedwidth)
+        cmdlineP->desiredWidth = 0;
+    else
+        cmdlineP->desiredWidth = 1728;
+
+    if (argc-1 == 0)
         cmdlineP->inputFileName = "-";
     else if (argc-1 != 1)
         pm_error("Program takes zero or one argument (filename).  You "
@@ -125,31 +116,79 @@ parseCommandLine(int argc, char ** const argv,
 
 
 static void
-reversebuffer(unsigned char * const p, 
+reversebuffer(unsigned char * const p,
               unsigned int    const n) {
 
     unsigned int i;
+
     for (i = 0; i < n; ++i)
         p[i] = bitreverse[p[i]];
 }
 
 
 
-static struct bitString
-makeBs(wordint      const bits, 
-       unsigned int const bitCount) {
+static void
+flushBuffer(struct OutStream * const outP) {
+/*----------------------------------------------------------------------------
+  Flush the contents of the bit buffer
+-----------------------------------------------------------------------------*/
+    struct BitString const buffer = outP->buffer;
 
-    struct bitString retval;
-    retval.intBuffer = bits;
-    retval.bitCount  = bitCount;
+    assert (buffer.bitCount <= 32);
 
-    return retval;
+    if (buffer.bitCount > 0) {
+        unsigned int const fullBuffer = sizeof(buffer.intBuffer) * 8;
+        unsigned int const bytesToWrite = (buffer.bitCount+7)/8;
+        bigend32 outbytes;
+        size_t rc;
+
+        outbytes = pm_bigendFromUint32(
+                   buffer.intBuffer << (fullBuffer - buffer.bitCount));
+        if (outP->reverseBits)
+    	reversebuffer((unsigned char *)&outbytes, bytesToWrite);
+        rc = fwrite((unsigned char *)&outbytes, 1, bytesToWrite, outP->fp);
+        if (rc != bytesToWrite)
+            pm_error("Output error");
+    }
 }
 
 
+#if 1==0
+static void
+putbitsDump(struct OutStream * const outP,
+            struct BitString   const newBits) {
+/*----------------------------------------------------------------------------
+  Print the content of the bit put request, in human readable text form
+  For debugging.  Also good for studying how the coding scheme works.
+
+  By default the compiler ignores this function.
+  To turn on, remove the "#if" - "#endif" lines enclosing the function and
+  edit the output function name in putbits().
+-----------------------------------------------------------------------------*/
+    unsigned int const bitCount = newBits.bitCount;
+
+    unsigned int i;
+    char charBuff[128];
+
+    assert (bitCount >= 0 && bitCount < 32);
+    assert (sizeof(newBits.intBuffer) + 2 < 128);
+
+    for (i = 0; i < bitCount; ++i) {
+        unsigned int const n = bitCount - i - 1;
+        charBuff[i] = ((newBits.intBuffer >> n) & 0x01) + '0';
+    }
+
+    charBuff[bitCount]   = '\n';
+    charBuff[bitCount+1] = '\0';
+    fwrite(charBuff, 1, bitCount+1, outP->fp);
+}
+#endif
+
+
 
-static __inline__ void
-putbits(struct bitString const newBits) {
+static void
+putbitsBinary(struct OutStream * const outP,
+              struct BitString   const newBits) {
 /*----------------------------------------------------------------------------
    Push the bits 'newBits' onto the right end of output buffer
    out.buffer (moving the bits already in the buffer left).
@@ -158,334 +197,462 @@ putbits(struct bitString const newBits) {
 
    'newBits' must be shorter than a whole word.
 
-   N.B. the definition of struct bitString requires upper bits to be zero.
+   N.B. the definition of struct BitString requires upper bits to be zero.
 -----------------------------------------------------------------------------*/
-    unsigned int const spaceLeft = 
-        sizeof(out.buffer.intBuffer)*8 - out.buffer.bitCount;
+    unsigned int const fullBuffer = sizeof(outP->buffer.intBuffer) * 8;
+    unsigned int const spaceLeft = fullBuffer - outP->buffer.bitCount;
         /* Number of bits of unused space (at the high end) in buffer */
 
-    assert(newBits.bitCount < sizeof(out.buffer.intBuffer) * 8);
+    assert(newBits.bitCount < fullBuffer);
     assert(newBits.intBuffer >> newBits.bitCount == 0);
 
     if (spaceLeft > newBits.bitCount) {
         /* New bits fit with bits to spare */
-        out.buffer.intBuffer = 
-            out.buffer.intBuffer << newBits.bitCount | newBits.intBuffer;
-        out.buffer.bitCount += newBits.bitCount;
-    } else { 
+        outP->buffer.intBuffer =
+            outP->buffer.intBuffer << newBits.bitCount | newBits.intBuffer;
+        outP->buffer.bitCount += newBits.bitCount;
+    } else {
         /* New bits fill buffer.  We'll have to flush the buffer to stdout
            and put the rest of the bits in the new buffer.
         */
         unsigned int const nextBufBitCount = newBits.bitCount - spaceLeft;
+        unsigned int const bitMask = ((1<<nextBufBitCount) - 1);
 
-        wordintBytes outbytes;
-        size_t rc;
+        outP->buffer.intBuffer = ( (outP->buffer.intBuffer << spaceLeft)
+                                 | (newBits.intBuffer >> nextBufBitCount));
+        outP->buffer.bitCount  = fullBuffer;
+        flushBuffer(outP);
 
-        wordintToBytes(&outbytes, 
-                       (out.buffer.intBuffer << spaceLeft) 
-                       | (newBits.intBuffer >> nextBufBitCount));
-        if (out.reverseBits)
-            reversebuffer(outbytes, sizeof(outbytes));
+        outP->buffer.intBuffer = newBits.intBuffer & bitMask;
+        outP->buffer.bitCount = nextBufBitCount;
+    }
+}
 
-        rc = fwrite(outbytes, 1, sizeof(outbytes), stdout);
-        if (rc != sizeof(outbytes))
-            pm_error("Output error.  Unable to fwrite() to stdout");
 
-        out.buffer.intBuffer = newBits.intBuffer & ((1<<nextBufBitCount) - 1); 
-        out.buffer.bitCount = nextBufBitCount;
-    }
+
+static void
+initOutStream(struct OutStream * const outP,
+              bool               const reverseBits,
+              enum G3eol         const eolAlign) {
+
+    outP->buffer.intBuffer = 0;
+    outP->buffer.bitCount  = 0;
+    outP->reverseBits      = reverseBits;
+    outP->fp               = stdout;
+    outP->eolAlign         = eolAlign;
+}
+
+
+
+static struct BitString
+tableEntryToBitString(G3TableEntry const tableEntry) {
+
+    struct BitString retval;
+
+    retval.intBuffer = tableEntry.code;
+    retval.bitCount  = tableEntry.length;
+
+    return retval;
 }
 
 
 
-static void 
-initOutStream(bool const reverseBits) {
-    out.buffer.intBuffer = 0;
-    out.buffer.bitCount  = 0;
-    out.reverseBits = reverseBits;
+static void
+putbits(struct OutStream * const outP,
+        struct BitString   const newBits) {
+
+    putbitsBinary(outP, newBits);
+    /* Change to putbitsDump() for human readable output */
 }
 
 
 
-static __inline__ void
-putcode(unsigned int const clr, 
-        unsigned int const ix) {
+static void
+putcodeShort(struct OutStream * const outP,
+             bit                const color,
+             unsigned int       const runLength) {
 
-    /* Note that this requires ttable to be aligned white entry, black
-       entry, white, black, etc.  
+    /* Note that this requires g3ttable_table to be aligned white entry, black
+       entry, white, black, etc.
     */
-    putbits(makeBs(ttable[ix * 2 + clr].code, ttable[ix * 2 + clr].length));
+    unsigned int index = runLength * 2 + color;
+    putbits(outP, tableEntryToBitString(g3ttable_table[index]));
 }
 
 
 
-static __inline__ void
-putcode2(int const clr,
-         int const ix) {
+static void
+putcodeLong(struct OutStream * const outP,
+            bit                const color,
+            unsigned int       const runLength) {
 /*----------------------------------------------------------------------------
    Output Make-up code and Terminating code at once.
 
-   For run lengths above TC_MC threshold (usually 64).
+   For run lengths which require both: length 64 and above
 
    The codes are combined here to avoid calculations in putbits()
-   wordint is usually wide enough, with 32 or 64 bits.
-   Provisions are made for 16 bit wordint (for debugging).
 
    Terminating code is max 12 bits, Make-up code is max 13 bits.
-   (See ttable, mtable entries in pbmtog3.h)
+   (See g3ttable_table, g3ttable_mtable entries in g3ttable.h)
 
    Also reduces object code size when putcode is compiled inline.
 -----------------------------------------------------------------------------*/
-    unsigned int const loIndex = ix % 64 * 2 + clr;
-    unsigned int const hiIndex = ix / 64 * 2 + clr;
-
-    if (sizeof(wordint) * 8 > 24) {
-        unsigned int const l1 = ttable[loIndex].length;
-
-        putbits(
-            makeBs(mtable[hiIndex].code << l1 | ttable[loIndex].code,
-                   mtable[hiIndex].length + l1)
-            );
-    } else { /* typically 16 bit wordint used for debugging */
-        putbits(makeBs(mtable[hiIndex].code, mtable[hiIndex].length));
-        putbits(makeBs(ttable[loIndex].code, ttable[loIndex].length));
-    }
+    unsigned int const loIndex = runLength % 64 * 2 + color;
+    unsigned int const hiIndex = runLength / 64 * 2 + color;
+    unsigned int const loLength = g3ttable_table[loIndex].length;
+    unsigned int const hiLength = g3ttable_mtable[hiIndex].length;
+
+    struct BitString combinedCode;
+
+    combinedCode.intBuffer = g3ttable_mtable[hiIndex].code << loLength |
+                             g3ttable_table[loIndex].code;
+    combinedCode.bitCount  = hiLength + loLength;
+
+    putbits(outP, combinedCode);
 }
 
 
 
-static __inline__ void
-putspan_normal(bit          const color, 
-               unsigned int const len) {
+static  void
+putcodeExtra(struct OutStream * const outP,
+             int                const color,
+             int                const runLength) {
+/*----------------------------------------------------------------------------
+   Lengths over 2560.  This is rare.
+   According to the standard, the mark-up code for 2560 can be issued as
+   many times as necessary without terminal codes.
+   --------------------------------------------------------------------------*/
+    G3TableEntry const markUp2560 = g3ttable_mtable[2560/64*2];
+                              /* Same code for black and white */
 
-    if (len < TC_MC)
-        putcode(color, len);
-    else if (len < 2624)
-        putcode2(color, len);
-    else {  /* len >= 2624 : rare */
-        unsigned int remainingLen;
+    unsigned int remainingLen;
 
-        for (remainingLen = len;
-             remainingLen >= 2624;
-             remainingLen -= 2623) {
+    for (remainingLen = runLength; remainingLen > 2560; remainingLen -= 2560)
+      putbits(outP, tableEntryToBitString(markUp2560));
+    /* after the above: 0 < remainingLen <= 2560 */
 
-            putcode2(color, 2560+63);
-            putcode(!color, 0);
-        }
-        if (remainingLen < TC_MC)
-            putcode(color, remainingLen);
-        else  /* TC_MC <= len < 2624 */
-            putcode2(color, remainingLen);
-    }
+    if (remainingLen >= 64)
+        putcodeLong(outP, color, remainingLen);
+    else
+        putcodeShort(outP, color, remainingLen);
 }
 
 
 
-static __inline__ void
-putspan(bit          const color, 
-        unsigned int const len) {
-/*----------------------------------------------------------------------------
-   Put a span of 'len' pixels of color 'color' in the output.
------------------------------------------------------------------------------*/
-    if (pbmtorl) {
-        if (len > 0) 
-            printf("%c %d\n", color == PBM_WHITE ? 'W' : 'B', len);
-    } else 
-        putspan_normal(color, len);
+static void
+putspan(struct OutStream * const outP,
+        bit                const color,
+        unsigned int       const runLength) {
+
+    if (runLength < 64)
+        putcodeShort(outP, color, runLength);
+    else if (runLength < 2560)
+        putcodeLong (outP, color, runLength);
+    else  /* runLength > 2560 : rare */
+        putcodeExtra(outP, color, runLength);
 }
 
 
 
 static void
-puteol(void) {
+puteol(struct OutStream * const outP) {
+
+    switch (outP->eolAlign) {
+    case EOL: {
+        struct BitString const eol = { 1, 12 };
+
+        putbits(outP, eol);
+    } break;
+    case ALIGN8:  case ALIGN16: {
+        unsigned int const bitCount = outP->buffer.bitCount;
+        unsigned int const fillbits =
+            (outP->eolAlign == ALIGN8) ? (44 - bitCount) % 8
+            : (52 - bitCount) % 16;
+
+        struct BitString eol;
+
+        eol.bitCount = 12 + fillbits;     eol.intBuffer = 1;
+        putbits(outP, eol);
+    } break;
+    case NO_EOL: case NO_EOLRTC:
+        break;
+    case NO_RTC:
+        pm_error("INTERNAL ERROR: no-RTC EOL treatment not implemented");
+        break;
+    }
+}
 
-    if (pbmtorl)
-        puts("EOL");
-    else {
-        struct bitString const eol = {12, 1};
 
-        putbits(eol);
+
+static void
+putrtc(struct OutStream * const outP) {
+
+    switch (outP->eolAlign) {
+    case NO_RTC: case NO_EOLRTC:
+        break;
+    default:
+        puteol(outP);    puteol(outP);    puteol(outP);
+        puteol(outP);    puteol(outP);    puteol(outP);
     }
 }
 
 
 
-/*
-  PBM raw bitrow to inflection point array
+static void
+readOffSideMargins(unsigned char * const bitrow,
+                   unsigned int    const colChars,
+                   unsigned int  * const firstNonWhiteCharP,
+                   unsigned int  * const lastNonWhiteCharP,
+                   bool          * const blankRowP) {
+/*----------------------------------------------------------------------------
+  Determine the white margins on the left and right side of a row.
+  This is an enhancement: convertRowToG3() works without this.
+-----------------------------------------------------------------------------*/
+    unsigned int charCnt;
+    unsigned int firstChar;
+    unsigned int lastChar;
+    bool         blankRow;
 
-  Write inflection (=color change) points into array milepost[].  
-  It is easy to calculate run length from this.
+    assert(colChars > 0);
 
-  In milepost, a white-to-black (black-to-white) inflection point
-  always has an even (odd) index.  A line starting with black is
-  indicated by bitrow[0] == 0.
+    for (charCnt = 0; charCnt < colChars && bitrow[charCnt] == 0; ++charCnt);
 
-  WWWWWWWBBWWWWWWW ... = 7,2,7, ...
-  BBBBBWBBBBBWBBBB ... = 0,5,1,5,1,4, ...
+    if (charCnt >= colChars) {
+        /* Reached end of bitrow with no black pixels encountered */
+        firstChar = lastChar = 0;
+        blankRow  = true;
+    } else {
+        /* There is at least one black pixel in the row */
+        firstChar = charCnt;
+        blankRow = false;
 
-  Return the number of milepost elements written.
-  Note that max number of entries into milepost = cols+1 .
+        charCnt = colChars - 1;
 
-  The inflection points are calculated like this:
+        while (bitrow[charCnt--] == 0x00)
+            ;
+        lastChar = charCnt + 1;
+    }
 
-   r1: 00000000000111111110011111000000
-   r2: c0000000000011111111001111100000 0->carry
-  xor: ?0000000000100000001010000100000
+    *firstNonWhiteCharP = firstChar;
+    *lastNonWhiteCharP  = lastChar;
+    *blankRowP          = blankRow;
+}
 
-  The 1 bits in the xor above are the inflection points.
-*/
 
-static __inline__ void
-convertRowToRunLengths(unsigned char * const bitrow, 
-                       int             const cols, 
-                       unsigned int *  const milepost,
-                       unsigned int *  const lengthP) {
-
-    unsigned int   const bitsPerWord  = sizeof(wordint) * 8;
-    wordint      * const bitrowByWord = (wordint *) bitrow;
-    int            const wordCount    = (cols + bitsPerWord - 1)/bitsPerWord; 
-        /* Number of full and partial words in the row */
-
-
-    if (cols % bitsPerWord != 0) {
-        /* Clean final word in row.  For loop simplicity */
-        wordint r1;
-        r1 = bytesToWordint((unsigned char *)&bitrowByWord[wordCount - 1]);
-        r1 >>= bitsPerWord - cols % bitsPerWord;
-        r1 <<= bitsPerWord - cols % bitsPerWord;
-        wordintToBytes((wordintBytes *)&bitrowByWord[wordCount - 1], r1);
+
+static void
+setBlockBitsInFinalChar(unsigned char * const finalByteP,
+                        unsigned int    const cols) {
+/*----------------------------------------------------------------------------
+   If the char in the row is fractional, set it up so that the don't care
+   bits are the opposite color of the last valid pixel.
+----------------------------------------------------------------------------*/
+    unsigned char const finalByte  = *finalByteP;
+    unsigned int const silentBitCnt = 8 - cols % 8;
+    bit const rowEndColor = (finalByte >> silentBitCnt) & 0x01;
+
+    if (rowEndColor == PBM_WHITE) {
+        unsigned char const blackMask = (0x01 << silentBitCnt) - 1;
+
+        *finalByteP = finalByte | blackMask;
     }
-    {
-
-        wordint carry;
-        wordint r1, r2;
-        unsigned int n;
-        int i,c,k;
-
-        for (i = carry = n = 0; i < wordCount; ++i) {
-            r1 = r2 = bytesToWordint((unsigned char *)&bitrowByWord[i]);
-            r2 = r1 ^ (carry << (bitsPerWord-1) | r2 >> 1);
-            carry = r1 & 0x1;  k = 0;
-            while (r2 != 0) {
-                /* wordintClz(r2) reports most significant "1" bit of r2
-                   counting from MSB = position 0.
-                */
-                c = wordintClz(r2);
-                milepost[n++] = i * bitsPerWord + k + c;
-                r2 <<= c++; r2 <<= 1;  k += c; 
-            } 
+    /* No adjustment required if the row ends with a black pixel.
+       pbm_cleanrowend_packed() takes care of this.
+    */
+}
+
+
+
+static void
+trimFinalChar(struct OutStream * const outP,
+              bit                const color,
+              int                const carryLength,
+              int                const existingCols,
+              int                const desiredWidth) {
+/*---------------------------------------------------------------------------
+   If the carry value from the last char in the row represents a valid
+   sequence, output it.
+
+   (1) If input row width is not a whole multiple of 8 and -nofixwidth
+       was specified, the final carry value represents inactive bits
+       at the row end.  Emit no code.  See setBlockBitsInFinalChar().
+
+   (2) If there is white margin on the right side, the final carry value
+       is valid.  We add to it the margin width.  Right-side margin may
+       be added in main() to a narrow input image, detected in the
+       input row by readOffSideMargins() or both.  The same treatment
+       applies regardless of the nature of the right-side margin.
+----------------------------------------------------------------------------*/
+    if (existingCols == desiredWidth) {
+        if (existingCols % 8 == 0)
+            putspan(outP, color, carryLength);  /* Code up to byte boundary */
+        /* Emit nothing if existingCols is not a whole multiple of 8 */
+    } else if (existingCols < desiredWidth) {
+        if (color == 0) {       /* Last bit sequence in final char: white */
+            unsigned int const totalLength =
+                carryLength + (desiredWidth - existingCols);
+            putspan(outP, 0, totalLength);
+        } else {                 /* Black */
+            unsigned int const padLength = desiredWidth - existingCols;
+            putspan(outP, 1, carryLength);
+            putspan(outP, 0, padLength);
         }
-        if (n == 0 || milepost[n - 1] != cols) 
-            milepost[n++] = cols;
-        *lengthP = n;
     }
 }
 
 
 
 static void
-padToDesiredWidth(unsigned int * const milepost,
-                  unsigned int * const nRunP,
-                  int            const existingCols,
-                  int            const desiredCols) {
-
-    if (existingCols < desiredCols) {
-        /* adjustment for narrow input in fixed width mode
-           nRun % 2 == 1 (0) means last (=rightmost) pixel is white (black)
-           if white, extend the last span to outwidth
-           if black, fill with a white span len (outwidth - readcols)
-        */
-        if (*nRunP % 2 == 0)
-            ++*nRunP;
-        milepost[*nRunP - 1] = desiredCols;
+convertRowToG3(struct OutStream * const outP,
+               unsigned char    * const bitrow,
+               unsigned int       const existingCols,
+               unsigned int       const desiredWidth) {
+/*----------------------------------------------------------------------------
+   Table based Huffman coding
+
+   Normally Huffman code encoders count sequences of ones and zeros
+   and convert them to binary codes as they terminate.  This program
+   recognizes chains of pixels and converts them directly, reading
+   prefabricated code chains from an indexed table.
+
+   For example the 8-bit sequence 01100110 translates to
+   Huffman code: 000111 11 0111 11 000111.
+
+   In reality things are more complicated.  The leftmost 0 (MSB) may be
+   part of a longer sequence starting in the adjacent byte or perhaps
+   spanning several bytes.  Likewise for the rightmost 0.
+
+   So we first remove the sequence on the left side and compare its
+   color with the leftmost pixel of the adjacent byte and emit either
+   one code for a single sequence if they agree or two if they disagree.
+   Next the composite code for the central part (in the above example
+   110011 -> 11 0111 11) is emitted.  Finally we save the length and
+   color of the sequence on the right end as carry-over for the next
+   byte cycle.  Some 8-bit input sequences (00000000, 01111111,
+   00111111, etc.) have no central part: these are special cases.
+---------------------------------------------------------------------------*/
+    unsigned int const colChars = pbm_packed_bytes(existingCols);
+
+    unsigned int charCnt;
+    unsigned int firstActiveChar;
+    unsigned int lastActiveChar;
+    bool         blankRow;
+    bit          borderColor;
+
+    borderColor = PBM_WHITE; /* initial value */
+
+    if (existingCols == desiredWidth && (desiredWidth % 8) > 0)
+        setBlockBitsInFinalChar(&bitrow[colChars-1], desiredWidth);
+
+    readOffSideMargins(bitrow, colChars,
+                       &firstActiveChar, &lastActiveChar, &blankRow);
+
+    if (blankRow)
+        putspan(outP, PBM_WHITE, desiredWidth);
+    else {
+        unsigned int carryLength;
+
+        for (charCnt = firstActiveChar, carryLength = firstActiveChar * 8;
+             charCnt <=lastActiveChar;
+             ++charCnt) {
+
+            unsigned char const byte = bitrow[charCnt];
+            bit const rColor = !borderColor;
+
+            if (byte == borderColor * 0xFF) {
+                carryLength += 8;
+            } else if (byte == (unsigned char) ~(borderColor * 0xFF)) {
+                putspan(outP, borderColor, carryLength);
+                carryLength = 8;
+                borderColor = rColor;
+            } else {
+                struct PrefabCode const code = g3prefab_code[byte];
+                unsigned int const activeLength =
+                    8 - code.leadBits - code.trailBits;
+
+                if (borderColor == (byte >> 7)) {
+                    putspan(outP, borderColor, carryLength + code.leadBits);
+                } else {
+                    putspan(outP, borderColor, carryLength);
+                    putcodeShort(outP, rColor, code.leadBits);
+                }
+                if (activeLength > 0)
+                    putbits(outP, code.activeBits);
+
+                borderColor = byte & 0x01;
+                carryLength = code.trailBits;
+            }
+        }
+        trimFinalChar(outP, borderColor, carryLength,
+                      (lastActiveChar + 1) * 8, desiredWidth);
     }
+    puteol(outP);
 }
 
 
 
 int
-main(int    argc,
-     char * argv[]) {
+main(int          argc,
+     const char * argv[]) {
 
     struct CmdlineInfo cmdline;
     FILE * ifP;
     unsigned char * bitrow;
        /* This is the bits of the current row, as read from the input and
-           modified various ways at various points in the program.  It has
-           a word of zero padding on the high (right) end for the convenience
-           of code that accesses this buffer in word-size bites.
+          modified various ways at various points in the program.  It has
+          a word of zero padding on the high (right) end for the convenience
+          of code that accesses this buffer in word-size bites.
         */
 
     int rows;
     int cols;
-    int readcols;
-    int outwidth;
     int format;
-    int row;
-    unsigned int * milepost;
+    unsigned int existingCols;
+    unsigned int desiredWidth;
+    unsigned int row;
+    struct OutStream out;
 
-    pbm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
     ifP = pm_openr(cmdline.inputFileName);
 
     pbm_readpbminit(ifP, &cols, &rows, &format);
-    if (cmdline.nofixedwidth)
-        readcols = outwidth = cols;
+
+    if (cmdline.desiredWidth == 0)
+        desiredWidth = existingCols = cols;
     else {
-        readcols = MIN(cols, 1728);
-        outwidth = 1728;
+        if (cmdline.desiredWidth < cols)
+            existingCols = desiredWidth = cmdline.desiredWidth;
+        else {
+            existingCols = pbm_packed_bytes(cols) * 8;
+            desiredWidth = cmdline.desiredWidth;
+        }
     }
 
-    MALLOCARRAY_NOFAIL(bitrow, pbm_packed_bytes(cols) + sizeof(wordint));
+    MALLOCARRAY(bitrow, pbm_packed_bytes(cols) + sizeof(uint32_t));
 
-    MALLOCARRAY_NOFAIL(milepost, readcols + 2);
+    if (!bitrow)
+        pm_error("Failed to allocate a row buffer for %u columns", cols);
 
-    initOutStream(cmdline.reversebits);
-    puteol();
+    initOutStream(&out, cmdline.reversebits, cmdline.align);
 
-    for (row = 0; row < rows; ++row) {
-        unsigned int nRun;  /* Number of runs in milepost[] */
-        unsigned int p;
-        unsigned int i;
+    puteol(&out);
 
+    for (row = 0; row < rows; ++row) {
         pbm_readpbmrow_packed(ifP, bitrow, cols, format);
-
-        convertRowToRunLengths(bitrow, readcols, milepost, &nRun);
-
-        padToDesiredWidth(milepost, &nRun, readcols, outwidth);
-
-        for (i = p = 0; i < nRun; p = milepost[i++])
-            putspan(i%2 == 0 ? PBM_WHITE : PBM_BLACK, milepost[i] - p);
-        /* TODO 2-dimensional coding MR, MMR */
-        puteol();
+        pbm_cleanrowend_packed(bitrow, cols);
+        convertRowToG3(&out, bitrow, existingCols, desiredWidth);
     }
 
-    free(milepost);
     pbm_freerow_packed(bitrow);
-
-    {
-        unsigned int i;  
-        for( i = 0; i < 6; ++i)
-            puteol();
-    }
-    if (out.buffer.bitCount > 0) {
-        /* flush final partial buffer */
-        unsigned int const bytesToWrite = (out.buffer.bitCount+7)/8;
-
-        unsigned char outbytes[sizeof(wordint)];
-        size_t rc;
-        wordintToBytes(&outbytes, 
-                       out.buffer.intBuffer << (sizeof(out.buffer.intBuffer)*8 
-                                                - out.buffer.bitCount));
-        if (out.reverseBits)
-            reversebuffer(outbytes, bytesToWrite);
-        rc = fwrite(outbytes, 1, bytesToWrite, stdout);
-        if (rc != bytesToWrite)
-            pm_error("Output error");
-    }
+    putrtc(&out);
+    flushBuffer(&out);
     pm_close(ifP);
 
     return 0;
 }
+
+
+
diff --git a/converter/pbm/pbmtolj.c b/converter/pbm/pbmtolj.c
index 0cceb4fe..3cd76703 100644
--- a/converter/pbm/pbmtolj.c
+++ b/converter/pbm/pbmtolj.c
@@ -1,14 +1,14 @@
 /* pbmtolj.c - read a portable bitmap and produce a LaserJet bitmap file
-**  
+**
 **  based on pbmtops.c
 **
 **  Michael Haberler HP Vienna mah@hpuviea.uucp
 **                 mcvax!tuvie!mah
-**  misfeatures: 
+**  misfeatures:
 **      no positioning
 **
 **      Bug fix Dec 12, 1988 :
-**              lines in putbit() reshuffled 
+**              lines in putbit() reshuffled
 **              now runs OK on HP-UX 6.0 with X10R4 and HP Laserjet II
 **      Bo Thide', Swedish Institute of Space Physics, Uppsala <bt@irfu.se>
 **
@@ -75,7 +75,7 @@ parseCommandLine(int argc, char ** argv,
     MALLOCARRAY(option_def, 100);
 
     option_def_index = 0;   /* incremented by OPTENTRY */
-    OPTENT3(0,   "resolution",  OPT_UINT, &cmdlineP->dpi, 
+    OPTENT3(0,   "resolution",  OPT_UINT, &cmdlineP->dpi,
             &dpiSpec, 0);
     OPTENT3(0,   "copies",      OPT_UINT, &cmdlineP->copies,
             &copiesSpec, 0);
@@ -97,7 +97,7 @@ parseCommandLine(int argc, char ** argv,
     pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
-    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 "
@@ -246,7 +246,7 @@ packbits(void) {
           We drop out here after having found a [possibly empty]
           literal, followed by a [possibly degenerate] run of repeated
           bytes.  Degenerate runs can occur at the end of the scan line...
-          there may be a "repeat" of 1 byte (which can't actually be 
+          there may be a "repeat" of 1 byte (which can't actually be
           represented as a repeat) so we simply fold it into the previous
           literal.
         */
@@ -320,11 +320,11 @@ deltarow(void) {
             skip = 1;
         }
         if (mustBurst) {
-            burstCode = burstEnd - burstStart; 
+            burstCode = burstEnd - burstStart;
                 /* 0-7, means 1-8 bytes follow */
             code = (burstCode << 5) | skipped;
             deltaBuffer[deltaBufferIndex++] = (char) code;
-            memcpy(deltaBuffer+deltaBufferIndex, rowBuffer+burstStart, 
+            memcpy(deltaBuffer+deltaBufferIndex, rowBuffer+burstStart,
                    burstCode + 1);
             deltaBufferIndex += burstCode + 1;
             burstStart = -1;
@@ -369,7 +369,7 @@ convertRow(const bit *    const bitrow,
            bool *         const rowIsBlankP) {
 
     unsigned int rightmostBlackCol;
-        
+
     findRightmostBlackCol(bitrow, cols, rowIsBlankP, &rightmostBlackCol);
 
     if (!*rowIsBlankP) {
@@ -377,7 +377,7 @@ convertRow(const bit *    const bitrow,
             /* Number of columns excluding white right margin */
         unsigned int const rucols = ((nzcol + 7) / 8) * 8;
             /* 'nzcol' rounded up to nearest multiple of 8 */
-        
+
         unsigned int col;
 
         memset(rowBuffer, 0, rowBufferSize);
@@ -404,7 +404,7 @@ convertRow(const bit *    const bitrow,
 
         if (delta) {
             /* May need to temporarily bump the row buffer index up to
-               whatever the previous line's was - if this line is shorter 
+               whatever the previous line's was - if this line is shorter
                than the previous would otherwise leave dangling cruft.
             */
             unsigned int const savedRowBufferIndex = rowBufferIndex;
@@ -421,7 +421,7 @@ convertRow(const bit *    const bitrow,
 }
 
 
-    
+
 static void
 printBlankRows(unsigned int const count) {
 
@@ -430,12 +430,12 @@ printBlankRows(unsigned int const count) {
         /* The code used to be this, but Charles Howes reports that
            this escape sequence does not exist on his HP Laserjet IIP
            plus, so we use the following less elegant code instead.
-           
-           printf("\033*b%dY", (*blankRowsP)); 
+
+           printf("\033*b%dY", (*blankRowsP));
         */
-        for (x = 0; x < count; ++x) 
+        for (x = 0; x < count; ++x)
             printf("\033*b0W");
-        
+
         memset(prevRowBuffer, 0, rowBufferSize);
     }
 }
@@ -494,7 +494,7 @@ printRow(void) {
 
 
 static void
-doPage(FILE *             const ifP, 
+doPage(FILE *             const ifP,
        struct cmdlineInfo const cmdline) {
 
     bit * bitrow;
@@ -525,10 +525,10 @@ doPage(FILE *             const ifP,
         else {
             printBlankRows(blankRows);
             blankRows = 0;
-            
+
             printRow();
         }
-    }    
+    }
     printBlankRows(blankRows);
     blankRows = 0;
 
@@ -563,3 +563,6 @@ main(int argc, char * argv[]) {
 
     return 0;
 }
+
+
+
diff --git a/converter/pbm/pbmtoln03.c b/converter/pbm/pbmtoln03.c
index f7cf53c7..235429cd 100644
--- a/converter/pbm/pbmtoln03.c
+++ b/converter/pbm/pbmtoln03.c
@@ -40,116 +40,130 @@ work.
  */
 
 #include <stdio.h>
-#include "pbm.h"
-
-FILE *input ;
-
-#ifndef print
-#define print(s)        fputs (s, stdout)
-#define fprint(f, s)    fputs (s, f)
-#endif
 
+#include "mallocvar.h"
+#include "pbm.h"
 
-static void 
-output_sixel_record (unsigned char * record, int width) {
 
-   int i, j, k ;
-   unsigned char last_char ;
-   int start_repeat = 0 ;
-   int repeated ;
-   char repeated_str[16] ;
-   char *p ;
 
-   /* Do RLE */
-   last_char = record[0] ;
-   j = 0 ;
+static void
+outputSixelRecord(unsigned char * const record,
+                  unsigned int    const width) {
 
-   /* This will make the following loop complete */
-   record[width] = '\0' ;
+    unsigned int i, j;
+    unsigned char lastChar;
+    int startRepeat;
+    char repeatedStr[16];
 
-   for (i = 1 ; i <= width ; i++) {
+    /* Do RLE */
+    lastChar = record[0];
+    j = 0;
 
-      repeated = i - start_repeat ;
+    /* This will make the following loop complete */
+    record[width] = '\0' ;
 
-      if (record[i] != last_char || repeated >= 32766) {
+    for (i = 1, startRepeat = 0; i <= width; ++i) {
+        unsigned int const repeated = i - startRepeat;
 
-         /* Repeat has ended */
+        if (record[i] != lastChar || repeated >= 32766) {
 
-         if (repeated > 3) {
+            /* Repeat has ended */
 
-            /* Do an encoding */
-            record[j++] = '!' ;
-            sprintf (repeated_str, "%d", i - start_repeat) ;
-            for (p = repeated_str ; *p ; p++)
-               record[j++] = *p ;
-               record[j++] = last_char ; }
+            if (repeated > 3) {
+                /* Do an encoding */
+                char * p;
+                record[j++] = '!' ;
+                sprintf(repeatedStr, "%u", i - startRepeat);
+                for (p = repeatedStr; *p; ++p)
+                    record[j++] = *p;
+                record[j++] = lastChar;
+            } else {
+                unsigned int k;
 
-         else {
-            for (k = 0 ; k < repeated ; k++)
-               record[j++] = last_char ; }
+                for (k = 0; k < repeated; ++k)
+                    record[j++] = lastChar;
+            }
 
-         start_repeat = i ;
-         last_char = record[i] ; }
-      }
+            startRepeat = i ;
+            lastChar = record[i];
+        }
+    }
 
-   fwrite ((char *) record, j, 1, stdout) ;
-   putchar ('-') ;      /* DECGNL (graphics new-line) */
-   putchar ('\n') ;
-   }
+    fwrite((char *) record, j, 1, stdout) ;
+    putchar('-') ;      /* DECGNL (graphics new-line) */
+    putchar('\n') ;
+}
 
 
-static void 
-convert (int width, int height, int format) {
-
-   int i ;
-   unsigned char *sixel ;               /* A row of sixels */
-   int sixel_row ;
-
-   bit *row = pbm_allocrow (width) ;
-
-   sixel = (unsigned char *) malloc (width + 2) ;
-
-   sixel_row = 0 ;
-   while (height--) {
-      pbm_readpbmrow (input, row, width, format) ;
-      switch (sixel_row) {
-         case 0 :
-           for (i = 0 ; i < width ; i++)
-              sixel[i] = row[i] ;
-           break ;
-         case 1 :
-           for (i = 0 ; i < width ; i++)
-              sixel[i] += row[i] << 1 ;
-           break ;
-         case 2 :
-           for (i = 0 ; i < width ; i++)
-              sixel[i] += row[i] << 2 ;
-           break ;
-         case 3 :
-           for (i = 0 ; i < width ; i++)
-              sixel[i] += row[i] << 3 ;
-           break ;
-         case 4 :
-           for (i = 0 ; i < width ; i++)
-              sixel[i] += row[i] << 4 ;
-           break ;
-         case 5 :
-           for (i = 0 ; i < width ; i++)
-              sixel[i] += (row[i] << 5) + 077 ;
-           output_sixel_record (sixel, width) ;
-           break ; }
-      if (sixel_row == 5)
-         sixel_row = 0 ;
-      else
-         sixel_row++ ;
-      }
 
-   if (sixel_row > 0) {
-      /* Incomplete sixel record needs to be output */
-      for (i = 0 ; i < width ; i++)
-         sixel[i] += 077 ;
-      output_sixel_record (sixel, width) ; }
-   }
+static void
+convert(FILE *       const ifP,
+        unsigned int const width,
+        unsigned int const height,
+        unsigned int const format) {
+
+    unsigned char * sixel;  /* A row of sixels */
+    unsigned int sixelRow;
+    bit * bitrow;
+    unsigned int remainingHeight;
+
+    bitrow = pbm_allocrow(width);
+
+    MALLOCARRAY(sixel, width + 2);
+    if (!sixel)
+        pm_error("Unable to allocation %u bytes for a row buffer", width + 2);
+
+    for (remainingHeight = height, sixelRow = 0;
+         remainingHeight > 0;
+         --remainingHeight) {
+
+        unsigned int i;
+
+        pbm_readpbmrow(ifP, bitrow, width, format);
+
+        switch (sixelRow) {
+        case 0 :
+            for (i = 0; i < width; ++i)
+                sixel[i] = bitrow[i] ;
+            break ;
+        case 1 :
+            for (i = 0; i < width; ++i)
+                sixel[i] += bitrow[i] << 1;
+            break ;
+        case 2 :
+            for (i = 0; i < width; ++i)
+                sixel[i] += bitrow[i] << 2;
+            break ;
+        case 3 :
+            for (i = 0; i < width; ++i)
+                sixel[i] += bitrow[i] << 3;
+            break ;
+        case 4 :
+            for (i = 0; i < width; ++i)
+                sixel[i] += bitrow[i] << 4;
+            break ;
+        case 5 :
+            for (i = 0; i < width; ++i)
+                sixel[i] += (bitrow[i] << 5) + 077;
+            outputSixelRecord(sixel, width);
+            break ; }
+        if (sixelRow == 5)
+            sixelRow = 0;
+        else
+            ++sixelRow;
+    }
+
+    if (sixelRow > 0) {
+        /* Incomplete sixel record needs to be output */
+        unsigned int i;
+        for (i = 0; i < width; ++i)
+            sixel[i] += 077;
+        outputSixelRecord(sixel, width);
+    }
+
+    pbm_freerow(bitrow);
+    free(sixel);
+}
 
 
 
@@ -169,6 +183,7 @@ main (int argc, char **argv) {
    const char *opt_bottom_margin = "3400";
    const char *opt_form_length = opt_bottom_margin;
 
+   FILE * ifP;
    int width, height, format ;
 
    pbm_init (&argc_copy, argv_copy) ;
@@ -210,16 +225,16 @@ main (int argc, char **argv) {
    }
 
    if( argn < argc ) {
-      input = pm_openr( argv[argn] );
+      ifP = pm_openr( argv[argn] );
       argn++;
    }
    else
-      input = stdin;
+      ifP = stdin;
 
    if( argn != argc )
       pm_usage(usage);
 
-   pbm_readpbminit (input, &width, &height, &format) ;
+   pbm_readpbminit (ifP, &width, &height, &format) ;
 
 /*
  * In explanation of the sequence below:
@@ -241,10 +256,10 @@ main (int argc, char **argv) {
       opt_form_length);
 
    /* Convert data */
-   convert (width, height, format) ;
+   convert (ifP, width, height, format) ;
 
    /* Terminate sixel data */
-   print ("\033\\\n") ;
+   puts ("\033\\") ;
 
    /* If the program failed, it previously aborted with nonzero completion
       code, via various function calls.
diff --git a/converter/pbm/pbmtomacp.c b/converter/pbm/pbmtomacp.c
index df5cbb0c..e02f5559 100644
--- a/converter/pbm/pbmtomacp.c
+++ b/converter/pbm/pbmtomacp.c
@@ -177,7 +177,7 @@ calculateCropPad(struct CmdlineInfo         const cmdline,
             pm_message("Specified -bottom value %u is beyond edge of "
                        "input image", cmdline.bottom);
 
-            bottom = MIN3(cmdline.bottom, rows - 1, top + MACP_ROWS - 1);
+        bottom = MIN3(cmdline.bottom, rows - 1, top + MACP_ROWS - 1);
     } else
         bottom = MIN(rows - 1, top + MACP_ROWS - 1);
 
@@ -234,7 +234,7 @@ writeMacpRowUnpacked(const bit  * const imageBits,
     char const marginByte = 0x00;  /* White bits for margin */
     unsigned int const rightMarginCharCt =
         MACP_COLCHARS - leftMarginCharCt - imageColCharCt;
-    
+
     unsigned int i;
 
     fputc(MACP_COLCHARS - 1, ofP);
@@ -361,7 +361,7 @@ encodeRowsWithShift(bit *                    const bitrow,
 
     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;
diff --git a/converter/pbm/pbmtomgr.c b/converter/pbm/pbmtomgr.c
index e8e30148..0e529740 100644
--- a/converter/pbm/pbmtomgr.c
+++ b/converter/pbm/pbmtomgr.c
@@ -40,7 +40,7 @@ putinit(unsigned int const rows,
 
 int
 main(int argc,
-     char * argv[]) {
+     const char * argv[]) {
 
     FILE * ifP;
     unsigned char * bitrow;
@@ -50,15 +50,11 @@ main(int argc,
     unsigned int row;
     unsigned int bytesPerRow;
         /* Number of packed bytes (8 columns per byte) in a row. */
-    unsigned int padright;
-        /* Number of columns added to the right of each row to get up to
-           a multiple of 8, i.e. an integral number of packed bytes.
-        */
     const char * inputFileName;
     unsigned int const maxDimension = 4095;
         /* Dimensions are 2 characters of the header -- 12 bits */
 
-    pbm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     if (argc-1 > 1)
         pm_error("Too many arguments (%u).  "
@@ -78,7 +74,6 @@ main(int argc,
     
     bitrow = pbm_allocrow_packed(cols);
     bytesPerRow = pbm_packed_bytes(cols);
-    padright = bytesPerRow * 8 - cols;
 
     putinit(rows, cols);
     
diff --git a/converter/pbm/pbmtonokia.c b/converter/pbm/pbmtonokia.c
index bf3b9e41..73b62b96 100644
--- a/converter/pbm/pbmtonokia.c
+++ b/converter/pbm/pbmtonokia.c
@@ -4,6 +4,7 @@
    Copyright information is at end of file.
 */
 
+#define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE    /* Make sure strcaseeq() is in nstring.h */
 #include <string.h>
@@ -321,10 +322,8 @@ convertToNol(bit **       const image,
     /* image */
     for (row = 0; row < rows; ++row) {
         unsigned int col;
-        unsigned int p;
-        unsigned int c;
 
-        for (p = 0, c = 0, col = 0; col < cols; ++col) {
+        for (col = 0; col < cols; ++col) {
             char const output = image[row][col] == PBM_BLACK ? '1' : '0';
 
             putc(output, ofP);
@@ -374,10 +373,8 @@ convertToNgg(bit **       const image,
 
     for (row = 0; row < rows; ++row) {
         unsigned int col;
-        unsigned int p;
-        unsigned int c;
 
-        for (p = 0, c = 0, col = 0; col < cols; ++col) {
+        for (col = 0; col < cols; ++col) {
             char const output = image[row][col] == PBM_BLACK ? '1' : '0';
 
             putc(output, ofP);
diff --git a/converter/pbm/pbmtopk.c b/converter/pbm/pbmtopk.c
index 3948ae0d..2f05d449 100644
--- a/converter/pbm/pbmtopk.c
+++ b/converter/pbm/pbmtopk.c
@@ -9,6 +9,7 @@
   https://www.tug.org/TUGboat/tb06-1/tb11gf.pdf
 */
 
+#define _DEFAULT_SOURCE 1  /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
diff --git a/converter/pbm/pbmtoppa/pbm.c b/converter/pbm/pbmtoppa/pbm.c
index 2f8a42b1..ae36e0d2 100644
--- a/converter/pbm/pbmtoppa/pbm.c
+++ b/converter/pbm/pbmtoppa/pbm.c
@@ -11,126 +11,185 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <limits.h>
 
+#include "pm.h"
+#include "nstring.h"
 #include "ppapbm.h"
 
-int make_pbm_stat(pbm_stat* pbm,FILE* fptr)
-{
-  char line[1024];
-
-  pbm->fptr=fptr;
-  pbm->version=none;
-  pbm->current_line=0;
-  pbm->unread = 0;
+int
+make_pbm_stat(pbm_stat * const pbmStatP,
+              FILE *     const ifP) {
+
+    char line[1024];
+    char * rc;
+    int retval;
+
+    pbmStatP->fptr         = ifP;
+    pbmStatP->version      = none;
+    pbmStatP->current_line = 0;
+    pbmStatP->unread       = 0;
+
+    rc = fgets(line, 1024, ifP);
+    if (rc == NULL)
+        retval = 0;
+    else {
+        line[strlen(line)-1] = 0;
+
+        if (streq(line,"P1"))
+            pbmStatP->version=P1;
+        if (streq(line,"P4"))
+            pbmStatP->version=P4;
+
+        if (pbmStatP->version == none) {
+            pm_message("unknown PBM magic '%s'", line);
+            retval = 0;
+        } else {
+            do {
+                char * rc;
+                rc = fgets(line, 1024, ifP);
+                if (rc == NULL)
+                    return 0;
+            } while (line[0] == '#');
+            {
+                int rc;
+                rc = sscanf(line, "%d %d",
+                            &pbmStatP->width, &pbmStatP->height);
+                if (rc != 2)
+                    retval = 0;
+                else {
+                    if (pbmStatP->width < 0) {
+                        pm_message("Image has negative width");
+                        retval = 0;
+                    } else if (pbmStatP->width > INT_MAX/2) {
+                        pm_message("Uncomputeably large width: %d",
+                                   pbmStatP->width);
+                        retval = 0;
+                    } else if (pbmStatP->height < 0) {
+                        pm_message("Image has negative height");
+                        retval = 0;
+                    } else if (pbmStatP->height > INT_MAX/2) {
+                        pm_message("Uncomputeably large height: %d",
+                                   pbmStatP->height);
+                        retval = 0;
+                    } else
+                        retval = 1;
+                }
+            }
+        }
+    }
+    return retval;
+}
 
-  if (fgets (line, 1024, fptr) == NULL)
-    return 0;
-  line[strlen(line)-1] = 0;
 
-  if(!strcmp(line,"P1")) pbm->version=P1;
-  if(!strcmp(line,"P4")) pbm->version=P4;
-  if(pbm->version == none)
-  {
-    fprintf(stderr,"pbm_readheader(): unknown PBM magic '%s'\n",line);
-    return 0;
-  }
 
-  do
-    if (fgets (line, 1024, fptr) == NULL)
-      return 0;
-  while (line[0] == '#');
+static int
+getbytes(FILE *          const ifP,
+         unsigned int    const width,
+         unsigned char * const data) {
+
+    unsigned char mask;
+    unsigned char acc;
+    unsigned char * place;
+    unsigned int num;
+    int retval;
+
+    if (width == 0)
+        retval = 0;
+    else {
+        for (mask = 0x80, acc = 0, num = 0, place = data; num < width; ) {
+            switch (getc(ifP)) {
+            case EOF:
+                return 0;
+            case '1':
+                acc |= mask;
+                /* fall through */
+            case '0':
+                mask >>= 1;
+                ++num;
+                if (mask == 0x00) { /* if (num % 8 == 0) */
+                    *place++ = acc;
+                    acc = 0;
+                    mask = 0x80;
+                }
+            }
+        }
+        if (width % 8 != 0)
+            *place = acc;
+
+        retval = 1;
+    }
+    return retval;
+}
 
-  if (2 != sscanf (line, "%d %d", &pbm->width, &pbm->height))
-    return 0;
 
-  return 1;
-}
 
-static int getbytes(FILE *fptr,int width,unsigned char* data)
-{
-  unsigned char mask,acc,*place;
-  int num;
-
-  if(!width) return 0;
-  for(mask=0x80, acc=0, num=0, place=data; num<width; )
-  {
-    switch(getc(fptr))
-    {
-    case EOF:      
-      return 0;
-    case '1':
-      acc|=mask;
-      /* fall through */
-    case '0':
-      mask>>=1;
-      num++;
-      if(!mask) /* if(num%8 == 0) */
-      {
-	*place++ = acc;
-	acc=0;
-	mask=0x80;
-      }
+int
+pbm_readline(pbm_stat *      const pbmStatP,
+             unsigned char * const data) {
+/*----------------------------------------------------------------------------
+  Read a single line into data which must be at least (pbmStatP->width+7)/8
+  bytes of storage.
+-----------------------------------------------------------------------------*/
+    int retval;
+
+    if (pbmStatP->current_line >= pbmStatP->height)
+        retval = 0;
+    else {
+        if (pbmStatP->unread) {
+            memcpy(data, pbmStatP->revdata, (pbmStatP->width+7)/8);
+            ++pbmStatP->current_line;
+            pbmStatP->unread = 0;
+            free(pbmStatP->revdata);
+            pbmStatP->revdata = NULL;
+            retval = 1;
+        } else {
+            switch (pbmStatP->version) {
+            case P1:
+                if (getbytes(pbmStatP->fptr, pbmStatP->width, data)) {
+                    pbmStatP->current_line++;
+                    retval = 1;
+                } else
+                    retval = 0;
+                break;
+            case P4: {
+                int tmp, tmp2;
+                tmp = (pbmStatP->width+7)/8;
+                tmp2 = fread(data,1,tmp,pbmStatP->fptr);
+                if (tmp2 == tmp) {
+                    ++pbmStatP->current_line;
+                    retval = 1;
+                } else {
+                    pm_message("error reading line data (%d)", tmp2);
+                    retval = 0;
+                }
+            } break;
+
+            default:
+                pm_message("unknown PBM version");
+                retval = 0;
+            }
+        }
     }
-  }
-  if(width%8)
-    *place=acc;
-  return 1;
+    return retval;
 }
 
-/* Reads a single line into data which must be at least (pbm->width+7)/8
-   bytes of storage */
-int pbm_readline(pbm_stat* pbm,unsigned char* data)
-{
-  int tmp,tmp2;
-
-  if(pbm->current_line >= pbm->height) return 0;
-
-  if (pbm->unread)
-    {
-      memcpy (data, pbm->revdata, (pbm->width+7)/8);
-      pbm->current_line++;
-      pbm->unread = 0;
-      free (pbm->revdata);
-      pbm->revdata = NULL;
-      return 1;
-    }
 
-  switch(pbm->version)
-  {
-  case P1:
-    if(getbytes(pbm->fptr,pbm->width,data))
-    {
-      pbm->current_line++;
-      return 1;
-    }
-    return 0;
-
-  case P4:
-    tmp=(pbm->width+7)/8;
-    tmp2=fread(data,1,tmp,pbm->fptr);
-    if(tmp2 == tmp)
-    {
-      pbm->current_line++;
-      return 1;
-    }
-    fprintf(stderr,"pbm_readline(): error reading line data (%d)\n",tmp2);
-    return 0;
 
-  default:
-    fprintf(stderr,"pbm_readline(): unknown PBM version\n");
-    return 0;
-  }
-}
+void
+pbm_unreadline(pbm_stat * const pbmStatP,
+               void *     const data) {
+/*----------------------------------------------------------------------------
+  Push a line back into the buffer; we read too much!
+-----------------------------------------------------------------------------*/
+    /* can store only one line in the unread buffer */
 
-/* push a line back into the buffer; we read too much! */
-void pbm_unreadline (pbm_stat *pbm, void *data)
-{
-  /* can only store one line in the unread buffer */
-  if (pbm->unread)
-    return;
-
-  pbm->unread = 1;
-  pbm->revdata = malloc ((pbm->width+7)/8);
-  memcpy (pbm->revdata, data, (pbm->width+7)/8);
-  pbm->current_line--;
+    if (!pbmStatP->unread) {
+        pbmStatP->unread = 1;
+        pbmStatP->revdata = malloc ((pbmStatP->width+7)/8);
+        memcpy(pbmStatP->revdata, data, (pbmStatP->width+7)/8);
+        --pbmStatP->current_line;
+    }
 }
+
+
diff --git a/converter/pbm/pbmtoppa/pbmtoppa.c b/converter/pbm/pbmtoppa/pbmtoppa.c
index f43c08a8..ff4a599e 100644
--- a/converter/pbm/pbmtoppa/pbmtoppa.c
+++ b/converter/pbm/pbmtoppa/pbmtoppa.c
@@ -4,6 +4,7 @@
  * 2-24-98
  */
 
+#define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE
     /* This makes sure strcasecmp() is in string.h */
 #include <stdio.h>
diff --git a/converter/pbm/pbmtox10bm b/converter/pbm/pbmtox10bm
index 9a1a7286..deb3aeab 100644
--- a/converter/pbm/pbmtox10bm
+++ b/converter/pbm/pbmtox10bm
@@ -1,3 +1,27 @@
+#!/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
 
 #============================================================================
@@ -14,6 +38,19 @@ use strict;
 use File::Basename;
 use Cwd 'abs_path';
 
+sub doVersionHack($) {
+    my ($argvR) = @_;
+
+    my $arg1 = $argvR->[0];
+
+    if (defined($arg1) && (($arg1 eq "--version") || ($arg1 eq "-version"))) {
+        my $termStatus = system('pbmtoxbm', '--version');
+        exit($termStatus == 0 ? 0 : 1);
+    }
+}
+
+doVersionHack(\@ARGV);
+
 my $infile;
 
 foreach (@ARGV) {
diff --git a/converter/pbm/pbmtoxbm.c b/converter/pbm/pbmtoxbm.c
index 14c6b85e..ecb72b30 100644
--- a/converter/pbm/pbmtoxbm.c
+++ b/converter/pbm/pbmtoxbm.c
@@ -20,6 +20,7 @@
 
 */
 
+#define _DEFAULT_SOURCE 1  /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
diff --git a/converter/pgm/sbigtopgm.c b/converter/pgm/sbigtopgm.c
index 3c223c47..7833cb62 100644
--- a/converter/pgm/sbigtopgm.c
+++ b/converter/pgm/sbigtopgm.c
@@ -54,7 +54,6 @@ parseCommandLine(int argc, const char ** argv,
 
     MALLOCARRAY_NOFAIL(option_def, 100);
     
-    option_def_index = 0;   /* incremented by OPTENT3 */
     OPTENTINIT;
 
     opt.opt_table     = option_def;
diff --git a/converter/ppm/hpcdtoppm/Makefile b/converter/ppm/hpcdtoppm/Makefile
index ddf79ee5..59ba3630 100644
--- a/converter/ppm/hpcdtoppm/Makefile
+++ b/converter/ppm/hpcdtoppm/Makefile
@@ -14,12 +14,12 @@ MERGE_OBJECTS =
 
 include $(SRCDIR)/common.mk
 
-install: install.bin.local
+install.bin install.merge: install.bin.local
 .PHONY: install.bin.local
 install.bin.local: $(PKGDIR)/bin
 # In June 2002, pcdovtoppm replaced pcdindex
 	cd $(PKGDIR)/bin ; \
-	$(SYMLINK) pcdindex$(EXE) pcdovtoppm$(EXE)
+	$(SYMLINK) pcdovtoppm$(EXE) pcdindex$(EXE) 
 
 
 FORCE:
diff --git a/converter/ppm/hpcdtoppm/hpcdtoppm b/converter/ppm/hpcdtoppm/hpcdtoppm
index cb6d084b..2af4a384 100755
--- a/converter/ppm/hpcdtoppm/hpcdtoppm
+++ b/converter/ppm/hpcdtoppm/hpcdtoppm
@@ -1,24 +1,24 @@
 #!/bin/sh
 
 cat <<EOF 1>&2
-You are running a program named 'hpcdtoppm' that stands in for the
-real program by that name.
+You are running a program named 'hpcdtoppm' that stands in for the real
+program by that name.
 
-The real 'hpcdtoppm' is a program that converts an image from Photo CD
-format to PPM format.  The program you are running now just issues the
-message you are reading.
+The real 'hpcdtoppm' is a program that converts an image from Photo CD format
+to PPM format.  The program you are running now just issues the message you
+are reading.
 
 Please get a copy of Hpcdtoppm from 
 
-  ftp://ibiblio.org/pub/linux/graphics/convert
+  http://terasaur.org/item/show/hpcdtoppm-netpbm-convert-photo-cd/4967
 
 and replace this stand-in program with the real one.
 
-The point of this is that this stand-in 'hpcdtoppm' is distributed
-with Netpbm on Sourceforge.  Hpcdtoppm does not meet the requirements
-to be distributed on Sourceforge because a copyright holder does not
-permit people to sell copies.  In the past the real Hpcdtoppm was
-distributed with Netpbm on Sourceforge by mistake.
+The point of this is that this stand-in 'hpcdtoppm' is distributed with Netpbm
+on Sourceforge.  Hpcdtoppm does not meet the requirements to be distributed on
+Sourceforge because a copyright holder does not permit people to sell copies.
+At one time, the real Hpcdtoppm was distributed with Netpbm on Sourceforge by
+mistake.
 
 EOF
 
diff --git a/converter/ppm/ilbmtoppm.c b/converter/ppm/ilbmtoppm.c
index 280ca939..92d4d6f2 100644
--- a/converter/ppm/ilbmtoppm.c
+++ b/converter/ppm/ilbmtoppm.c
@@ -99,10 +99,9 @@ static bool debug = FALSE;
 
 
 static char *
-ID2string(id)
-    IFF_ID id;
-{
-    static char str[] = "abcd";
+ID2string(IFF_ID const id) {
+
+    static char str[4];
 
     str[0] = (char)(id >> 24 & 0xff);
     str[1] = (char)(id >> 16 & 0xff);
@@ -141,16 +140,15 @@ alloc_cmap(void) {
 
 
 static rawtype *
-alloc_rawrow(cols)
-    int cols;
-{
-    rawtype *r;
-    int i;
+alloc_rawrow(unsigned int const cols) {
+
+    rawtype * r;
+    unsigned int col;
 
     MALLOCARRAY_NOFAIL(r, cols);
 
-    for( i = 0; i < cols; i++ )
-        r[i] = 0;
+    for (col = 0; col < cols; ++col)
+        r[col] = 0;
 
     return r;
 }
@@ -161,17 +159,17 @@ alloc_rawrow(cols)
  ****************************************************************************/
 
 static void
-readerr(f, iffid)
-    FILE *f;
-    IFF_ID iffid;
-{
-    if( ferror(f) )
+readerr(FILE * const fP,
+        IFF_ID const iffId) {
+
+    if (ferror(fP))
         pm_error("read error");
     else
-        pm_error("premature EOF in %s chunk", ID2string(iffid));
+        pm_error("premature EOF in %s chunk", ID2string(iffId));
 }
 
 
+
 static void
 read_bytes(FILE *          const ifP,
            int             const bytes,
@@ -191,21 +189,20 @@ read_bytes(FILE *          const ifP,
 
 
 static unsigned char
-get_byte(ifP, iffid, counter)
-    FILE* ifP;
-    IFF_ID iffid;
-    long *counter;
-{
+get_byte(FILE *          const ifP,
+         IFF_ID          const iffId,
+         unsigned long * const counterP) {
+
     int i;
 
-    if( counter ) {
-        if( *counter == 0 )
-            pm_error("insufficient data in %s chunk", ID2string(iffid));
-        --(*counter);
+    if (counterP) {
+        if (*counterP == 0 )
+            pm_error("insufficient data in %s chunk", ID2string(iffId));
+        --(*counterP);
     }
     i = getc(ifP);
-    if( i == EOF )
-        readerr(ifP, iffid);
+    if (i == EOF)
+        readerr(ifP, iffId);
 
     return (unsigned char) i;
 }
@@ -216,7 +213,7 @@ get_big_long(FILE *          const ifP,
              unsigned long * const counterP) {
 
     long l;
-    
+
     if (counterP) {
         if (*counterP < 4)
             pm_error("insufficient data in %s chunk", ID2string(iffid));
@@ -273,114 +270,117 @@ chunk_end(FILE *        const ifP,
 
 static void
 skip_chunk(FILE *        const ifP,
-           IFF_ID        const iffid,
-           unsigned long const chunksize) {
-    unsigned long remainingChunksize;
+           IFF_ID        const iffId,
+           unsigned long const chunkSize) {
 
-    remainingChunksize = chunksize;  /* initial value */
+    unsigned long remainingChunkSize;
 
-    while (remainingChunksize > 0)
-        get_byte(ifP, iffid, &remainingChunksize);
+    for (remainingChunkSize = chunkSize; remainingChunkSize > 0; )
+        get_byte(ifP, iffId, &remainingChunkSize);
 }
 
 
 
 static void
 display_chunk(FILE *        const ifP,
-              IFF_ID        const iffid,
-              unsigned long const chunksize) {
+              IFF_ID        const iffId,
+              unsigned long const chunkSize) {
 
     int byte;
-    unsigned long remainingChunksize;
+    unsigned long remainingChunkSize;
 
-    pm_message("contents of %s chunk:", ID2string(iffid));
+    pm_message("contents of %s chunk:", ID2string(iffId));
 
-    remainingChunksize = chunksize;  /* initial value */
-    byte = '\0';
+    for (remainingChunkSize = chunkSize, byte = '\0';
+         remainingChunkSize > 0; ) {
 
-    while (remainingChunksize > 0) {
-        byte = get_byte(ifP, iffid, &remainingChunksize);
+        byte = get_byte(ifP, iffId, &remainingChunkSize);
         if (fputc(byte, stderr) == EOF)
             pm_error("write error");
     }
-    if (byte != '\n')
-        if (fputc('\n', stderr) == EOF)
+    if (byte != '\n') {
+        int rc;
+        rc = fputc('\n', stderr);
+        if (rc == EOF)
             pm_error("write error");
+    }
 }
 
 
+
 static void
-read_cmap(FILE *     const ifP,
-          IFF_ID     const iffid,
-          long       const chunksize,
-          ColorMap * const cmap) {
+read_cmap(FILE *        const ifP,
+          IFF_ID        const iffId,
+          unsigned long const chunkSize,
+          ColorMap *    const cmapP) {
 
-    long colors;
+    unsigned long const colorCt = chunkSize / 3;
 
-    colors = chunksize / 3;
-    if( colors == 0 ) {
-        pm_error("warning - empty %s colormap", ID2string(iffid));
-        skip_chunk(ifP, iffid, chunksize);
+    if (colorCt == 0) {
+        pm_error("warning - empty %s colormap", ID2string(iffId));
+        skip_chunk(ifP, iffId, chunkSize);
     } else {
         unsigned int i;
-        if( cmap->color )               /* prefer CMAP-chunk over CMYK-chunk */
-            ppm_freerow(cmap->color);
-        cmap->color = ppm_allocrow(colors);
-        cmap->ncolors = colors;
-        
-        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);
-            PPM_ASSIGN(cmap->color[i], r, g, b);
+        unsigned long remainingChunkSize;
+
+        if (cmapP->color)               /* prefer CMAP-chunk over CMYK-chunk */
+            ppm_freerow(cmapP->color);
+        cmapP->color   = ppm_allocrow(colorCt);
+        cmapP->ncolors = colorCt;
+
+        for (i = 0, remainingChunkSize = chunkSize; i < colorCt; ++i ) {
+            int const r = get_byte(ifP, iffId, &remainingChunkSize);
+            int const g = get_byte(ifP, iffId, &remainingChunkSize);
+            int const b = get_byte(ifP, iffId, &remainingChunkSize);
+            PPM_ASSIGN(cmapP->color[i], r, g, b);
         }
-        chunk_end(ifP, iffid, chunksize);
+        chunk_end(ifP, iffId, remainingChunkSize);
     }
 }
 
 
 
 static void
-read_cmyk(FILE *     const ifP,
-          IFF_ID     const iffid,
-          long       const chunksize,
-          ColorMap * const cmap) {
+read_cmyk(FILE *        const ifP,
+          IFF_ID        const iffId,
+          unsigned long const chunkSize,
+          ColorMap *    const cmapP) {
 
-    if( HAS_COLORMAP(cmap) ) {      /* prefer RGB color map */
-        skip_chunk(ifP, iffid, chunksize);
+    if (HAS_COLORMAP(cmapP)) {      /* prefer RGB color map */
+        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);
+        unsigned long const colorCt = chunkSize / 4;
+        if (colorCt == 0 ) {
+            pm_error("warning - empty %s colormap", ID2string(iffId));
+            skip_chunk(ifP, iffId, chunkSize);
         } else {
             unsigned int i;
-            cmap->color = ppm_allocrow(colors);
-            cmap->ncolors = colors;
-            
-            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);
+            unsigned long remainingChunkSize;
+
+            cmapP->color   = ppm_allocrow(colorCt);
+            cmapP->ncolors = colorCt;
+
+            for (i = 0, remainingChunkSize = chunkSize; i < colorCt; ++i) {
+                int const c = get_byte(ifP, iffId, &remainingChunkSize);
+                int const m = get_byte(ifP, iffId, &remainingChunkSize);
+                int const y = get_byte(ifP, iffId, &remainingChunkSize);
+                int const k = get_byte(ifP, iffId, &remainingChunkSize);
 
                 {
-                    pixval const red = 
+                    pixval const red =
                         MAXCOLVAL - MIN(MAXCOLVAL,
-                                        c*(MAXCOLVAL-k)/MAXCOLVAL+k); 
-                    pixval const green = 
+                                        c * (MAXCOLVAL-k) / MAXCOLVAL+k);
+                    pixval const green =
                         MAXCOLVAL - MIN(MAXCOLVAL,
-                                        m*(MAXCOLVAL-k)/MAXCOLVAL+k);
-                    pixval const blue = 
+                                        m * (MAXCOLVAL-k) / MAXCOLVAL+k);
+                    pixval const blue =
                         MAXCOLVAL - MIN(MAXCOLVAL,
-                                        y*(MAXCOLVAL-k)/MAXCOLVAL+k);
+                                        y * (MAXCOLVAL-k) / MAXCOLVAL+k);
 
-                    PPM_ASSIGN(cmap->color[i], red, green, blue);
+                    PPM_ASSIGN(cmapP->color[i], red, green, blue);
                 }
             }
-            chunk_end(ifP, iffid, chunksize);
+            chunk_end(ifP, iffId, remainingChunkSize);
         }
     }
 }
@@ -389,45 +389,45 @@ read_cmyk(FILE *     const ifP,
 
 static void
 read_clut(FILE *        const ifP,
-          IFF_ID        const iffid,
-          unsigned long const chunksize,
-          ColorMap *    const cmap) {
-
-    if (chunksize != CLUTSize) {
-        pm_message("invalid size for %s chunk - skipping it", 
-                   ID2string(iffid));
-        skip_chunk(ifP, iffid, chunksize);
+          IFF_ID        const iffId,
+          unsigned long const chunkSize,
+          ColorMap *    const cmapP) {
+
+    if (chunkSize != CLUTSize) {
+        pm_message("invalid size for %s chunk - skipping it",
+                   ID2string(iffId));
+        skip_chunk(ifP, iffId, chunkSize);
     } else {
         long type;
         unsigned char * lut;
-        unsigned long remainingChunksize;
+        unsigned long remainingChunkSize;
         unsigned int i;
 
-        remainingChunksize = chunksize;  /* initial value */
+        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:
-            cmap->monolut  = lut;
+            cmapP->monolut  = lut;
             break;
         case CLUT_RED:
-            cmap->redlut   = lut;
+            cmapP->redlut   = lut;
             break;
         case CLUT_GREEN:
-            cmap->greenlut = lut;
+            cmapP->greenlut = lut;
             break;
         case CLUT_BLUE:
-            cmap->bluelut  = lut;
+            cmapP->bluelut  = lut;
             break;
         default:
             pm_message("warning - %s type %ld not recognized",
-                       ID2string(iffid), type);
+                       ID2string(iffId), type);
             free(lut);
         }
     }
@@ -446,8 +446,8 @@ warnNonsquarePixels(uint8_t const xAspect,
             pm_message("%s; to fix do a 'pamscale -%cscale %g'",
                        baseMsg,
                        xAspect > yAspect ? 'x' : 'y',
-                       xAspect > yAspect ? 
-                       (float)xAspect/yAspect : 
+                       xAspect > yAspect ?
+                       (float)xAspect/yAspect :
                        (float)yAspect/xAspect);
         else
             pm_message("%s", baseMsg);
@@ -464,7 +464,7 @@ read_bmhd(FILE *        const ifP,
     BitMapHeader * bmhdP;
 
     if (chunksize != BitMapHeaderSize) {
-        pm_message("invalid size for %s chunk - skipping it", 
+        pm_message("invalid size for %s chunk - skipping it",
                    ID2string(iffid));
         skip_chunk(ifP, iffid, chunksize);
         bmhdP = NULL;
@@ -474,7 +474,7 @@ read_bmhd(FILE *        const ifP,
         MALLOCVAR_NOFAIL(bmhdP);
 
         remainingChunksize = chunksize;  /* initial value */
-        
+
         bmhdP->w = get_big_short(ifP, iffid, &remainingChunksize);
         bmhdP->h = get_big_short(ifP, iffid, &remainingChunksize);
         bmhdP->x = get_big_short(ifP, iffid, &remainingChunksize);
@@ -492,7 +492,7 @@ read_bmhd(FILE *        const ifP,
 
         if (verbose) {
             if (typeid == ID_ILBM)
-                pm_message("dimensions: %dx%d, %d planes", 
+                pm_message("dimensions: %dx%d, %d planes",
                            bmhdP->w, bmhdP->h, bmhdP->nPlanes);
             else
                 pm_message("dimensions: %dx%d", bmhdP->w, bmhdP->h);
@@ -509,11 +509,11 @@ read_bmhd(FILE *        const ifP,
                 case mskHasTransparentColor:
                     if (!maskfile)
                         pm_message("use '-maskfile <filename>' "
-                                   "to generate a PBM mask file from %s", 
+                                   "to generate a PBM mask file from %s",
                                    mskNAME[bmhdP->masking]);
                     break;
                 case mskLasso:
-                    pm_message("warning - masking type '%s' not recognized", 
+                    pm_message("warning - masking type '%s' not recognized",
                                mskNAME[bmhdP->masking]);
                     break;
                 default:
@@ -529,7 +529,7 @@ read_bmhd(FILE *        const ifP,
 
         /* fix aspect ratio */
         if (bmhdP->xAspect == 0 || bmhdP->yAspect == 0) {
-            pm_message("warning - illegal aspect ratio %d:%d, using 1:1", 
+            pm_message("warning - illegal aspect ratio %d:%d, using 1:1",
                        bmhdP->xAspect, bmhdP->yAspect);
             bmhdP->xAspect = bmhdP->yAspect = 1;
         }
@@ -647,7 +647,7 @@ decode_mask(FILE *          const ifP,
     case mskNone:
         break;
     case mskHasMask:        /* mask plane */
-        read_ilbm_plane(ifP, remainingChunksizeP, RowBytes(cols), 
+        read_ilbm_plane(ifP, remainingChunksizeP, RowBytes(cols),
                         bmhdP->compression);
         if (maskfile) {
             ilp = ilbmrow;
@@ -669,7 +669,7 @@ decode_mask(FILE *          const ifP,
     case mskHasTransparentColor:
         if (!chunkyrow)
             pm_error("decode_mask(): chunkyrow == NULL - can't happen");
-        
+
         if (maskfile) {
             for (col = 0; col < cols; ++col) {
                 if (chunkyrow[col] == bmhdP->transparentColor)
@@ -685,7 +685,7 @@ decode_mask(FILE *          const ifP,
         pm_error("This program does not know how to process Lasso masking");
         break;
     default:
-        pm_error("decode_mask(): unknown masking type %d - can't happen", 
+        pm_error("decode_mask(): unknown masking type %d - can't happen",
                  bmhdP->masking);
     }
 }
@@ -697,79 +697,82 @@ decode_mask(FILE *          const ifP,
 
 
 static void
-multi_adjust(cmap, row, palchange)
-    ColorMap *cmap;
-    int row;
-    PaletteChange *palchange;
-{
-    int i, reg;
-
-    for( i = 0; palchange[i].reg != MP_REG_END; i++ ) {
-        reg = palchange[i].reg;
-        if( reg >= cmap->ncolors ) {
+multi_adjust(ColorMap *            const cmapP,
+             unsigned int          const row,
+             const PaletteChange * const palchange) {
+
+    unsigned int i;
+
+    for (i = 0; palchange[i].reg != MP_REG_END; ++i) {
+        int const reg = palchange[i].reg;
+        if (reg >= cmapP->ncolors) {
             pm_message("warning - palette change register out of range");
-            pm_message("    row %d  change structure %d  reg=%d (max %d)", 
-                       row, i, reg, cmap->ncolors-1);
+            pm_message("    row %u  change structure %d  reg=%d (max %d)",
+                       row, i, reg, cmapP->ncolors-1);
             pm_message("    ignoring it...  "
                        "colors might get messed up from here");
-        }
-        else
-        if( reg != MP_REG_IGNORE ) {
-            PPM_ASSIGN(cmap->color[reg], 
-                       palchange[i].r, palchange[i].g, palchange[i].b);
+        } else {
+            if (reg != MP_REG_IGNORE) {
+                PPM_ASSIGN(cmapP->color[reg],
+                           palchange[i].r, palchange[i].g, palchange[i].b);
+            }
         }
     }
 }
 
+
+
 static void
-multi_init(cmap, viewportmodes)
-    ColorMap *cmap;
-    long viewportmodes;
-{
-    if( cmap->mp_init )
-        multi_adjust(cmap, -1, cmap->mp_init);
-    if( !(viewportmodes & vmLACE) )
-        cmap->mp_flags &= ~(MP_FLAGS_SKIPLACED);
+multi_init(ColorMap * const cmapP,
+           long       const viewportmodes) {
+
+    if (cmapP->mp_init)
+        multi_adjust(cmapP, -1, cmapP->mp_init);
+    if (!(viewportmodes & vmLACE) )
+        cmapP->mp_flags &= ~(MP_FLAGS_SKIPLACED);
 }
 
+
+
 static void
-multi_update(cmap, row)
-    ColorMap *cmap;
-    int row;
-{
-    if( cmap->mp_flags & MP_FLAGS_SKIPLACED ) {
-        if( ODD(row) )
+multi_update(ColorMap *   const cmapP,
+             unsigned int const row) {
+
+    if (cmapP->mp_flags & MP_FLAGS_SKIPLACED) {
+        if (ODD(row))
             return;
-        if( row/2 < cmap->mp_rows && cmap->mp_change[row/2] )
-            multi_adjust(cmap, row, cmap->mp_change[row/2]);
-    }
-    else {
-        if( row < cmap->mp_rows && cmap->mp_change[row] )
-            multi_adjust(cmap, row, cmap->mp_change[row]);
+        if (row/2 < cmapP->mp_rows && cmapP->mp_change[row/2])
+            multi_adjust(cmapP, row, cmapP->mp_change[row/2]);
+    } else {
+        if (row < cmapP->mp_rows && cmapP->mp_change[row])
+            multi_adjust(cmapP, row, cmapP->mp_change[row]);
     }
 }
 
+
+
 static void
-multi_free(cmap)
-    ColorMap *cmap;
-{
-    int i;
+multi_free(ColorMap * const cmapP) {
 
-    if( cmap->mp_init ) {
-        free(cmap->mp_init);
-        cmap->mp_init = NULL;
+    if (cmapP->mp_init) {
+        free(cmapP->mp_init);
+        cmapP->mp_init = NULL;
     }
-    if( cmap->mp_change ) {
-        for( i = 0; i < cmap->mp_rows; i++ ) {
-            if( cmap->mp_change[i] )
-                free(cmap->mp_change[i]);
+
+    if (cmapP->mp_change) {
+        unsigned int i;
+
+        for (i = 0; i < cmapP->mp_rows; ++i) {
+            if (cmapP->mp_change[i])
+                free(cmapP->mp_change[i]);
         }
-        free(cmap->mp_change);
-        cmap->mp_change = NULL;
+        free(cmapP->mp_change);
+        cmapP->mp_change = NULL;
     }
-    cmap->mp_rows = 0;
-    cmap->mp_type = 0;
-    cmap->mp_flags = 0;
+
+    cmapP->mp_rows  = 0;
+    cmapP->mp_type  = 0;
+    cmapP->mp_flags = 0;
 }
 
 
@@ -788,7 +791,7 @@ analyzeCmapSamples(const ColorMap * const cmapP,
     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]);
@@ -857,7 +860,7 @@ transpColor(const BitMapHeader * const bmhdP,
     pixel * transpColorP;
 
     if (bmhdP) {
-        if (bmhdP->masking == mskHasTransparentColor || 
+        if (bmhdP->masking == mskHasTransparentColor ||
             bmhdP->masking == mskLasso) {
             MALLOCVAR_NOFAIL(transpColorP);
 
@@ -909,80 +912,83 @@ prepareCmap(const BitMapHeader * const bmhdP,
 
 
 static pixval
-lookup_red(cmap, oldval)
-    ColorMap *cmap;
-    int oldval;
-{
-    if( cmap && cmap->redlut && oldval < 256 )
-        return cmap->redlut[oldval];
+lookup_red(ColorMap *   const cmapP,
+           unsigned int const oldval) {
+
+    if (cmapP && cmapP->redlut && oldval < 256)
+        return cmapP->redlut[oldval];
     else
         return oldval;
 }
 
+
+
 static pixval
-lookup_green(cmap, oldval)
-    ColorMap *cmap;
-    int oldval;
-{
-    if( cmap && cmap->greenlut && oldval < 256 )
-        return cmap->greenlut[oldval];
+lookup_green(ColorMap *   const cmapP,
+             unsigned int const oldval) {
+
+    if (cmapP && cmapP->greenlut && oldval < 256)
+        return cmapP->greenlut[oldval];
     else
         return oldval;
 }
 
+
+
 static pixval
-lookup_blue(cmap, oldval)
-    ColorMap *cmap;
-    int oldval;
-{
-    if( cmap && cmap->bluelut && oldval < 256 )
-        return cmap->bluelut[oldval];
+lookup_blue(ColorMap *   const cmapP,
+            unsigned int const oldval) {
+
+    if (cmapP && cmapP->bluelut && oldval < 256)
+        return cmapP->bluelut[oldval];
     else
         return oldval;
 }
 
+
+
 static pixval
-lookup_mono(cmap, oldval)
-    ColorMap *cmap;
-    int oldval;
-{
-    if( cmap && cmap->monolut && oldval < 256 )
-        return cmap->monolut[oldval];
+lookup_mono(ColorMap *   const cmapP,
+            unsigned int const oldval) {
+
+    if (cmapP && cmapP->monolut && oldval < 256)
+        return cmapP->monolut[oldval];
     else
         return oldval;
 }
 
+
+
 static ColorMap *
-ehbcmap(cmap)
-    ColorMap *cmap;
-{
-    pixel *tempcolor = NULL;
-    int i, col;
-
-    col = cmap->ncolors;
-
-    tempcolor = ppm_allocrow(col * 2);
-    for( i = 0; i < col; i++ ) {
-        tempcolor[i] = cmap->color[i];
-        PPM_ASSIGN(tempcolor[col + i],  PPM_GETR(cmap->color[i]) / 2,
-                                        PPM_GETG(cmap->color[i]) / 2,
-                                        PPM_GETB(cmap->color[i]) / 2 );
+ehbcmap(ColorMap * const cmapP) {
+
+    pixel * tempcolor;
+    unsigned int i;
+
+    tempcolor = ppm_allocrow(cmapP->ncolors * 2);
+
+    for (i = 0; i < cmapP->ncolors; ++i) {
+        tempcolor[i] = cmapP->color[i];
+        PPM_ASSIGN(tempcolor[cmapP->ncolors + i],
+                   PPM_GETR(cmapP->color[i]) / 2,
+                   PPM_GETG(cmapP->color[i]) / 2,
+                   PPM_GETB(cmapP->color[i]) / 2);
     }
-    ppm_freerow(cmap->color);
-    cmap->color = tempcolor;
-    cmap->ncolors *= 2;
+    ppm_freerow(cmapP->color);
+    cmapP->color = tempcolor;
+    cmapP->ncolors *= 2;
 
-    return cmap;
+    return cmapP;
 }
 
 
 
 static pixval
-lut_maxval(ColorMap * const cmap, 
+lut_maxval(ColorMap * const cmap,
            pixval     const maxval) {
 
     pixval retval;
-    
+
     if (maxval >= 255)
         retval = maxval;
     else {
@@ -993,16 +999,16 @@ lut_maxval(ColorMap * const cmap,
             unsigned char maxlut;
             maxlut = maxval;
             for( i = 0; i < maxval; i++ ) {
-                if( cmap->redlut   && cmap->redlut[i]   > maxlut ) 
+                if( cmap->redlut   && cmap->redlut[i]   > maxlut )
                     maxlut = cmap->redlut[i];
-                if( cmap->greenlut && cmap->greenlut[i] > maxlut ) 
+                if( cmap->greenlut && cmap->greenlut[i] > maxlut )
                     maxlut = cmap->greenlut[i];
-                if( cmap->bluelut  && cmap->bluelut[i]  > maxlut ) 
+                if( cmap->bluelut  && cmap->bluelut[i]  > maxlut )
                     maxlut = cmap->bluelut[i];
             }
             pm_message("warning - "
                        "%d-bit index into 8-bit color lookup table, "
-                       "table maxval=%d", 
+                       "table maxval=%d",
                        pm_maxvaltobits(maxval), maxlut);
             if( maxlut != maxval )
                 retval = 255;
@@ -1017,49 +1023,49 @@ lut_maxval(ColorMap * const cmap,
 
 
 static void
-get_color(cmap, idx, red, green, blue)
-    ColorMap *cmap;
-    int idx;
-    pixval *red, *green, *blue;
-{
-    if( HAS_COLORMAP(cmap) ) {
-        pixval r, g, b;
-
-        if( idx >= cmap->ncolors )
-            pm_error("color index out of range: %d (max %d)", 
-                     idx, cmap->ncolors);
-        r = PPM_GETR(cmap->color[idx]);
-        g = PPM_GETG(cmap->color[idx]);
-        b = PPM_GETB(cmap->color[idx]);
-
-        *red    = lookup_red(cmap, r);
-        *green  = lookup_green(cmap, g);
-        *blue   = lookup_blue(cmap, b);
-    }
-    else {
-        *red = *green = *blue = lookup_mono(cmap, idx);
-    }
+get_color(ColorMap *   const cmapP,
+          unsigned int const idx,
+          pixval *     const redP,
+          pixval *     const greenP,
+          pixval *     const blueP) {
+
+    if (HAS_COLORMAP(cmapP)) {
+        if (idx >= cmapP->ncolors)
+            pm_error("color index out of range: %u (max %u)",
+                     idx, cmapP->ncolors);
+        else {
+            pixval const r = PPM_GETR(cmapP->color[idx]);
+            pixval const g = PPM_GETG(cmapP->color[idx]);
+            pixval const b = PPM_GETB(cmapP->color[idx]);
+
+            *redP    = lookup_red   (cmapP, r);
+            *greenP  = lookup_green (cmapP, g);
+            *blueP   = lookup_blue  (cmapP, b);
+        }
+    } else
+        *redP = *greenP = *blueP = lookup_mono(cmapP, idx);
 }
 
 
+
 /****************************************************************************
  Conversion functions
  ****************************************************************************/
 
 static void
-std_to_ppm(FILE *         const ifP, 
-           long           const chunksize, 
-           BitMapHeader * const bmhdP, 
-           ColorMap *     const cmap, 
+std_to_ppm(FILE *         const ifP,
+           long           const chunksize,
+           BitMapHeader * const bmhdP,
+           ColorMap *     const cmap,
            long           const viewportmodes);
 
 
 
 static void
-ham_to_ppm(FILE *         const ifP, 
-           long           const chunksize, 
-           BitMapHeader * const bmhdP, 
-           ColorMap *     const cmap, 
+ham_to_ppm(FILE *         const ifP,
+           long           const chunksize,
+           BitMapHeader * const bmhdP,
+           ColorMap *     const cmap,
            long           const viewportmodes) {
 
     int cols, rows, hambits, hammask, hamshift, hammask2, col, row;
@@ -1077,7 +1083,7 @@ ham_to_ppm(FILE *         const ifP,
     if( hambits > 8 || hambits < 1 ) {
         int const assumed_viewportmodes = viewportmodes & ~(vmHAM);
 
-        pm_message("%d-plane HAM?? - interpreting image as a normal ILBM", 
+        pm_message("%d-plane HAM?? - interpreting image as a normal ILBM",
                    bmhdP->nPlanes);
         std_to_ppm(ifP, chunksize, bmhdP, cmap, assumed_viewportmodes);
         return;
@@ -1085,8 +1091,8 @@ ham_to_ppm(FILE *         const ifP,
         unsigned long remainingChunksize;
         pixel * transpColorP;
 
-        pm_message("input is a %sHAM%d file", 
-                   HAS_MULTIPALETTE(cmap) ? "multipalette " : "", 
+        pm_message("input is a %sHAM%d file",
+                   HAS_MULTIPALETTE(cmap) ? "multipalette " : "",
                    bmhdP->nPlanes);
 
         if( HAS_COLORLUT(cmap) || HAS_MONOLUT(cmap) ) {
@@ -1095,7 +1101,7 @@ ham_to_ppm(FILE *         const ifP,
             if( cmap->greenlut )    free(cmap->greenlut);
             if( cmap->bluelut )     free(cmap->bluelut);
             if( cmap->monolut )     free(cmap->monolut);
-            cmap->redlut = cmap->greenlut = cmap->bluelut = 
+            cmap->redlut = cmap->greenlut = cmap->bluelut =
                 cmap->monolut = NULL;
         }
         if( !HAS_COLORMAP(cmap) ) {
@@ -1165,10 +1171,10 @@ ham_to_ppm(FILE *         const ifP,
 
 
 static void
-std_to_ppm(FILE *         const ifP, 
-           long           const chunksize, 
-           BitMapHeader * const bmhdP, 
-           ColorMap *     const cmap, 
+std_to_ppm(FILE *         const ifP,
+           long           const chunksize,
+           BitMapHeader * const bmhdP,
+           ColorMap *     const cmap,
            long           const viewportmodes) {
 
     if (viewportmodes & vmHAM) {
@@ -1213,7 +1219,7 @@ std_to_ppm(FILE *         const ifP,
         ppm_writeppminit( stdout, cols, rows, maxval, 0 );
 
         remainingChunksize = chunksize;  /* initial value */
-    
+
         for (row = 0; row < rows; ++row) {
 
             if( HAS_MULTIPALETTE(cmap) )
@@ -1240,9 +1246,9 @@ std_to_ppm(FILE *         const ifP,
 
 
 static void
-deep_to_ppm(FILE *         const ifP, 
-            long           const chunksize, 
-            BitMapHeader * const bmhdP, 
+deep_to_ppm(FILE *         const ifP,
+            long           const chunksize,
+            BitMapHeader * const bmhdP,
             ColorMap *     const cmap) {
 
     unsigned int const cols = bmhdP->w;
@@ -1259,9 +1265,9 @@ deep_to_ppm(FILE *         const ifP,
     if( planespercolor > MAXPLANES )
         pm_error("too many planes (max %d)", MAXPLANES * 3);
 
-    if( bmhdP->masking == mskHasTransparentColor || 
+    if( bmhdP->masking == mskHasTransparentColor ||
         bmhdP->masking == mskLasso ) {
-        pm_message("masking type '%s' in a deep ILBM?? - ignoring it", 
+        pm_message("masking type '%s' in a deep ILBM?? - ignoring it",
                    mskNAME[bmhdP->masking]);
         bmhdP->masking = mskNone;
     }
@@ -1271,7 +1277,7 @@ deep_to_ppm(FILE *         const ifP,
         pm_error("nPlanes is too large");
 
     transpColorP = transpColor(bmhdP, cmap, transpName, maxval);
-        
+
     Rrow = alloc_rawrow(cols);
     Grow = alloc_rawrow(cols);
     Brow = alloc_rawrow(cols);
@@ -1313,7 +1319,7 @@ dcol_to_ppm(FILE *         const ifP,
     unsigned int const redplanes   = dcol->r;
     unsigned int const greenplanes = dcol->g;
     unsigned int const blueplanes  = dcol->b;
-    
+
     int col, row;
     rawtype *Rrow, *Grow, *Brow;
     pixval maxval, redmaxval, greenmaxval, bluemaxval;
@@ -1324,16 +1330,16 @@ dcol_to_ppm(FILE *         const ifP,
     pm_message("input is a %d:%d:%d direct color ILBM",
                 redplanes, greenplanes, blueplanes);
 
-    if( redplanes > MAXPLANES || 
-        blueplanes > MAXPLANES || 
+    if( redplanes > MAXPLANES ||
+        blueplanes > MAXPLANES ||
         greenplanes > MAXPLANES )
         pm_error("too many planes (max %d per color component)", MAXPLANES);
 
     if( bmhdP->nPlanes != (redplanes + greenplanes + blueplanes) )
-        pm_error("%s/%s plane number mismatch", 
+        pm_error("%s/%s plane number mismatch",
                  ID2string(ID_BMHD), ID2string(ID_DCOL));
 
-    if( bmhdP->masking == mskHasTransparentColor || 
+    if( bmhdP->masking == mskHasTransparentColor ||
         bmhdP->masking == mskLasso ) {
         pm_message("masking type '%s' in a direct color ILBM?? - ignoring it",
                    mskNAME[bmhdP->masking]);
@@ -1356,7 +1362,7 @@ dcol_to_ppm(FILE *         const ifP,
 
     if( redmaxval != maxval || greenmaxval != maxval || bluemaxval != maxval )
         pm_message("scaling colors to %d bits", pm_maxvaltobits(maxval));
-    
+
     MALLOCARRAY_NOFAIL(redtable,   redmaxval   +1);
     MALLOCARRAY_NOFAIL(greentable, greenmaxval +1);
     MALLOCARRAY_NOFAIL(bluetable,  bluemaxval  +1);
@@ -1403,7 +1409,7 @@ dcol_to_ppm(FILE *         const ifP,
 
 static void
 cmapToPpm(FILE *     const ofP,
-            ColorMap * const cmapP) {
+          ColorMap * const cmapP) {
 
     ppm_colorrowtomapfile(ofP, cmapP->color, cmapP->ncolors, MAXCOLVAL);
 }
@@ -1425,11 +1431,11 @@ ipbm_to_ppm(FILE *         const ifP,
     unsigned long remainingChunksize;
     pixel * transpColorP;
 
-    pm_message("input is a %sPBM ", 
+    pm_message("input is a %sPBM ",
                HAS_MULTIPALETTE(cmap) ? "multipalette " : "");
 
     if( bmhdP->nPlanes != 8 )
-        pm_error("invalid number of planes for IFF-PBM: %d (must be 8)", 
+        pm_error("invalid number of planes for IFF-PBM: %d (must be 8)",
                  bmhdP->nPlanes);
 
     if( bmhdP->masking == mskHasMask )
@@ -1456,7 +1462,7 @@ ipbm_to_ppm(FILE *         const ifP,
     for( row = 0; row < rows; row++ ) {
         if( HAS_MULTIPALETTE(cmap) )
             multi_update(cmap, row);
-        
+
         read_ilbm_plane(ifP, &remainingChunksize, cols, bmhdP->compression);
 
         for( col = 0; col < cols; col++ ) {
@@ -1494,24 +1500,24 @@ rgbn_to_ppm(FILE *         const ifP,
     pm_message("input is a %d-bit RGB image", (typeid == ID_RGB8 ? 8 : 4));
 
     if (bmhdP->compression != 4)
-        pm_error("invalid compression mode for %s: %d (must be 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)", 
+            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)", 
+            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", 
+        pm_error("rgbn_to_ppm(): invalid IFF ID %s - can't happen",
                  ID2string(typeid));
     }
 
@@ -1533,7 +1539,7 @@ rgbn_to_ppm(FILE *         const ifP,
             tries = 0;
             while (count == 0) {
                 if (typeid == ID_RGB8) {
-                    r = lookup_red(cmap,   get_byte(ifP, ID_BODY, 
+                    r = lookup_red(cmap,   get_byte(ifP, ID_BODY,
                                                     &remainingChunksize));
                     g = lookup_green(cmap, get_byte(ifP, ID_BODY,
                                                     &remainingChunksize));
@@ -1675,40 +1681,46 @@ read_4bit_mp(FILE *     const ifP,
 
 
 static void
-PCHG_DecompHuff(src, dest, tree, origsize)
-    unsigned char *src, *dest;
-    short *tree;
-    unsigned long origsize;
-{
-    unsigned long i = 0, bits = 0;
+PCHG_DecompHuff(const unsigned char * const src,
+                unsigned char *       const dst,
+                short *               const tree,
+                unsigned long         const origsize) {
+
+    const unsigned char * srcCursor;
+    unsigned char * dstCursor;
+    unsigned long i;
+    unsigned long bits;
     unsigned char thisbyte;
-    short *p;
+    short * p;
 
-    p = tree;
-    while( i < origsize ) {
-        if( bits == 0 ) {
-            thisbyte = *src++;
+    srcCursor = &src[0];  /* initial value */
+    dstCursor = &dst[0];  /* initial value */
+    i = 0;     /* initial value */
+    bits = 0;  /* initial value */
+    p = tree;  /* initial value */
+
+    while (i < origsize) {
+        if (bits == 0)  {
+            thisbyte = *srcCursor++;
             bits = 8;
         }
-        if( thisbyte & (1 << 7) ) {
-            if( *p >= 0 ) {
-                *dest++ = (unsigned char)*p;
-                i++;
+        if (thisbyte & (1 << 7)) {
+            if (*p >= 0) {
+                *dstCursor++ = (unsigned char)*p;
+                ++i;
                 p = tree;
-            }
-            else
+            } else
                 p += (*p / 2);
-        }
-        else {
-            p--;
-            if( *p > 0 && (*p & 0x100) ) {
-                *dest++ = (unsigned char )*p;
-                i++;
+        } else {
+            --p;
+            if (*p > 0 && (*p & 0x100)) {
+                *dstCursor++ = (unsigned char )*p;
+                ++i;
                 p = tree;
             }
         }
         thisbyte <<= 1;
-        bits--;
+        --bits;
     }
 }
 
@@ -1743,9 +1755,9 @@ PCHG_Decompress(PCHGHeader *     const PCHG,
             hufftree[i] = pm_uintFromBigend16(bigendComptree[i]);
 
         /* decompress the change structure data */
-        PCHG_DecompHuff(compdata, data, &hufftree[huffsize-1], 
+        PCHG_DecompHuff(compdata, data, &hufftree[huffsize-1],
                         CompHdr->OriginalDataSize);
-        
+
         free(hufftree);
     } break;
         default:
@@ -1755,99 +1767,103 @@ PCHG_Decompress(PCHGHeader *     const PCHG,
 
 
 static void
-PCHG_ConvertSmall(PCHG, cmap, mask, datasize)
-    PCHGHeader *PCHG;
-    ColorMap *cmap;
-    unsigned char *mask;
-    unsigned long datasize;
-{
+PCHG_ConvertSmall(PCHGHeader *    const pchgP,
+                  ColorMap *      const cmapP,
+                  unsigned char * const mask,
+                  unsigned long   const dataSize) {
+
     unsigned char *data;
+    unsigned long remDataSize;
     unsigned char thismask;
     int bits, row, i, changes, masklen, reg;
     unsigned char ChangeCount16, ChangeCount32;
     unsigned short SmallChange;
-    unsigned long totalchanges = 0;
-    int changedlines = PCHG->ChangedLines;
+    unsigned long totalchanges;
+    int changedlines;
+    unsigned char * maskCursor;
 
-    masklen = 4 * MaskLongWords(PCHG->LineCount);
-    data = mask + masklen; datasize -= masklen;
+    totalchanges = 0;  /* initial value */
+    changedlines = pchgP->ChangedLines;  /* initial value */
+    masklen = 4 * MaskLongWords(pchgP->LineCount);
+    maskCursor = mask;
+    data = maskCursor + masklen; remDataSize = dataSize - masklen;
 
     bits = 0;
-    for( row = PCHG->StartLine; changedlines && row < 0; row++ ) {
-        if( bits == 0 ) {
-            if( masklen == 0 ) goto fail2;
-            thismask = *mask++;
+    for (row = pchgP->StartLine; changedlines && row < 0; ++row) {
+        if (bits == 0) {
+            if (masklen == 0) goto fail2;
+            thismask = *maskCursor++;
             --masklen;
             bits = 8;
         }
-        if( thismask & (1<<7) ) {
-            if( datasize < 2 ) goto fail;
+        if (thismask & (1<<7)) {
+            if (remDataSize < 2) goto fail;
             ChangeCount16 = *data++;
             ChangeCount32 = *data++;
-            datasize -= 2;
+            remDataSize -= 2;
 
             changes = ChangeCount16 + ChangeCount32;
-            for( i = 0; i < changes; i++ ) {
-                if( totalchanges >= PCHG->TotalChanges ) goto fail;
-                if( datasize < 2 ) goto fail;
-                SmallChange = BIG_WORD(data); data += 2; datasize -= 2;
-                reg = ((SmallChange & 0xf000) >> 12) + 
+            for (i = 0; i < changes; ++i) {
+                if (totalchanges >= pchgP->TotalChanges) goto fail;
+                if (remDataSize < 2) goto fail;
+                SmallChange = BIG_WORD(data); data += 2; remDataSize -= 2;
+                reg = ((SmallChange & 0xf000) >> 12) +
                     (i >= ChangeCount16 ? 16 : 0);
-                cmap->mp_init[reg - PCHG->MinReg].reg = reg;
-                cmap->mp_init[reg - PCHG->MinReg].r = 
+                cmapP->mp_init[reg - pchgP->MinReg].reg = reg;
+                cmapP->mp_init[reg - pchgP->MinReg].r =
                     ((SmallChange & 0x0f00) >> 8) * FACTOR_4BIT;
-                cmap->mp_init[reg - PCHG->MinReg].g = 
+                cmapP->mp_init[reg - pchgP->MinReg].g =
                     ((SmallChange & 0x00f0) >> 4) * FACTOR_4BIT;
-                cmap->mp_init[reg - PCHG->MinReg].b = 
+                cmapP->mp_init[reg - pchgP->MinReg].b =
                     ((SmallChange & 0x000f) >> 0) * FACTOR_4BIT;
                 ++totalchanges;
             }
             --changedlines;
         }
         thismask <<= 1;
-        bits--;
+        --bits;
     }
 
-    for( row = PCHG->StartLine; changedlines && row < cmap->mp_rows; row++ ) {
-        if( bits == 0 ) {
-            if( masklen == 0 ) goto fail2;
-            thismask = *mask++;
+    for (row = pchgP->StartLine; changedlines && row < cmapP->mp_rows; row++) {
+        if (bits == 0) {
+            if (masklen == 0) goto fail2;
+            thismask = *maskCursor++;
             --masklen;
             bits = 8;
         }
-        if( thismask & (1<<7) ) {
-            if( datasize < 2 ) goto fail;
+        if(thismask & (1<<7)) {
+            if (remDataSize < 2) goto fail;
             ChangeCount16 = *data++;
             ChangeCount32 = *data++;
-            datasize -= 2;
+            remDataSize -= 2;
 
             changes = ChangeCount16 + ChangeCount32;
-            MALLOCARRAY_NOFAIL(cmap->mp_change[row], changes + 1);
-            for( i = 0; i < changes; i++ ) {
-                if( totalchanges >= PCHG->TotalChanges ) goto fail;
-                if( datasize < 2 ) goto fail;
-                SmallChange = BIG_WORD(data); data += 2; datasize -= 2;
-                reg = ((SmallChange & 0xf000) >> 12) + 
+            MALLOCARRAY_NOFAIL(cmapP->mp_change[row], changes + 1);
+            for (i = 0; i < changes; ++i) {
+                if (totalchanges >= pchgP->TotalChanges) goto fail;
+                if (remDataSize < 2) goto fail;
+                SmallChange = BIG_WORD(data); data += 2; remDataSize -= 2;
+                reg = ((SmallChange & 0xf000) >> 12) +
                     (i >= ChangeCount16 ? 16 : 0);
-                cmap->mp_change[row][i].reg = reg;
-                cmap->mp_change[row][i].r = 
+                cmapP->mp_change[row][i].reg = reg;
+                cmapP->mp_change[row][i].r =
                     ((SmallChange & 0x0f00) >> 8) * FACTOR_4BIT;
-                cmap->mp_change[row][i].g = 
+                cmapP->mp_change[row][i].g =
                     ((SmallChange & 0x00f0) >> 4) * FACTOR_4BIT;
-                cmap->mp_change[row][i].b = 
+                cmapP->mp_change[row][i].b =
                     ((SmallChange & 0x000f) >> 0) * FACTOR_4BIT;
                 ++totalchanges;
             }
-            cmap->mp_change[row][changes].reg = MP_REG_END;
+            cmapP->mp_change[row][changes].reg = MP_REG_END;
             --changedlines;
         }
         thismask <<= 1;
-        bits--;
+        --bits;
     }
-    if( totalchanges != PCHG->TotalChanges )
+    if (totalchanges != pchgP->TotalChanges)
         pm_message("warning - got %ld change structures, "
-                   "chunk header reports %ld", 
-                   totalchanges, PCHG->TotalChanges);
+                   "chunk header reports %ld",
+                   totalchanges, pchgP->TotalChanges);
     return;
 fail:
     pm_error("insufficient data in SmallLineChanges structures");
@@ -1872,16 +1888,16 @@ PCHG_ConvertBig(PCHGHeader *    const PCHG,
     int reg;
     unsigned long totalchanges;
     int changedlines;
-    unsigned long dataRemaining;
+    unsigned long remDataSize;
     unsigned char * mask;
 
     mask = maskStart;  /* initial value */
-    dataRemaining = datasize;  /* initial value */
+    remDataSize = datasize;  /* initial value */
     changedlines = PCHG->ChangedLines;  /* initial value */
     totalchanges = 0;  /* initial value */
 
     masklen = 4 * MaskLongWords(PCHG->LineCount);
-    data = mask + masklen; dataRemaining -= masklen;
+    data = mask + masklen; remDataSize -= masklen;
 
     for (row = PCHG->StartLine, bits = 0; changedlines && row < 0; ++row) {
         if (bits == 0) {
@@ -1894,16 +1910,16 @@ PCHG_ConvertBig(PCHGHeader *    const PCHG,
         if (thismask & (1<<7)) {
             unsigned int i;
 
-            if (dataRemaining < 2)
+            if (remDataSize < 2)
                 pm_error("insufficient data in BigLineChanges structures");
 
-            changes = BIG_WORD(data); data += 2; dataRemaining -= 2;
+            changes = BIG_WORD(data); data += 2; remDataSize -= 2;
 
             for (i = 0; i < changes; ++i) {
                 if (totalchanges >= PCHG->TotalChanges)
                     pm_error("insufficient data in BigLineChanges structures");
 
-                if (dataRemaining < 6)
+                if (remDataSize < 6)
                     pm_error("insufficient data in BigLineChanges structures");
 
                 reg = BIG_WORD(data); data += 2;
@@ -1912,7 +1928,7 @@ PCHG_ConvertBig(PCHGHeader *    const PCHG,
                 cmap->mp_init[reg - PCHG->MinReg].r = *data++;
                 cmap->mp_init[reg - PCHG->MinReg].b = *data++;  /* yes, RBG */
                 cmap->mp_init[reg - PCHG->MinReg].g = *data++;
-                dataRemaining -= 6;
+                remDataSize -= 6;
                 ++totalchanges;
             }
             --changedlines;
@@ -1933,17 +1949,17 @@ PCHG_ConvertBig(PCHGHeader *    const PCHG,
         if (thismask & (1<<7)) {
             unsigned int i;
 
-            if (dataRemaining < 2)
+            if (remDataSize < 2)
                 pm_error("insufficient data in BigLineChanges structures");
 
-            changes = BIG_WORD(data); data += 2; dataRemaining -= 2;
+            changes = BIG_WORD(data); data += 2; remDataSize -= 2;
 
             MALLOCARRAY_NOFAIL(cmap->mp_change[row], changes + 1);
             for (i = 0; i < changes; ++i) {
                 if (totalchanges >= PCHG->TotalChanges)
                     pm_error("insufficient data in BigLineChanges structures");
 
-                if (dataRemaining < 6)
+                if (remDataSize < 6)
                     pm_error("insufficient data in BigLineChanges structures");
 
                 reg = BIG_WORD(data); data += 2;
@@ -1952,7 +1968,7 @@ PCHG_ConvertBig(PCHGHeader *    const PCHG,
                 cmap->mp_change[row][i].r = *data++;
                 cmap->mp_change[row][i].b = *data++;    /* yes, RBG */
                 cmap->mp_change[row][i].g = *data++;
-                dataRemaining -= 6;
+                remDataSize -= 6;
                 ++totalchanges;
             }
             cmap->mp_change[row][changes].reg = MP_REG_END;
@@ -1963,7 +1979,7 @@ PCHG_ConvertBig(PCHGHeader *    const PCHG,
     }
     if (totalchanges != PCHG->TotalChanges)
         pm_message("warning - got %ld change structures, "
-                   "chunk header reports %ld", 
+                   "chunk header reports %ld",
                    totalchanges, PCHG->TotalChanges);
 }
 
@@ -2027,7 +2043,7 @@ read_pchg(FILE *     const ifP,
 
             datasize = CompHdr.OriginalDataSize;
             MALLOCARRAY_NOFAIL(data, datasize);
-            PCHG_Decompress(&PCHG, &CompHdr, compdata, 
+            PCHG_Decompress(&PCHG, &CompHdr, compdata,
                             compsize, comptree, data);
 
             free(comptree);
@@ -2073,7 +2089,7 @@ read_pchg(FILE *     const ifP,
             }
             else
                 pm_error("unknown palette changes structure "
-                         "format in %s chunk", 
+                         "format in %s chunk",
                          ID2string(iffid));
         }
         free(data);
@@ -2101,7 +2117,7 @@ ignored_iffid(IFF_ID       const iffid,
 
 
 
-static void 
+static void
 process_body( FILE *          const ifP,
               long            const chunksize,
               BitMapHeader *  const bmhdP,
@@ -2111,9 +2127,9 @@ process_body( FILE *          const ifP,
               int             const isdeepopt,
               DirectColor *   const dcol,
               int *           const viewportmodesP) {
-    
+
     if (bmhdP == NULL)
-        pm_error("%s chunk without %s chunk", 
+        pm_error("%s chunk without %s chunk",
                  ID2string(ID_BODY), ID2string(ID_BMHD));
 
     prepareCmap(bmhdP, cmap);
@@ -2132,12 +2148,12 @@ process_body( FILE *          const ifP,
 
         if( isdeepopt > 0 && (bmhdP->nPlanes % 3 != 0) ) {
             pm_message("cannot interpret %d-plane image as 'deep' "
-                       "(# of planes must be divisible by 3)", 
+                       "(# of planes must be divisible by 3)",
                        bmhdP->nPlanes);
             isdeep = 0;
         } else
             isdeep = isdeepopt;
-        
+
         if (isdeep > 0)
             deep_to_ppm(ifP, chunksize, bmhdP, cmap);
         else if (dcol)
@@ -2151,7 +2167,7 @@ process_body( FILE *          const ifP,
                 /* will be interpreted as grayscale */
                 std_to_ppm(ifP, chunksize, bmhdP, cmap, *viewportmodesP);
             else
-                pm_error("don't know how to interpret %d-plane image", 
+                pm_error("don't know how to interpret %d-plane image",
                          bmhdP->nPlanes);
         } else
             std_to_ppm(ifP, chunksize, bmhdP, cmap, *viewportmodesP);
@@ -2163,7 +2179,7 @@ process_body( FILE *          const ifP,
 
 
 
-static void 
+static void
 processChunk(FILE *          const ifP,
              long            const formsize,
              IFF_ID          const ignorelist[],
@@ -2200,7 +2216,7 @@ processChunk(FILE *          const ifP,
     } else if (iffid == ID_END) {
         /* END chunks are not officially valid in IFF, but
            suggested as a future expansion for stream-writing,
-           see Amiga RKM Devices, 3rd Ed, page 376 
+           see Amiga RKM Devices, 3rd Ed, page 376
         */
         if (chunksize != 0 ) {
             pm_message("warning - non-0 %s chunk", ID2string(iffid));
@@ -2212,7 +2228,7 @@ processChunk(FILE *          const ifP,
                        ID2string(iffid), formsize, 0xffffffff);
         *endchunkP = 1;
     } else if (*bodyChunkProcessedP) {
-        pm_message("%s chunk found after %s chunk - skipping", 
+        pm_message("%s chunk found after %s chunk - skipping",
                    ID2string(iffid), ID2string(ID_BODY));
         skip_chunk(ifP, iffid, chunksize);
     } else
@@ -2251,11 +2267,11 @@ processChunk(FILE *          const ifP,
             (*dcolP)->b = get_byte(ifP, iffid, NULL);
             get_byte(ifP, iffid, NULL);       /* skip pad byte */
             break;
-        case ID_BODY: 
+        case ID_BODY:
             if (cmaponly || (*bmhdP && (*bmhdP)->nPlanes == 0))
                 skip_chunk(ifP, ID_BODY,  chunksize);
             else {
-                process_body(ifP, chunksize, *bmhdP, cmap, 
+                process_body(ifP, chunksize, *bmhdP, cmap,
                              maskfile, fakeviewport, isdeepopt, *dcolP,
                              viewportmodesP);
 
@@ -2280,11 +2296,11 @@ processChunk(FILE *          const ifP,
             x = get_big_short(ifP, ID_DPI, NULL);
             y = get_big_short(ifP, ID_DPI, NULL);
             if (verbose)
-                pm_message("%s chunk:  dpi_x = %d    dpi_y = %d", 
+                pm_message("%s chunk:  dpi_x = %d    dpi_y = %d",
                            ID2string(ID_DPI), x, y);
         } break;
         default:
-            pm_message("unknown chunk type %s - skipping", 
+            pm_message("unknown chunk type %s - skipping",
                        ID2string(iffid));
             skip_chunk(ifP, iffid, chunksize);
             break;
@@ -2295,7 +2311,7 @@ processChunk(FILE *          const ifP,
     if (ODD(chunksize)) {
         get_byte(ifP, iffid, NULL);
         bytesread += 1;
-    } 
+    }
     *bytesReadP = bytesread;
 }
 
@@ -2338,7 +2354,7 @@ maybeWriteColorMap(FILE *               const ofP,
             prepareCmap(bmhdP, cmapP);
             cmapToPpm(ofP, cmapP);
         } else
-            pm_error("ILBM has neither %s or %s chunk", 
+            pm_error("ILBM has neither %s or %s chunk",
                      ID2string(ID_BODY), ID2string(ID_CMAP));
     }
 }
@@ -2413,13 +2429,13 @@ main(int argc, char *argv[]) {
                          "as argument");
             if( ignorecount >= MAX_IGNORE )
                 pm_error("max %d chunk IDs to ignore", MAX_IGNORE);
-            ignorelist[ignorecount++] = 
-                MAKE_ID(argv[argn][0], argv[argn][1], argv[argn][2], 
+            ignorelist[ignorecount++] =
+                MAKE_ID(argv[argn][0], argv[argn][1], argv[argn][2],
                         argv[argn][3]);
         } else
             pm_usage(usage);
         ++argn;
-    }    
+    }
 
     if( argn < argc ) {
         ifP = pm_openr( argv[argn] );
@@ -2439,7 +2455,7 @@ main(int argc, char *argv[]) {
         pm_error("input is not a FORM type IFF file");
     formsize = get_big_long(ifP, ID_FORM, NULL);
     typeid = get_big_long(ifP, ID_FORM, NULL);
-    if (typeid != ID_ILBM && typeid != ID_RGBN && typeid != ID_RGB8 && 
+    if (typeid != ID_ILBM && typeid != ID_RGBN && typeid != ID_RGB8 &&
         typeid != ID_PBM)
         pm_error("input is not an ILBM, RGBN, RGB8 or PBM "
                  "type FORM IFF file");
@@ -2474,7 +2490,7 @@ main(int argc, char *argv[]) {
 
     {
         unsigned int skipped;
-        
+
         for (skipped = 0; fgetc(ifP) != EOF; ++skipped)
             ++bytesread;
 
@@ -2492,3 +2508,6 @@ main(int argc, char *argv[]) {
 
     return 0;
 }
+
+
+
diff --git a/converter/ppm/picttoppm.c b/converter/ppm/picttoppm.c
index b14675c7..b8fb8642 100644
--- a/converter/ppm/picttoppm.c
+++ b/converter/ppm/picttoppm.c
@@ -15,7 +15,7 @@
  * University of British Columbia
  *
  *
- * 2003-02:    Handling for DirectBitsRgn opcode (0x9b) added by 
+ * 2003-02:    Handling for DirectBitsRgn opcode (0x9b) added by
  *             kabe@sra-tohoku.co.jp.
  *
  * 2004-03-27: Several bugs fixed by Steve Summit, scs@eskimo.com.
@@ -153,7 +153,7 @@ struct raster {
 
    Each pixel is either a palette index or an RGB triple, depending on
    the format of the associated PICT.
-   
+
    Each pixel is one byte if the associated PICT has 8 or fewer bits
    per pixel.  If the associated PICT has 16 or 32 bits per pixel, an
    element herein is 2 or 4 bytes, respectively.
@@ -231,7 +231,7 @@ allocateRaster(struct raster * const rasterP,
     default:
         pm_error("INTERNAL ERROR: impossible bitsPerPixel value in "
                  "unpackbits(): %u", bitsPerPixel);
-    }    
+    }
     if (UINT_MAX / rasterP->rowSize < rasterP->rowCount)
         pm_error("Arithmetic overflow computing size of %u x %u pixel "
                  "array.", rasterP->rowSize, rasterP->rowCount);
@@ -360,7 +360,7 @@ readSignedByte(void) {
 
 
 
-static void 
+static void
 readShortPoint(struct Point * const p) {
     p->x = readSignedByte();
     p->y = readSignedByte();
@@ -457,15 +457,15 @@ const_name(const struct const_name * const table,
     for (i = 0; table[i].name; ++i)
         if (table[i].value == ct)
             return table[i].name;
-    
+
     sprintf(numbuf, "? (%u)", ct);
     return numbuf;
 }
 
 
 
-static void 
-picComment(Word const type, 
+static void
+picComment(Word const type,
            int const length) {
 
     unsigned int remainingLength;
@@ -736,7 +736,7 @@ parseFontLine(const char **      const token,
 
 
 
-static int 
+static int
 load_fontdir(const char * const dirfile) {
 /*----------------------------------------------------------------------------
    Load the font directory from file named 'dirfile'.  Add its contents
@@ -744,7 +744,7 @@ load_fontdir(const char * const dirfile) {
 -----------------------------------------------------------------------------*/
     FILE * ifP;
     unsigned int nFont;
-    char line[1024]; 
+    char line[1024];
 
     ifP = pm_openr(dirfile);
 
@@ -832,7 +832,7 @@ rectheight(const struct Rect * const r) {
 
 
 static bool
-rectsamesize(struct Rect const r1, 
+rectsamesize(struct Rect const r1,
              struct Rect const r2) {
     return r1.right - r1.left == r2.right - r2.left &&
            r1.bottom - r1.top == r2.bottom - r2.top ;
@@ -841,8 +841,8 @@ rectsamesize(struct Rect const r1,
 
 
 static void
-rectinter(struct Rect   const r1, 
-          struct Rect   const r2, 
+rectinter(struct Rect   const r1,
+          struct Rect   const r2,
           struct Rect * const intersectionP) {
 
     intersectionP->left   = MAX(r1.left,   r2.left);
@@ -854,8 +854,8 @@ rectinter(struct Rect   const r1,
 
 
 static void
-rectscale(struct Rect * const r, 
-          double        const xscale, 
+rectscale(struct Rect * const r,
+          double        const xscale,
           double        const yscale) {
     r->left *= xscale;
     r->right *= xscale;
@@ -886,7 +886,7 @@ addBlitList(blitList *        const blitListP,
             int               const mode) {
 
     struct blit_info * biP;
-    
+
     MALLOCVAR(biP);
     if (biP == NULL)
         pm_error("out of memory for blit list");
@@ -940,8 +940,8 @@ rgbIsBlack(const struct RGBColor * const colorP) {
 }
 
 
-static void 
-srcCopy(struct RGBColor * const src, 
+static void
+srcCopy(struct RGBColor * const src,
         struct RGBColor * const dst) {
 
     if (rgbIsBlack(src))
@@ -952,8 +952,8 @@ srcCopy(struct RGBColor * const src,
 
 
 
-static void 
-srcOr(struct RGBColor * const src, 
+static void
+srcOr(struct RGBColor * const src,
       struct RGBColor * const dst) {
     if (rgbIsBlack(src))
         *dst = foreground;
@@ -961,8 +961,8 @@ srcOr(struct RGBColor * const src,
 
 
 
-static void 
-srcXor(struct RGBColor * const src, 
+static void
+srcXor(struct RGBColor * const src,
        struct RGBColor * const dst) {
     dst->red ^= ~src->red;
     dst->grn ^= ~src->grn;
@@ -971,8 +971,8 @@ srcXor(struct RGBColor * const src,
 
 
 
-static void 
-srcBic(struct RGBColor * const src, 
+static void
+srcBic(struct RGBColor * const src,
        struct RGBColor * const dst) {
     if (rgbIsBlack(src))
         *dst = background;
@@ -980,8 +980,8 @@ srcBic(struct RGBColor * const src,
 
 
 
-static void 
-notSrcCopy(struct RGBColor * const src, 
+static void
+notSrcCopy(struct RGBColor * const src,
            struct RGBColor * const dst) {
     if (rgbIsWhite(src))
         *dst = foreground;
@@ -991,8 +991,8 @@ notSrcCopy(struct RGBColor * const src,
 
 
 
-static void 
-notSrcOr(struct RGBColor * const src, 
+static void
+notSrcOr(struct RGBColor * const src,
          struct RGBColor * const dst) {
     if (rgbIsWhite(src))
         *dst = foreground;
@@ -1000,8 +1000,8 @@ notSrcOr(struct RGBColor * const src,
 
 
 
-static void 
-notSrcBic(struct RGBColor * const src, 
+static void
+notSrcBic(struct RGBColor * const src,
           struct RGBColor * const dst) {
     if (rgbIsWhite(src))
         *dst = background;
@@ -1009,8 +1009,8 @@ notSrcBic(struct RGBColor * const src,
 
 
 
-static void 
-notSrcXor(struct RGBColor * const src, 
+static void
+notSrcXor(struct RGBColor * const src,
           struct RGBColor * const dst) {
     dst->red ^= src->red;
     dst->grn ^= src->grn;
@@ -1019,8 +1019,8 @@ notSrcXor(struct RGBColor * const src,
 
 
 
-static void 
-addOver(struct RGBColor * const src, 
+static void
+addOver(struct RGBColor * const src,
         struct RGBColor * const dst) {
     dst->red += src->red;
     dst->grn += src->grn;
@@ -1029,8 +1029,8 @@ addOver(struct RGBColor * const src,
 
 
 
-static void 
-addPin(struct RGBColor * const src, 
+static void
+addPin(struct RGBColor * const src,
        struct RGBColor * const dst) {
     if ((long)dst->red + (long)src->red > (long)op_color.red)
         dst->red = op_color.red;
@@ -1050,8 +1050,8 @@ addPin(struct RGBColor * const src,
 
 
 
-static void 
-subOver(struct RGBColor * const src, 
+static void
+subOver(struct RGBColor * const src,
         struct RGBColor * const dst) {
     dst->red -= src->red;
     dst->grn -= src->grn;
@@ -1063,8 +1063,8 @@ subOver(struct RGBColor * const src,
 /* or maybe its src - dst; my copy of Inside Mac is unclear */
 
 
-static void 
-subPin(struct RGBColor * const src, 
+static void
+subPin(struct RGBColor * const src,
        struct RGBColor * const dst) {
     if ((long)dst->red - (long)src->red < (long)op_color.red)
         dst->red = op_color.red;
@@ -1084,8 +1084,8 @@ subPin(struct RGBColor * const src,
 
 
 
-static void 
-adMax(struct RGBColor * const src, 
+static void
+adMax(struct RGBColor * const src,
       struct RGBColor * const dst) {
     if (src->red > dst->red) dst->red = src->red;
     if (src->grn > dst->grn) dst->grn = src->grn;
@@ -1094,8 +1094,8 @@ adMax(struct RGBColor * const src,
 
 
 
-static void 
-adMin(struct RGBColor * const src, 
+static void
+adMin(struct RGBColor * const src,
       struct RGBColor * const dst) {
     if (src->red < dst->red) dst->red = src->red;
     if (src->grn < dst->grn) dst->grn = src->grn;
@@ -1104,8 +1104,8 @@ adMin(struct RGBColor * const src,
 
 
 
-static void 
-blend(struct RGBColor * const src, 
+static void
+blend(struct RGBColor * const src,
       struct RGBColor * const dst) {
 #define blend_component(cmp)    \
     ((long)src->cmp * (long)op_color.cmp) / 65536 +    \
@@ -1118,8 +1118,8 @@ blend(struct RGBColor * const src,
 
 
 
-static void 
-transparent(struct RGBColor * const src, 
+static void
+transparent(struct RGBColor * const src,
             struct RGBColor * const dst) {
     if (src->red != background.red ||
         src->grn != background.grn ||
@@ -1130,7 +1130,7 @@ transparent(struct RGBColor * const src,
 
 
 
-static transfer_func 
+static transfer_func
 transfer(int const mode) {
     switch (mode) {
     case  0: return srcCopy;
@@ -1162,7 +1162,7 @@ transfer(int const mode) {
 static pixval
 redepth(pixval const c,
         pixval const oldMaxval) {
-    
+
     return ROUNDDIV(c * PPM_MAXMAXVAL, oldMaxval);
 }
 
@@ -1185,7 +1185,7 @@ decode16(unsigned char * const sixteen) {
     retval.red = (sixteen[0] & 0x7c) >> 2;
     retval.grn = (sixteen[0] & 0x03) << 3 | (sixteen[1] & 0xe0) >> 5;
     retval.blu = (sixteen[1] & 0x1f) >> 0;
-                
+
     return retval;
 }
 
@@ -1198,9 +1198,9 @@ doDiffSize(struct Rect       const clipsrc,
            int               const xsize,
            int               const ysize,
            transfer_func     const trf,
-           struct RGBColor * const color_map, 
+           struct RGBColor * const color_map,
            unsigned char *   const src,
-           int               const srcwid, 
+           int               const srcwid,
            struct rgbPlanes  const dst,
            unsigned int      const dstwid) {
 
@@ -1275,7 +1275,7 @@ doDiffSize(struct Rect       const clipsrc,
     case 32: {
         unsigned int const planeSize = srcwid / 4;
         unsigned int rowNumber;
-        
+
         for (rowNumber = 0; rowNumber < ysize; ++rowNumber) {
             unsigned char * const row = &src[rowNumber * srcwid];
             unsigned char * const redPlane = &row[planeSize * 0];
@@ -1300,16 +1300,16 @@ doDiffSize(struct Rect       const clipsrc,
     ppm_readppminit(scaled = pm_openr(tempFilename), &cols, &rows,
                     &maxval, &format);
     row = ppm_allocrow(cols);
-    /* couldn't hurt to assert cols, rows and maxval... */  
+    /* couldn't hurt to assert cols, rows and maxval... */
 
     if (trf == NULL) {
         while (rows-- > 0) {
             unsigned int i;
             ppm_readppmrow(scaled, row, cols, maxval, format);
             for (i = 0, rowp = row; i < cols; ++i, ++rowp) {
-                *reddst++ = PPM_GETR(*rowp) * 65536L / (maxval + 1); 
-                *grndst++ = PPM_GETG(*rowp) * 65536L / (maxval + 1); 
-                *bludst++ = PPM_GETB(*rowp) * 65536L / (maxval + 1); 
+                *reddst++ = PPM_GETR(*rowp) * 65536L / (maxval + 1);
+                *grndst++ = PPM_GETG(*rowp) * 65536L / (maxval + 1);
+                *bludst++ = PPM_GETB(*rowp) * 65536L / (maxval + 1);
             }
             reddst += dstadd;
             grndst += dstadd;
@@ -1325,9 +1325,9 @@ doDiffSize(struct Rect       const clipsrc,
                 dst_c.red = *reddst;
                 dst_c.grn = *grndst;
                 dst_c.blu = *bludst;
-                src_c.red = PPM_GETR(*rowp) * 65536L / (maxval + 1); 
-                src_c.grn = PPM_GETG(*rowp) * 65536L / (maxval + 1); 
-                src_c.blu = PPM_GETB(*rowp) * 65536L / (maxval + 1); 
+                src_c.red = PPM_GETR(*rowp) * 65536L / (maxval + 1);
+                src_c.grn = PPM_GETG(*rowp) * 65536L / (maxval + 1);
+                src_c.blu = PPM_GETB(*rowp) * 65536L / (maxval + 1);
                 (*trf)(&src_c, &dst_c);
                 *reddst++ = dst_c.red;
                 *grndst++ = dst_c.grn;
@@ -1480,7 +1480,7 @@ blitIdempotent(unsigned int          const pixSize,
                unsigned int          const ysize,
                unsigned char *       const src,
                unsigned int          const srcwid,
-               struct RGBColor *     const colorMap, 
+               struct RGBColor *     const colorMap,
                struct rgbPlanes      const dst,
                unsigned int          const dstwid) {
 /*----------------------------------------------------------------------------
@@ -1494,7 +1494,7 @@ blitIdempotent(unsigned int          const pixSize,
     switch (pixSize) {
     case 8: {
         unsigned int rowNumber;
-        
+
         for (rowNumber = 0; rowNumber < ysize; ++rowNumber) {
             unsigned char * const srcrow = &src[rowNumber * srcwid];
             unsigned int const dstRowCurs = rowNumber * dstwid;
@@ -1554,15 +1554,15 @@ blitIdempotent(unsigned int          const pixSize,
 
 
 static void
-doBlit(struct Rect       const srcRect, 
-       struct Rect       const dstRect, 
-       struct Rect       const srcBounds, 
+doBlit(struct Rect       const srcRect,
+       struct Rect       const dstRect,
+       struct Rect       const srcBounds,
        struct raster     const srcplane,
-       struct Rect       const dstBounds, 
+       struct Rect       const dstBounds,
        struct rgbPlanes  const canvasPlanes,
-       int               const pixSize, 
-       int               const dstwid, 
-       struct RGBColor * const color_map, 
+       int               const pixSize,
+       int               const dstwid,
+       struct RGBColor * const color_map,
        int               const mode) {
 /*----------------------------------------------------------------------------
    Transfer some pixels from 'srcplane' to 'canvasPlanes', applying the
@@ -1583,7 +1583,6 @@ doBlit(struct Rect       const srcRect,
     int dstoff;
     int xsize;
     int ysize;
-    int srcadd;
     transfer_func trf;
 
     if (verbose) {
@@ -1604,7 +1603,6 @@ doBlit(struct Rect       const srcRect,
         src = srcplane.bytes + srcRowNumber * srcplane.rowSize + srcRowOffset;
         xsize = rectwidth(&srcRect);
         ysize = rectheight(&srcRect);
-        srcadd = srcplane.rowSize - xsize * pkpixsize;
     }
 
     dstoff = (dstRect.top - dstBounds.top) * dstwid +
@@ -1635,16 +1633,16 @@ doBlit(struct Rect       const srcRect,
 
 
 static int
-blit(struct Rect       const srcRect, 
-     struct Rect       const srcBounds, 
+blit(struct Rect       const srcRect,
+     struct Rect       const srcBounds,
      struct raster     const srcplane,
      struct canvas *   const canvasP,
      blitList *        const blitListP,
-     int               const pixSize, 
-     struct Rect       const dstRect, 
-     struct Rect       const dstBounds, 
-     int               const dstwid, 
-     struct RGBColor * const color_map, 
+     int               const pixSize,
+     struct Rect       const dstRect,
+     struct Rect       const dstBounds,
+     int               const dstwid,
+     struct RGBColor * const color_map,
      int               const mode) {
 /*----------------------------------------------------------------------------
    'srcplane' contains the rectangle 'srcBounds' of the image.
@@ -1657,7 +1655,7 @@ blit(struct Rect       const srcRect,
 
     /* I can't tell what the result value of this function is supposed to mean,
        but I found several return statements that did not set it to anything,
-       and several calls that examine it.  I'm guessing that "1" is the 
+       and several calls that examine it.  I'm guessing that "1" is the
        appropriate thing to return in those cases, so I made it so.
        -Bryan 00.03.02
     */
@@ -1701,7 +1699,7 @@ blit(struct Rect       const srcRect,
  * even only a bit, but that would require even more extra work).
  */
 
-static void 
+static void
 allocPlanes(unsigned int       const width,
             unsigned int       const height,
             struct rgbPlanes * const planesP) {
@@ -1858,7 +1856,7 @@ doBlitList(struct canvas * const canvasP,
 
     if (xscale != 1.0 || yscale != 1.0) {
         struct blit_info * biP;
-        
+
         for (biP = blitListP->firstP; biP; biP = biP->next)
             rectscale(&biP->dstRect, xscale, yscale);
 
@@ -2078,7 +2076,7 @@ readColorTable(void) {
 
 static void
 readBytes(FILE *          const ifP,
-          unsigned int    const n, 
+          unsigned int    const n,
           unsigned char * const buf) {
 
     align += n;
@@ -2111,7 +2109,7 @@ expand4Bits(unsigned char * const packed,
     unsigned char * dst;
 
     dst = &expanded[0];
-    
+
     for (i = 0; i < packedLen; ++i) {
         *dst++ = (packed[i] >> 4) & 0x0f;
         *dst++ = (packed[i] >> 0) & 0x0f;
@@ -2124,12 +2122,12 @@ static void
 expand2Bits(unsigned char * const packed,
             unsigned char * const expanded,
             unsigned int    const packedLen) {
-        
+
     unsigned int i;
     unsigned char * dst;
 
     dst = &expanded[0];
-    
+
     for (i = 0; i < packedLen; ++i) {
         *dst++ = (packed[i] >> 6) & 0x03;
         *dst++ = (packed[i] >> 4) & 0x03;
@@ -2149,7 +2147,7 @@ expand1Bit(unsigned char * const packed,
     unsigned char * dst;
 
     dst = &expanded[0];
-    
+
     for (i = 0; i < packedLen; ++i) {
         *dst++ = (packed[i] >> 7) & 0x01;
         *dst++ = (packed[i] >> 6) & 0x01;
@@ -2165,7 +2163,7 @@ expand1Bit(unsigned char * const packed,
 
 
 static void
-unpackBuf(unsigned char *  const packed, 
+unpackBuf(unsigned char *  const packed,
           unsigned int     const packedLen,
           int              const bitsPerPixel,
           unsigned char ** const expandedP,
@@ -2186,14 +2184,9 @@ unpackBuf(unsigned char *  const packed,
    'packedLen' must not be greater than 256.
 -----------------------------------------------------------------------------*/
     static unsigned char expanded[256 * 8];
-    unsigned char * src;
-    unsigned char * dst;
 
     assert(packedLen <= 256);
 
-    src = &packed[0];
-    dst = &expanded[0];
-
     switch (bitsPerPixel) {
     case 8:
     case 16:
@@ -2247,7 +2240,7 @@ unpackUncompressedBits(FILE *          const ifP,
     if (linebuf == NULL)
         pm_error("can't allocate memory for line buffer");
 
-    for (rowOfRect = 0; rowOfRect < raster.rowCount; ++rowOfRect) { 
+    for (rowOfRect = 0; rowOfRect < raster.rowCount; ++rowOfRect) {
         unsigned char * bytePixels;
         unsigned int expandedByteCount;
         unsigned char * rasterRow;
@@ -2331,12 +2324,12 @@ expandRun(unsigned char * const block,
                  "Invalid PICT file.");
     else {
         unsigned int const runLength = (block[0] ^ 0xff) + 2;
-        
+
         unsigned int i;
         unsigned char * bytePixels;  /* Points to static storage */
         unsigned int expandedByteCount;
         unsigned int outputCursor;
-        
+
         assert(block[0] & 0x80);  /* It's a run */
 
         if (verbose > 1)
@@ -2356,13 +2349,13 @@ expandRun(unsigned char * const block,
 
            That is what I saw in a test image.
         */
-        
+
         if (expandedByteCount * runLength > expandedSize)
             pm_error("Invalid PICT image.  It contains a row with more pixels "
                      "than the width of the rectangle containing it, "
                      "even padded up to a "
                      "multiple of 16 pixels.  Use -verbose to see details.");
-        
+
         outputCursor = 0;
         for (i = 0; i < runLength; ++i) {
             unsigned int j;
@@ -2414,14 +2407,14 @@ copyPixelGroup(unsigned char * const block,
         unsigned char * bytePixels;  /* Points to static storage */
         unsigned int bytePixelLen;
         unsigned int rasterBytesGenerated;
-        
+
         assert(blockLimit >= 1);  /* block[0] exists */
         assert((block[0] & 0x80) == 0);  /* It's not a run */
-        
+
         if (verbose > 1)
             pm_message("Block: %u explicit packed %u-byte units",
                        groupLen, pkpixsize);
-        
+
         unpackBuf(&block[1], groupLen * pkpixsize, bitsPerPixel,
                   &bytePixels, &bytePixelLen);
 
@@ -2439,7 +2432,7 @@ copyPixelGroup(unsigned char * const block,
 
         for (i = 0; i < rasterBytesGenerated; ++i)
             dest[i] = bytePixels[i];
-        
+
         *blockLengthP = blockLength;
         *rasterBytesGeneratedP = rasterBytesGenerated;
     }
@@ -2506,9 +2499,9 @@ interpretCompressedLine(unsigned char * const linebuf,
 
     for (lineCursor = 0, rasterCursor = 0; lineCursor < linelen; ) {
         unsigned int blockLength, rasterBytesGenerated;
-        
+
         assert(lineCursor <= linelen);
-            
+
         if (verbose > 2)
             pm_message("At Byte %u of line, Column %u of row",
                        lineCursor, rasterCursor);
@@ -2518,7 +2511,7 @@ interpretCompressedLine(unsigned char * const linebuf,
             bitsPerPixel,
             &rowRaster[rasterCursor], rowSize - rasterCursor,
             &blockLength, &rasterBytesGenerated);
-        
+
         lineCursor += blockLength;
         rasterCursor += rasterBytesGenerated;
         assert(rasterCursor <= rowSize);
@@ -2546,7 +2539,7 @@ interpretCompressedLine(unsigned char * const linebuf,
   Of course, neither 200 nor 250 make any logical sense.  In the worst case,
   you can represent 254 pixels of 8 bpp or less in a 255 byte line.
   In the worst case, you can represent 127 16bpp pixels in a 255 byte line.
-  So with 200 being the cutoff, it's actually impossible to represent some 
+  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 official spec for PICT.
@@ -2616,13 +2609,13 @@ 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) {
 
     unsigned int const rectHeight = boundsP->bottom - boundsP->top;
     unsigned int const rectWidth  = boundsP->right  - boundsP->left;
-    
+
     struct raster raster;
     unsigned int rowBytes;
 
@@ -2631,7 +2624,7 @@ unpackbits(FILE *           const ifP,
     if (verbose)
         pm_message("rowBytes = %u, bitsPerPixel = %d",
                    rowBytesArg, bitsPerPixel);
-        
+
     allocateRaster(&raster, rectWidth, rectHeight, bitsPerPixel);
 
     rowBytes = rowBytesArg ? rowBytesArg : raster.rowSize;
@@ -2769,7 +2762,7 @@ read8x8Pattern(struct Pattern * const pat) {
 
 static drawFn BkPat;
 
-static void 
+static void
 BkPat(struct canvas * const canvasP,
       blitList *      const blitListP,
       int             const version) {
@@ -2781,7 +2774,7 @@ BkPat(struct canvas * const canvasP,
 
 static drawFn PnPat;
 
-static void 
+static void
 PnPat(struct canvas * const canvasP,
       blitList *      const blitListP,
       int             const version) {
@@ -2793,7 +2786,7 @@ PnPat(struct canvas * const canvasP,
 
 static drawFn FillPat;
 
-static void 
+static void
 FillPat(struct canvas * const canvasP,
         blitList *      const blitListP,
         int             const version) {
@@ -2805,7 +2798,7 @@ FillPat(struct canvas * const canvasP,
 
 static drawFn PnSize;
 
-static void 
+static void
 PnSize(struct canvas * const canvasP,
        blitList *      const blitListP,
        int             const version) {
@@ -2820,7 +2813,7 @@ PnSize(struct canvas * const canvasP,
 
 static drawFn PnSize;
 
-static void 
+static void
 PnMode(struct canvas * const canvasP,
        blitList *      const blitListP,
        int             const version) {
@@ -2832,13 +2825,13 @@ PnMode(struct canvas * const canvasP,
     if (verbose)
         pm_message("pen transfer mode = %s",
             const_name(transfer_name, pen_mode));
-    
+
     pen_trf = transfer(pen_mode);
 }
 
 
 
-static void 
+static void
 readRgb(struct RGBColor * const rgb) {
     rgb->red = readWord();
     rgb->grn = readWord();
@@ -2849,14 +2842,14 @@ readRgb(struct RGBColor * const rgb) {
 
 static drawFn RGBFgCol;
 
-static void 
+static void
 RGBFgCol(struct canvas * const canvasP,
          blitList *      const blitListP,
          int             const version) {
 
     readRgb(&foreground);
     if (verbose)
-        pm_message("foreground now [%d,%d,%d]", 
+        pm_message("foreground now [%d,%d,%d]",
             foreground.red, foreground.grn, foreground.blu);
 }
 
@@ -2864,14 +2857,14 @@ RGBFgCol(struct canvas * const canvasP,
 
 static drawFn RGBBkCol;
 
-static void 
+static void
 RGBBkCol(struct canvas * const canvasP,
          blitList *      const blitListP,
          int             const version) {
 
     readRgb(&background);
     if (verbose)
-        pm_message("background now [%d,%d,%d]", 
+        pm_message("background now [%d,%d,%d]",
             background.red, background.grn, background.blu);
 }
 
@@ -2879,11 +2872,11 @@ RGBBkCol(struct canvas * const canvasP,
 
 #define PIXEL_INDEX(x,y) ((y) - picFrame.top) * rowlen + (x) - picFrame.left
 
-static void 
+static void
 draw_pixel(struct canvas *   const canvasP,
-           int               const x, 
-           int               const y, 
-           struct RGBColor * const clr, 
+           int               const x,
+           int               const y,
+           struct RGBColor * const clr,
            transfer_func           trf) {
 
     if (x < clip_rect.left || x >= clip_rect.right ||
@@ -2896,7 +2889,7 @@ draw_pixel(struct canvas *   const canvasP,
         dst.red = canvasP->planes.red[i];
         dst.grn = canvasP->planes.grn[i];
         dst.blu = canvasP->planes.blu[i];
- 
+
         (*trf)(clr, &dst);
 
         canvasP->planes.red[i] = dst.red;
@@ -2907,7 +2900,7 @@ draw_pixel(struct canvas *   const canvasP,
 
 
 
-static void 
+static void
 draw_pen_rect(struct canvas * const canvasP,
               struct Rect *   const r) {
 
@@ -2918,7 +2911,7 @@ draw_pen_rect(struct canvas * const canvasP,
     struct RGBColor dst;
 
     i = PIXEL_INDEX(r->left, r->top);  /* initial value */
-    
+
     for (y = r->top; y < r->bottom; y++) {
         for (x = r->left; x < r->right; x++) {
             dst.red = canvasP->planes.red[i];
@@ -2942,9 +2935,9 @@ draw_pen_rect(struct canvas * const canvasP,
 
 
 
-static void 
+static void
 draw_pen(struct canvas * const canvasP,
-         int             const x, 
+         int             const x,
          int             const y) {
 
     struct Rect penrect;
@@ -2972,11 +2965,11 @@ draw_pen(struct canvas * const canvasP,
  *
  * Paul Heckbert    3 Sep 85
  */
-static void 
+static void
 scan_line(struct canvas * const canvasP,
-          short           const x1, 
-          short           const y1, 
-          short           const x2, 
+          short           const x1,
+          short           const y1,
+          short           const x2,
           short           const y2) {
 
     int d, x, y, ax, ay, sx, sy, dx, dy;
@@ -3023,7 +3016,7 @@ scan_line(struct canvas * const canvasP,
 
 static drawFn Line;
 
-static void 
+static void
 Line(struct canvas * const canvasP,
      blitList *      const blitListP,
      int             const version) {
@@ -3041,7 +3034,7 @@ Line(struct canvas * const canvasP,
 
 static drawFn LineFrom;
 
-static void 
+static void
 LineFrom(struct canvas * const canvasP,
          blitList *      const blitListP,
          int             const version) {
@@ -3062,7 +3055,7 @@ LineFrom(struct canvas * const canvasP,
 
 static drawFn ShortLine;
 
-static void 
+static void
 ShortLine(struct canvas * const canvasP,
           blitList *      const blitListP,
           int             const version) {
@@ -3074,7 +3067,7 @@ ShortLine(struct canvas * const canvasP,
         pm_message("(%d,%d) delta (%d, %d)", p1.x, p1.y, current.x, current.y);
     current.x += p1.x;
     current.y += p1.y;
-    
+
     if (!blitListP)
         scan_line(canvasP, p1.x, p1.y, current.x, current.y);
 }
@@ -3083,7 +3076,7 @@ ShortLine(struct canvas * const canvasP,
 
 static drawFn ShortLineFrom;
 
-static void 
+static void
 ShortLineFrom(struct canvas * const canvasP,
               blitList *      const blitListP,
               int             const version) {
@@ -3103,12 +3096,12 @@ ShortLineFrom(struct canvas * const canvasP,
 
 
 
-static void 
+static void
 do_paintRect(struct canvas * const canvasP,
              struct Rect     const prect) {
 
     struct Rect rect;
-  
+
     if (verbose)
         dumpRect("painting", prect);
 
@@ -3121,7 +3114,7 @@ do_paintRect(struct canvas * const canvasP,
 
 static drawFn paintRect;
 
-static void 
+static void
 paintRect(struct canvas * const canvasP,
           blitList *      const blitListP,
           int             const version) {
@@ -3135,7 +3128,7 @@ paintRect(struct canvas * const canvasP,
 
 static drawFn paintSameRect;
 
-static void 
+static void
 paintSameRect(struct canvas * const canvasP,
               blitList *      const blitListP,
               int             const version) {
@@ -3146,7 +3139,7 @@ paintSameRect(struct canvas * const canvasP,
 
 
 
-static void 
+static void
 do_frameRect(struct canvas * const canvasP,
              struct Rect     const rect) {
 
@@ -3160,7 +3153,7 @@ do_frameRect(struct canvas * const canvasP,
             draw_pen(canvasP, x, rect.top);
             draw_pen(canvasP, x, rect.bottom - pen_height);
         }
-        
+
         for (y = rect.top; y <= rect.bottom - pen_height ; y += pen_height) {
             draw_pen(canvasP, rect.left, y);
             draw_pen(canvasP, rect.right - pen_width, y);
@@ -3172,7 +3165,7 @@ do_frameRect(struct canvas * const canvasP,
 
 static drawFn frameRect;
 
-static void 
+static void
 frameRect(struct canvas * const canvasP,
           blitList *      const blitListP,
           int             const version) {
@@ -3186,7 +3179,7 @@ frameRect(struct canvas * const canvasP,
 
 static drawFn frameSameRect;
 
-static void 
+static void
 frameSameRect(struct canvas * const canvasP,
               blitList *      const blitListP,
               int             const version) {
@@ -3199,7 +3192,7 @@ frameSameRect(struct canvas * const canvasP,
 
 /* a stupid shell sort - I'm so embarrassed  */
 
-static void 
+static void
 poly_sort(int const sort_index, struct Point points[]) {
   int d, i, j, temp;
 
@@ -3232,9 +3225,9 @@ poly_sort(int const sort_index, struct Point points[]) {
 
 /* Watch out for the lack of error checking in the next two functions ... */
 
-static void 
+static void
 scan_poly(struct canvas * const canvasP,
-          int             const np, 
+          int             const np,
           struct Point          pts[]) {
 
   int dx,dy,dxabs,dyabs,i,scan_index,j,k,px,py;
@@ -3260,7 +3253,7 @@ scan_poly(struct canvas * const canvasP,
     scan_index++;
   }
 
-#define sign(x) ((x) > 0 ? 1 : ((x)==0 ? 0:(-1)) )   
+#define sign(x) ((x) > 0 ? 1 : ((x)==0 ? 0:(-1)) )
 
   old_sdy = sy0 = sign(pts[1].y - pts[0].y);
   for (j=0; j<np; j++) {
@@ -3319,7 +3312,7 @@ scan_poly(struct canvas * const canvasP,
   if (sy0 + sdy == 0) scan_index--;
 
   poly_sort(scan_index, coord);
-  
+
   toggle = 0;
   for (i = 0; i < scan_index; i++) {
     if ((coord[i].y == coord[i+1].y) && (toggle == 0))
@@ -3332,12 +3325,12 @@ scan_poly(struct canvas * const canvasP,
       toggle = 0;
   }
 }
-  
+
 
 
 static drawFn paintPoly;
-  
-static void 
+
+static void
 paintPoly(struct canvas * const canvasP,
           blitList *      const blitListP,
           int             const version) {
@@ -3359,7 +3352,7 @@ paintPoly(struct canvas * const canvasP,
 
 static drawFn PnLocHFrac;
 
-static void 
+static void
 PnLocHFrac(struct canvas * const canvasP,
            blitList *      const blitListP,
            int             const version) {
@@ -3374,7 +3367,7 @@ PnLocHFrac(struct canvas * const canvasP,
 
 static drawFn TxMode;
 
-static void 
+static void
 TxMode(struct canvas * const canvasP,
        blitList *      const blitListP,
        int             const version) {
@@ -3386,7 +3379,7 @@ TxMode(struct canvas * const canvasP,
     if (verbose)
         pm_message("text transfer mode = %s",
             const_name(transfer_name, text_mode));
-    
+
     /* ignore the text mask bit 'cause we don't handle it yet */
     text_trf = transfer(text_mode & ~64);
 }
@@ -3395,7 +3388,7 @@ TxMode(struct canvas * const canvasP,
 
 static drawFn TxFont;
 
-static void 
+static void
 TxFont(struct canvas * const canvasP,
        blitList *      const blitListP,
        int             const version) {
@@ -3409,7 +3402,7 @@ TxFont(struct canvas * const canvasP,
 
 static drawFn TxFace;
 
-static void 
+static void
 TxFace(struct canvas * const canvasP,
        blitList *      const blitListP,
        int             const version) {
@@ -3423,7 +3416,7 @@ TxFace(struct canvas * const canvasP,
 
 static drawFn TxSize;
 
-static void 
+static void
 TxSize(struct canvas * const canvasP,
        blitList *      const blitListP,
        int             const version) {
@@ -3445,7 +3438,7 @@ skip_text(blitList * const blitListP) {
 
 
 
-static int 
+static int
 abs_value(int const x) {
     if (x < 0)
         return -x;
@@ -3455,9 +3448,9 @@ abs_value(int const x) {
 
 
 
-static struct font* 
-get_font(int const font, 
-         int const size, 
+static struct font*
+get_font(int const font,
+         int const size,
          int const style) {
 
     int closeness, bestcloseness;
@@ -3490,8 +3483,8 @@ get_font(int const font,
 
 /* This only does 0, 90, 180 and 270 degree rotations */
 
-static void 
-rotate(int * const x, 
+static void
+rotate(int * const x,
        int * const y) {
     int tmp;
 
@@ -3522,7 +3515,7 @@ rotate(int * const x,
 
 static void
 do_ps_text(struct canvas * const canvasP,
-           Word            const tx, 
+           Word            const tx,
            Word            const ty) {
 
     int len, width, i, w, h, x, y, rx, ry, o;
@@ -3563,7 +3556,7 @@ do_ps_text(struct canvas * const canvasP,
     for (i = 0; i < len; i++) {
         if (!(glyph = tfont->glyph[str[i]]))
             continue;
-        
+
         y = ty - glyph->height - glyph->y;
         for (h = 0; h < glyph->height; h++) {
             for (w = 0; w < glyph->width; w++) {
@@ -3592,7 +3585,7 @@ do_ps_text(struct canvas * const canvasP,
 static void
 do_text(struct canvas *  const canvasP,
         blitList *       const blitListP,
-        Word             const startx, 
+        Word             const startx,
         Word             const starty) {
 
     if (blitListP)
@@ -3615,11 +3608,11 @@ do_text(struct canvas *  const canvasP,
                     int dy;
                     int h;
                     for (h = 0, dy = y - glyph->height - glyph->y;
-                         h < glyph->height; 
+                         h < glyph->height;
                          ++h, ++dy) {
                         int w;
                         for (w = 0; w < glyph->width; ++w) {
-                            struct RGBColor * const colorP = 
+                            struct RGBColor * const colorP =
                                 glyph->bmap[h * glyph->width + w] ?
                                 &black : &white;
                             draw_pixel(canvasP,
@@ -3710,7 +3703,7 @@ DHDVText(struct canvas * const canvasP,
 static void
 directBits(struct canvas * const canvasP,
            blitList *      const blitListP,
-           unsigned int    const pictVersion, 
+           unsigned int    const pictVersion,
            bool            const skipRegion) {
 
     struct pixMap   p;
@@ -3718,13 +3711,11 @@ directBits(struct canvas * const canvasP,
     struct Rect     dstRect;
     struct raster   raster;
     Word            mode;
-    unsigned int    rectWidth;
 
     /* skip fake len, and fake EOF */
     skip(4);    /* Ptr baseAddr == 0x000000ff */
     readWord();    /* version */
     readRect(&p.Bounds);
-    rectWidth = p.Bounds.right - p.Bounds.left;
     p.packType = readWord();
     p.packSize = readLong();
     p.hRes = readLong();
@@ -3750,7 +3741,7 @@ directBits(struct canvas * const canvasP,
     if (verbose)
         pm_message("transfer mode = %s", const_name(transfer_name, mode));
 
-    if (skipRegion) 
+    if (skipRegion)
         skip_poly_or_region(canvasP, blitListP, pictVersion);
 
     unpackbits(ifp, &p.Bounds, 0, p.pixelSize, &raster);
@@ -3793,8 +3784,8 @@ DirectBitsRgn(struct canvas * const canvasP,
 static void
 do_pixmap(struct canvas * const canvasP,
           blitList *      const blitListP,
-          int             const version, 
-          Word            const rowBytes, 
+          int             const version,
+          Word            const rowBytes,
           int             const is_region) {
 /*----------------------------------------------------------------------------
    Do a paletted image.
@@ -3850,8 +3841,8 @@ static void
 do_bitmap(FILE *          const ifP,
           struct canvas * const canvasP,
           blitList *      const blitListP,
-          int             const version, 
-          int             const rowBytes, 
+          int             const version,
+          int             const rowBytes,
           int             const is_region) {
 /*----------------------------------------------------------------------------
    Do a bitmap.  That's one bit per pixel, 0 is white, 1 is black.
@@ -3866,7 +3857,7 @@ do_bitmap(FILE *          const ifP,
         /* This raster contains padding on the right to make a multiple
            of 16 pixels per row.
         */
-    static struct RGBColor color_table[] = { 
+    static struct RGBColor color_table[] = {
         {65535L, 65535L, 65535L}, {0, 0, 0} };
 
     readRect(&Bounds);
@@ -3921,7 +3912,7 @@ static void
 BitsRegion(struct canvas * const canvasP,
            blitList *      const blitListP,
            int             const version) {
-    
+
     Word rowBytesWord;
     bool pixMap;
     unsigned int rowBytes;
@@ -3978,18 +3969,18 @@ static struct opdef const optable[] = {
 /* 0x1f */  { "OpColor", NA, OpColor, "RGB OpColor for arithmetic modes" },
 /* 0x20 */  { "Line", 8, Line, "pnLoc (point), newPt (point)" },
 /* 0x21 */  { "LineFrom", 4, LineFrom, "newPt (point)" },
-/* 0x22 */  { "ShortLine", 6, ShortLine, 
+/* 0x22 */  { "ShortLine", 6, ShortLine,
               "pnLoc (point, dh, dv (-128 .. 127))" },
 /* 0x23 */  { "ShortLineFrom", 2, ShortLineFrom, "dh, dv (-128 .. 127)" },
 /* 0x24 */  RESERVED_OP(WORD_LEN),
 /* 0x25 */  RESERVED_OP(WORD_LEN),
 /* 0x26 */  RESERVED_OP(WORD_LEN),
 /* 0x27 */  RESERVED_OP(WORD_LEN),
-/* 0x28 */  { "LongText", NA, LongText, 
+/* 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, 
+/* 0x2b */  { "DHDVText", NA, DHDVText,
               "dh, dv (0..255), count (0..255), text" },
 /* 0x2c */  RESERVED_OP(WORD_LEN),
 /* 0x2d */  RESERVED_OP(WORD_LEN),
@@ -4101,23 +4092,23 @@ static struct opdef const optable[] = {
 /* 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, 
+/* 0x9a */  { "DirectBitsRect", NA, DirectBitsRect,
               "PixMap, srcRect, dstRect, int copymode, PixData" },
-/* 0x9b */  { "DirectBitsRgn", NA, DirectBitsRgn, 
+/* 0x9b */  { "DirectBitsRgn", NA, DirectBitsRgn,
               "PixMap, srcRect, dstRect, int copymode, maskRgn, PixData" },
 /* 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, 
+/* 0xa1 */  { "LongComment", NA, LongComment,
               "kind (word), size (word), data" }
 };
 
 
 
 static void
-processOpcode(Word            const opcode, 
+processOpcode(Word            const opcode,
               struct canvas * const canvasP,
               blitList *      const blitListP,
               unsigned int    const version) {
@@ -4257,7 +4248,7 @@ interpretPict(FILE * const ofP) {
 
     while((opcode = get_op(version)) != 0xff)
         processOpcode(opcode, &canvas, fullres ? &blitList : NULL, version);
-    
+
     if (fullres) {
         if (blitList.unblittableText)
             pm_message("Warning: text is omitted from the output because "
@@ -4281,7 +4272,7 @@ loadDefaultFontDir(void) {
     int rc;
 
     rc = stat("fontdir", &statbuf);
-    
+
     if (rc == 0)
         load_fontdir("fontdir");
 }
diff --git a/converter/ppm/pjtoppm.c b/converter/ppm/pjtoppm.c
index 7b694fb3..b8b94f74 100644
--- a/converter/ppm/pjtoppm.c
+++ b/converter/ppm/pjtoppm.c
@@ -11,65 +11,87 @@
 */
 
 #include "ppm.h"
+#include "pm_c_util.h"
 #include "mallocvar.h"
 
 static char usage[] =  "[paintjetfile]";
 
-static int egetc ARGS((FILE *fp));
+
+
+static unsigned int
+uintProduct(unsigned int const multiplicand,
+            unsigned int const multiplier) {
+
+    if (UINT_MAX / multiplier < multiplicand)
+        pm_error("Airthmetic overflow");
+
+    return multiplicand * multiplier;
+}
+
+
+
 static int
-egetc(fp)
-    FILE *fp;
-{
+egetc(FILE * const ifP) {
     int c;
-    if ((c = fgetc(fp)) == -1)
+
+    c = fgetc(ifP);
+
+    if (c == -1)
         pm_error("unexpected end of file");
-    return(c);
+
+    return c;
 }
 
+
+
 int
-main(argc, argv)
-    int argc;
-    char *argv[];
-{
+main(int argc, const char ** argv) {
+
     int cmd, val;
     char buffer[BUFSIZ];
     int planes = 3, rows = -1, cols = -1;
-    int r = 0, c = 0, p = 0, i;
     unsigned char **image = NULL;
     int *imlen;
-    FILE *fp = stdin;
+    FILE * ifP;
     int mode;
     int argn;
     unsigned char bf[3];
-    pixel *pixrow;
+    pixel * pixrow;
+    int c;
+    int row;
+    int plane;
 
+    pm_proginit(&argc, argv);
 
-    ppm_init(&argc, argv);
     argn = 1;
     if (argn != argc)
-        fp = pm_openr(argv[argn++]);
+        ifP = pm_openr(argv[argn++]);
     else
-        fp = stdin;
+        ifP = stdin;
 
     if (argn != argc)
         pm_usage(usage);
 
-    while ((c = fgetc(fp)) != -1) {
+    row = 0;  /* initial value */
+    plane = 0;  /* initial value */
+
+    while ((c = fgetc(ifP)) != -1) {
         if (c != '\033')
             continue;
-        switch (c = egetc(fp)) {
+        switch (c = egetc(ifP)) {
         case 'E':   /* reset */
             break;
-        case '*':
-            cmd = egetc(fp);
+        case '*': {
+            unsigned int i;
+            cmd = egetc(ifP);
             for (i = 0; i < BUFSIZ; i++) {
-                if (!isdigit(c = egetc(fp)) && c != '+' && c != '-')
+                if (!isdigit(c = egetc(ifP)) && c != '+' && c != '-')
                     break;
                 buffer[i] = c;
             }
             if (i != 0) {
                 buffer[i] = '\0';
-                if (sscanf(buffer, "%d", &val) != 1) 
+                if (sscanf(buffer, "%d", &val) != 1)
                     pm_error("bad value `%s' at <ESC>*%c%c", buffer, cmd, c);
             }
             else
@@ -100,7 +122,7 @@ main(argc, argv)
                     break;
                 case 'U':   /* planes */
                     planes = val;
-                    if (planes != 3) 
+                    if (planes != 3)
                         pm_error("can handle only 3 plane files");
                     break;
                 case 'A':   /* begin raster */
@@ -126,38 +148,33 @@ main(argc, argv)
                     break;
                 case 'V':   /* send plane */
                 case 'W':   /* send last plane */
-                    if (rows == -1 || r >= rows || image == NULL) {
-                        if (rows == -1 || r >= rows)
+                    if (rows == -1 || row >= rows || image == NULL) {
+                        if (rows == -1 || row >= rows)
                             rows += 100;
                         if (image == NULL) {
-                            MALLOCARRAY(image, rows * planes);
-                            MALLOCARRAY(imlen, rows * planes);
-                        }
-                        else {
-                            image = (unsigned char **) 
-                                realloc(image, 
-                                        rows * planes * 
-                                        sizeof(unsigned char *));
-                            imlen = (int *) 
-                                realloc(imlen, rows * planes * sizeof(int));
+                            MALLOCARRAY(image, uintProduct(rows, planes));
+                            MALLOCARRAY(imlen, uintProduct(rows, planes));
+                        } else {
+                            REALLOCARRAY(image, uintProduct(rows, planes));
+                            REALLOCARRAY(imlen, uintProduct(rows, planes));
                         }
                     }
                     if (image == NULL || imlen == NULL)
                         pm_error("out of memory");
-                    if (p == planes) 
+                    if (plane >= planes)
                         pm_error("too many planes");
-                    cols = cols > val ? cols : val;
-                    imlen[r * planes + p] = val;
-                    MALLOCARRAY(image[r * planes + p], val);
-                    if (image[r * planes + p] == NULL) 
+                    cols = MAX(cols, val);
+                    imlen[row * planes + plane] = val;
+                    MALLOCARRAY(image[row * planes + plane], val);
+                    if (image[row * planes + plane] == NULL)
                         pm_error("out of memory");
-                    if (fread(image[r * planes + p], 1, val, fp) != val) 
+                    if (fread(image[row * planes + plane], 1, val, ifP) != val)
                         pm_error("short data");
                     if (c == 'V')
-                        p++;
+                        ++plane;
                     else {
-                        p = 0;
-                        r++;
+                        plane = 0;
+                        ++row;
                     }
                     break;
                 default:
@@ -166,7 +183,7 @@ main(argc, argv)
                 }
                 break;
             case 'p': /* Position */
-                if (p != 0) 
+                if (plane != 0)
                     pm_error("changed position in the middle of "
                              "transferring planes");
                 switch (c) {
@@ -175,15 +192,15 @@ main(argc, argv)
                     break;
                 case 'Y':
                     if (buffer[0] == '+')
-                        val = r + val;
+                        val = row + val;
                     if (buffer[0] == '-')
-                        val = r - val;
-                    for (; val > r; r++) 
-                        for (p = 0; p < 3; p++) {
-                            imlen[r * planes + p] = 0;
-                            image[r * planes + p] = NULL;
+                        val = row - val;
+                    for (; val > row; ++row)
+                        for (plane = 0; plane < 3; ++plane) {
+                            imlen[row * planes + plane] = 0;
+                            image[row * planes + plane] = NULL;
                         }
-                    r = val;
+                    row = val;
                     break;
                 default:
                     pm_message("uninmplemented <ESC>*%c%d%c", cmd, val, c);
@@ -192,62 +209,80 @@ main(argc, argv)
             default:
                 pm_message("uninmplemented <ESC>*%c%d%c", cmd, val, c);
                 break;
-            }
-        }
+             }
+        } /* case */
+        } /* switch */
     }
-    pm_close(fp);
-    rows = r;
+    pm_close(ifP);
+    rows = row;
     if (mode == 1) {
-        unsigned char *buf;
-        int newcols = 0;
-        newcols = 10240; /* It could not be larger that that! */
-        cols = 0;
-        for (r = 0; r < rows; r++) {
-            if (image[r * planes] == NULL)
+        int const newcols = 10240;  /* It could not be larger that that! */
+        unsigned char * buf;
+        unsigned int row;
+
+        for (row = 0, cols = 0; row < rows; ++row) {
+            unsigned int plane;
+            if (image[row * planes] == NULL)
                 continue;
-            for (p = 0; p < planes; p++) {
+            for (plane = 0; plane < planes; ++plane) {
+                unsigned int i;
+                unsigned int col;
                 MALLOCARRAY(buf, newcols);
-                if (buf == NULL) 
+                if (buf == NULL)
                     pm_error("out of memory");
-                for (i = 0, c = 0; c < imlen[p + r * planes]; c += 2)
-                    for (cmd = image[p + r * planes][c],
-                             val = image[p + r * planes][c+1]; 
-                         cmd >= 0 && i < newcols; cmd--, i++) 
+                for (i = 0, col = 0;
+                     col < imlen[plane + row * planes];
+                     col += 2)
+                    for (cmd = image[plane + row * planes][col],
+                             val = image[plane + row * planes][col+1];
+                         cmd >= 0 && i < newcols; cmd--, i++)
                         buf[i] = val;
-                cols = cols > i ? cols : i;
-                free(image[p + r * planes]);
-                /* 
-                 * This is less than what we have so it realloc should 
+                cols = MAX(cols, i);
+                free(image[plane + row * planes]);
+                /*
+                 * This is less than what we have so it realloc should
                  * not return null. Even if it does, tough! We will
                  * lose a line, and probably die on the next line anyway
                  */
-                image[p + r * planes] = (unsigned char *) realloc(buf, i);
+                image[plane + row * planes] = realloc(buf, i);
             }
         }
         cols *= 8;
     }
-            
-       
+
     ppm_writeppminit(stdout, cols, rows, (pixval) 255, 0);
     pixrow = ppm_allocrow(cols);
-    for (r = 0; r < rows; r++) {
-        if (image[r * planes] == NULL) {
-            for (c = 0; c < cols; c++)
-                PPM_ASSIGN(pixrow[c], 0, 0, 0);
+
+    for (row = 0; row < rows; ++row) {
+        if (image[row * planes] == NULL) {
+            unsigned int col;
+            for (col = 0; col < cols; ++col)
+                PPM_ASSIGN(pixrow[col], 0, 0, 0);
             continue;
         }
-        for (cmd = 0, c = 0; c < cols; c += 8, cmd++) 
-            for (i = 0; i < 8 && c + i < cols; i++) {
-                for (p = 0; p < planes; p++) 
-                    if (mode == 0 && cmd >= imlen[r * planes + p])
-                        bf[p] = 0;
-                    else
-                        bf[p] = (image[r * planes + p][cmd] & 
-                                 (1 << (7 - i))) ? 255 : 0;
-                PPM_ASSIGN(pixrow[c + i], bf[0], bf[1], bf[2]);
+        {
+            unsigned int col;
+            unsigned int cmd;
+            for (cmd = 0, col = 0; col < cols; col += 8, ++cmd) {
+                unsigned int i;
+                for (i = 0; i < 8 && col + i < cols; ++i) {
+                    unsigned int plane;
+                    for (plane = 0; plane < planes; ++plane)
+                        if (mode == 0 && cmd >= imlen[row * planes + plane])
+                            bf[plane] = 0;
+                        else
+                            bf[plane] = (image[row * planes + plane][cmd] &
+                                     (1 << (7 - i))) ? 255 : 0;
+                    PPM_ASSIGN(pixrow[col + i], bf[0], bf[1], bf[2]);
+                }
             }
-        ppm_writeppmrow(stdout, pixrow, cols, (pixval) 255, 0);
+        }
+        ppm_writeppmrow(stdout, pixrow, cols, 255, 0);
     }
     pm_close(stdout);
-    exit(0);
+
+    return 0;
 }
+
+
+
diff --git a/converter/ppm/ppmtoarbtxt.c b/converter/ppm/ppmtoarbtxt.c
index fc8927ce..6d4ed04e 100644
--- a/converter/ppm/ppmtoarbtxt.c
+++ b/converter/ppm/ppmtoarbtxt.c
@@ -734,15 +734,20 @@ interpretObjType(const char * const typstr) {
 
 
 static SkeletonObject *
-newIcSkelFromReplString(const char *       const objstr,
+newIcSkelFromReplString(const char *       const icolorObjstr,
                         SkeletonObjectType const objType) {
-
+/*----------------------------------------------------------------------------
+  A new skeleton for an integer color substitution specifier (class
+  OBJTYP_ICOLOR) whose replacement string (the stuff between the parentheses
+  in #(...)) says substitution type 'objType' and the rest of the
+  replacement string is 'icolorObjstr'.
+-----------------------------------------------------------------------------*/
     SkeletonObject * retval;
     unsigned int icolmin, icolmax;
     char formstr[MAX_OBJ_BUF];
-    unsigned int nOdata;
+    int nOdata;
 
-    nOdata = sscanf(objstr, "%*s%s%u%u", formstr, &icolmin, &icolmax);
+    nOdata = sscanf(icolorObjstr, "%s%u%u", formstr, &icolmin, &icolmax);
 
     if (nOdata == 3)
         retval = newIcolDataObj(objType, formstr, icolmin, icolmax);
@@ -758,16 +763,20 @@ newIcSkelFromReplString(const char *       const objstr,
 
 
 static SkeletonObject *
-newFcSkelFromReplString(const char *       const objstr,
+newFcSkelFromReplString(const char *       const fcolorObjstr,
                         SkeletonObjectType const objType) {
-
+/*----------------------------------------------------------------------------
+  A new skeleton for a floating point color substitution specifier (class
+  OBJTYP_FCOLOR) whose replacement string (the stuff between the parentheses
+  in #(...)) says substitution type 'objType' and the rest of the
+  replacement string is 'fcolorObjstr'.
+-----------------------------------------------------------------------------*/
     SkeletonObject * retval;
     double fcolmin, fcolmax;
     char formstr[MAX_OBJ_BUF];
-    unsigned int nOdata;
+    int nOdata;
 
-    nOdata = sscanf(objstr, "%*s%s%lf%lf", formstr,
-                    &fcolmin, &fcolmax);
+    nOdata = sscanf(fcolorObjstr, "%s%lf%lf", formstr, &fcolmin, &fcolmax);
 
     if (nOdata == 3)
         retval = newFcolDataObj(objType, formstr, fcolmin, fcolmax);
@@ -783,12 +792,19 @@ newFcSkelFromReplString(const char *       const objstr,
 
 
 static SkeletonObject *
-newISkelFromReplString(const char *       const objstr,
+newISkelFromReplString(const char *       const intObjstr,
                        SkeletonObjectType const objType) {
-
+/*----------------------------------------------------------------------------
+  A new skeleton for an integer substitution specifier (class OBJTYP_INT)
+  whose replacement string (the stuff between the parentheses in #(...))
+  says substitution type 'objType' and the rest of the replacement string is
+  'intObjstr'.
+-----------------------------------------------------------------------------*/
     SkeletonObject * retval;
     char formstr[MAX_OBJ_BUF];
-    unsigned int const nOdata = sscanf(objstr, "%*s%s", formstr);
+    int nOdata;
+
+    nOdata = sscanf(intObjstr, "%s", formstr);
     
     if (nOdata == 1)
         retval = newIdataObj(objType, formstr);
@@ -806,7 +822,7 @@ newISkelFromReplString(const char *       const objstr,
 static SkeletonObject *
 newSkeletonFromReplString(const char * const objstr) {
 /*----------------------------------------------------------------------------
-  Create a skeleton from the replacement string 'objstr' (the stuff
+  A new skeleton created from the replacement string 'objstr' (the stuff
   between the parentheses in #(...) ).
 
   Return NULL if it isn't a valid replacement string.
@@ -821,6 +837,7 @@ newSkeletonFromReplString(const char * const objstr) {
 
     SkeletonObject * retval;
     char typstr[MAX_OBJ_BUF];
+    int typlen;
     SkeletonObjectType objType;
     int conversionCt;
     char s1[MAX_OBJ_BUF];    /* Dry read. */
@@ -829,7 +846,8 @@ newSkeletonFromReplString(const char * const objstr) {
 
     typstr[0] = '\0';  /* initial value */
 
-    conversionCt = sscanf(objstr, "%s%s%f%f%s", typstr, s1, &f1, &f2, s2);
+    conversionCt = sscanf(objstr, "%s%n%s%f%f%s",
+                          typstr, &typlen, s1, &f1, &f2, s2);
     switch (conversionCt) {
     case 1: case 2: case 4:
         objType = interpretObjType(typstr);
@@ -840,13 +858,13 @@ newSkeletonFromReplString(const char * const objstr) {
 
     switch (objClass(objType)) {
     case OBJTYP_ICOLOR:
-        retval = newIcSkelFromReplString(objstr, objType);
+        retval = newIcSkelFromReplString(&objstr[typlen], objType);
         break;
     case OBJTYP_FCOLOR:
-        retval = newFcSkelFromReplString(objstr, objType);
+        retval = newFcSkelFromReplString(&objstr[typlen], objType);
         break;
     case OBJTYP_INT:
-        retval = newISkelFromReplString(objstr, objType);
+        retval = newISkelFromReplString(&objstr[typlen], objType);
         break;
     case OBJTYP_BDATA:
         retval = NULL;
diff --git a/converter/ppm/ppmtobmp.c b/converter/ppm/ppmtobmp.c
index 6d65d744..f89cec8d 100644
--- a/converter/ppm/ppmtobmp.c
+++ b/converter/ppm/ppmtobmp.c
@@ -13,6 +13,7 @@
  *
  */
 
+#define _DEFAULT_SOURCE 1 /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE 1    /* Make sure strdup() is in string.h */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
@@ -29,9 +30,9 @@
 
 #define MAXCOLORS 256
 
-enum colortype {TRUECOLOR, PALETTE};
+typedef enum {TRUECOLOR, PALETTE} Colortype;
 
-struct rgb {
+struct Rgb {
     unsigned char red;
     unsigned char grn;
     unsigned char blu;
@@ -50,13 +51,13 @@ typedef struct {
     colorhash_table cht;
 
     /* Indices in the following array are the same as in 'cht', above. */
-    struct rgb bmpMap[MAXCOLORS];
-} colorMap;
+    struct Rgb bmpMap[MAXCOLORS];
+} ColorMap;
 
 
 
 static void
-freeColorMap(const colorMap * const colorMapP) {
+freeColorMap(const ColorMap * const colorMapP) {
 
     if (colorMapP->cht)
         ppm_freecolorhash(colorMapP->cht);
@@ -68,11 +69,11 @@ struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    const char * inputFilename;
-    int class;  /* C_WIN or C_OS2 */
-    unsigned int bppSpec;
-    unsigned int bpp;
-    const char * mapfile;
+    const char *  inputFilename;
+    enum bmpClass class;
+    unsigned int  bppSpec;
+    unsigned int  bpp;
+    const char *  mapfile;
 };
 
 
@@ -93,15 +94,15 @@ parseCommandLine(int argc, const char ** argv,
     unsigned int windowsSpec, os2Spec, mapfileSpec;
 
     unsigned int option_def_index;
-    
+
     MALLOCARRAY(option_def, 100);
 
     option_def_index = 0;   /* incremented by OPTENTRY */
     OPTENT3('w', "windows",   OPT_FLAG, NULL, &windowsSpec,            0);
     OPTENT3('o', "os2",       OPT_FLAG, NULL, &os2Spec,                0);
-    OPTENT3(0,   "bpp",       OPT_UINT, &cmdlineP->bpp, 
+    OPTENT3(0,   "bpp",       OPT_UINT, &cmdlineP->bpp,
             &cmdlineP->bppSpec,      0);
-    OPTENT3(0,   "mapfile",   OPT_STRING, &cmdlineP->mapfile, 
+    OPTENT3(0,   "mapfile",   OPT_STRING, &cmdlineP->mapfile,
             &mapfileSpec,             0);
 
     opt.opt_table = option_def;
@@ -110,18 +111,18 @@ parseCommandLine(int argc, const char ** argv,
 
     pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
 
-    if (windowsSpec && os2Spec) 
+    if (windowsSpec && os2Spec)
         pm_error("Can't specify both -windows and -os2 options.");
-    else if (windowsSpec) 
-        cmdlineP->class = C_WIN;
+    else if (windowsSpec)
+        cmdlineP->class = BMP_C_WIN_V1;
     else if (os2Spec)
-        cmdlineP->class = C_OS2;
-    else 
-        cmdlineP->class = C_WIN;
+        cmdlineP->class = BMP_C_OS2_1x;
+    else
+        cmdlineP->class = BMP_C_WIN_V1;
 
 
     if (cmdlineP->bppSpec) {
-        if (cmdlineP->bpp != 1 && cmdlineP->bpp != 4 && 
+        if (cmdlineP->bpp != 1 && cmdlineP->bpp != 4 &&
             cmdlineP->bpp != 8 && cmdlineP->bpp != 24)
         pm_error("Invalid -bpp value specified: %u.  The only values valid "
                  "in the BMP format are 1, 4, 8, and 24 bits per pixel",
@@ -135,7 +136,7 @@ parseCommandLine(int argc, const char ** argv,
         cmdlineP->inputFilename = pm_strdup("-");  /* he wants stdin */
     else if (argc - 1 == 1)
         cmdlineP->inputFilename = pm_strdup(argv[1]);
-    else 
+    else
         pm_error("Too many arguments.  The only argument accepted "
                  "is the input file specificaton");
 
@@ -153,8 +154,8 @@ freeCommandLine(struct CmdlineInfo const cmdline) {
 
 
 static void
-PutByte(FILE * const fp, unsigned char const v) {
-    if (putc(v, fp) == EOF) 
+putByte(FILE * const fp, unsigned char const v) {
+    if (putc(v, fp) == EOF)
         pm_error("Write of a byte to a file failed.");
 
     /* Note:  a Solaris/SPARC user reported on 2003.09.29 that the above
@@ -168,139 +169,166 @@ PutByte(FILE * const fp, unsigned char const v) {
 
 
 static void
-PutShort(FILE * const fp, short const v) {
-    if (pm_writelittleshort(fp, v) == -1) 
+putShort(FILE * const fp, short const v) {
+    if (pm_writelittleshort(fp, v) == -1)
         pm_error("Write of a halfword to a file failed.");
 }
 
 
 
 static void
-PutLong(FILE * const fp, long const v) {
+putLong(FILE * const fp, long const v) {
     if (pm_writelittlelong(fp, v) == -1)
         pm_error("Write of a word to a file failed.");
 }
 
 
-
-/*
- * BMP writing
- */
+/*----------------------------------------------------------------------------
+   BMP writing
+-----------------------------------------------------------------------------*/
 
 static unsigned int
-BMPwritefileheader(FILE *        const fp, 
+bmpWriteFileHeader(FILE *        const fp,
                    unsigned int  const cbSize,
                    unsigned int  const offBits) {
 /*----------------------------------------------------------------------------
   Return the number of bytes written.
 -----------------------------------------------------------------------------*/
-    PutByte(fp, 'B');
-    PutByte(fp, 'M');
+    putByte(fp, 'B');
+    putByte(fp, 'M');
 
     /* cbSize */
-    PutLong(fp, cbSize);
-    
+    putLong(fp, cbSize);
+
     /* xHotSpot */
-    PutShort(fp, 0);
-    
+    putShort(fp, 0);
+
     /* yHotSpot */
-    PutShort(fp, 0);
-    
+    putShort(fp, 0);
+
     /* offBits */
-    PutLong(fp, offBits);
-    
+    putLong(fp, offBits);
+
+    assert(BMPlenfileheader() == 14);
+
     return 14;
 }
 
 
 
-static int
-BMPwriteinfoheader(FILE *        const fp, 
-                   int           const class, 
-                   unsigned long const bitcount, 
-                   unsigned long const x, 
+static unsigned int
+bmpWriteInfoHeader(FILE *        const fp,
+                   enum bmpClass const class,
+                   unsigned long const bitcount,
+                   unsigned long const x,
                    unsigned long const y) {
 /*----------------------------------------------------------------------------
+  Write the INFO header.
+
   Return the number of bytes written.
 ----------------------------------------------------------------------------*/
     unsigned int cbFix;
 
     switch (class) {
-    case C_WIN: {
-        cbFix = 40;
-        PutLong(fp, cbFix);
-
-        PutLong(fp, x);         /* cx */
-        PutLong(fp, y);         /* cy */
-        PutShort(fp, 1);        /* cPlanes */
-        PutShort(fp, bitcount); /* cBitCount */
+    case BMP_C_WIN_V1:
+    case BMP_C_WIN_V2:
+    case BMP_C_WIN_V3:
+    case BMP_C_WIN_V4:
+    case BMP_C_WIN_V5:
+    {
+        cbFix = BMP_HDRLEN_WIN_V1;
+        putLong(fp, cbFix);
+
+        putLong(fp, x);         /* cx */
+        putLong(fp, y);         /* cy */
+        putShort(fp, 1);        /* cPlanes */
+        putShort(fp, bitcount); /* cBitCount */
 
         /*
          * We've written 16 bytes so far, need to write 24 more
          * for the required total of 40.
          */
 
-        PutLong(fp, 0);   /* Compression */
-        PutLong(fp, 0);   /* ImageSize */
-        PutLong(fp, 0);   /* XpixelsPerMeter */
-        PutLong(fp, 0);   /* YpixelsPerMeter */
-        PutLong(fp, 0);   /* ColorsUsed */
-        PutLong(fp, 0);   /* ColorsImportant */
+        putLong(fp, 0);   /* Compression */
+        putLong(fp, 0);   /* ImageSize */
+        putLong(fp, 0);   /* XpixelsPerMeter */
+        putLong(fp, 0);   /* YpixelsPerMeter */
+        putLong(fp, 0);   /* ColorsUsed */
+        putLong(fp, 0);   /* ColorsImportant */
+
+        assert(BMP_HDRLEN_WIN_V1 == 40);  /* We wrote 40 bytes */
+
+        if (class != BMP_C_WIN_V1) {
+            /* Invalid call to this function - we don't know how to write
+               these header extensions.
+            */
+            assert(false);
+        }
     }
     break;
-    case C_OS2: {
-        cbFix = 12;
-        PutLong(fp, cbFix);
-
-        PutShort(fp, x);        /* cx */
-        PutShort(fp, y);        /* cy */
-        PutShort(fp, 1);        /* cPlanes */
-        PutShort(fp, bitcount); /* cBitCount */
+    case BMP_C_OS2_1x: {
+        cbFix = BMP_HDRLEN_OS2_1x;
+        putLong(fp, cbFix);
+
+        putShort(fp, x);        /* cx */
+        putShort(fp, y);        /* cy */
+        putShort(fp, 1);        /* cPlanes */
+        putShort(fp, bitcount); /* cBitCount */
+
+        assert(BMP_HDRLEN_OS2_1x == 12);  /* We wrote 12 bytes */
     }
     break;
-    default:
-        pm_error(er_internal, "BMPwriteinfoheader");
+    case BMP_C_OS2_2x:
+        /* Invalid call to this function */
+        assert(false);
+        break;
     }
-
     return cbFix;
 }
 
 
 
-static int
-BMPwriteRgb(FILE * const fp, 
-            int    const class, 
-            pixval const R, 
-            pixval const G, 
-            pixval const B) {
+static unsigned int
+bmpWriteRgb(FILE *        const fp,
+            enum bmpClass const class,
+            pixval        const R,
+            pixval        const G,
+            pixval        const B) {
 /*----------------------------------------------------------------------------
   Return the number of bytes written.
 -----------------------------------------------------------------------------*/
+    unsigned int retval;
+
     switch (class) {
-    case C_WIN:
-        PutByte(fp, B);
-        PutByte(fp, G);
-        PutByte(fp, R);
-        PutByte(fp, 0);
-        return 4;
-    case C_OS2:
-        PutByte(fp, B);
-        PutByte(fp, G);
-        PutByte(fp, R);
-        return 3;
-    default:
-        pm_error(er_internal, "BMPwriteRgb");
-        return -1;  /* avoid compiler warning. */
+    case BMP_C_WIN_V1:
+    case BMP_C_WIN_V2:
+    case BMP_C_WIN_V3:
+    case BMP_C_WIN_V4:
+    case BMP_C_WIN_V5:
+        putByte(fp, B);
+        putByte(fp, G);
+        putByte(fp, R);
+        putByte(fp, 0);
+        retval = 4;
+        break;
+    case BMP_C_OS2_1x:
+    case BMP_C_OS2_2x:
+        putByte(fp, B);
+        putByte(fp, G);
+        putByte(fp, R);
+        retval = 3;
+        break;
     }
+    return retval;
 }
 
 
 
-static int
-BMPwriteColormap(FILE *           const ifP, 
-                 int              const class, 
+static unsigned int
+bmpWriteColormap(FILE *           const ifP,
+                 enum bmpClass    const class,
                  int              const bpp,
-                 const colorMap * const colorMapP) {
+                 const ColorMap * const colorMapP) {
 /*----------------------------------------------------------------------------
   Return the number of bytes written.
 -----------------------------------------------------------------------------*/
@@ -314,12 +342,12 @@ BMPwriteColormap(FILE *           const ifP,
 
     nbyte = 0;
     for (i = 0; i < colorMapP->count; ++i) {
-        const struct rgb * const mapEntryP = &colorMapP->bmpMap[i];
-        nbyte += BMPwriteRgb(ifP, class,
+        const struct Rgb * const mapEntryP = &colorMapP->bmpMap[i];
+        nbyte += bmpWriteRgb(ifP, class,
                              mapEntryP->red, mapEntryP->grn, mapEntryP->blu);
     }
     for (; i < ncolors; ++i)
-        nbyte += BMPwriteRgb(ifP, class, 0, 0, 0);
+        nbyte += bmpWriteRgb(ifP, class, 0, 0, 0);
 
     return nbyte;
 }
@@ -345,10 +373,10 @@ lookupColor(colorhash_table const cht,
 
 
 static void
-bmpWriteRow_palette(FILE *          const fp, 
-                    const pixel *   const row, 
+bmpWriteRow_palette(FILE *          const fp,
+                    const pixel *   const row,
                     unsigned int    const cols,
-                    unsigned short  const bpp, 
+                    unsigned short  const bpp,
                     colorhash_table const cht,
                     unsigned int *  const nBytesP) {
 /*----------------------------------------------------------------------------
@@ -357,7 +385,7 @@ bmpWriteRow_palette(FILE *          const fp,
    Return the number of bytes written as *nBytesP.
 -----------------------------------------------------------------------------*/
     BITSTREAM b;
-    
+
     b = pm_bitinit(fp, "w");
     if (b == NULL)
         pm_error("Failed to initialize output file for output");
@@ -365,9 +393,9 @@ bmpWriteRow_palette(FILE *          const fp,
         int rc;
         unsigned int nbyte;
         unsigned int col;
-        
+
         nbyte = 0;      /* initial value */
-        
+
         for (col = 0; col < cols; ++col) {
             unsigned int colorIndex;
             int rc;
@@ -385,10 +413,10 @@ bmpWriteRow_palette(FILE *          const fp,
         rc = pm_bitfini(b);
 
         nbyte += rc;
-                
+
         /* Make sure we write a multiple of 4 bytes.  */
         while (nbyte % 4 != 0) {
-            PutByte(fp, 0);
+            putByte(fp, 0);
             ++nbyte;
         }
         *nBytesP = nbyte;
@@ -398,13 +426,13 @@ bmpWriteRow_palette(FILE *          const fp,
 
 
 static void
-bmpWriteRow_truecolor(FILE *         const fp, 
-                      const pixel *  const row, 
+bmpWriteRow_truecolor(FILE *         const fp,
+                      const pixel *  const row,
                       unsigned long  const cols,
                       pixval         const maxval,
                       unsigned int * const nBytesP) {
 /*----------------------------------------------------------------------------
-  Write a row of a truecolor BMP image to the file 'fp'.  The row is 
+  Write a row of a truecolor BMP image to the file 'fp'.  The row is
   'row', which is 'cols' columns long.
 
 
@@ -413,20 +441,20 @@ bmpWriteRow_truecolor(FILE *         const fp,
     /* This works only for 24 bits per pixel.  To implement this for the
        general case (which is only hypothetical -- this program doesn't
        write any truecolor images except 24 bit and apparently no one
-       else does either), you would move this function into 
+       else does either), you would move this function into
        BMPwriterow_palette, which writes arbitrary bit strings.  But
        that would be a lot slower and less robust.
     */
 
     int nbyte;  /* Number of bytes we have written to file so far */
-    int col;  
-        
+    int col;
+
     nbyte = 0;  /* initial value */
     for (col = 0; col < cols; ++col) {
         /* We scale to the BMP maxval, which is always 255. */
-        PutByte(fp, PPM_GETB(row[col]) * 255 / maxval);
-        PutByte(fp, PPM_GETG(row[col]) * 255 / maxval);
-        PutByte(fp, PPM_GETR(row[col]) * 255 / maxval);
+        putByte(fp, PPM_GETB(row[col]) * 255 / maxval);
+        putByte(fp, PPM_GETG(row[col]) * 255 / maxval);
+        putByte(fp, PPM_GETR(row[col]) * 255 / maxval);
         nbyte += 3;
     }
 
@@ -434,25 +462,27 @@ bmpWriteRow_truecolor(FILE *         const fp,
      * Make sure we write a multiple of 4 bytes.
      */
     while (nbyte % 4) {
-        PutByte(fp, 0);
+        putByte(fp, 0);
         ++nbyte;
     }
-    
+
     *nBytesP = nbyte;
 }
 
 
 
-static int
-BMPwritebits(FILE *          const fp, 
-             unsigned long   const cols, 
-             unsigned long   const rows,
-             enum colortype  const colortype,
-             unsigned short  const cBitCount, 
-             const pixel **  const pixels, 
-             pixval          const maxval,
-             colorhash_table const cht) {
+static unsigned int
+bmpWriteRaster(FILE *          const fp,
+               unsigned long   const cols,
+               unsigned long   const rows,
+               Colortype       const colortype,
+               unsigned short  const cBitCount,
+               const pixel **  const pixels,
+               pixval          const maxval,
+               colorhash_table const cht) {
 /*----------------------------------------------------------------------------
+  Write the raster.
+
   Return the number of bytes written.
 -----------------------------------------------------------------------------*/
     unsigned int nbyte;
@@ -469,9 +499,9 @@ BMPwritebits(FILE *          const fp,
         unsigned int nBytesThisRow;
 
         if (colortype == PALETTE)
-            bmpWriteRow_palette(fp, pixels[row], cols, 
+            bmpWriteRow_palette(fp, pixels[row], cols,
                                 cBitCount, cht, &nBytesThisRow);
-        else 
+        else
             bmpWriteRow_truecolor(fp, pixels[row], cols, maxval,
                                   &nBytesThisRow);
 
@@ -486,15 +516,15 @@ BMPwritebits(FILE *          const fp,
 
 
 static void
-bmpEncode(FILE *           const ifP, 
-          int              const class, 
-          enum colortype   const colortype,
+bmpEncode(FILE *           const ifP,
+          enum bmpClass    const class,
+          Colortype        const colortype,
           unsigned int     const bpp,
-          int              const x, 
-          int              const y, 
-          const pixel **   const pixels, 
+          int              const x,
+          int              const y,
+          const pixel **   const pixels,
           pixval           const maxval,
-          const colorMap * const colorMapP) {
+          const ColorMap * const colorMapP) {
 /*----------------------------------------------------------------------------
   Write a BMP file of the given class.
 -----------------------------------------------------------------------------*/
@@ -509,16 +539,16 @@ bmpEncode(FILE *           const ifP,
         pm_message("Writing %u bits per pixel truecolor (no palette)", bpp);
 
     nbyte = 0;  /* initial value */
-    nbyte += BMPwritefileheader(ifP, cbSize, offbits);
-    nbyte += BMPwriteinfoheader(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);
+        nbyte += bmpWriteColormap(ifP, class, bpp, colorMapP);
 
     if (nbyte != offbits)
         pm_error(er_internal, "BmpEncode 1");
 
-    nbyte += BMPwritebits(ifP, x, y, colortype, bpp, pixels, maxval,
-                          colorMapP->cht);
+    nbyte += bmpWriteRaster(ifP, x, y, colortype, bpp, pixels, maxval,
+                            colorMapP->cht);
     if (nbyte != cbSize)
         pm_error(er_internal, "BmpEncode 2");
 }
@@ -526,7 +556,7 @@ bmpEncode(FILE *           const ifP,
 
 
 static void
-makeBilevelColorMap(colorMap * const colorMapP) {
+makeBilevelColorMap(ColorMap * const colorMapP) {
 
     colorMapP->count  = 2;
     colorMapP->cht    = NULL;
@@ -541,10 +571,10 @@ makeBilevelColorMap(colorMap * const colorMapP) {
 
 
 static void
-bmpEncodePbm(FILE *           const ifP, 
-             int              const class, 
-             int              const cols, 
-             int              const rows, 
+bmpEncodePbm(FILE *           const ifP,
+             enum bmpClass    const class,
+             int              const cols,
+             int              const rows,
              unsigned char ** const bitrow) {
 /*----------------------------------------------------------------------------
   Write a bi-level BMP file of the given class.
@@ -559,19 +589,19 @@ bmpEncodePbm(FILE *           const ifP,
     unsigned int const packedBytes  = adjustedCols / 8;
 
     unsigned long nbyte;
-    colorMap bilevelColorMap;
+    ColorMap bilevelColorMap;
     unsigned int row;
-    
+
     /* colortype == PALETTE */
     pm_message("Writing 1 bit per pixel with a black-white palette");
 
     nbyte = 0;  /* initial value */
-    nbyte += BMPwritefileheader(ifP, cbSize, offbits);
-    nbyte += BMPwriteinfoheader(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);
+    nbyte += bmpWriteColormap(ifP, class, 1, &bilevelColorMap);
 
     if (nbyte != offbits)
         pm_error(er_internal, "bmpEncodePbm 1");
@@ -583,7 +613,7 @@ bmpEncodePbm(FILE *           const ifP,
         if (bytesWritten != packedBytes){
             if (feof(ifP))
                 pm_error("End of file writing row %u of BMP raster.", row);
-            else 
+            else
                 pm_error("Error writing BMP raster.  Errno=%d (%s)",
                          errno, strerror(errno));
         }  else
@@ -597,7 +627,7 @@ bmpEncodePbm(FILE *           const ifP,
 
 
 static void
-makeHashFromBmpMap(const struct rgb * const bmpMap,
+makeHashFromBmpMap(const struct Rgb * const bmpMap,
                    unsigned int       const nColors,
                    colorhash_table *  const chtP) {
 
@@ -607,7 +637,7 @@ makeHashFromBmpMap(const struct rgb * const bmpMap,
     MALLOCARRAY_NOFAIL(chv, nColors);
 
     for (i = 0; i < nColors; ++i) {
-        const struct rgb * const mapEntryP = &bmpMap[i];
+        const struct Rgb * const mapEntryP = &bmpMap[i];
 
         PPM_ASSIGN(chv[i].color,
                    mapEntryP->red, mapEntryP->grn, mapEntryP->blu);
@@ -629,7 +659,7 @@ minBmpBitsForColorCount(unsigned int const colorCount) {
        implement and other bpp's have in fact been seen to confuse
        viewers.  There is an extended BMP format that has 16 bpp
        too, but this program doesn't know how to generate that
-       (see Bmptopnm.c, though).  
+       (see Bmptopnm.c, though).
     */
     if (minbits == 1)
         return 1;
@@ -646,7 +676,7 @@ minBmpBitsForColorCount(unsigned int const colorCount) {
 static void
 getMapFile(const char *   const mapFileName,
            unsigned int * const minimumBppP,
-           colorMap *     const colorMapP) {
+           ColorMap *     const colorMapP) {
 /*----------------------------------------------------------------------------
    Get the color map (palette) for the BMP from file 'mapFileName'.
 
@@ -655,7 +685,6 @@ getMapFile(const char *   const mapFileName,
    Return as *minimumBppP the minimum number of bits per pixel it will
    take to represent all the colors in the map in the BMP format.
 -----------------------------------------------------------------------------*/
-
     FILE * mapFileP;
     int cols, rows;
     pixval maxval;
@@ -675,12 +704,12 @@ getMapFile(const char *   const mapFileName,
                  MAXCOLORS, cols * rows);
 
     count = 0; /* initial value */
-    
+
     for (row = 0; row < rows; ++row) {
         unsigned int col;
         for (col = 0; col < cols; ++col) {
             pixel        const color     = pixels[row][col];
-            struct rgb * const mapEntryP = &colorMapP->bmpMap[count++];
+            struct Rgb * const mapEntryP = &colorMapP->bmpMap[count++];
 
             assert(count <= ARRAY_SIZE(colorMapP->bmpMap));
 
@@ -703,15 +732,15 @@ getMapFile(const char *   const mapFileName,
 
 
 static void
-analyzeColors(const pixel **    const pixels, 
-              int               const cols, 
-              int               const rows, 
-              pixval            const maxval, 
+analyzeColors(const pixel **    const pixels,
+              int               const cols,
+              int               const rows,
+              pixval            const maxval,
               unsigned int *    const minimumBppP,
-              colorMap *        const colorMapP) {
+              ColorMap *        const colorMapP) {
 /*----------------------------------------------------------------------------
   Look at the colors in the image 'pixels' and compute values to use in
-  representing those colors in a BMP image.  
+  representing those colors in a BMP image.
 
   First of all, count the distinct colors.  Return as *minimumBppP
   the minimum number of bits per pixel it will take to represent all
@@ -733,7 +762,7 @@ analyzeColors(const pixel **    const pixels,
     int colorCount;
 
     pm_message("analyzing colors...");
-    chv = ppm_computecolorhist((pixel**)pixels, cols, rows, MAXCOLORS, 
+    chv = ppm_computecolorhist((pixel**)pixels, cols, rows, MAXCOLORS,
                                &colorCount);
     colorMapP->count = colorCount;
     if (chv == NULL) {
@@ -751,12 +780,12 @@ analyzeColors(const pixel **    const pixels,
          * Now scale the maxval to 255 as required by BMP format.
          */
         for (i = 0; i < colorMapP->count; ++i) {
-            struct rgb * const mapEntryP = &colorMapP->bmpMap[i];
+            struct Rgb * const mapEntryP = &colorMapP->bmpMap[i];
             mapEntryP->red = (pixval) PPM_GETR(chv[i].color) * 255 / maxval;
             mapEntryP->grn = (pixval) PPM_GETG(chv[i].color) * 255 / maxval;
             mapEntryP->blu = (pixval) PPM_GETB(chv[i].color) * 255 / maxval;
         }
-    
+
         /* And make a hash table for fast lookup. */
         colorMapP->cht = ppm_colorhisttocolorhash(chv, colorMapP->count);
         ppm_freecolorhist(chv);
@@ -769,7 +798,7 @@ static void
 chooseColortypeBpp(bool             const userRequestsBpp,
                    unsigned int     const requestedBpp,
                    unsigned int     const minimumBpp,
-                   enum colortype * const colortypeP, 
+                   Colortype *      const colortypeP,
                    unsigned int *   const bitsPerPixelP) {
 /*----------------------------------------------------------------------------
    Determine whether the BMP raster should contain RGB values or palette
@@ -803,12 +832,12 @@ chooseColortypeBpp(bool             const userRequestsBpp,
             *bitsPerPixelP = requestedBpp;
     }
 
-    assert(*bitsPerPixelP == 1 || 
-           *bitsPerPixelP == 4 || 
-           *bitsPerPixelP == 8 || 
+    assert(*bitsPerPixelP == 1 ||
+           *bitsPerPixelP == 4 ||
+           *bitsPerPixelP == 8 ||
            *bitsPerPixelP == 24);
 
-    if (*bitsPerPixelP > 8) 
+    if (*bitsPerPixelP > 8)
         *colortypeP = TRUECOLOR;
     else {
         *colortypeP = PALETTE;
@@ -818,13 +847,13 @@ chooseColortypeBpp(bool             const userRequestsBpp,
 
 
 static void
-doPbm(FILE *       const ifP,
-      unsigned int const cols,
-      unsigned int const rows,
-      int          const format,
-      int          const class,
-      FILE *       const ofP) {
-    
+doPbm(FILE *        const ifP,
+      unsigned int  const cols,
+      unsigned int  const rows,
+      int           const format,
+      enum bmpClass const class,
+      FILE *        const ofP) {
+
     /* We read the raster directly from the input with
         pbm_readpbmrow_packed().  The raster format is almost
         identical, except that BMP specifies rows to be zero-filled to
@@ -835,7 +864,7 @@ doPbm(FILE *       const ifP,
     int const adjustedCols = (cols+31) /32 * 32;
     int const packedBytes  =  adjustedCols /8;
 
-    unsigned char ** bitrow;    
+    unsigned char ** bitrow;
     unsigned int row;
 
     bitrow = pbm_allocarray_packed(adjustedCols, rows);
@@ -848,15 +877,15 @@ doPbm(FILE *       const ifP,
         thisRow[packedBytes-2] = 0x00;
         thisRow[packedBytes-3] = 0x00;
         thisRow[packedBytes-4] = 0x00;
-        
+
         pbm_readpbmrow_packed(ifP, thisRow, cols, format);
 
         {
             unsigned int i;
-            for (i = 0; i < colChars; ++i) 
+            for (i = 0; i < colChars; ++i)
                 thisRow[i] = ~thisRow[i]; /* flip all pixels */
         }
-        /* This may seem unnecessary, because the color palette 
+        /* This may seem unnecessary, because the color palette
            (RGB[] in bmpEncodePbm) can be inverted for the same effect.
            However we take this precaution, for there is indication that
            some BMP viewers may get confused with that.
@@ -867,21 +896,21 @@ doPbm(FILE *       const ifP,
     }
 
     bmpEncodePbm(ofP, class, cols, rows, bitrow);
-}            
+}
 
 
 
 static void
-doPgmPpm(FILE *       const ifP,
-         unsigned int const cols,
-         unsigned int const rows,
-         pixval       const maxval,
-         int          const ppmFormat,
-         int          const class,
-         bool         const userRequestsBpp,
-         unsigned int const requestedBpp,
-         const char * const mapFileName,
-         FILE *       const ofP) {
+doPgmPpm(FILE *        const ifP,
+         unsigned int  const cols,
+         unsigned int  const rows,
+         pixval        const maxval,
+         int           const ppmFormat,
+         enum bmpClass const class,
+         bool          const userRequestsBpp,
+         unsigned int  const requestedBpp,
+         const char *  const mapFileName,
+         FILE *        const ofP) {
 
     /* PGM and PPM.  We read the input image into a PPM array, scan it
        to analyze the colors, and convert it to a BMP raster.  Logic
@@ -889,29 +918,29 @@ doPgmPpm(FILE *       const ifP,
     */
     unsigned int minimumBpp;
     unsigned int bitsPerPixel;
-    enum colortype colortype;
+    Colortype colortype;
     unsigned int row;
-    
+
     pixel ** pixels;
-    colorMap colorMap;
-    
+    ColorMap colorMap;
+
     pixels = ppm_allocarray(cols, rows);
-    
+
     for (row = 0; row < rows; ++row)
         ppm_readppmrow(ifP, pixels[row], cols, maxval, ppmFormat);
-    
+
     if (mapFileName)
         getMapFile(mapFileName, &minimumBpp, &colorMap);
     else
-        analyzeColors((const pixel**)pixels, cols, rows, maxval, 
+        analyzeColors((const pixel**)pixels, cols, rows, maxval,
                       &minimumBpp, &colorMap);
-    
+
     chooseColortypeBpp(userRequestsBpp, requestedBpp, minimumBpp,
                        &colortype, &bitsPerPixel);
-    
+
     bmpEncode(ofP, class, colortype, bitsPerPixel,
               cols, rows, (const pixel**)pixels, maxval, &colorMap);
-    
+
     freeColorMap(&colorMap);
 
     ppm_freearray(pixels, rows);
@@ -935,10 +964,11 @@ main(int           argc,
     parseCommandLine(argc, argv, &cmdline);
 
     ifP = pm_openr(cmdline.inputFilename);
-    
+
     ppm_readppminit(ifP, &cols, &rows, &maxval, &ppmFormat);
-    
-    if (PPM_FORMAT_TYPE(ppmFormat) == PBM_TYPE)
+
+    if ((PPM_FORMAT_TYPE(ppmFormat) == PBM_TYPE) &&
+        (!cmdline.bppSpec || cmdline.bpp == 1))
         doPbm(ifP, cols, rows, ppmFormat, cmdline.class, stdout);
     else
         doPgmPpm(ifP, cols, rows, maxval, ppmFormat,
@@ -952,3 +982,6 @@ main(int           argc,
 
     return 0;
 }
+
+
+
diff --git a/converter/ppm/ppmtogif.c b/converter/ppm/ppmtogif.c
index fa7d1dbe..8dd133b5 100644
--- a/converter/ppm/ppmtogif.c
+++ b/converter/ppm/ppmtogif.c
@@ -10,6 +10,7 @@
      - Pamtogif requires a user-specififed map file (-mapfile) to
        match the input in depth.
 */
+#define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE   /* Make sure strdup() is in string.h */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
diff --git a/converter/ppm/ppmtoicr.c b/converter/ppm/ppmtoicr.c
index 3c8be421..1720f790 100644
--- a/converter/ppm/ppmtoicr.c
+++ b/converter/ppm/ppmtoicr.c
@@ -53,35 +53,6 @@ makeIcrColormap(colorhist_vector const chv,
 
 
 static int
-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
 colorIndexAtPosition(unsigned int    const x,
                      unsigned int    const y,
                      pixel **        const pixels,
@@ -166,7 +137,6 @@ main(int argc, const char ** const argv) {
     int rows, cols;
     int colorCt;
     int argn;
-    unsigned int bitsPerPixel;
     pixval maxval;
     colorhist_vector chv;
     char rgb[CLUTCOLORCT];
@@ -235,8 +205,6 @@ main(int argc, const char ** const argv) {
 
     makeIcrColormap(chv, colorCt, maxval, rgb);
 
-    bitsPerPixel = bppFromColorCt(colorCt);
-
     /* And make a hash table for fast lookup. */
     cht = ppm_colorhisttocolorhash(chv, colorCt);
 
diff --git a/converter/ppm/ppmtomitsu.c b/converter/ppm/ppmtomitsu.c
index e59f09b3..5b0b3245 100644
--- a/converter/ppm/ppmtomitsu.c
+++ b/converter/ppm/ppmtomitsu.c
@@ -274,7 +274,7 @@ static void
 doLookupTableColors(colorhist_vector const table,
                     unsigned int     const nColor,
                     hashinfo *       const colorhashtable) {
-                
+
     unsigned int colval;
     for (colval = 0; colval < nColor; ++colval) {
         struct hashinfo * const hashchain =
@@ -287,7 +287,7 @@ doLookupTableColors(colorhist_vector const table,
         datum(PPM_GETR((table[colval]).color));
         datum(PPM_GETG((table[colval]).color));
         datum(PPM_GETB((table[colval]).color));
-        
+
         hashrun = hashchain;  /* start at beginning of chain */
 
         if (hashrun->flag == -1) {
@@ -323,7 +323,7 @@ doLookupTableGrays(colorhist_vector const table,
         datum(PPM_GETB((table[colval]).color));
         datum(PPM_GETB((table[colval]).color));
         datum(PPM_GETB((table[colval]).color));
-        
+
         hashrun = hashchain;  /* start at beginning of chain */
 
         if (hashrun->flag == -1) {
@@ -410,7 +410,7 @@ writeColormapRaster(pixel **         const pixels,
             struct hashinfo * const hashchain =
                 &colorhashtable[myhash(pixrow[col])];
             struct hashinfo * p;
-                
+
             p = hashchain;
             while (!PPM_EQUAL((p->color), pixrow[col])) {
                 assert(p->next);
@@ -554,7 +554,7 @@ doTiny(FILE *           const ifP,
        int              const enlarge,
        int              const copy,
        struct mediasize const medias) {
-       
+
     pixel * pixelrow;
     unsigned char * redrow;
     unsigned char * grnrow;
@@ -653,7 +653,7 @@ main(int argc, char * argv[]) {
         tiny = TRUE;
     else
         pm_usage(usage);
-        ++argn;
+    ++argn;
     }
 
     if (argn < argc) {
@@ -690,7 +690,7 @@ main(int argc, char * argv[]) {
     }
 
     ppm_readppminit(ifP, &cols, &rows, &maxval, &format);
-    
+
     if (tiny) {
         doTiny(ifP, cols, rows, maxval, format,
                sharpness, enlarge, copy, medias);
@@ -707,7 +707,7 @@ main(int argc, char * argv[]) {
 
         /* first check wether we can use the lut transfer */
 
-        table = ppm_computecolorhist(pixels, cols, rows, MAXLUTCOL+1, 
+        table = ppm_computecolorhist(pixels, cols, rows, MAXLUTCOL+1,
                                      &nColor);
         if (table)
             useLookupTable(pixels, table, sharpness, enlarge, copy, medias,
diff --git a/converter/ppm/ppmtompeg/HISTORY b/converter/ppm/ppmtompeg/HISTORY
index c9f4932a..e6fb86d3 100644
--- a/converter/ppm/ppmtompeg/HISTORY
+++ b/converter/ppm/ppmtompeg/HISTORY
@@ -1,4 +1,4 @@
-The entire ppmtojpeg directory was adapted by Bryan from the package
+The entire ppmtompeg directory was adapted by Bryan from the package
 mpeg_encode-1.5b-src (subdirectory mpeg_encode) on March 30, 1999.  The 
 program was called mpeg_encode in that package.  It was dated August 16,
 1995 and came from ftp://mm-ftp.cs.berkeley.edu/pub/multimedia/mpeg/
diff --git a/converter/ppm/ppmtompeg/frame.c b/converter/ppm/ppmtompeg/frame.c
index 75b209f8..f91f2cc6 100644
--- a/converter/ppm/ppmtompeg/frame.c
+++ b/converter/ppm/ppmtompeg/frame.c
@@ -82,8 +82,10 @@ Resize_Array_Width(uint8 ** const inarray,
     int out_total;
     uint8 *inptr;
     uint8 *outptr;
+#ifdef DOING_INTERPOLATION
     uint8 pointA,pointB;
-    /* double slope,diff; */
+    double slope,diff;
+#endif
     
     for (i = 0; i < in_y; ++i) {     /* For each row */
         unsigned int j;
@@ -105,11 +107,15 @@ Resize_Array_Width(uint8 ** const inarray,
                     --inptr;
                 }
             } else {  
+#ifdef DOING_INTERPOLATION
                 pointA = *inptr;
+#endif
                 ++inptr;
+#ifdef DOING_INTERPOLATION
                 pointB = *inptr;
+#endif
                 --inptr;
-#if 0
+#ifdef DOING_INTERPOLATION
                 /*Interpolative solution */
                 slope = ((double)(pointB -pointA))/((double)(out_x));
                 diff = (((double)(out_total - in_total)));
@@ -159,8 +165,10 @@ Resize_Array_Height(uint8 ** const inarray,
     for(i=0; i < in_x; ++i){    /* for each column */
         int in_total;
         int out_total;
+#ifdef DOING_INTERPOLATION
         uint8 pointA, pointB;
         double slope, diff;
+#endif
         unsigned int j;
         int k;
 
@@ -180,15 +188,17 @@ Resize_Array_Height(uint8 ** const inarray,
                     --k;
                 }
             } else {  
+#ifdef DOING_INTERPOLATION
                 pointA = inarray[k][i];
-                if (k != (in_y -1)) {
+                if (k != (in_y - 1)) {
                     pointB = inarray[k+1][i];
                 } else
                     pointB = pointA;
                 /* Interpolative case */
                 slope = ((double)(pointB -pointA))/(double)(out_y);
                 diff = (double)(out_total - in_total);
-                /*  outarray[j][i] = (inarray[k][i] + (uint8)(slope*diff)); */
+                outarray[j][i] = (inarray[k][i] + (uint8)(slope*diff));
+#endif
                 /* Non-Interpolative case */
                 outarray[j][i] = inarray[k][i];
                 out_total = out_total + in_y;
diff --git a/converter/ppm/ppmtompeg/frametype.c b/converter/ppm/ppmtompeg/frametype.c
index b7daacc9..09afa403 100644
--- a/converter/ppm/ppmtompeg/frametype.c
+++ b/converter/ppm/ppmtompeg/frametype.c
@@ -264,7 +264,7 @@ ComputeFrameTable(unsigned int const numFramesArg) {
   'numFrames' == 0 means number of frames is not known at this time.
 -----------------------------------------------------------------------------*/
     int index;
-    FrameTable	*lastI, *lastIP, *firstB, *secondIP;
+    FrameTable	*lastIP, *firstB, *secondIP;
     FrameTable	*ptr;
     char typ;
     int table_size;
@@ -278,7 +278,6 @@ ComputeFrameTable(unsigned int const numFramesArg) {
 
     MALLOCARRAY_NOFAIL(frameTable, 1 + table_size);
 
-    lastI = NULL;
     lastIP = NULL;
     firstB = NULL;
     secondIP = NULL;
diff --git a/converter/ppm/ppmtompeg/gethostname.c b/converter/ppm/ppmtompeg/gethostname.c
index d20af17c..649fff91 100644
--- a/converter/ppm/ppmtompeg/gethostname.c
+++ b/converter/ppm/ppmtompeg/gethostname.c
@@ -1,3 +1,4 @@
+#define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE   /* Make sure strdup() is in string.h */
 
diff --git a/converter/ppm/ppmtompeg/jrevdct.c b/converter/ppm/ppmtompeg/jrevdct.c
index bf9196c4..dd1f9fff 100644
--- a/converter/ppm/ppmtompeg/jrevdct.c
+++ b/converter/ppm/ppmtompeg/jrevdct.c
@@ -26,7 +26,11 @@
  * matrix, perhaps with the difference cases encoded.
  */
 
+#define _XOPEN_SOURCE 500  /* get M_PI in math.h */
+
 #include <memory.h>
+#include <math.h>
+
 #include "all.h"
 #include "dct.h"
 
@@ -1211,35 +1215,29 @@ mpeg_jrevdct_quick(data)
 */
 
 
-/* Here we use math.h to generate constants.  Compiler results may
-   vary a little */
-
-#ifndef PI
-#ifdef M_PI
-#define PI M_PI
-#else
-#define PI 3.14159265358979323846
-#endif
-#endif
-
 /* cosine transform matrix for 8x1 IDCT */
 static double itrans_coef[8][8];
 
-/* initialize DCT coefficient matrix */
 
-void init_idctref()
-{
-  int freq, time;
-  double scale;
-
-  for (freq=0; freq < 8; freq++)
-  {
-    scale = (freq == 0) ? sqrt(0.125) : 0.5;
-    for (time=0; time<8; time++)
-      itrans_coef[freq][time] = scale*cos((PI/8.0)*freq*(time + 0.5));
-  }
+
+void init_idctref() {
+/*----------------------------------------------------------------------------
+   initialize DCT coefficient matrix 
+-----------------------------------------------------------------------------*/
+    unsigned int freq;
+
+    for (freq=0; freq < 8; ++freq) {
+        double const scale = (freq == 0) ? sqrt(0.125) : 0.5;
+
+        unsigned int time;
+
+        for (time = 0; time < 8; ++time)
+            itrans_coef[freq][time] = scale*cos((M_PI/8.0)*freq*(time + 0.5));
+    }
 }
 
+
+
 /* perform IDCT matrix multiply for 8x8 coefficient block */
 
 void reference_rev_dct(block)
diff --git a/converter/ppm/ppmtompeg/mfwddct.c b/converter/ppm/ppmtompeg/mfwddct.c
index 75c3a718..9381e51c 100644
--- a/converter/ppm/ppmtompeg/mfwddct.c
+++ b/converter/ppm/ppmtompeg/mfwddct.c
@@ -15,6 +15,10 @@
  * instead of floating point.
  */
 
+#define _XOPEN_SOURCE 500  /* get M_PI in math.h */
+
+#include <math.h>
+
 #include "all.h"
 
 #include "dct.h"
@@ -375,26 +379,19 @@ mp_fwd_dct_block2(data, dest)
  *
  */
 
-#ifndef PI
-#ifdef M_PI
-#define PI M_PI
-#else
-#define PI 3.14159265358979323846
-#endif
-#endif
 
-void init_fdct()
-{
-  int i, j;
-  double s;
+void init_fdct() {
 
-  for (i=0; i<8; i++)
-  {
-    s = (i==0) ? sqrt(0.125) : 0.5;
+    unsigned int i;
 
-    for (j=0; j<8; j++)
-      trans_coef[i][j] = s * cos((PI/8.0)*i*(j+0.5));
-  }
+    for (i = 0; i < 8; ++i) {
+        double const s = i == 0 ? sqrt(0.125) : 0.5;
+
+        unsigned int j;
+
+        for (j = 0; j < 8; ++j)
+            trans_coef[i][j] = s * cos((M_PI/8.0) * i * (j+0.5));
+    }
 }
 
 
diff --git a/converter/ppm/ppmtompeg/mpeg.c b/converter/ppm/ppmtompeg/mpeg.c
index 24d337ed..33e1a9f9 100644
--- a/converter/ppm/ppmtompeg/mpeg.c
+++ b/converter/ppm/ppmtompeg/mpeg.c
@@ -30,6 +30,7 @@
  * HEADER FILES *
  *==============*/
 
+#define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE   /* Make sure strdup() is in string.h */
 
diff --git a/converter/ppm/ppmtompeg/parallel.c b/converter/ppm/ppmtompeg/parallel.c
index 2835c67c..e3bcec1a 100644
--- a/converter/ppm/ppmtompeg/parallel.c
+++ b/converter/ppm/ppmtompeg/parallel.c
@@ -2149,7 +2149,6 @@ DecodeServer(int          const numInputFiles,
     int     otherSock;
     int     decodePortNum;
     int     frameReady;
-    boolean *ready;
     int     *waitMachine;
     int     *waitPort;
     int     *waitList;
@@ -2162,7 +2161,6 @@ DecodeServer(int          const numInputFiles,
 
     /* should keep list of port numbers to notify when frames become ready */
 
-    ready = (boolean *) calloc(numInputFiles, sizeof(boolean));
     waitMachine = (int *) calloc(numInputFiles, sizeof(int));
     waitPort = (int *) malloc(numMachines*sizeof(int));
     waitList = (int *) calloc(numMachines, sizeof(int));
diff --git a/converter/ppm/ppmtompeg/param.c b/converter/ppm/ppmtompeg/param.c
index 45605981..87fdfa6b 100644
--- a/converter/ppm/ppmtompeg/param.c
+++ b/converter/ppm/ppmtompeg/param.c
@@ -7,6 +7,8 @@
 
 /* COPYRIGHT INFORMATION IS AT THE END OF THIS FILE */
 
+#define _DEFAULT_SOURCE 1
+    /* New name for SVID & BSD source defines */
 #define _XOPEN_SOURCE 500
     /* This makes sure popen() is in stdio.h.  In GNU libc 2.1.3, 
      _POSIX_C_SOURCE = 2 is sufficient, but on AIX 4.3, the higher level
diff --git a/converter/ppm/ppmtompeg/ppmtompeg.c b/converter/ppm/ppmtompeg/ppmtompeg.c
index cd94db39..837b1b9d 100644
--- a/converter/ppm/ppmtompeg/ppmtompeg.c
+++ b/converter/ppm/ppmtompeg/ppmtompeg.c
@@ -30,6 +30,7 @@
  * HEADER FILES *
  *==============*/
 
+#define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE   /* Make sure strdup() is in string.h */
 
@@ -190,16 +191,17 @@ parseArgs(int     const argc,
             ++idx;
         } else if (streq(argv[idx], "-child")) {
             if (idx+7 < argc-1) {
-                int combinePortNumber;
-                    /* This used to be important information, when the child
-                       notified the combine server.  Now the master notifies
-                       the combine server after the child notifies the master
-                       it is done.  So this value is unused.
-                    */
                 cmdlineP->masterHostname = argv[idx+1];
                 cmdlineP->masterPortNumber = atoi(argv[idx+2]);
                 ioPortNumber = atoi(argv[idx+3]);
-                combinePortNumber = atoi(argv[idx+4]);
+                /*
+                  combinePortNumber = atoi(argv[idx+4]);
+
+                  This used to be important information, when the child
+                  notified the combine server.  Now the master notifies
+                  the combine server after the child notifies the master
+                  it is done.  So this value is unused.
+                */
                 decodePortNumber = atoi(argv[idx+5]);
                 machineNumber = atoi(argv[idx+6]);
                 remoteIO = atoi(argv[idx+7]);
diff --git a/converter/ppm/ppmtompeg/rate.c b/converter/ppm/ppmtompeg/rate.c
index c775e055..6ec330cf 100644
--- a/converter/ppm/ppmtompeg/rate.c
+++ b/converter/ppm/ppmtompeg/rate.c
@@ -389,7 +389,6 @@ targetRateControl(MpegFrame * const frame) {
     float tempX, tempY, tempZ;
     int result;
     int frameType;
-    const char *strPtr;
   
     minimumBits = (bit_rate / (8 * frameRateRounded));
   
@@ -445,14 +444,18 @@ targetRateControl(MpegFrame * const frame) {
     Qscale = (mquant > 31 ? 31 : mquant);
     Qscale = (Qscale < 1 ? 1 : Qscale);
   
-    /*   Print headers for Frame info */
-    strPtr = Frame_header1;
-    DBG_PRINT(("%s\n",strPtr));
-    strPtr = Frame_header2;
-    DBG_PRINT(("%s\n",strPtr));
-    strPtr = Frame_header3;
-    DBG_PRINT(("%s\n",strPtr));
-  
+#ifdef HEINOUS_DEBUG_MODE
+    {
+        const char * strPtr;
+        /*   Print headers for Frame info */
+        strPtr = Frame_header1;
+        DBG_PRINT(("%s\n",strPtr));
+        strPtr = Frame_header2;
+        DBG_PRINT(("%s\n",strPtr));
+        strPtr = Frame_header3;
+        DBG_PRINT(("%s\n",strPtr));
+    }
+#endif
     /*   Print Frame info */
     sprintf(rc_buffer, "%4d     %1c  %4d  %6d %7d  "
             "%2d %2d %2d   %2.2f  %6d %4d    %3d",
@@ -467,10 +470,13 @@ targetRateControl(MpegFrame * const frame) {
   
     /*  Print headers for Macroblock info */
     if (RC_MB_SAMPLE_RATE) {
+#ifdef HEINOUS_DEBUG_MODE
+        const char * strPtr;
         strPtr = MB_header1;
         DBG_PRINT(("%s\n",strPtr));
         strPtr = MB_header2;
         DBG_PRINT(("%s\n",strPtr));
+#endif
     }
 }
 
@@ -519,7 +525,6 @@ void
 updateRateControl(int const type) {
     int totalBits, frameComplexity, pctAllocUsed, pctGOPUsed;
     float avgQuant;
-    const char *strPtr;
 
     totalBits = rc_totalFrameBits;
     avgQuant = ((float) rc_totalQuant / (float) rc_numBlocks);
@@ -559,15 +564,18 @@ updateRateControl(int const type) {
         break;
     }
   
-  
-    /*  Print Frame info */
-    strPtr = Frame_trailer1;
-    DBG_PRINT(("%s\n",strPtr));
-    strPtr = Frame_trailer2;
-    DBG_PRINT(("%s\n",strPtr));
-    strPtr = Frame_trailer3;
-    DBG_PRINT(("%s\n",strPtr));
-  
+#ifdef HEINOUS_DEBUG_MODE
+    {  
+        /*  Print Frame info */
+        const char * strPtr;
+        strPtr = Frame_trailer1;
+        DBG_PRINT(("%s\n",strPtr));
+        strPtr = Frame_trailer2;
+        DBG_PRINT(("%s\n",strPtr));
+        strPtr = Frame_trailer3;
+        DBG_PRINT(("%s\n",strPtr));
+    }
+#endif  
     sprintf(rc_buffer, "%6d  %2.2f  %6d  %3d  %2.2f %7d   "
             "%3d %7d   %3d  %6d %6d",
             totalBits, avgQuant, frameComplexity, avg_act, N_act, 
diff --git a/converter/ppm/ppmtompeg/readframe.c b/converter/ppm/ppmtompeg/readframe.c
index 23752706..2a359b2f 100644
--- a/converter/ppm/ppmtompeg/readframe.c
+++ b/converter/ppm/ppmtompeg/readframe.c
@@ -17,6 +17,7 @@
  * HEADER FILES *
  *==============*/
 
+#define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE   /* Make sure popen() is in stdio.h */
 #include "all.h"
 #include <time.h>
diff --git a/converter/ppm/ppmtompeg/specifics.c b/converter/ppm/ppmtompeg/specifics.c
index fb5e3649..1a1fda44 100644
--- a/converter/ppm/ppmtompeg/specifics.c
+++ b/converter/ppm/ppmtompeg/specifics.c
@@ -36,6 +36,7 @@
  * HEADER FILES *
  *==============*/
 
+#include "netpbm/mallocvar.h"
 #include "all.h"
 #include "mtypes.h"
 #include "frames.h"
@@ -239,7 +240,6 @@ FILE *fp;
   FrameSpecList *current, *new;
   char typ; 
   int fnum,snum, bnum, qs, newqs;
-  int num_scanned;
 
   fsl = MakeFslEntry();
   current = fsl;
@@ -274,7 +274,7 @@ FILE *fp;
       break;
     case 'B':
       lp += 6;
-      num_scanned = sscanf(lp, "%d %d", &bnum, &newqs);
+      sscanf(lp, "%d %d", &bnum, &newqs);
       if (qs == newqs) break;
       qs = newqs;
       AddBs(current, bnum, FALSE, qs);
@@ -289,116 +289,140 @@ FILE *fp;
   
 }
 
-/* Version 2 */
-void Parse_Specifics_File_v2(fp)
-FILE *fp;
-{
-  char line[1024], *lp;
-  FrameSpecList *current, *new;
-  char typ;
-  int fnum, snum, bnum, qs, newqs;
-  int num_scanned, fx=0, fy=0, sx=0, sy=0;
-  char kind[100];
-  Block_Specifics *new_blk;
-  boolean relative;
 
-  fsl = MakeFslEntry();
-  current = fsl;
 
-  while ((fgets(line,1023,fp))!=NULL) {
-    lp = &line[0];
-    while ((*lp == ' ') || (*lp == '\t')) lp++;
-    if (( *lp == '#' ) || (*lp=='\n')) {
-      continue;
+void
+Parse_Specifics_File_v2(FILE * const fP) {
+/*----------------------------------------------------------------------------
+   Parse Version 2 specific file.
+-----------------------------------------------------------------------------*/
+    char line[1024];
+    FrameSpecList * current;
+    int qs;
+    int numScanned;
+    int fx, fy, sx, sy;
+    char kind[100];
+    Block_Specifics *new_blk;
+    boolean relative;
+
+    fx = fy = sx = sy = 0;  /* initial value */
+
+    fsl = MakeFslEntry();
+    current = fsl;
+
+    while ((fgets(line,1023, fP))!=NULL) {
+        const char * lp;
+
+        lp = &line[0];  /* initial value */
+
+        while ((*lp == ' ') || (*lp == '\t'))
+            ++lp;
+        if (*lp == '#' || *lp == '\n') {
+            /* comment or blank line */
+        } else {
+            switch (my_upper(*lp)) {
+            case 'F': {
+                char typ;
+                FrameSpecList * new;
+                int fnum;
+
+                lp += 6;
+                sscanf(lp,"%d %c %d", &fnum, &typ, &qs);
+                new = MakeFslEntry();
+                if (current->framenum != -1) {
+                    current->next = new;
+                    current = new;
+                }
+                current->framenum = fnum;
+                current->frametype = CvtType(typ);
+                if (qs <= 0)
+                    qs = -1;
+                current->qscale = qs;
+            } break;
+            case 'S': {
+                int snum;
+                int newqs;
+                lp += 6;
+                sscanf(lp,"%d %d", &snum, &newqs);
+                if (qs == newqs)
+                    break;
+                qs = newqs;
+                AddSlc(current, snum, qs);
+            } break;
+            case 'B': {
+                int bnum;
+                int newqs;
+                lp += 6;
+                numScanned = 0;
+                bnum = atoi(lp);
+                SkipToSpace(lp);
+                while ((*lp != '-') && (*lp != '+') &&
+                       ((*lp < '0') || (*lp > '9')))
+                    ++lp;
+                relative = (*lp == '-' || *lp == '+');
+                newqs = atoi(lp);
+                SkipToSpace(lp);
+                if (EndString(lp)) {
+                    numScanned = 2;
+                } else {
+                    numScanned =
+                        2 + sscanf(lp, "%s %d %d %d %d",
+                                   kind, &fx, &fy, &sx, &sy); 
+                }
+
+                qs = newqs;
+                new_blk = AddBs(current, bnum, relative, qs);
+                if (numScanned > 2) {
+                    BlockMV * tmp;
+
+                    MALLOCVAR(tmp);
+
+                    switch (numScanned) {
+                    case 7:
+                        tmp->typ = TYP_BOTH;
+                        tmp->fx = fx;
+                        tmp->fy = fy;
+                        tmp->bx = sx;
+                        tmp->by = sy;
+                        new_blk->mv = tmp;
+                        break;
+                    case 3:
+                        tmp->typ = TYP_SKIP;
+                        new_blk->mv = tmp;
+                        break;
+                    case 5:
+                        if (my_upper(kind[0]) == 'B') {
+                            tmp->typ = TYP_BACK;
+                            tmp->bx = fx;
+                            tmp->by = fy;
+                        } else {
+                            tmp->typ = TYP_FORW;
+                            tmp->fx = fx;
+                            tmp->fy = fy;
+                        }
+                        new_blk->mv = tmp;
+                        break;
+                    default:
+                        fprintf(stderr,
+                                "Bug in specifics file!  "
+                                "Skipping short/long entry: %s\n",line);
+                        break;
+                    }
+                } else
+                    new_blk->mv = NULL;
+
+            } break;
+            case 'V':
+                fprintf(stderr,
+                        "Cannot specify version twice!  Taking first (%d).\n",
+                        version);
+                break;
+            default:
+                printf("What? *%s*\n",line);
+                break;
+            }
+        }
     }
-
-    switch (my_upper(*lp)) {
-    case 'F':
-      lp += 6;
-      sscanf(lp,"%d %c %d", &fnum, &typ, &qs);
-      new = MakeFslEntry();
-      if (current->framenum != -1) {
-	current->next = new;
-	current = new;
-      }
-      current->framenum = fnum;
-      current->frametype = CvtType(typ);
-      if (qs <= 0) qs = -1;
-      current->qscale = qs;
-      break;
-    case 'S':
-      lp += 6;
-      sscanf(lp,"%d %d", &snum, &newqs);
-      if (qs == newqs) break;
-      qs = newqs;
-      AddSlc(current, snum, qs);
-      break;
-    case 'B':
-      lp += 6;
-      num_scanned = 0;
-      bnum = atoi(lp);
-      SkipToSpace(lp);
-      while ((*lp != '-') && (*lp != '+') &&
-	     ((*lp < '0') || (*lp > '9'))) lp++;
-      relative = ((*lp == '-') || (*lp == '+'));
-      newqs = atoi(lp);
-      SkipToSpace(lp);
-      if (EndString(lp)) {
-	num_scanned = 2;
-      } else {
-	num_scanned = 2+sscanf(lp, "%s %d %d %d %d", kind, &fx, &fy, &sx, &sy); 
-      }
-
-      qs = newqs;
-      new_blk = AddBs(current, bnum, relative, qs);
-      if (num_scanned > 2) {
-	BlockMV *tmp;
-	tmp = (BlockMV *) malloc(sizeof(BlockMV));
-	switch (num_scanned) {
-	case 7:
-	  tmp->typ = TYP_BOTH;
-	  tmp->fx = fx;
-	  tmp->fy = fy;
-	  tmp->bx = sx;
-	  tmp->by = sy;
-	  new_blk->mv = tmp;
-	  break;
-	case 3:
-	  tmp->typ = TYP_SKIP;
-	  new_blk->mv = tmp;
-	  break;
-	case 5:
-	  if (my_upper(kind[0]) == 'B') {
-	    tmp->typ = TYP_BACK;
-	    tmp->bx = fx;
-	    tmp->by = fy;
-	  } else {
-	    tmp->typ = TYP_FORW;
-	    tmp->fx = fx;
-	    tmp->fy = fy;
-	  }
-	  new_blk->mv = tmp;
-	  break;
-	default:
-	  fprintf(stderr,
-		  "Bug in specifics file!  Skipping short/long entry: %s\n",line);
-	  break;
-	}
-      } else {
-	new_blk->mv = (BlockMV *) NULL;
-      }
-
-      break;
-    case 'V':
-      fprintf(stderr,
-	      "Cannot specify version twice!  Taking first (%d).\n",
-	      version);
-      break;
-    default:
-      printf("What? *%s*\n",line);
-      break;
-    }}
-  
 }
 
 
diff --git a/converter/ppm/ppmtoxpm.c b/converter/ppm/ppmtoxpm.c
index 38d99972..0e316928 100644
--- a/converter/ppm/ppmtoxpm.c
+++ b/converter/ppm/ppmtoxpm.c
@@ -33,6 +33,7 @@
 **    (base 93 not base 28 -> saves a lot of space for colorful xpms)
 */
 
+#define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE   /* Make sure strdup() is in string.h */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
diff --git a/converter/ppm/sldtoppm.c b/converter/ppm/sldtoppm.c
index 550eed5b..2dc049f8 100644
--- a/converter/ppm/sldtoppm.c
+++ b/converter/ppm/sldtoppm.c
@@ -50,7 +50,7 @@ struct spoint {
 
 /* Screen polygon */
 
-struct spolygon { 
+struct spolygon {
     int npoints,              /* Number of points in polygon */
           fill;           /* Fill type */
     struct spoint pt[11];         /* Actual points */
@@ -130,7 +130,7 @@ sli(void) {
 
 /*  SLIB  --  Input byte from slide file  */
 
-static int 
+static int
 slib(void) {
     unsigned char ch;
 
@@ -154,85 +154,127 @@ vscale(int * const px,
 
 
 
+static void
+upcase(const char * const sname,
+       char *       const uname) {
+
+    unsigned int i;
+    const char * ip;
+
+    for (i = 0, ip = sname; i < 31; ++i) {
+        char const ch = *ip++;
+
+        if (ch != EOS)
+            uname[i] = islower(ch) ? toupper(ch) : ch;
+    }
+    uname[i] = EOS;
+}
+
+
+
+static void
+skipBytes(FILE *       const fileP,
+          unsigned int const count) {
+
+    unsigned int i;
+
+    for (i = 0; i < count; ++i)
+        getc(fileP);
+}
+
+
+
+static void
+scanDirectory(FILE *       const slFileP,
+              long         const dirPos,
+              bool         const dirOnly,
+              const char * const uname,
+              bool *       const foundP) {
+/*----------------------------------------------------------------------------
+   Scan the directory at the current position in *slFileP, either listing
+   the directory ('dirOnly' true) or searching for a slide named
+   'uname' ('dirOnly' false).
+
+   'dirPos' is the offset in the file of the directory, i.e. the current
+   position of *slFileP.
+
+   In the latter case, return as *foundP whether the slide name is there.
+-----------------------------------------------------------------------------*/
+    bool found;
+    bool eof;
+    long pos;
+    unsigned char libent[36];
+
+    for (found = false, eof = false, pos = dirPos; !found && !eof; ) {
+        size_t readCt;
+        readCt = fread(libent, 36, 1, slFileP);
+        if (readCt != 1)
+            eof = true;
+        else {
+            /* The directory entry is 32 bytes of NUL-terminated slide name
+               followed by 4 bytes of offset of the next directory entry.
+            */
+            const char * const slideName = (const char *)(&libent[0]);
+            if (pm_strnlen(slideName, 32) == 32)
+                pm_error("Invalid input: slide name field is not "
+                         "nul-terminated");
+            else {
+                if (strlen(slideName) == 0)
+                    eof = true;
+                else {
+                    pos += 36;
+                    if (dirOnly) {
+                        pm_message("  %s", slideName);
+                    } else if (streq(slideName, uname)) {
+                        long const dpos =
+                            (((((libent[35] << 8) | libent[34]) << 8) |
+                              libent[33]) << 8) | libent[32];
+
+                        if ((slFileP == stdin) ||
+                            (fseek(slFileP, dpos, 0) == -1)) {
+
+                            skipBytes(slFileP, dpos - pos);
+                        }
+                        found = true;
+                    }
+                }
+            }
+        }
+    }
+    *foundP = found;
+}
+
 /*  SLIDEFIND  --  Find  a  slide  in  a  library  or,  if  DIRONLY is
            nonzero, print a directory listing of the  library.
            If  UCASEN  is nonzero, the requested slide name is
            converted to upper case. */
 
 static void
-slidefind(const char * const sname,
-          bool         const dironly,
+slidefind(const char * const slideName,
+          bool         const dirOnly,
           bool         const ucasen) {
 
-    char uname[32];
-    unsigned char libent[36];
-    long pos;
+    char uname[32];  /* upper case translation of 'slideName' */
+    char header[32]; /* (supposed) header read from file */
     bool found;
-    bool eof;
 
-    if (dironly)
+    if (dirOnly)
         pm_message("Slides in library:");
     else {
-        unsigned int i;
-        const char * ip;
-        
-        ip = sname; /* initial value */
-        
-        for (i = 0; i < 31; ++i) {
-            char const ch = *ip++;
-            if (ch == EOS)
-                break;
-
-            {
-                char const upperCh =
-                    ucasen && islower(ch) ? toupper(ch) : ch;
-                
-                uname[i] = upperCh;
-            }
-        }
-        uname[i] = EOS;
+        upcase(slideName, uname);
     }
-    
+
     /* Read slide library header and verify. */
-    
-    if ((fread(libent, 32, 1, slfile) != 1) ||
-        (!strneq((char *)libent, "AutoCAD Slide Library 1.0\015\012\32", 32))) {
+
+    if ((fread(header, 32, 1, slfile) != 1) ||
+        (!STRSEQ(header, "AutoCAD Slide Library 1.0\r\n\32"))) {
         pm_error("not an AutoCAD slide library file.");
     }
-    pos = 32;
-    
-    /* Search for a slide with the requested name or list the directory */
-    
-    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) {
-                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];
-        
-                if ((slfile == stdin) || (fseek(slfile, dpos, 0) == -1)) {
-                    dpos -= pos;
-
-                    while (dpos-- > 0)
-                        getc(slfile);
-                }
-                found = true;
-            }
-        }
-    }
-    if (!found && !dironly)
-        pm_error("slide '%s' not in library.", sname);
+    scanDirectory(slfile, 32, dirOnly, ucasen ? uname : slideName, &found);
+
+    if (!found && !dirOnly)
+        pm_error("slide '%s' not in library.", slideName);
 }
 
 
@@ -295,14 +337,14 @@ flood(struct spolygon * const poly,
         assert(poly->pt[i].x >= 0 && poly->pt[i].x < pixcols);
         assert(poly->pt[i].y >= 0 && poly->pt[i].y < pixrows);
         ppmd_line(pixels, pixcols, pixrows, pixmaxval,
-                  poly->pt[i].x, iydots - poly->pt[i].y, 
+                  poly->pt[i].x, iydots - poly->pt[i].y,
                   poly->pt[(i + 1) % poly->npoints].x,
                   iydots - poly->pt[(i + 1) % poly->npoints].y,
                   ppmd_fill_drawproc, handle);
     }
     ppmd_fill(pixels, pixcols, pixrows, pixmaxval,
               handle, PPMD_NULLDRAWPROC, (char *) &rgbcolor);
-    
+
     ppmd_fill_destroy(handle);
 }
 
@@ -333,11 +375,11 @@ slider(slvecfn   slvec,
     {"AutoCAD Slide\r\n\32", 86,2, 0,0, 0.0, 0};
     int curcolor = 7;             /* Current vector color */
     pixel rgbcolor;           /* Pixel used to clear pixmap */
-    
+
     lx = ly = 32000;
-    
+
     /* Process the header of the slide file.  */
-    
+
     sdrawkcab = false;            /* Initially guess byte order is OK */
     fread(slfrof.slh, 17, 1, slfile);
     fread(&slfrof.sntype, sizeof(char), 1, slfile);
@@ -361,12 +403,12 @@ slider(slvecfn   slvec,
         pm_error("incompatible slide file format");
 
     /* Build SDSAR value from long scaled version. */
-    
+
     ldsar = 0L;
     for (i = 3; i >= 0; --i)
         ldsar = (ldsar << 8) | ubfr[i];
     slfrof.sdsar = ((double) ldsar) / 1E7;
-    
+
     /* Examine the byte order test value.   If it's backwards, set the
        byte-reversal flag and correct all of the values we've read  in
        so far.
@@ -380,7 +422,7 @@ slider(slvecfn   slvec,
         rshort(slfrof.shwfill);
         #undef rshort
     }
-    
+
     /* Dump the header if we're blithering. */
 
     if (blither || info) {
@@ -455,29 +497,29 @@ slider(slvecfn   slvec,
         }
         iydots = sysize - 1;
     }
-    
+
     if (adjust) {
         pm_message(
             "Resized from %dx%d to %dx%d to correct pixel aspect ratio.",
             slfrof.sxdots + 1, slfrof.sydots + 1, ixdots + 1, iydots + 1);
     }
-    
+
     /* Allocate image buffer and clear it to black. */
-    
+
     pixels = ppm_allocarray(pixcols = ixdots + 1, pixrows = iydots + 1);
     PPM_ASSIGN(rgbcolor, 0, 0, 0);
     ppmd_filledrectangle(pixels, pixcols, pixrows, pixmaxval, 0, 0,
                          pixcols, pixrows, PPMD_NULLDRAWPROC,
                          (char *) &rgbcolor);
-    
+
     if ((rescale = slfrof.sxdots != ixdots ||
          slfrof.sydots != iydots ||
          slfrof.sdsar != dsar) != 0) {
-        
+
         /* Rescale all coords. so they'll look (more or less)
            right on this display.
         */
-        
+
         xfac = (ixdots + 1) * 0x10000L;
         xfac /= (long) (slfrof.sxdots + 1);
         yfac = (iydots + 1) * 0x10000L;
@@ -490,7 +532,7 @@ slider(slvecfn   slvec,
     }
 
     poly.npoints = 0;             /* No flood in progress. */
-    
+
     while ((cw = sli()) != 0xFC00) {
         switch (cw & 0xFF00) {
         case 0xFB00:          /*  Short vector compressed  */
@@ -508,10 +550,10 @@ slider(slvecfn   slvec,
             slx = vec.f.x;        /* Save scaled point */
             sly = vec.f.y;
             break;
-            
+
         case 0xFC00:          /*  End of file  */
             break;
-            
+
         case 0xFD00:          /*  Flood command  */
             vec.f.x = sli();
             vec.f.y = sli();
@@ -538,7 +580,7 @@ slider(slvecfn   slvec,
                 poly.npoints++;
             }
             break;
-            
+
         case 0xFE00:          /*  Common endpoint compressed  */
             vec.f.x = lx + extend(cw & 0xFF);
             vec.f.y = ly + slib();
@@ -553,7 +595,7 @@ slider(slvecfn   slvec,
             slx = vec.f.x;        /* Save scaled point */
             sly = vec.f.y;
             break;
-            
+
         case 0xFF00:          /*  Change color  */
             curcolor = cw & 0xFF;
             break;
@@ -661,7 +703,7 @@ main(int          argc,
     }
 
     /* If a file name is specified, open it.  Otherwise read from
-       standard input. 
+       standard input.
     */
 
     if (argn < argc) {
@@ -670,24 +712,24 @@ main(int          argc,
     } else {
         slfile = stdin;
     }
-    
+
     if (argn != argc) {           /* Extra bogus arguments ? */
         pm_usage(usage);
     }
-    
+
     /* If we're extracting an item from a slide library, position the
        input stream to the start of the chosen slide.
     */
- 
+
     if (dironly || slobber)
         slidefind(slobber, dironly, ucasen);
- 
+
     if (!dironly) {
         slider(draw, flood);
         ppm_writeppm(stdout, pixels, pixcols, pixrows, pixmaxval, 0);
     }
     pm_close(slfile);
     pm_close(stdout);
-    
+
     return 0;
 }
diff --git a/converter/ppm/tgatoppm.c b/converter/ppm/tgatoppm.c
index 95893089..662f741b 100644
--- a/converter/ppm/tgatoppm.c
+++ b/converter/ppm/tgatoppm.c
@@ -12,6 +12,7 @@
 ** implied warranty.
 */
 
+#define _DEFAULT_SOURCE 1  /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
@@ -29,7 +30,6 @@ static int mapped, rlencoded;
 
 static pixel ColorMap[MAXCOLORS];
 static gray AlphaMap[MAXCOLORS];
-static int RLE_count = 0, RLE_flag = 0;
 
 struct cmdlineInfo {
     /* All the information the user supplied in the command line,
@@ -78,14 +78,14 @@ parseCommandLine(int argc, char ** argv,
         cmdlineP->input_filename = "-";  /* he wants stdin */
     else if (argc - 1 == 1)
         cmdlineP->input_filename = strdup(argv[1]);
-    else 
+    else
         pm_error("Too many arguments.  The only argument accepted "
                  "is the input file specification");
 
     if (alpha_spec &&
         streq(cmdlineP->alpha_filename, "-"))
         cmdlineP->alpha_stdout = 1;
-    else 
+    else
         cmdlineP->alpha_stdout = 0;
 
     if (!alpha_spec)
@@ -102,89 +102,111 @@ getbyte(FILE * const ifP) {
 
     if ( fread( (char*) &c, 1, 1, ifP ) != 1 )
         pm_error( "EOF / read error" );
-    
+
     return c;
 }
 
 
 
+static int RLE_count = 0, RLE_flag = 0;
+
+
+
 static void
-get_pixel(FILE * const ifP, pixel * dest, int Size, gray *alpha_p) {
+handleRun(FILE * const ifP,
+          bool * const repeatP) {
+
+    if (rlencoded) {
+        if (RLE_count == 0) {
+            /* Have to restart run. */
+            unsigned char i;
+            i = getbyte(ifP);
+            RLE_flag = (i & 0x80);
+            if (RLE_flag == 0) {
+                /* Stream of unencoded pixels. */
+                RLE_count = i + 1;
+            } else {
+                /* Single pixel replicated. */
+                RLE_count = i - 127;
+            }
+            /* Decrement count & get pixel. */
+            --RLE_count;
+            *repeatP = false;
+        } else {
+            /* Have already read count & (at least) first pixel. */
+            --RLE_count;
+            if (RLE_flag != 0) {
+                /* Replicated pixels. */
+                *repeatP = true;
+            } else
+                *repeatP = false;
+        }
+    } else
+        *repeatP = false;
+}
 
-    static pixval Red, Grn, Blu;
-    static pixval Alpha;
-    unsigned char j, k;
+
+
+static void
+getPixel(FILE *  const ifP,
+         pixel * const dest,
+         int     const size,
+         gray *  const alphaP) {
+
+    static pixval red, grn, blu;
+    static pixval alpha;
     static unsigned int l;
+    unsigned char j, k;
+    bool repeat;
+        /* Next pixel is just a repeat (from an encoded run) */
 
-    /* Check if run length encoded. */
-    if ( rlencoded )
-	{
-	if ( RLE_count == 0 )
-	    { /* Have to restart run. */
-	    unsigned char i;
-	    i = getbyte( ifP );
-	    RLE_flag = ( i & 0x80 );
-	    if ( RLE_flag == 0 )
-		/* Stream of unencoded pixels. */
-		RLE_count = i + 1;
-	    else
-		/* Single pixel replicated. */
-		RLE_count = i - 127;
-	    /* Decrement count & get pixel. */
-	    --RLE_count;
-	    }
-	else
-	    { /* Have already read count & (at least) first pixel. */
-	    --RLE_count;
-	    if ( RLE_flag != 0 )
-		/* Replicated pixels. */
-		goto PixEncode;
-	    }
-	}
-    /* Read appropriate number of bytes, break into RGB. */
-    switch ( Size )
-	{
-	case 8:				/* Grayscale, read and triplicate. */
-	Red = Grn = Blu = l = getbyte( ifP );
-    Alpha = 0;
-	break;
-
-	case 16:			/* 5 bits each of red green and blue. */
-	case 15:			/* Watch byte order. */
-	j = getbyte( ifP );
-	k = getbyte( ifP );
-	l = ( (unsigned int) k << 8 ) + j;
-	Red = ( k & 0x7C ) >> 2;
-	Grn = ( ( k & 0x03 ) << 3 ) + ( ( j & 0xE0 ) >> 5 );
-	Blu = j & 0x1F;
-    Alpha = 0;
-	break;
-
-	case 32:            /* 8 bits each of blue, green, red, and alpha */
-	case 24:			/* 8 bits each of blue, green, and red. */
-	Blu = getbyte( ifP );
-	Grn = getbyte( ifP );
-	Red = getbyte( ifP );
-	if ( Size == 32 )
-	    Alpha = getbyte( ifP );
-    else
-        Alpha = 0;
-	l = 0;
-	break;
+    handleRun(ifP, &repeat);
 
-	default:
-	pm_error( "unknown pixel size (#2) - %d", Size );
-	}
+    if (repeat) {
+        /* Use red, grn, blu, alpha, and l from prior call to getPixel */
+    } else {
+        /* Read appropriate number of bytes, break into RGB. */
+        switch (size) {
+        case 8:             /* Grayscale, read and triplicate. */
+            red = grn = blu = l = getbyte(ifP);
+            alpha = 0;
+            break;
 
-PixEncode:
-    if ( mapped ) {
+        case 16:            /* 5 bits each of red green and blue. */
+        case 15:            /* Watch byte order. */
+            j = getbyte(ifP);
+            k = getbyte(ifP);
+            l = ((unsigned int)k << 8) + j;
+            red = (k & 0x7C) >> 2;
+            grn = ((k & 0x03) << 3) + ((j & 0xE0) >> 5);
+            blu = j & 0x1F;
+            alpha = 0;
+            break;
+
+        case 32:            /* 8 bits each of blue, green, red, and alpha */
+        case 24:            /* 8 bits each of blue, green, and red. */
+            blu = getbyte(ifP);
+            grn = getbyte(ifP);
+            red = getbyte(ifP);
+            if (size == 32)
+                alpha = getbyte(ifP);
+            else
+                alpha = 0;
+            l = 0;
+            break;
+
+        default:
+            pm_error("unknown pixel size (#2) - %d", size);
+        }
+    }
+    if (mapped) {
         *dest = ColorMap[l];
-        *alpha_p = AlphaMap[l];
+        *alphaP = AlphaMap[l];
     } else {
-        PPM_ASSIGN( *dest, Red, Grn, Blu );
-        *alpha_p = Alpha;
-    }
+        PPM_ASSIGN(*dest, red, grn, blu);
+        *alphaP = alpha;
     }
+}
 
 
 
@@ -216,7 +238,7 @@ readtga(FILE * const ifP, struct ImageHeader * tgaP) {
     tgaP->Rsrvd = ( flags & 0x10 ) >> 4;
     tgaP->OrgBit = ( flags & 0x20 ) >> 5;
     tgaP->IntrLve = ( flags & 0xc0 ) >> 6;
-    
+
     if ( tgaP->IdLength != 0 )
         fread( junk, 1, (int) tgaP->IdLength, ifP );
 }
@@ -230,14 +252,14 @@ get_map_entry(FILE * const ifP, pixel * Value, int Size, gray * Alpha) {
 
     /* Read appropriate number of bytes, break into rgb & put in map. */
     switch ( Size )
-	{
-	case 8:				/* Grayscale, read and triplicate. */
+    {
+    case 8:             /* Grayscale, read and triplicate. */
         r = g = b = getbyte( ifP );
         a = 0;
         break;
-        
-	case 16:			/* 5 bits each of red green and blue. */
-	case 15:			/* Watch for byte order. */
+
+    case 16:            /* 5 bits each of red green and blue. */
+    case 15:            /* Watch for byte order. */
         j = getbyte( ifP );
         k = getbyte( ifP );
         r = ( k & 0x7C ) >> 2;
@@ -245,21 +267,21 @@ get_map_entry(FILE * const ifP, pixel * Value, int Size, gray * Alpha) {
         b = j & 0x1F;
         a = 0;
         break;
-        
-	case 32:            /* 8 bits each of blue, green, red, and alpha */
-	case 24:			/* 8 bits each of blue green and red. */
+
+    case 32:            /* 8 bits each of blue, green, red, and alpha */
+    case 24:            /* 8 bits each of blue green and red. */
         b = getbyte( ifP );
         g = getbyte( ifP );
         r = getbyte( ifP );
         if ( Size == 32 )
             a = getbyte( ifP );
-        else 
+        else
             a = 0;
         break;
-        
-	default:
+
+    default:
         pm_error( "unknown colormap pixel size (#2) - %d", Size );
-	}
+    }
     PPM_ASSIGN( *Value, r, g, b );
     *Alpha = a;
 }
@@ -325,84 +347,89 @@ main(int argc, char * argv[]) {
 
     if (cmdline.alpha_stdout)
         alpha_file = stdout;
-    else if (cmdline.alpha_filename == NULL) 
+    else if (cmdline.alpha_filename == NULL)
         alpha_file = NULL;
     else
         alpha_file = pm_openw(cmdline.alpha_filename);
-    
-    if (cmdline.alpha_stdout) 
+
+    if (cmdline.alpha_stdout)
         imageout_file = NULL;
     else
         imageout_file = stdout;
-    
+
     /* Read the Targa file header. */
     readtga(ifP, &tga_head);
 
-    if (cmdline.headerdump) 
+    if (cmdline.headerdump)
         dumpHeader(tga_head);
 
     rows = ((int) tga_head.Height_lo) + ((int) tga_head.Height_hi) * 256;
     cols = ((int) tga_head.Width_lo)  + ((int) tga_head.Width_hi)  * 256;
 
     switch (tga_head.ImgType) {
-	case TGA_Map:
-	case TGA_RGB:
-	case TGA_Mono:
-	case TGA_RLEMap:
-	case TGA_RLERGB:
-	case TGA_RLEMono:
+    case TGA_Map:
+    case TGA_RGB:
+    case TGA_Mono:
+    case TGA_RLEMap:
+    case TGA_RLERGB:
+    case TGA_RLEMono:
+        break;
+    case TGA_CompMap:
+    case TGA_CompMap4:
+        pm_error("Targa image type %d (compressed color-mapped data). "
+                 "Cannot handle this format.", tga_head.ImgType);
         break;
-	default:
+    default:
         pm_error("unknown Targa image type %d", tga_head.ImgType);
-	}
-    
+    }
+
     if (tga_head.ImgType == TGA_Map ||
         tga_head.ImgType == TGA_RLEMap ||
         tga_head.ImgType == TGA_CompMap ||
         tga_head.ImgType == TGA_CompMap4)
-	{ /* Color-mapped image */
+    { /* Color-mapped image */
         if (tga_head.CoMapType != 1)
-            pm_error( 
+            pm_error(
                 "mapped image (type %d) with color map type != 1",
                 tga_head.ImgType );
         mapped = true;
         /* Figure maxval from CoSize. */
         switch (tga_head.CoSize) {
-	    case 8:
-	    case 24:
-	    case 32:
+        case 8:
+        case 24:
+        case 32:
             maxval = 255;
             break;
 
-	    case 15:
-	    case 16:
+        case 15:
+        case 16:
             maxval = 31;
             break;
 
-	    default:
-	    pm_error(
-		"unknown colormap pixel size - %d", tga_head.CoSize );
-	    }
-	} else { 
+        default:
+        pm_error(
+        "unknown colormap pixel size - %d", tga_head.CoSize );
+        }
+    } else {
         /* Not colormap, so figure maxval from PixelSize. */
         mapped = false;
         switch ( tga_head.PixelSize ) {
-	    case 8:
-	    case 24:
-	    case 32:
+        case 8:
+        case 24:
+        case 32:
             maxval = 255;
             break;
-            
-	    case 15:
-	    case 16:
+
+        case 15:
+        case 16:
             maxval = 31;
             break;
-            
-	    default:
+
+        default:
             pm_error("unknown pixel size - %d", tga_head.PixelSize);
-	    }
-	}
-    
+        }
+    }
+
     /* If required, read the color map information. */
     if ( tga_head.CoMapType != 0 ) {
         unsigned int i;
@@ -415,7 +442,7 @@ main(int argc, char * argv[]) {
         for (i = temp1; i < (temp1 + temp2); ++i)
             get_map_entry(ifP, &ColorMap[i], (int) tga_head.CoSize,
                           &AlphaMap[i]);
-	}
+    }
 
     /* Check run-length encoding. */
     if (tga_head.ImgType == TGA_RLEMap ||
@@ -424,7 +451,7 @@ main(int argc, char * argv[]) {
         rlencoded = 1;
     else
         rlencoded = 0;
-    
+
     /* Read the Targa file body and convert to portable format. */
     pixels = ppm_allocarray( cols, rows );
     alpha = pgm_allocarray( cols, rows );
@@ -436,10 +463,10 @@ main(int argc, char * argv[]) {
         realrow = truerow;
         if (tga_head.OrgBit == 0)
             realrow = rows - realrow - 1;
-        
+
         for (col = 0; col < cols; ++col)
-            get_pixel(ifP, &(pixels[realrow][col]), (int) tga_head.PixelSize,
-                      &(alpha[realrow][col]));
+            getPixel(ifP, &(pixels[realrow][col]), (int) tga_head.PixelSize,
+                     &(alpha[realrow][col]));
         if (tga_head.IntrLve == TGA_IL_Four)
             truerow += 4;
         else if (tga_head.IntrLve == TGA_IL_Two)
@@ -448,17 +475,20 @@ main(int argc, char * argv[]) {
             ++truerow;
         if (truerow >= rows)
             truerow = ++baserow;
-	}
+    }
     pm_close(ifP);
-    
-    if (imageout_file) 
+
+    if (imageout_file)
         ppm_writeppm(imageout_file, pixels, cols, rows, (pixval) maxval, 0);
     if (alpha_file)
         pgm_writepgm(alpha_file, alpha, cols, rows, (pixval) maxval, 0);
-    if (imageout_file) 
+    if (imageout_file)
         pm_close(imageout_file);
     if (alpha_file)
         pm_close(alpha_file);
 
     return 0;
 }
+
+
+
diff --git a/converter/ppm/winicontoppm.c b/converter/ppm/winicontoppm.c
index 6b1376b2..2c9015f5 100644
--- a/converter/ppm/winicontoppm.c
+++ b/converter/ppm/winicontoppm.c
@@ -14,6 +14,7 @@
 ** 03/2003 - Added 24+32 bpp capability.
 */
 
+#define _DEFAULT_SOURCE 1  /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
diff --git a/converter/ppm/ximtoppm.c b/converter/ppm/ximtoppm.c
index ce5e6396..75faac69 100644
--- a/converter/ppm/ximtoppm.c
+++ b/converter/ppm/ximtoppm.c
@@ -10,6 +10,7 @@
 ** implied warranty.
 */
 
+#define _DEFAULT_SOURCE 1  /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
diff --git a/converter/ppm/xpmtoppm.c b/converter/ppm/xpmtoppm.c
index 27f17931..46101a09 100644
--- a/converter/ppm/xpmtoppm.c
+++ b/converter/ppm/xpmtoppm.c
@@ -3,6 +3,7 @@
    Copyright and history information is at end of file
 */
 
+#define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE   /* Make sure strdup() is in string.h */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
@@ -81,14 +82,14 @@ parseCommandLine(int argc, char ** argv,
         cmdlineP->input_filespec = NULL;  /* he wants stdin */
     else if (argc - 1 == 1)
         cmdlineP->input_filespec = strdup(argv[1]);
-    else 
+    else
         pm_error("Too many arguments.  The only argument accepted\n"
                  "is the input file specification");
 
-    if (cmdlineP->alpha_filename && 
+    if (cmdlineP->alpha_filename &&
         streq(cmdlineP->alpha_filename, "-"))
         cmdlineP->alpha_stdout = TRUE;
-    else 
+    else
         cmdlineP->alpha_stdout = FALSE;
 
 }
@@ -223,7 +224,7 @@ entryMatch(struct ColorNameHashTableEntry const entry,
 
     {
         unsigned int i;
-        
+
         for (i = 0; i < size; ++i) {
             if (name[i] != entry.colorName[i])
                 return false;
@@ -271,7 +272,7 @@ hash_find(const ColorNameHash *             const hashP,
     for (i = initialIndex;
          !entryMatch(hashP->table[i], name, hashP->nameSize);
          bumpIndex(&i, hashP->size, initialIndex));
-         
+
     *entryPP = &hashP->table[i];
 }
 
@@ -357,7 +358,7 @@ getLine(char * const line,
 
    If 'backup' is true, the "next line" is the previously read line, i.e.
    the one in that one-line buffer.  Otherwise, the "next line" is the next
-   line from the real file.  After reading the backed up line, we reset 
+   line from the real file.  After reading the backed up line, we reset
    'backup' to false.
 
    Return the line as a null terminated string in *line, which is an
@@ -370,7 +371,7 @@ getLine(char * const line,
                  "which is out of bounds");
 
     if (backup) {
-        strncpy(line, lastInputLine, size); 
+        strncpy(line, lastInputLine, size);
         backup = FALSE;
     } else {
         if (fgets(line, size, stream) == NULL)
@@ -397,7 +398,7 @@ getword(char * const output, char ** const cursorP) {
         strncpy(output, t1, t2 - t1);
     output[t2 - t1] = '\0';
     *cursorP = t2;
-}    
+}
 
 
 
@@ -437,7 +438,7 @@ validateColorName(const char * const name,
 
 static void
 interpretXpm3ColorTableLine(char               const line[],
-                            unsigned int       const seqNum, 
+                            unsigned int       const seqNum,
                             unsigned int       const charsPerPixel,
                             ColorNameHash *    const hashP) {
 /*----------------------------------------------------------------------------
@@ -457,22 +458,22 @@ interpretXpm3ColorTableLine(char               const line[],
 -----------------------------------------------------------------------------*/
     /* 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 takes only 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 str2[MAX_LINE+1];
     char * t1;
     char * t2;
     int endOfEntry;   /* boolean */
-    
+
     unsigned int curkey, key, highkey;  /* current color key */
-    bool lastwaskey;    
+    bool lastwaskey;
         /* The last token we processes was a key, and we have processed
            at least one token.
         */
     char curbuf[BUFSIZ];        /* current buffer */
     bool isTransparent;
-    
+
     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.
@@ -484,20 +485,20 @@ interpretXpm3ColorTableLine(char               const 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 %uth 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 */
-    
+
     validateColorName(t1, charsPerPixel);
     colorName = t1;
 
     t1 += charsPerPixel;
 
     /*
-     * read color keys and values 
+     * read color keys and values
      */
-    curkey = 0; 
+    curkey = 0;
     highkey = 1;
     lastwaskey = FALSE;
     t2 = t1;
@@ -511,8 +512,8 @@ interpretXpm3ColorTableLine(char               const line[],
             /* See if the word we got is a valid key (and get its key
                number if so)
             */
-            for (key = 1; 
-                 key <= NKEYS && !streq(xpmColorKeys[key - 1], str2); 
+            for (key = 1;
+                 key <= NKEYS && !streq(xpmColorKeys[key - 1], str2);
                  key++);
             isKey = (key <= NKEYS);
 
@@ -520,21 +521,21 @@ interpretXpm3ColorTableLine(char               const line[],
                 /* This word is a color specification (or "none" for
                    transparent).
                 */
-                if (!curkey) 
+                if (!curkey)
                     pm_error("Missing color key token in color table line "
                              "'%s' before '%s'.", line, str2);
-                if (!lastwaskey) 
+                if (!lastwaskey)
                     strcat(curbuf, " ");        /* append space */
-                if ( (strneq(str2, "None", 4)) 
+                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 
+                } else
                     strcat(curbuf, str2);       /* append buf */
                 lastwaskey = FALSE;
-            } else { 
-                /* This word is a key.  So we've seen the last of the 
+            } 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.
@@ -560,7 +561,7 @@ interpretXpm3ColorTableLine(char               const line[],
         addToColorMap(hashP, colorName, curbuf, isTransparent);
         highkey = curkey;
     }
-    if (highkey == 1) 
+    if (highkey == 1)
         pm_error("C error scanning color table");
 }
 
@@ -587,10 +588,10 @@ readV3ColorTable(FILE *             const ifP,
         /* skip the comment line if any */
         if (strneq(line, "/*", 2))
             getLine(line, sizeof(line), ifP);
-            
+
         interpretXpm3ColorTableLine(line, seqNum, charsPerPixel,
                                     colorNameHashP);
-                                    
+
     }
     *colorNameHashPP = colorNameHashP;
 }
@@ -600,7 +601,7 @@ readV3ColorTable(FILE *             const ifP,
 static void
 readXpm3Header(FILE *             const ifP,
                unsigned int *     const widthP,
-               unsigned int *     const heightP, 
+               unsigned int *     const heightP,
                unsigned int *     const charsPerPixelP,
                ColorNameHash **   const colorNameHashPP) {
 /*----------------------------------------------------------------------------
@@ -619,14 +620,14 @@ readXpm3Header(FILE *             const ifP,
 -----------------------------------------------------------------------------*/
     char line[MAX_LINE+1];
     const char * xpm3_signature = "/* XPM */";
-    
+
     unsigned int width, height;
     unsigned int nColors;
     unsigned int charsPerPixel;
 
     /* Read the XPM signature comment */
     getLine(line, sizeof(line), ifP);
-    if (!strneq(line, xpm3_signature, strlen(xpm3_signature))) 
+    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);
 
@@ -715,7 +716,7 @@ readV1ColorTable(FILE *           const ifP,
 static void
 readXpm1Header(FILE *           const ifP,
                unsigned int *   const widthP,
-               unsigned int *   const heightP, 
+               unsigned int *   const heightP,
                unsigned int *   const charsPerPixelP,
                ColorNameHash ** const colorNameHashPP) {
 /*----------------------------------------------------------------------------
@@ -723,11 +724,11 @@ readXpm1Header(FILE *           const ifP,
   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.
 -----------------------------------------------------------------------------*/
     int format, v;
-    bool processedStaticChar;  
+    bool processedStaticChar;
         /* We have read up to and interpreted the "static char..." line */
     char * t1;
     unsigned int nColors;
@@ -875,7 +876,7 @@ convertRow(char                  const line[],
                    "'%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
@@ -889,7 +890,7 @@ convertRow(char                  const line[],
 
             alpharow[col] = hash_isTransparent(colorNameHashP, lineCursor) ?
                 PBM_BLACK : PBM_WHITE;
-            
+
             lineCursor += charsPerPixel;
         }
         if (*lineCursor != '"')
@@ -902,7 +903,7 @@ convertRow(char                  const line[],
 static void
 convertRaster(FILE *                const ifP,
               unsigned int          const cols,
-              unsigned int          const rows, 
+              unsigned int          const rows,
               unsigned int          const charsPerPixel,
               const ColorNameHash * const colorNameHashP,
               FILE *                const imageOutFileP,
@@ -927,7 +928,7 @@ convertRaster(FILE *                const ifP,
         bool haveLine;
 
         for (haveLine = false; !haveLine; ) {
-            getLine(line, sizeof(line), ifP); 
+            getLine(line, sizeof(line), ifP);
 
             if (strneq(line, "/*", 2)) {
                 /* It's a comment.  Ignore it. */
@@ -938,22 +939,22 @@ convertRaster(FILE *                const ifP,
                    pixrow, alpharow);
 
         if (imageOutFileP)
-            ppm_writeppmrow(imageOutFileP, 
+            ppm_writeppmrow(imageOutFileP,
                             pixrow, cols, PPM_MAXMAXVAL, 0);
-            if (alphaOutFileP)
-                pbm_writepbmrow(alphaOutFileP, alpharow, cols, 0);
+        if (alphaOutFileP)
+            pbm_writepbmrow(alphaOutFileP, alpharow, cols, 0);
     }
 
     pbm_freerow(alpharow);
     ppm_freerow(pixrow);
 }
- 
+
 
 
 static void
 readXpmHeader(FILE *           const ifP,
               unsigned int *   const widthP,
-              unsigned int *   const heightP, 
+              unsigned int *   const heightP,
               unsigned int *   const charsPerPixelP,
               ColorNameHash ** const colorNameHashPP) {
 /*----------------------------------------------------------------------------
@@ -973,7 +974,7 @@ readXpmHeader(FILE *           const ifP,
     /* 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 */
@@ -986,7 +987,7 @@ readXpmHeader(FILE *           const ifP,
     *heightP        = height;
     *charsPerPixelP = charsPerPixel;
 }
- 
+
 
 
 int
@@ -1007,20 +1008,20 @@ main(int argc, char *argv[]) {
 
     verbose = cmdline.verbose;
 
-    if ( cmdline.input_filespec != NULL ) 
+    if ( cmdline.input_filespec != NULL )
         ifP = pm_openr( cmdline.input_filespec);
     else
         ifP = stdin;
 
     if (cmdline.alpha_stdout)
         alphaOutFileP = stdout;
-    else if (cmdline.alpha_filename == NULL) 
+    else if (cmdline.alpha_filename == NULL)
         alphaOutFileP = NULL;
     else {
         alphaOutFileP = pm_openw(cmdline.alpha_filename);
     }
 
-    if (cmdline.alpha_stdout) 
+    if (cmdline.alpha_stdout)
         imageOutFileP = NULL;
     else
         imageOutFileP = stdout;
@@ -1035,7 +1036,7 @@ main(int argc, char *argv[]) {
 
     convertRaster(ifP, cols, rows, charsPerPixel, colorNameHashP,
                   imageOutFileP, alphaOutFileP);
-    
+
     pm_close(ifP);
     if (imageOutFileP)
         pm_close(imageOutFileP);
@@ -1043,7 +1044,7 @@ main(int argc, char *argv[]) {
         pm_close(alphaOutFileP);
 
     hash_destroy(colorNameHashP);
-    
+
     return 0;
 }
 
@@ -1065,17 +1066,17 @@ main(int argc, char *argv[]) {
 **   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 
+**  - 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/xvminitoppm.c b/converter/ppm/xvminitoppm.c
index d76bea87..ad207ae9 100644
--- a/converter/ppm/xvminitoppm.c
+++ b/converter/ppm/xvminitoppm.c
@@ -13,12 +13,13 @@
 #include <assert.h>
 #include <string.h>
 #include <errno.h>
+#include <stdbool.h>
 
 #include "pm_c_util.h"
 #include "nstring.h"
+#include "pm.h"
 #include "ppm.h"
 
-#define BUFSIZE 256
 
 
 typedef struct xvPalette {
@@ -53,25 +54,6 @@ parseCommandLine(int const argc,
 
 
 static void
-getLine(FILE * const ifP,
-        char * const buf,
-        size_t const size) {
-
-    char * rc;
-
-    rc = fgets(buf, size, ifP);
-    if (rc == NULL) {
-        if (ferror(ifP))
-            pm_error("read error.  fgets() failed, errno=%d (%s)",
-                     errno, strerror(errno));
-        else
-            pm_error("unexpected EOF");
-    }
-}
-
-
-
-static void
 makeXvPalette(xvPalette * const xvPaletteP) {
 
     unsigned int paletteIndex;
@@ -101,37 +83,53 @@ readXvHeader(FILE *         const ifP,
              unsigned int * const colsP,
              unsigned int * const rowsP,
              unsigned int * const maxvalP) {
-           
-    char buf[256];
+
+    char * buf;
+    size_t bufferSz;
+    int eof;
+    size_t lineLen;
     unsigned int cols, rows, maxval;
     int rc;
     bool endOfComments;
-    
-    getLine(ifP, buf, sizeof(buf));
 
-    if (!strneq(buf, "P7 332", 6))
+    buf = NULL;   /* initial value */
+    bufferSz = 0; /* initial value */
+
+    pm_getline(ifP, &buf, &bufferSz, &eof, &lineLen);
+
+    if (eof || !strneq(buf, "P7 332", 6))
         pm_error("Input is not a XV thumbnail picture.  It does not "
                  "begin with the characters 'P7 332'.");
 
-    endOfComments = FALSE;
-    while (!endOfComments) {
-        getLine(ifP, buf, sizeof(buf));
+    for (endOfComments = false; !endOfComments; ) {
+        int eof;
+        size_t lineLen;
+        pm_getline(ifP, &buf, &bufferSz, &eof, &lineLen);
+        if (eof)
+            pm_error("EOF before #END_OF_COMMENTS line");
         if (strneq(buf, "#END_OF_COMMENTS", 16))
-            endOfComments = TRUE;
+            endOfComments = true;
         else if (strneq(buf, "#BUILTIN", 8))
             pm_error("This program does not know how to "
                      "convert builtin XV thumbnail pictures");
     }
-    getLine(ifP, buf, sizeof(buf));
+    pm_getline(ifP, &buf, &bufferSz, &eof, &lineLen);
+    if (eof)
+        pm_error("EOF where cols/rows/maxval line expected");
+
     rc = sscanf(buf, "%u %u %u", &cols, &rows, &maxval);
     if (rc != 3)
         pm_error("error parsing dimension info '%s'.  "
                  "It does not consist of 3 decimal numbers.", buf);
     if (maxval != 255)
         pm_error("bogus XV thumbnail maxval %u.  Should be 255", maxval);
+
     *colsP = cols;
     *rowsP = rows;
     *maxvalP = maxval;
+
+    if (buf)
+        free(buf);
 }
 
 
@@ -166,7 +164,7 @@ writePpm(FILE *             const ifP,
             else {
                 unsigned int const paletteIndex = byte;
                 assert(byte >= 0);
-                
+
                 PPM_ASSIGN(pixrow[col],
                            xvPaletteP->red[paletteIndex],
                            xvPaletteP->grn[paletteIndex],
@@ -181,7 +179,7 @@ writePpm(FILE *             const ifP,
 
 
 
-int 
+int
 main(int    argc,
      char * argv[]) {
 
@@ -190,7 +188,7 @@ main(int    argc,
     unsigned int cols, rows;
     pixval maxval;
     xvPalette xvPalette;
- 
+
     ppm_init(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
diff --git a/converter/ppm/yuvtoppm.c b/converter/ppm/yuvtoppm.c
index 151ff9f9..9c5d79c4 100644
--- a/converter/ppm/yuvtoppm.c
+++ b/converter/ppm/yuvtoppm.c
@@ -45,6 +45,7 @@ parseCommandLine(int argc, const char ** argv,
     optEntry * option_def;
         /* Instructions to OptParseOptions3 on how to parse our options */
     optStruct3 opt;
+    unsigned int option_def_index;
 
     MALLOCARRAY_NOFAIL(option_def, 100);
 
diff --git a/converter/tga.h b/converter/tga.h
index fdbe4047..1a65663d 100644
--- a/converter/tga.h
+++ b/converter/tga.h
@@ -44,3 +44,16 @@ typedef char ImageIDField[IMAGEIDFIELDMAXSIZE];
 #define TGA_IL_Four 2
 
 enum TGAbaseImageType {TGA_MAP_TYPE, TGA_MONO_TYPE, TGA_RGB_TYPE};
+
+enum TGAmapType {
+    /* A type of Targa color map */
+    TGA_MAPTYPE_SHORT,
+        /* Each entry in the color map is 2 bytes: 5 bits each of
+           red, green, and blue, and one optional alpha bit.
+        */
+    TGA_MAPTYPE_LONG
+        /* Each entry in the color map is 3 bytes: red, green, and blue,
+           plus one optional alpha byte
+        */
+};
+
diff --git a/doc/HISTORY b/doc/HISTORY
index 5f91d170..308f903b 100644
--- a/doc/HISTORY
+++ b/doc/HISTORY
@@ -4,12 +4,14 @@ Netpbm.
 CHANGE HISTORY 
 --------------
 
-19.06.28 BJH  Release 10.73.28
+19.06.15 BJH  Release 10.86.04
 
-              pbmtozinc: fix wrong output on big-endian machines.  Broken in
+              pamtris: Fix bug: debug trace left in
+
+              pbmtozinc: Fix wrong output on big-endian machines.  Broken in
               Netpbm 10.71 (June 2015).
 
-19.05.04 BJH  Release 10.73.27
+19.05.04 BJH  Release 10.86.03
 
               pnmtopng: Fix bug: Defaults to no filters.  Should be all
               filters.  Effect is larger PNG output.  Broken after Netpbm
@@ -20,7 +22,57 @@ CHANGE HISTORY
               Broken after Netpbm 10.26 (January 2005) but no later than
               Netpbm 10.35 (August 2006).
 
-19.03.10 BJH  Release 10.73.26
+              pnmcrop: Don't allow -borderfile with -reportXXX.  It doesn't
+              work.
+
+19.04.10 BJH  Release 10.86.02
+
+              pnmcrop: fix bug: -bgcolor doesn't work.  Always present
+              (-bgcolor was introduced in Netpbm 10.86 (March 2019)).
+
+19.04.06 BJH  Release 10.86.01
+
+              pnmcrop: fix bug: incorrect identification of background with
+              -bgcolor and PBM or PGM image.  Always present (-bgcolor was
+              introduced in Netpbm 10.86 (March 2019)).
+
+19.03.30 BJH  Release 10.86.00
+
+              Add pamfind.
+
+              Add pambrighten.
+
+              Add pamhue.
+
+              pnmcrop: Add -bg-color, bg-corner, -reportfull, -reportsize,
+              -blank-image .
+
+              pamtopng: Add -interlace .
+
+              pamenlarge: Add -scale, -xscale, -yscale .
+
+              pamenlarge: Much faster for PBM with xscale factor above 10;
+              slightly faster for xscale factor 2-10.
+
+              pamfile: Add -machine and -size .
+
+              pamscale: Add -reportonly
+
+              pamstretch-gen: Add -quiet, -plain.
+
+              pamstretch-gen: Use -dropedge on the 'pamstretch' piece for
+              better looking output.
+
+              pamstretch: Accept scale factor of 1 as a parameter (already
+              was accepted with -xscale and -yscale options).
+
+              pamstretch: Don't mess with edge when scale factor is 1,
+              regardless of -dropedge and -blackedge.
+
+              ppmwheel: Add -huevalue, -huesaturation, -maxval .
+
+              various: Fix unnormalizing code so a value exactly between two
+              sample values rounds consistently up.  Affects many programs.
 
               pstopnm: Fix bug: -textalphabits has no effect.  Always broken.
               (-textalphabits was new in Netpbm 10.53 (December 2010)).
@@ -32,8 +84,6 @@ CHANGE HISTORY
               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).
 
@@ -47,25 +97,59 @@ 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
+              Build: split link/ directory in package tree into staticlink/
+              and sharedlink/ .  Make 'installnetpbm' do separate prompts to
+              install static libraries and shared library stubs.  Make
+              'installnetpbm' install clean symlink in the usual case that the
+              shared library stub and shared library are in the same
+              directory.
 
-              bmptopnm: Fix wrong output for non-colormapped OS2 BMP.  Broken
-              in Netpbm 10.18 (September 2003).
+              Build: update 'mkdeb' so it works on Debian 9 (in addition to
+              Debian 8).  Thanks Neil R Ormos <ormos@ormos.org>.
 
-18.11.05 BJH  Release 10.73.23
+18.12.29 BJH  Release 10.85.00
 
-              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).
+              pnmpaste: Add -nand, -nor, and -nxor.
+
+              pamcut: add -cropleft, -cropright, -croptop, -cropbottom.
+
+              ppmtobmp: respect -bpp with PBM input.
+
+              bmptopnm: allow single color colormap with bpp 1.
+
+              bmptopnm: validate plane count = 1.
 
-              Merge build: fix so legacy program names 'pnmtopnm', 'ppmnorm',
-              and 'ppmtotga' work again.
+              bmptopnm: Add RLE4 compression decoding.  This was ostensibly
+              added in Netpbm 10.32 (February 2006), but never worked.
+              
+              Add pammixmulti.  Thanks Scott Pakin (scott+pbm@pakin.org).
 
-18.11.02 BJH  Release 10.73.22
+              pamtotga: Add -cmap16 .
+    
+              pamtris: Add -rgb, -grayscale.  Add w parameter to vertex
+              instructions.  Fix perspective correctness.  Thanks Lucas Brunno
+              Luna <lucaslunar32@hotmail.com>.
 
               picttoppm: accept rectangle specifications in input that have
               the corners in any order, not just upper left, then lower right.
 
+              pamtotga: identify compressed colormapped format in error
+              message saying the program doesn't know how to interpret it.
+
+              pamtotga: Ignore extra planes in black and white or grayscale
+              input instead of generating junk output.
+
+              Merge build: make old names for JPEG, TIFF, and PNG converter
+              programs work (function previously omitted by design because it
+              was too hard).
+
+              bmptopnm: Fix wrong output for non-colormapped OS2 BMP.  Broken
+              in Netpbm 10.18 (September 2003).
+
+              bmptopnm: Fix array bounds violation when index value in raster
+              is too big.  Broken after Netpbm 10.11 (October 2002) but before
+              10.19 (November 2003).
+
               libnetpbm: Fix invalid memory reference in color name processing
               when trivial memory allocation fails.
 
@@ -73,50 +157,132 @@ CHANGE HISTORY
               when it contains multiple delimiter characters in a row.  Always
               broken (pamtojpeg2k was new in Netpbm 10.12 (November 2002)).
 
-              pamtojpeg2k: Fix incorrect metadata in output with GRAYSCALE PAM
-              input.  Always broken (pamtojpeg2k was new in Netpbm 10.12
-              (November 2002)).
-
               pnmtojbig: fix incorrect handling of -x option.  Always broken
               (pnmtojbig was new in Netpbm 9.2 (May 2000)).
 
-18.09.29 BJH  Release 10.73.21
+              pjtoppm, pbmtoppa: fix arithmetic overflow.
 
-              pstopnm: Fix divide-by-zero crash when Postscript input says
-              the image has zero size.
+              Build: fix parallel make.
 
-              pstopnm: Fix divide-by-zero crash when computed resolution
-              rounds down to zero dots per inch.
+              Build: fix reference to nonexistent getline on Mac OS X 10.6.
+
+              Build: fix reference to nonexistent strndup on Mac OS X 10.6.
+
+              Merge build: fix duplicate symbol 'prefabCode' in g3topbm,
+              pbmtog3.  Introduced in Netpbm 10.79 (June 2017).
+
+18.09.29 BJH  Release 10.84.00
+
+              Add pamaltsat.  Thanks Anton Shepelev <anton.txt@gmail.com>.
+
+              Add pamtris.  Thanks Lucas Brunno Luna
+              <lucaslunar32@hotmail.com>.
+
+              libpbmfont, pbmtext: fix bugs with BDF file lines with
+              insufficient number of fields.  Unknown effect.
+
+              pbmtext: -wchar works with built-in fonts.
+
+              pbmtext: improved -verbose information about BDF fonts:
+              include CHARSET_REGISTRY, CHARSET_ENCODING.
+
+              libnetpbm font facilities: built-in fonts work with wide
+              characters.
 
               pbmtext; libnetpbm BDF font processing: fix invalid memory
               reference when BDF font file has invalid syntax.  Broken
               in primordial Netpbm, ca 1993.
 
+              pamgetcolor: fix bug: gets color of only the top half of a
+              region.
+
+              pnmfiasco, fiascotopnm: Fix trivial memory leak.  Always broken
+              (programs were new in Netpbm 9.6, July 2000).
+
+18.06.30 BJH  Release 10.83.00
+
+              Add pamlevels.  Thanks Anton Shepelev <anton.txt@gmail.com>.
+
+              Add pamgetcolor.  Thanks Anton Shepelev <anton.txt@gmail.com>.
+
+              Add rgb-<maxval>:r/g/b color specification format.
+
+              pngtopam: Fix bogus warning of non-square pixels when image does
+              not contain pixel resolution information.  Introduced in Netpbm
+              10.48 (September 2009).
+
+              pstopnm: Fix divide-by-zero crash when Postscript input says
+              the image has zero size.
+
+              pstopnm: Fix divide-by-zero crash when computed resolution
+              rounds down to zero dots per inch.
+
               pbmmask: Fix invalid memory reference with zero-dimension
               input image.  Broken in primordial Netpbm, ca 1989.
 
-18.06.27 BJH  Release 10.73.20
+              pamtojpeg2k: Fix incorrect metadata in output with GRAYSCALE PAM
+              input.  Always broken (pamtojpeg2k was new in Netpbm 10.12
+              (November 2002)).
+
+              libnetpbm: Add pnm_colorspec_rgb_integer,
+              pnm_colorspec_rgb_norm, pnm_colorspec_rgb_x11,
+              pnm_colorspec_dict, pnm_colorspec_dict_close.
 
-              Pngtopam: Fix bogus warning of non-square pixels when image does
-              not contain pixel resolution information.  Introduced in Netpbm
-              10.48 (September 2009)
+              libnetpbm: Add pnm_parsecolor2.
 
-18.04.28 BJH  Release 10.73.19
+              libnetpbm: Add pnm_allocpamtuplen, pnm_freepamtuplen.
+              
+              libnetpbm: Make the normalized tuple functions respect the
+              allocation depth specified in struct pam (member
+              'allocation_depth') instead of using the actual tuple depth
+              (member 'depth').
 
               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.02.17 BJH  Release 10.73.18
+18.03.25 BJH  Release 10.82.00
+
+              pbmtext: Add -wchar.
+
+              pbmtext: Add -text-dump option.
+
+              ppmhist: Add color summary to top of output, (except with
+              -noheader).
+
+              pnmremap: Add -randomseed.
+
+              pnmquant: Add -norandom, -randomseed.
+
+              pamtogif: Add -noclear option.
+
+              giftopnm: Check "data width" value from GIF image properly:
+              can't be bigger than 11, because the minimum code size is one
+              more than the data width and the maximum code size is 12.  (Note
+              that GIF spec prohibits anything more than 8).
+
+              pnmpsnr: Add -targetX options.
+
+              ppmrainbow: Add "ppmrainbow: " to error messages, like other
+              programs.
+
+              ppmrainbow: improve error message.
 
               g3topbm: Fix bug - produces invalid empty PBM image if input
               image is empty.
 
+              ppmpat: Fix bug - crash or junk output with -camo or -anticamo
+              and no -color.  Introduced in Netpbm 10.78 (March 2017).
+
               mrftopbm: Fix bug - wrong error messages or output when input
-              invalidly short.
+              invalidly short.  Always broken (mrftopbm was new in Netpbm
+              10.18 (September 2003).
 
-17.10.28 BJH  Release 10.73.17
+              Build: fix compile failure on systems without strnlen (such
+              as MacOS X before 10.7).  Broken in 10.81 (December 2017).
+
+17.12.30 BJH  Release 10.81.00
 
               sldtoppm: -lib and -dir don't work - always says slide not
               found.  Broken in Netpbm 10.63 (June 2013).
@@ -128,14 +294,29 @@ CHANGE HISTORY
               sldtoppm: fix bug: wild memory accesses, weird messages when
               invalid input file has unterminated strings.
               
-17.09.28 BJH  Release 10.73.16
+17.09.30 BJH  Release 10.80.00
+
+              pnmtopalm: Refuse to create a compressed image with more than 8
+              bits per pixel.
+
+              pbmtext, libnetpbm font facilities: allow glyphs in font files
+              that have no bitmap data; just used for their advance value to
+              code a space.
+
+              pbmtext: Improve error message when there is a problem reading
+              the font file.
+
+              libnetpbm font facilities: consider font invalid if a glyph is
+              more than 65536 pixels high or wide.
+
+              ppmshadow: handle images with a black background and low
+              contrast images (i.e. little difference between foreground and
+              background).
 
               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)).
@@ -144,34 +325,89 @@ CHANGE HISTORY
               -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)).
+              pnmtopalm: Correct error message recommending running
+              pnmquant when the real solution is to run pnmremap to modify the
+              image to Palm standard colors.
 
-17.08.11 BJH  Release 10.73.14
+              pnmtopalm: Fix bug causing "color not in colormap" failure with
+              -colormap and maxval other than 255.
 
               libnetpbm: font facilities: fix invalid memory reference with
               certain font files.
 
-17.06.30 BJH  Release 10.73.13
+              libnetpbm: ppm_readcolordict: Improve error message when there is
+              a problem reading the color dictionary.  Affects ppmhist.
 
-              ppmcolormask: fix incorrect output when input maxval is not 255.
-              Always broken (ppmcolormask was new in Netpbm 9.0, April 2000).
+              pgmmake: Fix bug: treats non-numeric gray-level argument as zero.
+              Always broken (Pgmmake was new in Netpbm 10.32, February 2006).
+
+              pdbimgtopam, pamtopdbimg: fix various cases of incorrect output,
+              some always present (programs were new in Netpbm 10.52.00
+              (October 2010)).
+
+              libnetpbm: pnm_parsecolorn(), pnm_parsecolor(): fix parsing of
+              rgb: color specifications: yields value slightly too dim.
+              Affects many programs.  Broken in Netpbm 10.79 (June 2017).
+
+              Makeman: make it work with Python 3.6 II.
+
+17.06.30 BJH  Release 10.79.00
+
+              Add pamtable .
+
+              pamgauss: Add -maximize, -oversample .  Thanks Anton Shepelev
+              <anton.txt@gmail.com>
+
+              pnmconvol: Extend -normalize to be applicable to convolution
+              kernels specified by PGM file.  Thanks Anton Shepelev
+              <anton.txt@gmail.com>
+
+              g3topbm: tolerate fill bits.
 
-17.06.28 BJH  Release 10.73.12
+              pbmtog3: Add -align8, -align16.
 
-              pamgauss: Fix skewed output with even dimension.  Always broken
-              (Pamgauss was added in Netpbm 10.23 (July 2004).
+              ppmshadow: Improve results for very small maxval.
+
+              ppmshadow: eliminate extraneous messages from pnmconvol.
+
+              libnetpbm: Add pnm_parsecolorn().
+
+              libnetpbm: Improve error message for purported image that is
+              exactly 1 byte long (so ends in the middle of what would be the
+              magic number).
+
+              pbmtox10bm: Get the Perl interpreter for this Perl program from
+              the PATH instead of hardcoded as /usr/bin/perl (like all other
+              Netpbm Perl programs).
+
+              ppmcolormask: fix incorrect output when input maxval is not 255.
+              Always broken (ppmcolormask was new in Netpbm 9.0, April 2000).
 
               bmptopnm: fix crash when input is a Windows BMP with negative
               number for width.  Always broken.  (bmptopnm was new, as
               bmptoppm, in original Netpbm, 1992).
 
-17.05.27 BJH  Release 10.73.11
+              pamcomp: fix incorrect output with -mixtransparency.
+              Always broken.  (-mixtransparency was new in Netpbm 10.56,
+              September 2011).
+
+              pamcomp: remove debug trace message with -mixtransparency.
+              Always broken.  (-mixtransparency was new in Netpbm 10.56,
+              September 2011).
+
+              pnmtojpeg: fix array bounds violation in argument list.  Always
+              broken (pnmtojpeg was new to Netpbm in Netpbm 8.2 (March 2000).
 
               pamcrater: fix incorrect output with non-square image.
               Introduced in Netpbm 10.69 (December 2014).
 
-17.04.30 BJH  Release 10.73.10
+              svgtopam: fix treating negative width or height values in SVG
+              like twos complement positive numbers; fail instead.  Always
+              broken (svgtopam was new in Netpbm 10.33 (March 2006)).
+
+              svgtopam: fix error messages when input has splines or negative
+              positions.  Always broken (svgtopam was new in Netpbm 10.33
+              (March 2006)).
 
               libnetpbm: fix bug: pm_system_XXX closes Standard Input if you
               supply a Standard Output accepter but not a Standard Input
@@ -184,105 +420,233 @@ CHANGE HISTORY
               (September 2007), but wouldn't even compile until Netpbm 10.42
               (March 2008)).
 
+              installnetpbm: Accept globs for directory name responses
+              (notably, ~/DIR).
+
+              makeman: Make it work with Python 3.6.
+
               Build: don't try to build standardppmdfont.c if it already
               exists (so don't require ppmdcfont to exist).  Broken around
               Netpbm 10.35 (2006).
 
-17.04.15 BJH  Release 10.73.09
+              Build: Define _XOPEN_SOURCE=500 in source files that use M_PI.
+              While C libraries in the past have always provided this with
+              just _XOPEN_SOURCE=null, it appears that M_PI is actually
+              defined by Single Unix Specification 2, aka UNIX98, for which
+              you need _XOPEN_SOURCE=500, and Cygwin has changed to enforce
+              this.
 
-              pamcomp: fix incorrect output with -mixtransparency.
-              Always broken.  (-mixtransparency was new in Netpbm 10.56,
-              September 2011).
+              Build: document MinGW _XOPEN_SOURCE incompatibility and add
+              warning to Configure.
 
-              pamcomp: remove debug trace message with -mixtransparency.
-              Always broken.  (-mixtransparency was new in Netpbm 10.56,
-              September 2011).
+              Debian packaging: fix bug: don't try to include Manweb files, as
+              it is no longer packaged by 'make package'.
 
-              pnmtojpeg: fix array bounds violation in argument list.  Always
-              broken (pnmtojpeg was new to Netpbm in Netpbm 8.2 (March 2000).
+17.03.28 BJH  Release 10.78.00
 
-17.03.28 BJH  Release 10.73.08
+              ppmpat: Add -color.
 
-              tifftonm: Fix incorrect PBM output with two-color paletted TIFF
-              image.  Broken in primordial Netpbm, ca 1990.
+              ppmpat: Add -argyle1, -argyle2.
+
+              pnmtotiff: Fail with -miniswhite or -minisblack on color image
+              rather than produce an invalid TIFF.
 
               tifftopnmcmyk: Default rows per strip to the TIFF library
               default instead of whatever yields 8K strips.
 
+              --version global option: with SOURCE_DATE_EPOCH environment
+              variable, display source code datetime instead of build
+              datetime.  And when displaying build datetime, do it in the
+              local time of the process running the command instead of the
+              process that did the build.
+
+              tifftonm: Fix incorrect PBM output with two-color paletted TIFF
+              image.  Broken in primordial Netpbm, ca 1990.
+
+              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).
+
               tifftopnmcmyk: Fix bug: fails with very wide images and no
               -rowsperstrip.  Always broken.  (Tifftopnmcmyk was new in Netpbm
               8.2 (March 2000).
 
+              svgtopam: Fix crash when out of memory.  Always broken (svgtopam
+              was new in Netpbm 10.33 (March 2006)).
+
+              pnmcrop: Add -closeness
+
+              libnetpbm: Add ppmd_pathbuilder_* functions.
+
               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
+              Build: don't create pointer man pages anymore.  These were
+              classic man pages, created by 'make package', one for each
+              program, that just told the user to get the manual from the web
+              and that other options for manuals are available at install
+              time.  Getting documentation online is commonplace enough now
+              that the user doesn't need to be told to do it or that there are
+              other options.  The existence of pointer man pages was,
+              meanwhile, misleading, since it looked from the outside like
+              they actually contained documentation.
 
-              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).
+              Build: Don't package or install Manweb setup (for accessing
+              manuals on the web with Manweb).  Probably 100% unused and
+              distracting.
 
-              svgtopam: Fix crash when out of memory.  Always broken
-              (svgtopam was new in Netpbm 10.33 (March 2006)).
+              Debian package: change dependencies to be compatible with
+              Debian 8.
 
-16.12.25 BJH  Release 10.73.06
+16.12.25 BJH  Release 10.77.00
 
-              pbmtoascii: fix bogus assertion failure.  Introduced in 
-              Netpbm 10.51 (June 2010) and visible only with a custom
-              build with assertion checking.
+              pnmpad: Fix bug: incorrect output width.  Introduced in
+              Netpbm 10.72 (July 2015).
 
-16.12.01 BJH  Release 10.73.05
+              Makeman: Slight improvement to formatting of man pages.
+              Thanks Werner LEMBERG <wl@gnu.org>.
 
-              pnmpad: fix bug: incorrect output width.  Introduced in
-              Netpbm 10.72 (July 2015).
+              Test: Skip tests of some parts that are configured out of the
+              build.
 
-16.09.02 BJH  Release 10.73.05
+16.09.27 BJH  Release 10.76.00
 
-              pnmquantall: Fix failure when temporary file location is
-              not the same filesystem as the output file.
+              pnmquantall: Fix failure when temporary file location is not the
+              same filesystem as the output file.  Always broken (pnmremap was
+              new in Netpbm 10.58 (March 2012)).
 
               pnmquantall: Fix incorrect handling of when the Pnmremap or
-              the final rename fails.
-
-16.08.13 BJH  Release 10.73.04
+              the final rename fails.  Always broken (pnmremap was new
+              in Netpbm 10.58 (March 2012)).
 
               giftopnm: Fix bug: crash on little-endian computers that can't
               toleration unaligned memory access.  Thanks Ignatios Souvatzis
               (is@netbsd.org).  Broken in Netpbm 10.47 (June 2009).
 
-16.06.26 BJH  Release 10.73.03
+              cmuwmtopbm: Fix trivial memory leak.  Always broken (cmuwmtopbm
+              was in primordial Pbmplus, in 1988).
 
-              pamarith: fix incorrect output when maxvals differ, for
-              -add, -multiply, -mean, -min, -max.  Broken in Netpbm 10.41
-              (December 2007).
+              Build: Add PKG_CONFIG make variable.
+
+              Build: tifftopnm.c: Fix undefined WIFSIGNALED, etc.
+
+16.06.26 BJH  Release 10.75.00
+
+              pbmtextps: Add -leftmargin, -rightmargin, -topmargin,
+              -bottommargin, -ascent, -descent, -pad, -crop.
 
-16.06.12 BJH  Release 10.73.02
+              pbmtextps: Add -dump-ps.
 
               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.
+              pbmtextps: Accept fractional -fontsize.
+              
+              pbmtextps: Change margins for non-default fonts.
+
+              pbmtextps: Minor output changes caused by code cleanup.
+
+              bmptopnm: Add ability to convert Version 4 and 5 Windows BMP.
+
+              pbmtext: Remove undocumented -dump option; add 'genfontc'
+              development tool (buildtools/ directory) to replace it.
+
+              pbmtext: Add -dry-run
+
+              pbmtext: Add -dump-sheet
+
+              pbmtext: Speedup: renders directly in raw PBM.
+
+              pbmreduce: Add -randomseed.
+
+              anytopnm, pnmmargin, pnmquant, ppmquant, pnmquantall, pgmcrater,
+              ppmfade, ppmrainbow, ppmshadow, pbmtox10bm, pamstretch-gen:
+              Add -version.
+
+              fiascotopnm: Change -version to include Netpbm version.
 
-16.05.09 BJH  Release 10.73.01
+              libnetpbm: Add pm_system2(), pm_system2_lp(), pm_system2_vp() -
+              same as pm_system(), etc. but returns the termination status.
 
-              bmptopnm: fail properly with Version 4, 5 Windows BMP.
+              pamarith: Fix incorrect output when maxvals differ, for
+              -add, -multiply, -mean, -min, -max.  Broken in Netpbm 10.41
+              (December 2007).
+
+              pbmtextps: Fix bug: input text or font name with Postscript
+              control characters messes up the Postscript program.  Always
+              broken (pbmtextps was new in Netpbm 10.0 (June 2002).
+
+              hpcdtoppm dummy version: Update web link to real version.
 
-              ppmhist: fix incorrect color names.  Introduced in
+              ppmhist: Fix incorrect color names.  Introduced in
               Netpbm 10.19 (November 2003).
 
-              pnmpaste: fix possible invalid memory access.  Introduced in
+              ppmshadow: Fix bug: don't ignore invalid option.  Introduced in
+              Netpbm 10.9 (September 2002).
+
+              pnmpaste: Fix possible invalid memory access.  Introduced in
               Netpbm 1.44 (September 2008).
 
-              pbmreduce: fix undefined behavior when scale factor argument is
+              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.
+              pbmtext: Fix bug: invalid memory reference when text contains
+              code points > 127.  Broken in 10.74 (March 2016).
+
+              pnmtofiasco, fiascotopnm: Fix incorrect math on systems with
+              unusual floating point representation.  Always broken (programs
+              were new in Netpbm 9.6 (July 2000).
+
+              cameratopam: Fix invalid memory reference; effect unknown.
               Introduced in Netpbm 10.68 (September 2014).
 
-              Install on Windows: fix backward compatibility symlinks for
+              Install on Windows: Fix backward compatibility symlinks for
               pnmtoplainpnm, pnmquantall.
 
+              Build: Remove use of strndup so it compiles on Mac OS X 10.6.
+              Broken in 10.74.
+
+              Build: Remove use of isfinite so it compiles with C89 standard
+              compiler.
+
+16.03.27 BJH  Release 10.74.00
+
+              pbmtext: Produce same image when you run pbmtext with -width
+              explicitly set to the width you get when you don't specify
+              width.
+
+              pbmtext: Ignore -nomargins when -width is specified.
+
+              pbmtext: Report when line ends are dropped because of
+              truncation.
+
+              pbmtext: Fix bug: if input has a code point that is not in the
+              font, Pbmtext substitutes space, but behavior is undefined if
+              font doesn't have space either.  Now it aborts the program in
+              that case.
+
+              pbmtext: Fix bug: Respect -width when specified.
+
+              pbmtext: Fix bug: Deal correctly with negative -space.
+
+              pbmtext: Fix bug: Consider all characters in line, not just
+              first and last, in determining line width.
+
+              libnetbpm font facility (so pbmtext): Fix bug: undefined
+              behavior when font definitions are invalid in any of various
+              ways.
+
+              libnetpbm font facility (so pbmtext): Fix incorrect font names
+              in error messages.
+
+              pnmtopsnr: Add -machine, -max .
+
+              Netpbmlib: Add /usr/local/netpbm/lib and /etc/X11 to search
+              path for rgb.txt.
+
+              makeman: Add some text replacements to solve glitches.
+              Handle &mdash, &minus.
+
 15.12.26 BJH  Release 10.73.00
 
               anytopnm: use --mime-type option instead of --mime on newer
@@ -373,10 +737,10 @@ CHANGE HISTORY
               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).
+              (scott+pbm@pakin.org).
 
               pamstereogram: Allow -xshift and -yshift to be negative.  Thanks
-              Scott Pakin (scott@pakin.org).
+              Scott Pakin (scott+pbm@pakin.org).
 
               pnmpsnr: Add -rgb.
 
@@ -485,10 +849,9 @@ CHANGE HISTORY
               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.
+              makeman: deal properly with backlash in source.  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.
@@ -1013,7 +1376,7 @@ CHANGE HISTORY
               get down to a compression ratio of 1.
 
               pamstereogram: -smoothing smooths images even without -texfile.
-              Thanks Scott Pakin (scott@pakin.org).
+              Thanks Scott Pakin (scott+pbm@pakin.org).
 
               pnmcat: set don't care bits in packed PBM output to zero so
               they are predictable.
@@ -1105,10 +1468,10 @@ CHANGE HISTORY
               Elijah Griffin <eli@panix.com>.
 
               pamstereogram: Add -planes .  Thanks Scott Pakin
-              (scott@pakin.org).
+              (scott+pbm@pakin.org).
 
               pamstereogram: improve verbose output.  Thanks Scott Pakin
-              (scott@pakin.org).
+              (scott+pbm@pakin.org).
 
               pamstereogram: fix crash introduced in Netpbm 10.54 (March 2011).
 
@@ -1171,7 +1534,7 @@ CHANGE HISTORY
 
               pngtopam: fix crash with invalid tIME chunk.  Always broken.
 
-              pamarith: fix wrong result with -multiply.  Broken in Neptbm
+              pamarith: fix wrong result with -multiply.  Broken in Netpbm
               10.41.
 
               pamscale: fix all black output with resampling.  Always broken.
@@ -1216,7 +1579,7 @@ CHANGE HISTORY
               "from bottom edge."
 
               ppmcie: fix bug: fails with "X coordinate out of range" error.
-              Introduced in Neptbm 10.51.
+              Introduced in Netpbm 10.51.
 
               bmptopnm: Fail properly with BMP that indicates an illegal bits
               per pixel.
@@ -1780,8 +2143,9 @@ CHANGE HISTORY
               (produces grayscale output).  Broken between Netpbm 10.19 and
               10.26, inclusive.
 
-              pbmtomrf, mrftopbm: fix crashes, incorrect output in all
-              cases.  Broken forever.
+              pbmtomrf, mrftopbm: fix crashes, incorrect output in all cases.
+              Always broken (mrftopbm was new in Netpbm 10.18 (September
+              2003).
 
               pnm_alloctupletable, pnm_tuplehashtotable,
               pnm_computetuplefreqtable3: fix crash when out of memory,
@@ -2114,7 +2478,7 @@ CHANGE HISTORY
 
 07.06.26 BJH  Release 10.39.00
 
-              Add pamtooctave.  Thanks Scott Pakin (scott@pakin.org).
+              Add pamtooctave.  Thanks Scott Pakin (scott+pbm@pakin.org).
 
               Add pamundice.
 
@@ -2627,7 +2991,7 @@ CHANGE HISTORY
               arguments; fix crash.
 
               pamstereogram: Fix crippling bugs.  Thanks Scott Pakin
-              <scott@pakin.org>.
+              <scott+pbm@pakin.org>.
 
               giftopnm: Handle case of a clear code at the end of a block.
 
@@ -3419,12 +3783,12 @@ CHANGE HISTORY
               Add pamperspective.  Thanks Mark Weyer
               <Mark.Weyer@math.uni-freiburg.de>.
 
-              Add pamstereogram.  Thanks Scott Pakin <scott@pakin.org>.
+              Add pamstereogram.  Thanks Scott Pakin <scott+pbm@pakin.org>.
 
               Add pc1toppm.  Thanks Roine Gustafsson
               <roine@users.sourceforge.net>.
 
-              Add pbmtodjvurle.  Thanks Scott Pakin <scott@pakin.org>.
+              Add pbmtodjvurle.  Thanks Scott Pakin <scott+pbm@pakin.org>.
 
               Add infotopam.  Thanks Rich Griswold <griswold@acm.org>.
 
@@ -3481,7 +3845,7 @@ CHANGE HISTORY
 
               Add pamsharpness, pamsharpmap.
 
-              Add ppmtodjvurle.  Thanks Scott Pakin <scott@pakin.org>.
+              Add ppmtodjvurle.  Thanks Scott Pakin <scott+pbm@pakin.org>.
 
               pstopnm: add -dpi option.
 
diff --git a/doc/INSTALL b/doc/INSTALL
index f0b3e87b..aad0a5e5 100644
--- a/doc/INSTALL
+++ b/doc/INSTALL
@@ -109,6 +109,35 @@ If you use the 'configure' program, be sure to edit config.mk _after_ you
 run 'configure', since 'configure' generates config.mk.
 
 
+COMPILED-IN BUILD DATETIME
+--------------------------
+
+By default, the Netpbm build system builds the datetime that you built it
+into libnetpbm, so the --version global command line option can display it.
+It's actually just when you began building from a clean build tree; if you
+modify code and rebuild, the build datetime does not change.
+
+This is problematic for any of various reasons you might want to compare two
+versions of libnetpbm, or anything of which it is part, for equality.  Two
+libnetpbms that are identical except that they were built at different times
+would compare as different.
+
+Furthermore, as version information, the modification datetime of the source
+code is often more useful than the build datetime.
+
+For these reasons, it is possible to change this timestamping behavior by
+setting the environment variable 'SOURCE_DATE_EPOCH'.  That is supposed to be
+the POSIX datetime value (decimal number of seconds since 1969, excluding leap
+seconds) for when the source code was last modified.  When you set this,
+before doing 'make' for the first time in a clean build tree, the resulting
+libnetpbm contains that information and not build datetime information, and
+the --version global option causes a different message.
+
+The name and meaning of the environment variable is taken from a standard
+described at https://reproducible-builds.org/specs/source-date-epoch/ on March
+16, 2017.
+
+
 AUTOMATING THE BUILD
 --------------------
 
@@ -153,6 +182,45 @@ Netpbm has also been built for Windows using Djgpp, as late as 2001.
 
 See also the general installation instructions above.
 
+Mingw Modification
+-------------------
+
+With at least some versions of MinGW, you have to make a small change to
+_mingw.h or you get a compilation failure for invalid syntax in that file
+where it expands the macro _XOPEN_SOURCE.
+
+The problem is that many Netpbm programs declare that they need a C library
+that conforms to any version of the X/Open (XPG, XSI, SUS) standard.  The way
+they do that is, in accordance with the earliest version of that standard, by
+defining the macro _XOPEN_SOURCE with a null (zero-length) body.  But MinGW is
+not designed to work with programs that request anything earlier than Issue
+(version) 5 of this standard, and in fact _mingw.h will not compile if the
+macro expands to something other than a number.
+
+But it is easy to add pre-Issue 5 function to MinGW and having done so, your
+MinGW installation will also work with lots of other code.  All you have to do
+is change
+
+  if _XOPEN_SOURCE < 500
+
+to
+
+  if (_XOPEN_SOURCE + 0) < 500
+
+in _mingw.h.  This is the trick other C libraries use.
+
+(A user proposed that this change be distributed in _mingw.h, in April 2017 on
+the mingw-users mailing list, but the maintainer was opposed to accomodating
+programs written for the older standards).
+
+If you cannot change _mingw.h, you can alternatively change Netpbm.  Find all
+instances of
+
+  #define _XOPEN_SOURCE
+
+and change them to
+
+  #define _XOPEN_SOURCE 0
 
 
 INSTALLATION - MAKING ONLY THE PARTS YOU NEED
@@ -428,5 +496,3 @@ 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/TESTS b/doc/TESTS
index 65f91d26..9434f580 100644
--- a/doc/TESTS
+++ b/doc/TESTS
@@ -1,3 +1,5 @@
+Please cut and paste from below:
+---------------------------------------------------------------------------
 Contents
 ========
 
@@ -10,7 +12,7 @@ Contents
   1.6 Testing package in designated directory
   1.7 Pre-packaging check
   1.8 Post-install check
-  1.9 Skipping test items
+  1.9 Testing less than everything
   1.10 Valgrind
   
 2. Troubleshooting
@@ -68,14 +70,16 @@ appear at the end:
 =================
 
 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.
+some expressions unique to Bash.  Quite old versions work, at least back
+to Bash v. 2.05b.
 
-The tests also use the following utilities:
+Awk is also required, but the constructs are all simple.  Gawk, Mawk and
+BWK Awk (the "One True Awk") are all known to work. 
+
+The tests use the following utilities:
 
  - sh
  - awk
- - perl
 
  - cat
  - cksum
@@ -89,6 +93,7 @@ The tests also use the following utilities:
  - grep
  - file
  - head
+ - iconv
  - mkdir
  - mktemp
  - rm
@@ -141,8 +146,8 @@ packaging.  This feature is intended for developers.
 
 This test method is incompatible with merge build.
 
-Currently this test method reports several errors when Netpbm is compiled in a
-separate build directory.
+This works when Netpbm is compiled in the source tree and also when
+a separate build directory is used.
 
 
 
@@ -161,22 +166,27 @@ a non-standard location.  This must be an absolute path.
 
 
 
-1.9 Skipping test items
-=======================
+1.9 Testing less than everything
+================================
 
 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 "#".
+skip a 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:
+If you want to run just a few of the tests, use the make variable "target", a
+comma-separated list of Netpbm programs.
 
   make check target=giftopnm,pamtogif
 
+    This runs only the tests of 'giftopnm' and 'pamtogif', in the package
+    (staging) directory.
+
+  make check-tree target=ppmbrighten
+
+    This runs only the tests of 'ppmbrighten', from the build tree.
 
 
 1.10 Valgrind
-============
+=============
 
 You can run the whole test under Valgrind.  This is an advanced feature
 intended for programmers who work on Netpbm code.
@@ -206,18 +216,23 @@ test/Execute-Tests:
 
   vg_command_base="valgrind --trace-children=yes"
 
-To run "valgrind --track-origins=yes", you must make two changes in
-config.mk:
+To get useful information from "--trace-origins=yes", you must make
+two changes to config.mk before compilation:
 
   - Add -g to CFLAGS
   - Turn stripping off: STRIPFLAG =
 
+Without these changes valgrind will report how many errors were
+detected, of what kind, and at what addresses (in binary).  With the
+above "-g" turned on during compilation valgrind reports source file
+and line numbers of detected problems.
+
 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".
+and "legacy-names.test"; consider commenting them out.
 
 The option "--trace-children-skip" is used to prevent valgrind from
 stepping into child processes that are not relevant.  This option
@@ -244,12 +259,20 @@ 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.
+Configuration choices affect tests.  If a choice you make during configure
+causes "make" to skip compilation of certain programs, the corresponding
+test routines will be skipped and reported as NOT TESTABLE.  However,
+if you override a glitch with "make --keep-going" the test programs
+won't know and quite certainly you'll get failure messages.
+
+If you run the tests as "make check-install" information about
+configuration choices is deliberately ignored.  This is because the
+post-install check is typically run on systems with multiple versions
+of Netpbm and in this case most users would want to get a clear idea
+of absent components.
 
-For details read 'Netpbm Library Prerequisites':
+For details on the effects of missing libraries, read
+'Netpbm Library Prerequisites':
 http://netpbm.sourceforge.net/prereq.html .
 
 
@@ -271,6 +294,9 @@ When a test fails, a ".out" file will be produced in the
 can tell exactly what went wrong.  Often one does not need to
 go this far; the error messages tell enough.
 
+In some cases the ".ok" file is generated dynamically.  See "2.5
+System Dependent Issues."
+
 
 
 2.3 Color dictionary file
@@ -335,22 +361,33 @@ the tests on installed Netpbm programs, regardless of the version:
    ./configure           # accept the defaults
    make check-install
 
+As of this writing (April 2018) Debian is working on resolving the
+fork.  A new version, tagged as "experimental", based on the Sourceforge
+line of source files is available.
+
+
 
 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.
+The tests have worked on x86 and x86_64 GNU/Linux systems and with several
+versions of GCC and Clang and also Mac OS/Clang.  Reports from users of
+other systems including 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.
+c compiler 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.
+Another cause of discrepancies is the qsort function.  The tests are
+written to circumvent this issue at some cost to accuracy and
+coverage.
+
+The test directory Makefile checks whether the random number generator
+is the one from glibc or Mac OS and automatically generates the proper
+".ok" files from master files with the suffix ".rand-ok".  If some other
+random number generator is detected the affected tests are skipped.
 
 
 
@@ -360,3 +397,7 @@ detected.
 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 9d883849..045794b9 100644
--- a/doc/USERDOC
+++ b/doc/USERDOC
@@ -32,11 +32,16 @@ This creates a directory "userguide" in your current directory containing
 all the same files that are on the web site.
 
 
-An apparent alternative is just to copy the web site with something like GNU
-Wget.  However, the Sourceforge web server has limitations on how much you can
+An alternative is just to copy the web site with something like GNU Wget.
+However, the Sourceforge web server has limitations on how much you can
 download.  In a February 2012 experiment, Sourceforge started refusing HTTP
 requests (with an Internal Server Error indication and a message saying there
-had been too many requests) before all the files could be fetched.
+had been too many requests) before all the files could be fetched.  But you
+can use Wget controls to fool Sourceforge.  The following worked in January
+2016:
+
+  wget --wait=1 --random-wait \
+       --recursive --relative http://netpbm.sourceforge.net/doc/
 
 
 
@@ -97,8 +102,8 @@ ways:
 
   2) convert the HTML to formatted plain text (suitable as man "cat"
      pages) with the 'makecat' program in the 'buildtools' directory
-     of the Netpbm source tree.  This program just does a 
-     'lynx -dump'.
+     of the Netpbm source tree.  This program uses the dump feature
+     of the lynx text web browser for conversion.
 
 The "loss of quality" mentioned above is because:
 
@@ -119,46 +124,81 @@ The "loss of quality" mentioned above is because:
 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 situation.
+integrity.  The examples are guidelines and may not work literally
+in your situation.
 
-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
-  wget --recursive --relative http://netpbm.sourceforge.net/doc/
-  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 installman
-  cd ../../..
-  rm -r netpbmdoc
+MAKING TROFF MAN PAGES
+----------------------
 
-  man ppmtogif
+1. Start work at the top-level directory of the source package. 
+
+2. Download the HTML files using the subversion command.
+The downloaded files will go into "./userguide/".
+
+  svn export https://svn.code.sf.net/p/netpbm/code/userguide
 
-Here is an example of making "cat" pages:
+3. Create a work directory named "netpbmdoc".  The troff man pages,
+as they are converted from HTML pages by the makeman utility, will
+be written here.
 
   mkdir netpbmdoc
   cd netpbmdoc
-  wget --recursive --relative http://netpbm.sourceforge.net/doc/
-  cd netpbm.sourceforge.net/doc
-  /usr/src/netpbm/buildtools/makecat *.html
-  cp *.1 /usr/man/cat1/
-  cd ../../..
-  rm -r netpbmdoc
+  make USERGUIDE=../userguide/ -f ../buildtools/manpage.mk manpages
 
-  man ppmtogif
+USERGUIDE is the directory containing the input HTML pages.  Keep
+it intact until installation is complete.
+
+4. Install the troff man pages, with MANDIR set up appropriately
+to the destination.
+
+MANDIR and MANDIR/{man1,man3,man5} must be existing directories.
+The install step usually requires superuser permissions.
+
+  make MANDIR=/usr/local/netpbm/man USERGUIDE=../userguide/ \
+       -f ../buildtools/manpage.mk installman
+  cd ../
+  rm -rf netpbmdoc
+
+5. Adjust MANPATH if necessary and try showing one manual page.
+
+  man pamflip
+
+
+MAKING CAT PAGES
+----------------
+
+As with the above example for troff man pages, start at the top-level
+directory of the source package. 
+
+  svn export https://svn.code.sf.net/p/netpbm/code/userguide
+
+  cd ./userguide
+  ../buildtools/makecat *.html
+
+  cp *.1 /usr/man/cat1/      # Usually requires superuser permission
+
+The makecat utility reads files from the current directory and writes
+the results into the same directory.  As noted above, it requires lynx
+<http://lynx.invisible-island.net/>.
 
 
 DOCBOOK
 -------
 
-You can turn the Netpbm user manual into Docbook XML pages using
-Doclifter.  Because Doclifter works on troff pages, you have to
-convert the documentation down to troff first, which means the Docbook
-pages are of lower quality than the main HTML documentation.
+You can turn the Netpbm user manual into Docbook XML pages using Doclifter.
+Because Doclifter works on troff pages, you have to convert the documentation
+down to troff first, which means the Docbook pages are of lower quality than
+the master HTML documentation.
+
+To create Docbook XML, do as follows:
+
+  mkdir netpbmdoc
+  cd netpbmdoc
+  make USERGUIDE=../userguide/ -f ../buildtools/manpage.mk xmlpages
+
+The 'make' executes the HTML to troff conversion step automatically.  For the
+troff to XML step, there are problems that have not been completely worked
+out; conversion of several files will fail.
 
-To create Docbook XML, follow the example above for creating troff
-pages, and use 'make xmlpages' instead of 'make manpages'.
+There is no equivalent for 'make installman' for the Docbook XML pages.
diff --git a/editor/Makefile b/editor/Makefile
index 39329f00..5b12e4ca 100644
--- a/editor/Makefile
+++ b/editor/Makefile
@@ -16,10 +16,10 @@ SUBDIRS = pamflip specialty
 # This package is so big, it's useful even when some parts won't 
 # build.
 
-PORTBINARIES = pamaddnoise pambackground pamcomp pamcut \
+PORTBINARIES = pamaddnoise pamaltsat pambackground pambrighten pamcomp pamcut \
 	       pamdice pamditherbw pamedge \
 	       pamenlarge \
-	       pamfunc pammasksharpen \
+	       pamfunc pamhue pamlevels pammasksharpen pammixmulti \
 	       pamperspective pamrecolor pamrubber \
 	       pamscale pamsistoaglyph pamstretch pamthreshold pamundice \
 	       pamwipeout \
@@ -51,12 +51,14 @@ OBJECTS = $(BINARIES:%=%.o)
 
 MERGE_OBJECTS = $(MERGEBINARIES:%=%.o2)
 
+HAVE_MERGE_COMPAT=YES
+
 .PHONY: all
 all: $(BINARIES) $(SUBDIRS:%=%/all)
 
 include $(SRCDIR)/common.mk
 
-install.bin: install.bin.local
+install.bin install.merge: install.bin.local
 
 .PHONY: install.bin.local
 install.bin.local: $(PKGDIR)/bin
@@ -98,3 +100,15 @@ install.bin.local: $(PKGDIR)/bin
 	cd $(PKGDIR)/bin ; \
 	rm -f pnmcomp$(EXE) ; \
 	$(SYMLINK) pamcomp$(EXE) pnmcomp$(EXE)
+
+mergecomptrylist:
+	cat /dev/null >$@
+	echo "TRY(\"pnminterp\",  main_pamstretch);" >>$@
+	echo "TRY(\"pgmnorm\",    main_pnmnorm);"    >>$@
+	echo "TRY(\"ppmnorm\",    main_pnmnorm);"    >>$@
+	echo "TRY(\"pgmedge\",    main_pamedge);"    >>$@
+	echo "TRY(\"pnmenlarge\", main_pamenlarge);" >>$@
+	echo "TRY(\"pnmcut\",     main_pamcut);"     >>$@
+	echo "TRY(\"pnmscale\",   main_pamscale);"   >>$@
+	echo "TRY(\"pnmcomp\",    main_pamcomp);"    >>$@
+
diff --git a/editor/pamaddnoise.c b/editor/pamaddnoise.c
index cf1af815..354e0dc4 100644
--- a/editor/pamaddnoise.c
+++ b/editor/pamaddnoise.c
@@ -26,7 +26,7 @@
 ** Prentice Hall, 1993  ISBN 0-13-145814-0
 */
 
-#define _XOPEN_SOURCE   /* get M_PI in math.h */
+#define _XOPEN_SOURCE 500  /* get M_PI in math.h */
 
 #include <math.h>
 
diff --git a/editor/pamaltsat.c b/editor/pamaltsat.c
new file mode 100644
index 00000000..6d9b91e0
--- /dev/null
+++ b/editor/pamaltsat.c
@@ -0,0 +1,535 @@
+#include <stdbool.h>
+#include <assert.h>
+#include <string.h>
+
+#include <pam.h>
+#include <pm_gamma.h>
+#include <nstring.h>
+
+#include "shhopt.h"
+#include "mallocvar.h"
+
+typedef unsigned int  uint;
+typedef unsigned char uchar;
+
+typedef enum {MLog,  MSpectrum } Method; /* method identifiers */
+
+typedef struct {
+    Method       method;
+    const char * name;
+} MethodTableEntry;
+
+MethodTableEntry methodTable[] = {
+    {MLog,      "log"},
+    {MSpectrum, "spectrum"}
+};
+
+/* Command-line arguments parsed: */
+typedef struct {
+    const char * inputFileName;
+        /* name of the input file. "-" for stdin */
+    float        strength;
+    uint         linear;
+    Method       method;
+} CmdlineInfo;
+
+
+
+
+static Method
+methodFmNm(const char * const methodNm) {
+/*----------------------------------------------------------------------------
+   The method of saturation whose name is 'methodNm'
+-----------------------------------------------------------------------------*/
+    uint  i;
+    bool found;
+    Method method;
+
+    for (i = 0, found = false; i < ARRAY_SIZE(methodTable) && !found; ++i) {
+        if (streq(methodNm, methodTable[i].name)) {
+            found = true;
+            method = methodTable[i].method;
+        }
+    }
+
+    if (!found) {
+        /* Issue error message and abort */
+        char * methodList;
+        uint   methodListLen;
+        uint   i;
+
+        /* Allocate a buffer to store the list of known saturation methods: */
+        for (i = 0, methodListLen = 0; i < ARRAY_SIZE(methodTable); ++i)
+            methodListLen += strlen(methodTable[i].name) + 2;
+
+        MALLOCARRAY(methodList, methodListLen);
+
+        if (!methodList)
+            pm_error("Failed to allocate memory for %lu saturation "
+                     "method names", (unsigned long)ARRAY_SIZE(methodTable));
+
+        /* Fill the list of methods: */
+        for (i = 0, methodList[0] = '\0'; i < ARRAY_SIZE(methodTable); ++i) {
+            if (i > 0)
+                strcat(methodList, ", ");
+            strcat(methodList, methodTable[i].name);
+        }
+
+        pm_error("Unknown saturation method: '%s'. Known methods are: %s",
+                 methodNm, methodList);
+
+        free(methodList);
+    }
+    return method;
+}
+
+
+
+static CmdlineInfo
+parsedCommandLine(int argc, const char ** argv) {
+
+    CmdlineInfo cmdline;
+    optStruct3 opt;
+
+    uint option_def_index;
+    uint methodSpec, strengthSpec, linearSpec;
+    const char * method;
+
+    optEntry * option_def;
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "method",   OPT_STRING, &method,            &methodSpec,   0);
+    OPTENT3(0, "strength", OPT_FLOAT,  &cmdline.strength,  &strengthSpec, 0);
+    OPTENT3(0, "linear",   OPT_FLAG,   &cmdline.linear,    &linearSpec,   0);
+
+    opt.opt_table     = option_def;
+    opt.short_allowed = 0;
+    opt.allowNegNum   = 0;
+
+    pm_optParseOptions3( &argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (methodSpec)
+        cmdline.method = methodFmNm(method);
+    else
+        cmdline.method = MSpectrum;
+
+    if (!strengthSpec)
+        pm_error("You must specify -strength");
+
+    if (!linearSpec)
+        cmdline.linear = 0;
+
+    if (argc-1 < 1)
+        cmdline.inputFileName = "-";
+    else {
+        cmdline.inputFileName = argv[1];
+        if (argc-1 > 1)
+            pm_error("Program takes at most one argument:  file name");
+    }
+
+    free(option_def);
+
+    return cmdline;
+}
+
+
+
+typedef struct {
+    double _[3];
+} TupleD;
+
+typedef struct {
+/*----------------------------------------------------------------------------
+  Information about a color sample in linear format
+-----------------------------------------------------------------------------*/
+    TupleD sample;    /* layer intensities                        */
+    double maxval;    /* the highest layer intensity              */
+    uint   maxl;      /* index of that layer                      */
+    uint   minl;      /* index of the layer with lowest intensity */
+    double intensity; /* total sample intensity                   */
+} LinSampleInfo;
+
+/* ---------------------------- Binary search ------------------------------ */
+/*                    ( a minimal drop-in implementation )                   */
+
+/* Function to search, where <data> is an arbitrary user-supplied parameter */
+typedef double (binsearchFunc)(double       const x,
+                               const void * const data);
+
+/* The binary-search function. Returns such <x> from [<min>, <max>] that
+   monotonically increasing function func(x, data) equals <value> within
+   precision <prec>. <dataP> is an arbitary parameter to <func>. */
+static double
+binsearch(binsearchFunc       func,
+          const void  * const dataP,
+          double        const prec,
+          double        const minArg,
+          double        const maxArg,
+          double        const value
+         ) {
+    double x;
+    double min, max;
+    bool found;
+
+    for (min = minArg, max = maxArg, found = false; !found;) {
+
+        x = (min + max) / 2;
+        {
+            double const f = func(x, dataP);
+
+            if ((fabs(f - value)) < prec)
+                found = true;
+            else {
+                assert(f != value);
+
+                if (f > value) max = x;
+                else           min = x;
+            }
+        }
+    }
+    return x;
+}
+
+/* ------------- Utilities not specific to saturation methods -------------- */
+
+/* Y chromaticities in Rec.709: R       G       B  */
+static double const yCoeffs[3]  = {0.3333, 0.6061, 0.0606};
+
+static void
+applyRatio(TupleD * const tupP,
+           double   const ratio) {
+/*----------------------------------------------------------------------------
+  Multiply the components of tuple *tupP by coefficient 'ratio'.
+-----------------------------------------------------------------------------*/
+    uint c;
+
+    for (c = 0; c < 3; ++c)
+        tupP->_[c] = tupP->_[c] * ratio;
+}
+
+
+
+static void
+getTupInfo(tuplen          const tup,
+           bool            const linear,
+           LinSampleInfo * const siP) {
+/*----------------------------------------------------------------------------
+  Convert PBM tuple <tup> into linear form with double precision siP->sample
+  and obtain also additional information required for further processing.
+  Return the result as *siP.
+-----------------------------------------------------------------------------*/
+    uint i;
+    double minval;
+
+    minval         = 1.1;
+    siP->intensity = 0;
+    siP->maxval    = 0.0;
+    siP->maxl      = 0;
+
+    for (i = 0; i < 3; ++i) {
+        double linval;
+        if (!linear)
+            linval = pm_ungamma709(tup[i]);
+        else
+            linval = tup[i];
+
+        siP->sample._[i] = linval;
+
+        if (linval > siP->maxval) {
+            siP->maxval = linval;
+            siP->maxl   = i;
+        }
+        if (linval < minval) {
+           siP->minl = i;
+           minval    = linval;
+        }
+        siP->intensity += linval * yCoeffs[i];
+    }
+}
+
+/* ------------------------ Logarithmic saturation ------------------------- */
+
+/* Method and algorithm by Anton Shepelev.  */
+
+static void
+tryLogSat(double          const sat,
+          LinSampleInfo * const siP,
+          TupleD *        const tupsatP,
+          double *        const intRatioP,
+          double *        const highestP) {
+/*----------------------------------------------------------------------------
+  Try to increase the saturation of siP->sample by a factor 'sat' and return
+  the result as *tupsatP.
+
+  Also return as *intRatioP the ratio of intensities of 'tupin' and
+  siP->sample.
+
+  Return as *highestP the highest component the saturated color would have if
+  normalized to intensity siP->intensity.
+
+  If the return value exceeds unity saturation cannot be properly increased by
+  the required factor.
+-----------------------------------------------------------------------------*/
+    uint   c;
+    double intSat;
+
+    for (c = 0, intSat = 0.0; c < 3; ++c) {
+        tupsatP->_[c] = pow(siP->sample._[c], sat);
+        intSat       = intSat + tupsatP->_[c] * yCoeffs[c];
+    }
+
+    {
+        double const intRatio = siP->intensity / intSat;
+
+        double const maxComp = tupsatP->_[siP->maxl] * intRatio;
+
+        *intRatioP = intRatio;
+        *highestP = maxComp;
+    }
+}
+
+
+
+/* Structure for the binary search of maximum saturation: */
+typedef struct {
+    LinSampleInfo * siP;
+        /* original color with precalculated information  */
+    TupleD *        tupsatP;
+        /* saturated color                            */
+    double *        intRatioP;
+        /* ratio of orignal and saturated intensities */
+} MaxLogSatInfo;
+
+
+
+static binsearchFunc binsearchMaxLogSat;
+
+static double
+binsearchMaxLogSat(double       const x,
+                   const void * const dataP) {
+/*----------------------------------------------------------------------------
+  Target function for the generic binary search routine, for the finding
+  of the maximum possible saturation of a given color. 'dataP' shall point
+  to a MaxSatInfo structure.
+-----------------------------------------------------------------------------*/
+    const MaxLogSatInfo * const infoP = dataP;
+
+    double highest;
+
+    tryLogSat(x, infoP->siP, infoP->tupsatP, infoP->intRatioP, &highest);
+
+    return highest;
+}
+
+
+
+static void
+getMaxLogSat(LinSampleInfo * const siP,
+             TupleD        * const tupsatP,
+             double        * const intRatioP,
+             double          const upperLimit
+          ) {
+/*  Saturates the color <siP->sample> as much as possible and stores the result
+    in <tupsatP>, which must be multiplied by <*intRatioP> in order to restore
+    the intensity of the original color. The range of saturation search is
+    [1.0..<upperlimit>]. */
+    const double PREC = 0.00001; /* precision of binary search */
+
+    MaxLogSatInfo info;
+
+    info.siP       = siP;
+    info.tupsatP   = tupsatP;
+    info.intRatioP = intRatioP;
+
+/*  Discarding return value (maximum saturation) because upon completion of
+    binsearch() info.tupsatP will contain the saturated color. The target value
+    of maximum channel intensity is decreased by PREC in order to avoid
+    overlow. */
+    binsearch(binsearchMaxLogSat, &info, PREC, 1.0, upperLimit, 1.0 - PREC);
+}
+
+
+
+static void
+saturateLog(LinSampleInfo* const siP,
+            double         const sat,
+            TupleD*        const tupsatP) {
+/*----------------------------------------------------------------------------
+  Saturate linear tuple *siP using the logarithmic saturation method.
+-----------------------------------------------------------------------------*/
+    double intRatio;
+        /* ratio of original and saturated intensities */
+    double maxlValSat;
+        /* maximum component intensity in the saturated sample */
+
+    tryLogSat(sat, siP, tupsatP, &intRatio, &maxlValSat);
+
+    /* if we cannot saturate siP->sample by 'sat', use the maximum possible
+       saturation
+    */
+    if (maxlValSat > 1.0)
+        getMaxLogSat(siP, tupsatP, &intRatio, sat);
+
+    /* restore the original intensity: */
+    applyRatio(tupsatP, intRatio);
+}
+
+
+
+/* ------------------------- Spectrum saturation --------------------------- */
+
+/* Method and algorithm by Anton Shepelev.  */
+
+static void
+saturateSpectrum(LinSampleInfo * const siP,
+                 double          const sat,
+                 TupleD *        const tupsatP) {
+/*----------------------------------------------------------------------------
+  Saturate linear tuple *siP using the Spectrum saturation method.
+-----------------------------------------------------------------------------*/
+    double k;
+    double * sample;
+
+    sample = siP->sample._; /* short-cut to the input sample data */
+
+    if (sample[siP->minl] == sample[siP->maxl])
+        k = 1.0; /* Cannot saturate a neutral sample */
+    else {
+        double const km1 =
+            (1.0 - siP->intensity)/(siP->maxval - siP->intensity);
+            /* Maximum saturation factor that keeps maximum layer intesity
+               within range
+            */
+        double const km2 = siP->intensity/(siP->intensity - sample[siP->minl]);
+            /* Maximum saturation factor  that keeps minimum layer intesity
+               within range
+            */
+
+        /* To satisfy both constraints, choose the strictest: */
+        double const km = km1 > km2 ? km2 : km1;
+
+        /* Ensure the saturation factor does not exceed the maximum
+           possible value:
+        */
+        k = sat < km ? sat : km;
+    }
+
+    {
+        /* Initialize the resulting sample with the input value */
+        uint i;
+        for (i = 0; i < 3; ++i)
+            tupsatP->_[i] = sample[i];
+    }
+
+    applyRatio(tupsatP, k); /* apply the saturation factor */
+
+    {
+         /* restore the original intensity */
+        uint i;
+        for (i = 0; i < 3; ++i)
+            tupsatP->_[i] = tupsatP->_[i] - siP->intensity * (k - 1.0);
+    }
+}
+
+
+
+/* --------------------- General saturation algorithm ---------------------- */
+
+static void
+saturateTup(Method const method,
+            double const sat,
+            bool   const linear,
+            tuplen const tup) {
+/*----------------------------------------------------------------------------
+  Saturate black and white tuple 'tup'
+-----------------------------------------------------------------------------*/
+    LinSampleInfo si;
+
+    getTupInfo(tup, linear, &si);
+
+    if (sat < 1.0 ||  /* saturation can always be decresed */
+        si.maxval < 1.0 ) { /* there is room for increase        */
+
+        TupleD tupsat;
+
+        /* Dispatch saturation methods:
+           (There seems too little benefit in using a table of
+           function pointers, so a manual switch should suffice)
+        */
+        switch (method) {
+            case MLog:      saturateLog     (&si, sat, &tupsat); break;
+            case MSpectrum: saturateSpectrum(&si, sat, &tupsat); break;
+        }
+
+        /* Put the processed tuple back in the tuple row, gamma-adjusting it
+           if required.
+        */
+        {
+            uint i;
+
+            for (i = 0; i < 3; ++i)
+                tup[i] = linear ? tupsat._[i] : pm_gamma709(tupsat._[i]);
+        }
+    }
+}
+
+
+
+static void
+pamaltsat(CmdlineInfo const cmdline,
+          FILE *      const ofP) {
+
+    struct pam inPam, outPam;
+    tuplen *   tuplerown;
+    FILE *     ifP;
+    uint       row;
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    pnm_readpaminit(ifP, &inPam, PAM_STRUCT_SIZE(tuple_type));
+
+    outPam = inPam;
+    outPam.file = ofP;
+
+    tuplerown = pnm_allocpamrown(&inPam);
+
+    pnm_writepaminit(&outPam);
+
+    for (row = 0; row < inPam.height; ++row) {
+        pnm_readpamrown(&inPam, tuplerown);
+
+        if (inPam.depth >= 3) {
+            uint col;
+
+            for (col = 0; col < inPam.width; ++col)
+                saturateTup(cmdline.method, cmdline.strength, cmdline.linear,
+                            tuplerown[col]);
+        }
+
+        pnm_writepamrown(&outPam, tuplerown);
+    }
+
+    pnm_freepamrown(tuplerown);
+    pm_close(ifP);
+}
+
+
+
+int
+main(int argc, const char ** argv) {
+
+    CmdlineInfo cmdline;
+
+    pm_proginit(&argc, argv);
+
+    cmdline = parsedCommandLine(argc, argv);
+
+    pamaltsat(cmdline, stdout);
+
+    return 0;
+}
+
+
+
diff --git a/editor/pambrighten.c b/editor/pambrighten.c
new file mode 100644
index 00000000..51bd0d23
--- /dev/null
+++ b/editor/pambrighten.c
@@ -0,0 +1,271 @@
+/*=============================================================================
+                                  pambrighten
+===============================================================================
+  Change Value and Saturation of Netpbm image.
+=============================================================================*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <math.h>
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "shhopt.h"
+#include "pam.h"
+
+
+
+struct CmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use. */
+    const char * inputFileName;  /* '-' if stdin */
+    float        valchange;
+    float        satchange;
+};
+
+
+
+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 valueSpec;
+    int          valueOpt;
+    unsigned int saturationSpec;
+    int          saturationOpt;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "value",       OPT_INT,    &valueOpt,
+            &valueSpec,           0 );
+    OPTENT3(0, "saturation",  OPT_INT,    &saturationOpt,
+            &saturationSpec,      0 );
+
+    opt.opt_table = option_def;
+    opt.short_allowed = false;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = false;    /* No negative arguments */
+
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (valueSpec) {
+        if (valueOpt < -100)
+            pm_error("Value reduction cannot be more than 100%%.  "
+                     "You specified %d", valueOpt);
+        else
+            cmdlineP->valchange = 1.0 + (float)valueOpt / 100;
+    } else
+        cmdlineP->valchange = 1.0;
+
+    if (saturationSpec) {
+        if (saturationOpt < -100)
+            pm_error("Saturation reduction cannot be more than 100%%.  "
+                     "You specified %d", saturationOpt);
+        else
+            cmdlineP->satchange = 1.0 + (float)saturationOpt / 100;
+    } else
+        cmdlineP->satchange = 1.0;
+
+    if (argc-1 < 1)
+        cmdlineP->inputFileName = "-";
+    else if (argc-1 == 1)
+        cmdlineP->inputFileName = argv[1];
+    else
+        pm_error("Program takes at most one argument:  file specification");
+}
+
+
+
+static void
+changeColorPix(tuple              const tupleval,
+               float              const valchange,
+               float              const satchange,
+               const struct pam * const pamP) {
+
+    pixel oldRgb, newRgb;
+    struct hsv oldHsv, newHsv;
+
+    PPM_PUTR(oldRgb, tupleval[PAM_RED_PLANE]);
+    PPM_PUTG(oldRgb, tupleval[PAM_GRN_PLANE]);
+    PPM_PUTB(oldRgb, tupleval[PAM_BLU_PLANE]);
+    oldHsv = ppm_hsv_from_color(oldRgb, pamP->maxval);
+
+    newHsv.h = oldHsv.h;
+
+    newHsv.s = MIN(1.0, MAX(0.0, oldHsv.s * satchange));
+
+    newHsv.v = MIN(1.0, MAX(0.0, oldHsv.v * valchange));
+
+    newRgb = ppm_color_from_hsv(newHsv, pamP->maxval);
+
+    tupleval[PAM_RED_PLANE] = PPM_GETR(newRgb);
+    tupleval[PAM_GRN_PLANE] = PPM_GETG(newRgb);
+    tupleval[PAM_BLU_PLANE] = PPM_GETB(newRgb);
+}
+
+
+
+static void
+changeGrayPix(tuple        const tupleval,
+              float        const valchange,
+              struct pam * const pamP) {
+
+    samplen const oldGray = pnm_normalized_sample(pamP, tupleval[0]);
+    samplen const newGray = MIN(1.0, MAX(0.0, oldGray * valchange));
+
+    tupleval[0] = pnm_unnormalized_sample(pamP, newGray);
+}
+
+
+
+typedef enum {COLORTYPE_COLOR, COLORTYPE_GRAY, COLORTYPE_BW} ColorType;
+
+
+
+static ColorType
+colorTypeOfImage(struct pam * const pamP) {
+/*----------------------------------------------------------------------------
+   The basic type of color represented in the image described by *pamP: full
+   color, grayscale, or black and white
+
+   Note that we're talking about the format of the image, not the reality of
+   the pixels.  A color image is still a color image even if all the colors in
+   it happen to be gray.
+
+   For a PAM image, as is customary in Netpbm, we do not consider the tuple
+   type, but rather infer the color type from the depth and maxval.  This
+   gives us more flexibility for future tuple types.
+-----------------------------------------------------------------------------*/
+    ColorType retval;
+
+    if (pamP->format == PPM_FORMAT ||
+        pamP->format == RPPM_FORMAT ||
+        (pamP->format == PAM_FORMAT && pamP->depth >= 3)) {
+
+        retval = COLORTYPE_COLOR;
+
+    } else if (pamP->format == PGM_FORMAT ||
+               pamP->format == RPGM_FORMAT ||
+               (pamP->format == PAM_FORMAT &&
+                pamP->depth >= 1 &&
+                pamP->maxval > 1)) {
+
+        retval = COLORTYPE_GRAY;
+
+    } else {
+
+        retval = COLORTYPE_BW;
+
+    }
+    return retval;
+}
+
+
+
+static void
+pambrighten(struct CmdlineInfo const cmdline,
+            FILE *             const ifP) {
+
+    struct pam inpam, outpam;
+    tuple * tuplerow;
+    ColorType colorType;
+    unsigned int row;
+
+    pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
+
+    colorType = colorTypeOfImage(&inpam);
+
+    outpam = inpam;
+    outpam.file = stdout;
+
+    pnm_writepaminit(&outpam);
+
+    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)  {
+            switch (colorType) {
+            case COLORTYPE_COLOR:
+                changeColorPix(tuplerow[col],
+                               cmdline.valchange, cmdline.satchange,
+                               &inpam);
+                break;
+            case COLORTYPE_GRAY:
+                changeGrayPix(tuplerow[col],
+                              cmdline.valchange,
+                              &inpam);
+                break;
+            case COLORTYPE_BW:
+                /* Nothing to change. */
+                break;
+            }
+        }
+        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);
+
+    pambrighten(cmdline, ifP);
+
+    pm_close(ifP);
+    pm_close(stdout);
+
+    return 0;
+}
+
+
+
+/*
+   This was derived from ppmbrighten code written by Jef Poskanzer and
+   Brian Moffet. Updated by Willem van Schaik to support PAM.
+
+   Copyright (C) 1989 by Jef Poskanzer.
+   Copyright (C) 1990 by Brian Moffet.
+   Copyright (C) 2019 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.
+
+   Bryan Henderson contributes his work to the public domain.
+*/
diff --git a/editor/pamcomp.c b/editor/pamcomp.c
index b76eb8c7..6e1e7f7d 100644
--- a/editor/pamcomp.c
+++ b/editor/pamcomp.c
@@ -1,18 +1,18 @@
-/*----------------------------------------------------------------------------
+/*=============================================================================
                               pamcomp
------------------------------------------------------------------------------
+===============================================================================
    This program composes two images together, with optional translucence.
 
    This program is derived from (and replaces) Pnmcomp, whose origin is
    as follows:
 
-       Copyright 1992, David Koblas.                                    
-         Permission to use, copy, modify, and distribute this software  
+       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.          
+         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.
 
    No code from the original remains in the present version.  The
    January 2004 version was coded entirely by Bryan Henderson.
@@ -20,9 +20,9 @@
 
    The current version is derived from the January 2004 version, with
    additional work by multiple authors.
------------------------------------------------------------------------------*/
-
-#define _BSD_SOURCE    /* Make sure strcaseceq() is in nstring.h */
+=============================================================================*/
+#define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
+#define _BSD_SOURCE    /* Make sure strcaseeq() is in nstring.h */
 #include <assert.h>
 #include <string.h>
 #include <math.h>
@@ -40,11 +40,11 @@ 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 
+       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};
@@ -64,7 +64,7 @@ enum alphaMix {AM_KEEPUNDER, AM_OVERLAY};
        its contribution to the composition.
     */
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
@@ -84,12 +84,12 @@ struct cmdlineInfo {
 
 
 static void
-parseCommandLine(int                        argc, 
+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.  
+   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.
@@ -111,11 +111,11 @@ 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,
             &opacitySpec,                 0);
@@ -125,9 +125,9 @@ parseCommandLine(int                        argc,
             &alignSpec,                   0);
     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,       
+    OPTENT3(0, "mixtransparency",    OPT_FLAG,   NULL,
             &cmdlineP->mixtransparency,   0);
 
     opt.opt_table = option_def;
@@ -158,9 +158,9 @@ parseCommandLine(int                        argc,
             cmdlineP->align = BEYONDRIGHT;
         else
             pm_error("Invalid value for align option: '%s'.  Only LEFT, "
-                     "RIGHT, CENTER, BEYONDLEFT, and BEYONDRIGHT are valid.", 
+                     "RIGHT, CENTER, BEYONDLEFT, and BEYONDRIGHT are valid.",
                      align);
-    } else 
+    } else
         cmdlineP->align = LEFT;
 
     if (valignSpec) {
@@ -176,12 +176,12 @@ parseCommandLine(int                        argc,
             cmdlineP->valign = BELOW;
         else
             pm_error("Invalid value for valign option: '%s'.  Only TOP, "
-                     "BOTTOM, MIDDLE, ABOVE, and BELOW are valid.", 
+                     "BOTTOM, MIDDLE, ABOVE, and BELOW are valid.",
                      align);
-    } else 
+    } else
         cmdlineP->valign = TOP;
 
-    if (!opacitySpec) 
+    if (!opacitySpec)
         cmdlineP->opacity = 1.0;
 
     if (argc-1 < 1)
@@ -219,7 +219,7 @@ commonFormat(int const formatA,
 
     int const typeA = PAM_FORMAT_TYPE(formatA);
     int const typeB = PAM_FORMAT_TYPE(formatB);
-    
+
     if (typeA == PAM_TYPE || typeB == PAM_TYPE)
         retval = PAM_FORMAT;
     else if (typeA == PPM_TYPE || typeB == PPM_TYPE)
@@ -242,7 +242,7 @@ typedef enum { TT_BLACKANDWHITE, TT_GRAYSCALE, TT_RGB } BaseTupletype;
 
 
 static BaseTupletype
-commonTupletype(const char * const tupletypeA, 
+commonTupletype(const char * const tupletypeA,
                 const char * const tupletypeB) {
 
     if (strneq(tupletypeA, "RGB", 3) ||
@@ -300,11 +300,11 @@ determineOutputType(const struct pam * const underlayPamP,
     composedPamP->height = underlayPamP->height;
     composedPamP->width  = underlayPamP->width;
 
-    composedPamP->format = commonFormat(underlayPamP->format, 
+    composedPamP->format = commonFormat(underlayPamP->format,
                                         overlayPamP->format);
     composedPamP->plainformat = FALSE;
 
-    composedPamP->maxval = pm_lcm(underlayPamP->maxval, overlayPamP->maxval, 
+    composedPamP->maxval = pm_lcm(underlayPamP->maxval, overlayPamP->maxval,
                                   1, PNM_OVERALLMAXVAL);
 
     composedPamP->visual = true;
@@ -325,7 +325,7 @@ determineOutputType(const struct pam * const underlayPamP,
 
 static void
 warnOutOfFrame(int const originLeft,
-               int const originTop, 
+               int const originTop,
                int const overCols,
                int const overRows,
                int const underCols,
@@ -362,7 +362,7 @@ warnOutOfFrame(int const originLeft,
 
 
 static void
-validateComputableHeight(int const originTop, 
+validateComputableHeight(int const originTop,
                          int const overRows) {
 
     if (originTop < 0) {
@@ -381,11 +381,11 @@ validateComputableHeight(int const originTop,
 
 
 static void
-computeOverlayPosition(int                const underCols, 
+computeOverlayPosition(int                const underCols,
                        int                const underRows,
-                       int                const overCols, 
+                       int                const overCols,
                        int                const overRows,
-                       struct cmdlineInfo const cmdline, 
+                       struct CmdlineInfo const cmdline,
                        int *              const originLeftP,
                        int *              const originTopP) {
 /*----------------------------------------------------------------------------
@@ -421,8 +421,8 @@ computeOverlayPosition(int                const underCols,
 
     validateComputableHeight(*originTopP, overRows);
 
-    warnOutOfFrame(*originLeftP, *originTopP, 
-                   overCols, overRows, underCols, underRows);    
+    warnOutOfFrame(*originLeftP, *originTopP,
+                   overCols, overRows, underCols, underRows);
 }
 
 
@@ -456,7 +456,7 @@ computeOverlayPosition(int                const underCols,
 
    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.
+   product of the underlay and overlay transparencies:
 
        C_T = U_T * O_T
 
@@ -484,7 +484,7 @@ computeOverlayPosition(int                const underCols,
 
 
 static sample
-composeComponents(sample           const compA, 
+composeComponents(sample           const compA,
                   sample           const compB,
                   float            const distrib,
                   float            const bFactor,
@@ -508,7 +508,7 @@ composeComponents(sample           const compA,
   useful.
 
   The inputs and result are based on a maxval of 'maxval'.
-  
+
   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 sample value.
@@ -520,7 +520,7 @@ composeComponents(sample           const compA,
         retval = compA;
     else {
         if (sampleScale == INTENSITY_SAMPLE) {
-            sample const mix = 
+            sample const mix =
                 ROUNDU(compA * distrib + compB * bFactor *(1.0 - distrib));
             retval = MIN(maxval, MAX(0, mix));
         } else {
@@ -529,10 +529,11 @@ composeComponents(sample           const compA,
             float const compALinear = pm_ungamma709(compANormalized);
             float const compBLinear = pm_ungamma709(compBNormalized);
             float const compBLinearAdj = compBLinear * bFactor;
-            float const mix = 
+            float const mix =
                 compALinear * distrib + compBLinearAdj * (1.0 - distrib)
                 * composedFactor;
-            sample const sampleValue = ROUNDU(pm_gamma709(mix) * maxval);
+            sample const sampleValue =
+                pnm_unnormalize(pm_gamma709(mix), maxval);
             retval = MIN(maxval, MAX(0, sampleValue));
         }
     }
@@ -624,13 +625,13 @@ overlayPixel(tuple            const overlayTuple,
         /* Part of formula for AM_OVERLAY -- see explanation above */
 
     overlayWeight = masterOpacity;  /* initial value */
-    
+
     if (overlayPamP->have_opacity)
         overlayWeight *= (float)
             overlayTuple[overlayPamP->opacity_plane] / overlayPamP->maxval;
-    
+
     if (alphaTuplen) {
-        float const alphaval = 
+        float const alphaval =
             invertAlpha ? (1.0 - alphaTuplen[0]) : alphaTuplen[0];
         overlayWeight *= alphaval;
     }
@@ -645,7 +646,7 @@ overlayPixel(tuple            const overlayTuple,
         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;
@@ -659,10 +660,10 @@ overlayPixel(tuple            const overlayTuple,
     }
     {
         unsigned int plane;
-        
+
         for (plane = 0; plane < composedPamP->color_depth; ++plane)
-            composedTuple[plane] = 
-                composeComponents(overlayTuple[plane], underlayTuple[plane], 
+            composedTuple[plane] =
+                composeComponents(overlayTuple[plane], underlayTuple[plane],
                                   overlayWeight, underlayWeight,
                                   composedWeight,
                                   composedPamP->maxval,
@@ -707,7 +708,7 @@ adaptRowFormat(struct pam * const inpamP,
 
 
 static void
-composeRow(int              const originleft, 
+composeRow(int              const originleft,
            struct pam *     const underlayPamP,
            struct pam *     const overlayPamP,
            bool             const invertAlpha,
@@ -732,7 +733,7 @@ composeRow(int              const originleft,
         int const ovlcol = col - originleft;
 
         if (ovlcol >= 0 && ovlcol < overlayPamP->width) {
-            tuplen const alphaTuplen = 
+            tuplen const alphaTuplen =
                 alphaTuplerown ? alphaTuplerown[ovlcol] : NULL;
 
             overlayPixel(overlayTuplerow[ovlcol], overlayPamP,
@@ -799,8 +800,8 @@ determineInputAdaptations(const struct pam * const underlayPamP,
 
 
 static void
-composite(int          const originleft, 
-          int          const origintop, 
+composite(int          const originleft,
+          int          const origintop,
           struct pam * const underlayPamP,
           struct pam * const overlayPamP,
           struct pam * const alphaPamP,
@@ -829,7 +830,7 @@ composite(int          const originleft,
    We assume that the span from the topmost row of the two images to
    the bottommost row is less than INT_MAX.
 -----------------------------------------------------------------------------*/
-    enum sampleScale const sampleScale = 
+    enum sampleScale const sampleScale =
         assumeLinear ? INTENSITY_SAMPLE : GAMMA_SAMPLE;
     enum alphaMix const alphaMix =
         mixTransparency ? AM_OVERLAY : AM_KEEPUNDER;
@@ -859,7 +860,7 @@ composite(int          const originleft,
     assert(INT_MAX - overlayPamP->height > origintop); /* arg constraint */
 
     for (underlayRow = MIN(0, origintop), overlayRow = MIN(0, -origintop);
-         underlayRow < MAX(underlayPamP->height, 
+         underlayRow < MAX(underlayPamP->height,
                            origintop + overlayPamP->height);
          ++underlayRow, ++overlayRow) {
 
@@ -872,19 +873,19 @@ composite(int          const originleft,
         if (underlayRow >= 0 && underlayRow < underlayPamP->height) {
             pnm_readpamrow(underlayPamP, underlayTuplerow);
             adaptRowFormat(underlayPamP, &adaptUnderlayPam, underlayTuplerow);
-            if (underlayRow < origintop || 
+            if (underlayRow < origintop ||
                 underlayRow >= origintop + overlayPamP->height) {
-            
+
                 /* Overlay image does not touch this underlay row. */
 
                 pnm_writepamrow(composedPamP, underlayTuplerow);
             } else {
                 composeRow(originleft, &adaptUnderlayPam, &adaptOverlayPam,
-                           invertAlpha, masterOpacity, 
+                           invertAlpha, masterOpacity,
                            composedPamP, sampleScale, alphaMix,
                            underlayTuplerow, overlayTuplerow, alphaTuplerown,
                            composedTuplerow);
-                
+
                 pnm_writepamrow(composedPamP, composedTuplerow);
             }
         }
@@ -899,19 +900,19 @@ composite(int          const originleft,
 
 
 static void
-initAlphaFile(struct cmdlineInfo const cmdline,
+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 || 
+        if (overlayPamP->width != pamP->width ||
             overlayPamP->height != pamP->height)
             pm_error("Opacity map and overlay image are not the same size");
     } else
@@ -925,7 +926,7 @@ initAlphaFile(struct cmdlineInfo const cmdline,
 int
 main(int argc, const char *argv[]) {
 
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
     FILE * underlayFileP;
     FILE * overlayFileP;
     FILE * alphaFileP;
@@ -942,7 +943,7 @@ main(int argc, const char *argv[]) {
     overlayFileP = pm_openr(cmdline.overlayFilespec);
 
     overlayPam.comment_p = NULL;
-    pnm_readpaminit(overlayFileP, &overlayPam, 
+    pnm_readpaminit(overlayFileP, &overlayPam,
                     PAM_STRUCT_SIZE(opacity_plane));
 
     if (overlayPam.len < PAM_STRUCT_SIZE(opacity_plane))
@@ -959,7 +960,7 @@ main(int argc, const char *argv[]) {
     underlayFileP = pm_openr(cmdline.underlyingFilespec);
 
     underlayPam.comment_p = NULL;
-    pnm_readpaminit(underlayFileP, &underlayPam, 
+    pnm_readpaminit(underlayFileP, &underlayPam,
                     PAM_STRUCT_SIZE(opacity_plane));
 
     assert(underlayPam.len >= PAM_STRUCT_SIZE(opacity_plane));
@@ -968,8 +969,8 @@ main(int argc, const char *argv[]) {
         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, 
+
+    computeOverlayPosition(underlayPam.width, underlayPam.height,
                            overlayPam.width,  overlayPam.height,
                            cmdline, &originLeft, &originTop);
 
@@ -983,7 +984,7 @@ main(int argc, const char *argv[]) {
 
     pnm_setminallocationdepth(&underlayPam, composedPam.depth);
     pnm_setminallocationdepth(&overlayPam,  composedPam.depth);
-    
+
     composite(originLeft, originTop,
               &underlayPam, &overlayPam, alphaFileP ? &alphaPam : NULL,
               cmdline.alphaInvert, cmdline.opacity,
diff --git a/editor/pamcut.c b/editor/pamcut.c
index 7c41af38..db5b5b3d 100644
--- a/editor/pamcut.c
+++ b/editor/pamcut.c
@@ -1,9 +1,9 @@
-/*============================================================================ 
+/*============================================================================
                                 pamcut
 ==============================================================================
   Cut a rectangle out of a Netpbm image
 
-  This is inspired by and intended as a replacement for Pnmcut by 
+  This is inspired by and intended as a replacement for Pnmcut by
   Jef Poskanzer, 1989.
 
   By Bryan Henderson, San Jose CA.  Contributed to the public domain
@@ -24,27 +24,55 @@
        but we hope not.
        */
 
+typedef struct {
+/*----------------------------------------------------------------------------
+   A location in one dimension (row or column) in the image.
+-----------------------------------------------------------------------------*/
+    enum { LOCTYPE_NONE, LOCTYPE_FROMNEAR, LOCTYPE_FROMFAR } locType;
+
+    unsigned int n;
+        /* Row or column count.
+
+           If LOCTYPE_NONE: Meaningless
+
+           If LOCTYPE_FROMFAR: Number of colums from the far edge of the image
+           (right or bottom).  Last column/row is 1.
+
+           If LOCTYPE_FROMNEAR: Number of colums from the near edge of the
+           image (left or top).  First column/row is 0.
+        */
+} Location;
+
+
+
 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 */
 
-    /* The following describe the rectangle the user wants to cut out. 
+    /* The following describe the rectangle the user wants to cut out.
        the value UNSPEC for any of them indicates that value was not
        specified.  A negative value means relative to the far edge.
-       'width' and 'height' are not negative.  These specifications 
+       'width' and 'height' are not negative.  These specifications
        do not necessarily describe a valid rectangle; they are just
        what the user said.
-       */
-    int left;
-    int right;
-    int top;
-    int bottom;
-    int width;
-    int height;
-    unsigned int pad;
 
+       These do not follow the Netpbm convention of having members of this
+       structure that are identical to the name of an option class being
+       the value of that option.  'left', for example, is not the value of
+       the -left option; it could reflect the value of a -cropleft option
+       instead.
+    */
+    Location leftLoc;
+    Location rghtLoc;
+    Location topLoc;
+    Location botLoc;
+    unsigned int widthSpec;
+    unsigned int width;
+    unsigned int heightSpec;
+    unsigned int height;
+    unsigned int pad;
     unsigned int verbose;
 };
 
@@ -63,26 +91,34 @@ parseCommandLine(int argc, const char ** const argv,
     optStruct3 opt;
     unsigned int option_def_index;
 
+    int left, right, top, bottom;
+    unsigned int cropleftSpec, croprightSpec, croptopSpec, cropbottomSpec;
+    unsigned int cropleft, cropright, croptop, cropbottom;
+    unsigned int leftSpec, rightSpec, topSpec, bottomSpec;
+
+    bool haveLegacyLocationArgs;
+        /* The user specified location with top, left, height, and width
+           arguments like in original Pnmcut instead of with named options.
+        */
+
     MALLOCARRAY_NOFAIL(option_def, 100);
 
     option_def_index = 0;   /* incremented by OPTENT3 */
-    OPTENT3(0,   "left",       OPT_INT,    &cmdlineP->left,     NULL,      0);
-    OPTENT3(0,   "right",      OPT_INT,    &cmdlineP->right,    NULL,      0);
-    OPTENT3(0,   "top",        OPT_INT,    &cmdlineP->top,      NULL,      0);
-    OPTENT3(0,   "bottom",     OPT_INT,    &cmdlineP->bottom,   NULL,      0);
-    OPTENT3(0,   "width",      OPT_INT,    &cmdlineP->width,    NULL,      0);
-    OPTENT3(0,   "height",     OPT_INT,    &cmdlineP->height,   NULL,      0);
+    OPTENT3(0,   "left",       OPT_INT,    &left,       &leftSpec,          0);
+    OPTENT3(0,   "right",      OPT_INT,    &right,      &rightSpec,         0);
+    OPTENT3(0,   "top",        OPT_INT,    &top,        &topSpec,           0);
+    OPTENT3(0,   "bottom",     OPT_INT,    &bottom,     &bottomSpec,        0);
+    OPTENT3(0,   "cropleft",   OPT_UINT,   &cropleft,   &cropleftSpec,      0);
+    OPTENT3(0,   "cropright",  OPT_UINT,   &cropright,  &croprightSpec,     0);
+    OPTENT3(0,   "croptop",    OPT_UINT,   &croptop,    &croptopSpec,       0);
+    OPTENT3(0,   "cropbottom", OPT_UINT,   &cropbottom, &cropbottomSpec,    0);
+    OPTENT3(0,   "width",      OPT_UINT,   &cmdlineP->width,
+            &cmdlineP->widthSpec,       0);
+    OPTENT3(0,   "height",     OPT_UINT,   &cmdlineP->height,
+            &cmdlineP->heightSpec,      0);
     OPTENT3(0,   "pad",        OPT_FLAG,   NULL, &cmdlineP->pad,           0);
     OPTENT3(0,   "verbose",    OPT_FLAG,   NULL, &cmdlineP->verbose,       0);
 
-    /* Set the defaults */
-    cmdlineP->left = UNSPEC;
-    cmdlineP->right = UNSPEC;
-    cmdlineP->top = UNSPEC;
-    cmdlineP->bottom = UNSPEC;
-    cmdlineP->width = UNSPEC;
-    cmdlineP->height = UNSPEC;
-
     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 */
@@ -90,10 +126,10 @@ parseCommandLine(int argc, const char ** const argv,
     pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
-    if (cmdlineP->width < 0)
-        pm_error("-width may not be negative.");
-    if (cmdlineP->height < 0)
-        pm_error("-height may not be negative.");
+    if (cmdlineP->widthSpec && cmdlineP->width == 0)
+        pm_error("-width may not be zero.");
+    if (cmdlineP->heightSpec && cmdlineP->height == 0)
+        pm_error("-height may not be zero.");
 
     if ((argc-1) != 0 && (argc-1) != 1 && (argc-1) != 4 && (argc-1) != 5)
         pm_error("Wrong number of arguments: %u.  The only argument in "
@@ -104,170 +140,250 @@ parseCommandLine(int argc, const char ** const argv,
     switch (argc-1) {
     case 0:
         cmdlineP->inputFileName = "-";
+        haveLegacyLocationArgs = false;
         break;
     case 1:
         cmdlineP->inputFileName = argv[1];
+        haveLegacyLocationArgs = false;
         break;
     case 4:
-    case 5: {
-        int warg, harg;  /* The "width" and "height" command line arguments */
+        cmdlineP->inputFileName = "-";
+        haveLegacyLocationArgs = true;
+        break;
+    case 5:
+        cmdlineP->inputFileName = argv[5];
+        haveLegacyLocationArgs = true;
+        break;
+    }
 
-        if (sscanf(argv[1], "%d", &cmdlineP->left) != 1)
+    if (haveLegacyLocationArgs) {
+        int leftArg, topArg, widthArg, heightArg;
+
+        if (sscanf(argv[1], "%d", &leftArg) != 1)
             pm_error("Invalid number for left column argument");
-        if (sscanf(argv[2], "%d", &cmdlineP->top) != 1)
+        if (sscanf(argv[2], "%d", &topArg) != 1)
             pm_error("Invalid number for right column argument");
-        if (sscanf(argv[3], "%d", &warg) != 1)
+        if (sscanf(argv[3], "%d", &widthArg) != 1)
             pm_error("Invalid number for width argument");
-        if (sscanf(argv[4], "%d", &harg) != 1)
+        if (sscanf(argv[4], "%d", &heightArg) != 1)
             pm_error("Invalid number for height argument");
 
-        if (warg > 0) {
-            cmdlineP->width = warg;
-            cmdlineP->right = UNSPEC;
+        if (leftArg < 0) {
+            cmdlineP->leftLoc.locType = LOCTYPE_FROMFAR;
+            cmdlineP->leftLoc.n       = -leftArg;
         } else {
-            cmdlineP->width = UNSPEC;
-            cmdlineP->right = warg -1;
+            cmdlineP->leftLoc.locType = LOCTYPE_FROMNEAR;
+            cmdlineP->leftLoc.n       = leftArg;
         }
-        if (harg > 0) {
-            cmdlineP->height = harg;
-            cmdlineP->bottom = UNSPEC;
+        if (topArg < 0) {
+            cmdlineP->topLoc.locType = LOCTYPE_FROMFAR;
+            cmdlineP->topLoc.n       = -topArg;
         } else {
-            cmdlineP->height = UNSPEC;
-            cmdlineP->bottom = harg - 1;
+            cmdlineP->topLoc.locType = LOCTYPE_FROMNEAR;
+            cmdlineP->topLoc.n       = topArg;
         }
-
-        if (argc-1 == 4)
-            cmdlineP->inputFileName = "-";
-        else
-            cmdlineP->inputFileName = argv[5];
-        break;
-    }
+        if (widthArg > 0) {
+            cmdlineP->width = widthArg;
+            cmdlineP->widthSpec = 1;
+            cmdlineP->rghtLoc.locType = LOCTYPE_NONE;
+        } else {
+            cmdlineP->widthSpec = 0;
+            cmdlineP->rghtLoc.locType = LOCTYPE_FROMFAR;
+            cmdlineP->rghtLoc.n = -(widthArg - 1);
+        }
+        if (heightArg > 0) {
+            cmdlineP->height = heightArg;
+            cmdlineP->heightSpec = 1;
+            cmdlineP->botLoc.locType = LOCTYPE_NONE;
+        } else {
+            cmdlineP->heightSpec = 0;
+            cmdlineP->botLoc.locType = LOCTYPE_FROMFAR;
+            cmdlineP->botLoc.n = -(heightArg - 1);
+        }
+    } else {
+        if (leftSpec && cropleftSpec)
+            pm_error("You cannot specify both -left and -cropleft");
+        if (leftSpec) {
+            if (left >= 0) {
+                cmdlineP->leftLoc.locType = LOCTYPE_FROMNEAR;
+                cmdlineP->leftLoc.n       = left;
+            } else {
+                cmdlineP->leftLoc.locType = LOCTYPE_FROMFAR;
+                cmdlineP->leftLoc.n       = -left;
+            }
+        } else if (cropleftSpec) {
+            cmdlineP->leftLoc.locType = LOCTYPE_FROMNEAR;
+            cmdlineP->leftLoc.n       = cropleft;
+        } else
+            cmdlineP->leftLoc.locType = LOCTYPE_NONE;
+
+        if (rightSpec && croprightSpec)
+            pm_error("You cannot specify both -right and -cropright");
+        if (rightSpec) {
+            if (right >= 0) {
+                cmdlineP->rghtLoc.locType = LOCTYPE_FROMNEAR;
+                cmdlineP->rghtLoc.n       = right;
+            } else {
+                cmdlineP->rghtLoc.locType = LOCTYPE_FROMFAR;
+                cmdlineP->rghtLoc.n       = -right;
+            }
+        } else if (croprightSpec) {
+            cmdlineP->rghtLoc.locType = LOCTYPE_FROMFAR;
+            cmdlineP->rghtLoc.n       = 1 + cropright;
+        } else
+            cmdlineP->rghtLoc.locType = LOCTYPE_NONE;
+
+        if (topSpec && croptopSpec)
+            pm_error("You cannot specify both -top and -croptop");
+        if (topSpec) {
+            if (top >= 0) {
+                cmdlineP->topLoc.locType = LOCTYPE_FROMNEAR;
+                cmdlineP->topLoc.n       = top;
+            } else {
+                cmdlineP->topLoc.locType = LOCTYPE_FROMFAR;
+                cmdlineP->topLoc.n       = -top;
+            }
+        } else if (croptopSpec) {
+            cmdlineP->topLoc.locType = LOCTYPE_FROMNEAR;
+            cmdlineP->topLoc.n       = croptop;
+        } else
+            cmdlineP->topLoc.locType = LOCTYPE_NONE;
+
+        if (bottomSpec && cropbottomSpec)
+            pm_error("You cannot specify both -bottom and -cropbottom");
+        if (bottomSpec) {
+            if (bottom >= 0) {
+                cmdlineP->botLoc.locType = LOCTYPE_FROMNEAR;
+                cmdlineP->botLoc.n       = bottom;
+            } else {
+                cmdlineP->botLoc.locType = LOCTYPE_FROMFAR;
+                cmdlineP->botLoc.n       = -bottom;
+            }
+        } else if (cropbottomSpec) {
+            cmdlineP->botLoc.locType = LOCTYPE_FROMFAR;
+            cmdlineP->botLoc.n       = 1 + cropbottom;
+        } else
+            cmdlineP->botLoc.locType = LOCTYPE_NONE;
     }
 }
 
 
+static int
+near(Location     const loc,
+     unsigned int const edge) {
 
-static void
-computeCutBounds(const int cols, const int rows,
-                 const int leftarg, const int rightarg, 
-                 const int toparg, const int bottomarg,
-                 const int widtharg, const int heightarg,
-                 int * const leftcolP, int * const rightcolP,
-                 int * const toprowP, int * const bottomrowP) {
-/*----------------------------------------------------------------------------
-   From the values given on the command line 'leftarg', 'rightarg',
-   'toparg', 'bottomarg', 'widtharg', and 'heightarg', determine what
-   rectangle the user wants cut out.
-
-   Any of these arguments may be UNSPEC to indicate "not specified".
-   Any except 'widtharg' and 'heightarg' may be negative to indicate
-   relative to the far edge.  'widtharg' and 'heightarg' are positive.
+    int retval;
 
-   Return the location of the rectangle as *leftcolP, *rightcolP,
-   *toprowP, and *bottomrowP.  
------------------------------------------------------------------------------*/
-
-    int leftcol, rightcol, toprow, bottomrow;
-        /* The left and right column numbers and top and bottom row numbers
-           specified by the user, except with negative values translated
-           into the actual values.
+    switch (loc.locType) {
+    case LOCTYPE_NONE:
+        assert(false);
+        retval = 0;
+        break;
+    case LOCTYPE_FROMNEAR:
+        retval = loc.n;
+        break;
+    case LOCTYPE_FROMFAR:
+        retval = (int)edge - (int)loc.n;
+    }
 
-           Note that these may very well be negative themselves, such
-           as when the user says "column -10" and there are only 5 columns
-           in the image.
-           */
+    return retval;
+}
 
-    /* Translate negative column and row into real column and row */
-    /* Exploit the fact that UNSPEC is a positive number */
 
-    if (leftarg >= 0)
-        leftcol = leftarg;
-    else
-        leftcol = cols + leftarg;
-    if (rightarg >= 0)
-        rightcol = rightarg;
-    else
-        rightcol = cols + rightarg;
-    if (toparg >= 0)
-        toprow = toparg;
-    else
-        toprow = rows + toparg;
-    if (bottomarg >= 0)
-        bottomrow = bottomarg;
-    else
-        bottomrow = rows + bottomarg;
 
-    /* Sort out left, right, and width specifications */
+static void
+computeCutBounds(unsigned int const cols,
+                 unsigned int const rows,
+                 Location     const leftArg,
+                 Location     const rghtArg,
+                 Location     const topArg,
+                 Location     const botArg,
+                 bool         const widthSpec,
+                 unsigned int const widthArg,
+                 bool         const heightSpec,
+                 unsigned int const heightArg,
+                 int *        const leftColP,
+                 int *        const rghtColP,
+                 int *        const topRowP,
+                 int *        const botRowP) {
+/*----------------------------------------------------------------------------
+   From the values given on the command line 'leftArg', 'rghtArg', 'topArg',
+   'botArg', 'widthArg', and 'heightArg', determine what rectangle the user
+   wants cut out.
 
-    if (leftcol == UNSPEC && rightcol == UNSPEC && widtharg == UNSPEC) {
-        *leftcolP = 0;
-        *rightcolP = cols - 1;
-    }
-    if (leftcol == UNSPEC && rightcol == UNSPEC && widtharg != UNSPEC) {
-        *leftcolP = 0;
-        *rightcolP = 0 + widtharg - 1;
-    }
-    if (leftcol == UNSPEC && rightcol != UNSPEC && widtharg == UNSPEC) {
-        *leftcolP = 0;
-        *rightcolP = rightcol;
-    }
-    if (leftcol == UNSPEC && rightcol != UNSPEC && widtharg != UNSPEC) {
-        *leftcolP = rightcol - widtharg + 1;
-        *rightcolP = rightcol;
-    }
-    if (leftcol != UNSPEC && rightcol == UNSPEC && widtharg == UNSPEC) {
-        *leftcolP = leftcol;
-        *rightcolP = cols - 1;
-    }
-    if (leftcol != UNSPEC && rightcol == UNSPEC && widtharg != UNSPEC) {
-        *leftcolP = leftcol;
-        *rightcolP = leftcol + widtharg - 1;
-    }
-    if (leftcol != UNSPEC && rightcol != UNSPEC && widtharg == UNSPEC) {
-        *leftcolP = leftcol;
-        *rightcolP = rightcol;
-    }
-    if (leftcol != UNSPEC && rightcol != UNSPEC && widtharg != UNSPEC) {
-        pm_error("You may not specify left, right, and width.\n"
-                 "Choose at most two of these.");
+   Return the location of the rectangle as *leftcolP, *rghtcolP, *toprowP, and
+   *botrowP.  Any of these can be outside the image, including by being
+   negative.
+-----------------------------------------------------------------------------*/
+    /* Find left and right bounds */
+
+    if (widthSpec)
+        assert(widthArg > 0);
+
+    if (leftArg.locType == LOCTYPE_NONE) {
+        if (rghtArg.locType == LOCTYPE_NONE) {
+            *leftColP = 0;
+            if (widthSpec)
+                *rghtColP = 0 + (int)widthArg - 1;
+            else
+                *rghtColP = (int)cols - 1;
+        } else {
+            *rghtColP = near(rghtArg, cols);
+            if (widthSpec)
+                *leftColP = near(rghtArg, cols) - (int)widthArg + 1;
+            else
+                *leftColP = 0;
+        }
+    } else {
+        *leftColP = near(leftArg, cols);
+        if (rghtArg.locType == LOCTYPE_NONE) {
+            if (widthSpec)
+                *rghtColP = near(leftArg, cols) + (int)widthArg - 1;
+            else
+                *rghtColP = (int)cols - 1;
+        } else {
+            if (widthSpec) {
+                pm_error("You may not specify left, right, and width.  "
+                         "Choose at most two of these.");
+            } else
+                *rghtColP = near(rghtArg, cols);
+        }
     }
 
+    /* Find top and bottom bounds */
 
-    /* Sort out top, bottom, and height specifications */
+    if (heightSpec)
+        assert(heightArg > 0);
 
-    if (toprow == UNSPEC && bottomrow == UNSPEC && heightarg == UNSPEC) {
-        *toprowP = 0;
-        *bottomrowP = rows - 1;
-    }
-    if (toprow == UNSPEC && bottomrow == UNSPEC && heightarg != UNSPEC) {
-        *toprowP = 0;
-        *bottomrowP = 0 + heightarg - 1;
-    }
-    if (toprow == UNSPEC && bottomrow != UNSPEC && heightarg == UNSPEC) {
-        *toprowP = 0;
-        *bottomrowP = bottomrow;
-    }
-    if (toprow == UNSPEC && bottomrow != UNSPEC && heightarg != UNSPEC) {
-        *toprowP = bottomrow - heightarg + 1;
-        *bottomrowP = bottomrow;
-    }
-    if (toprow != UNSPEC && bottomrow == UNSPEC && heightarg == UNSPEC) {
-        *toprowP = toprow;
-        *bottomrowP = rows - 1;
-    }
-    if (toprow != UNSPEC && bottomrow == UNSPEC && heightarg != UNSPEC) {
-        *toprowP = toprow;
-        *bottomrowP = toprow + heightarg - 1;
-    }
-    if (toprow != UNSPEC && bottomrow != UNSPEC && heightarg == UNSPEC) {
-        *toprowP = toprow;
-        *bottomrowP = bottomrow;
-    }
-    if (toprow != UNSPEC && bottomrow != UNSPEC && heightarg != UNSPEC) {
-        pm_error("You may not specify top, bottom, and height.\n"
-                 "Choose at most two of these.");
+    if (topArg.locType == LOCTYPE_NONE) {
+        if (botArg.locType == LOCTYPE_NONE) {
+            *topRowP = 0;
+            if (heightSpec)
+                *botRowP = 0 + (int)heightArg - 1;
+            else
+                *botRowP = (int)rows - 1;
+        } else {
+            *botRowP = near(botArg, rows);
+            if (heightSpec)
+                *topRowP = near(botArg, rows) - (int)heightArg + 1;
+            else
+                *topRowP = 0;
+        }
+    } else {
+        *topRowP = near(topArg, rows);
+        if (botArg.locType == LOCTYPE_NONE) {
+            if (heightSpec)
+                *botRowP = near(topArg, rows) + (int)heightArg - 1;
+            else
+                *botRowP = (int)rows - 1;
+        } else {
+            if (heightSpec) {
+                pm_error("You may not specify top, bottom, and height.  "
+                         "Choose at most two of these.");
+            } else
+                *botRowP = near(botArg, rows);
+        }
     }
-
 }
 
 
@@ -324,7 +440,7 @@ rejectOutOfBounds(unsigned int const cols,
 
 
 static void
-writeBlackRows(const struct pam * const outpamP, 
+writeBlackRows(const struct pam * const outpamP,
                int                const rows) {
 /*----------------------------------------------------------------------------
    Write out 'rows' rows of black tuples of the image described by *outpamP.
@@ -336,11 +452,11 @@ writeBlackRows(const struct pam * const outpamP,
     tuple blackTuple;
     tuple * blackRow;
     int col;
-    
+
     pnm_createBlackTuple(outpamP, &blackTuple);
 
     MALLOCARRAY_NOFAIL(blackRow, outpamP->width);
-    
+
     for (col = 0; col < outpamP->width; ++col)
         blackRow[col] = blackTuple;
 
@@ -360,14 +476,14 @@ struct rowCutter {
    pnm_readpamrow() and one pnm_writepamrow().  It works like this:
 
    The array inputPointers[] contains an element for each pixel in an input
-   row.  If it's a pixel that gets discarded in the cutting process, 
+   row.  If it's a pixel that gets discarded in the cutting process,
    inputPointers[] points to a special "discard" tuple.  All thrown away
    pixels have the same discard tuple to save CPU cache space.  If it's
    a pixel that gets copied to the output, inputPointers[] points to some
    tuple to which outputPointers[] also points.
 
    The array outputPointers[] contains an element for each pixel in an
-   output row.  If the pixel is one that gets copied from the input, 
+   output row.  If the pixel is one that gets copied from the input,
    outputPointers[] points to some tuple to which inputPointers[] also
    points.  If it's a pixel that gets padded with black, outputPointers[]
    points to a constant black tuple.  All padded pixels have the same
@@ -489,7 +605,7 @@ destroyRowCutter(struct rowCutter * const rowCutterP) {
     pnm_freepamtuple(rowCutterP->discardTuple);
     free(rowCutterP->inputPointers);
     free(rowCutterP->outputPointers);
-    
+
     free(rowCutterP);
 }
 
@@ -519,7 +635,7 @@ extractRowsGen(const struct pam * const inpamP,
             pnm_writepamrow(outpamP, rowCutterP->outputPointers);
         } else  /* row < toprow || row > bottomrow */
             pnm_readpamrow(inpamP, NULL);
-        
+
         /* Note that we may be tempted just to quit after reaching the bottom
            of the extracted image, but that would cause a broken pipe problem
            for the process that's feeding us the image.
@@ -527,7 +643,7 @@ extractRowsGen(const struct pam * const inpamP,
     }
 
     destroyRowCutter(rowCutterP);
-    
+
     /* Write out bottom padding */
     if ((bottomrow - (inpamP->height-1)) > 0)
         writeBlackRows(outpamP, bottomrow - (inpamP->height-1));
@@ -574,7 +690,7 @@ extractRowsPBM(const struct pam * const inpamP,
             /* Prevent overflows in pbm_allocrow_packed() */
             pm_error("Specified right edge is too far "
                      "from the right end of input image");
-        
+
         readOffset  = 0;
         writeOffset = leftcol;
     } else {
@@ -582,7 +698,7 @@ extractRowsPBM(const struct pam * const inpamP,
         if (totalWidth > INT_MAX)
             pm_error("Specified left/right edge is too far "
                      "from the left/right end of input image");
-        
+
         readOffset = -leftcol;
         writeOffset = 0;
     }
@@ -606,7 +722,7 @@ extractRowsPBM(const struct pam * const inpamP,
 
             pbm_writepbmrow_bitoffset(outpamP->file, bitrow, outpamP->width,
                                       0, writeOffset);
-  
+
             if (rightcol >= inpamP->width)
                 /* repair right padding */
                 bitrow[writeOffset/8 + pbm_packed_bytes(outpamP->width) - 1] =
@@ -632,18 +748,20 @@ cutOneImage(FILE *             const ifP,
             FILE *             const ofP) {
 
     int leftcol, rightcol, toprow, bottomrow;
+        /* Could be out of bounds, even negative */
     struct pam inpam;   /* Input PAM image */
     struct pam outpam;  /* Output PAM image */
 
     pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
-    
-    computeCutBounds(inpam.width, inpam.height, 
-                     cmdline.left, cmdline.right, 
-                     cmdline.top, cmdline.bottom, 
-                     cmdline.width, cmdline.height, 
+
+    computeCutBounds(inpam.width, inpam.height,
+                     cmdline.leftLoc, cmdline.rghtLoc,
+                     cmdline.topLoc, cmdline.botLoc,
+                     cmdline.widthSpec, cmdline.width,
+                     cmdline.heightSpec, cmdline.height,
                      &leftcol, &rightcol, &toprow, &bottomrow);
 
-    rejectOutOfBounds(inpam.width, inpam.height, leftcol, rightcol, 
+    rejectOutOfBounds(inpam.width, inpam.height, leftcol, rightcol,
                       toprow, bottomrow, cmdline.pad);
 
     if (cmdline.verbose) {
@@ -691,6 +809,6 @@ main(int argc, const char *argv[]) {
 
     pm_close(ifP);
     pm_close(ofP);
-    
+
     return 0;
 }
diff --git a/editor/pamditherbw.c b/editor/pamditherbw.c
index 36eb7d9e..4b192e6e 100644
--- a/editor/pamditherbw.c
+++ b/editor/pamditherbw.c
@@ -29,6 +29,10 @@ enum halftone {QT_FS,
 
 enum ditherType {DT_REGULAR, DT_CLUSTER};
 
+static sample blackSample = (sample) PAM_BLACK;
+static sample whiteSample = (sample) PAM_BW_WHITE;
+static tuple  const blackTuple = &blackSample;
+static tuple  const whiteTuple = &whiteSample;
 
 struct cmdlineInfo {
     /* All the information the user supplied in the command line,
@@ -95,6 +99,8 @@ parseCommandLine(int argc, char ** argv,
     pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
+    free(option_def);
+
     if (floydOpt + atkinsonOpt + thresholdOpt + hilbertOpt + dither8Opt + 
         cluster3Opt + cluster4Opt + cluster8Opt == 0)
         cmdlineP->halftone = QT_FS;
@@ -186,134 +192,143 @@ makeOutputPam(unsigned int const width,
 
 #define MAXORD 18
 
-static int hil_order,hil_ord;
-static int hil_turn;
-static int hil_dx,hil_dy;
-static int hil_x,hil_y;
-static int hil_stage[MAXORD];
-static int hil_width,hil_height;
+struct Hil {
+    int order;
+    int ord;
+    int turn;
+    int dx;
+    int dy;
+    int x;
+    int y;
+    int stage[MAXORD];
+    int width;
+    int height;
+};
 
 static void 
-initHilbert(int const w, 
-            int const h) {
+initHilbert(int          const w, 
+            int          const h,
+            struct Hil * const hilP) {
 /*----------------------------------------------------------------------------
   Initialize the Hilbert curve tracer 
 -----------------------------------------------------------------------------*/
-    int big,ber;
-    hil_width = w;
-    hil_height = h;
+    int big, ber;
+    hilP->width = w;
+    hilP->height = h;
     big = w > h ? w : h;
-    for (ber = 2, hil_order = 1; ber < big; ber <<= 1, hil_order++);
-    if (hil_order > MAXORD)
+    for (ber = 2, hilP->order = 1; ber < big; ber <<= 1, hilP->order++);
+    if (hilP->order > MAXORD)
         pm_error("Sorry, hilbert order is too large");
-    hil_ord = hil_order;
-    hil_order--;
+    hilP->ord = hilP->order;
+    hilP->order--;
 }
 
 
 
-static int 
-hilbert(int * const px, int * const py) {
+static bool
+hilbert(int *        const px,
+        int *        const py,
+        struct Hil * const hilP) {
 /*----------------------------------------------------------------------------
   Return non-zero if got another point
 -----------------------------------------------------------------------------*/
     int temp;
-    if (hil_ord > hil_order) {
+    if (hilP->ord > hilP->order) {
         /* have to do first point */
 
-        hil_ord--;
-        hil_stage[hil_ord] = 0;
-        hil_turn = -1;
-        hil_dy = 1;
-        hil_dx = hil_x = hil_y = 0;
+        hilP->ord--;
+        hilP->stage[hilP->ord] = 0;
+        hilP->turn = -1;
+        hilP->dy = 1;
+        hilP->dx = hilP->x = hilP->y = 0;
         *px = *py = 0;
-        return 1;
+        return true;
     }
 
     /* Operate the state machine */
     for(;;)  {
-        switch (hil_stage[hil_ord]) {
+        switch (hilP->stage[hilP->ord]) {
         case 0:
-            hil_turn = -hil_turn;
-            temp = hil_dy;
-            hil_dy = -hil_turn * hil_dx;
-            hil_dx = hil_turn * temp;
-            if (hil_ord > 0) {
-                hil_stage[hil_ord] = 1;
-                hil_ord--;
-                hil_stage[hil_ord]=0;
+            hilP->turn = -hilP->turn;
+            temp = hilP->dy;
+            hilP->dy = -hilP->turn * hilP->dx;
+            hilP->dx = hilP->turn * temp;
+            if (hilP->ord > 0) {
+                hilP->stage[hilP->ord] = 1;
+                hilP->ord--;
+                hilP->stage[hilP->ord]=0;
                 continue;
             }
         case 1:
-            hil_x += hil_dx;
-            hil_y += hil_dy;
-            if (hil_x < hil_width && hil_y < hil_height) {
-                hil_stage[hil_ord] = 2;
-                *px = hil_x;
-                *py = hil_y;
-                return 1;
+            hilP->x += hilP->dx;
+            hilP->y += hilP->dy;
+            if (hilP->x < hilP->width && hilP->y < hilP->height) {
+                hilP->stage[hilP->ord] = 2;
+                *px = hilP->x;
+                *py = hilP->y;
+                return true;
             }
         case 2:
-            hil_turn = -hil_turn;
-            temp = hil_dy;
-            hil_dy = -hil_turn * hil_dx;
-            hil_dx = hil_turn * temp;
-            if (hil_ord > 0) { 
+            hilP->turn = -hilP->turn;
+            temp = hilP->dy;
+            hilP->dy = -hilP->turn * hilP->dx;
+            hilP->dx = hilP->turn * temp;
+            if (hilP->ord > 0) { 
                 /* recurse */
 
-                hil_stage[hil_ord] = 3;
-                hil_ord--;
-                hil_stage[hil_ord]=0;
+                hilP->stage[hilP->ord] = 3;
+                hilP->ord--;
+                hilP->stage[hilP->ord]=0;
                 continue;
             }
         case 3:
-            hil_x += hil_dx;
-            hil_y += hil_dy;
-            if (hil_x < hil_width && hil_y < hil_height) {
-                hil_stage[hil_ord] = 4;
-                *px = hil_x;
-                *py = hil_y;
-                return 1;
+            hilP->x += hilP->dx;
+            hilP->y += hilP->dy;
+            if (hilP->x < hilP->width && hilP->y < hilP->height) {
+                hilP->stage[hilP->ord] = 4;
+                *px = hilP->x;
+                *py = hilP->y;
+                return true;
             }
         case 4:
-            if (hil_ord > 0) {
+            if (hilP->ord > 0) {
                 /* recurse */
-                hil_stage[hil_ord] = 5;
-                hil_ord--;
-                hil_stage[hil_ord]=0;
+                hilP->stage[hilP->ord] = 5;
+                hilP->ord--;
+                hilP->stage[hilP->ord]=0;
                 continue;
             }
         case 5:
-            temp = hil_dy;
-            hil_dy = -hil_turn * hil_dx;
-            hil_dx = hil_turn * temp;
-            hil_turn = -hil_turn;
-            hil_x += hil_dx;
-            hil_y += hil_dy;
-            if (hil_x < hil_width && hil_y < hil_height) {
-                hil_stage[hil_ord] = 6;
-                *px = hil_x;
-                *py = hil_y;
-                return 1;
+            temp = hilP->dy;
+            hilP->dy = -hilP->turn * hilP->dx;
+            hilP->dx = hilP->turn * temp;
+            hilP->turn = -hilP->turn;
+            hilP->x += hilP->dx;
+            hilP->y += hilP->dy;
+            if (hilP->x < hilP->width && hilP->y < hilP->height) {
+                hilP->stage[hilP->ord] = 6;
+                *px = hilP->x;
+                *py = hilP->y;
+                return true;
             }
         case 6:
-            if (hil_ord > 0) {
+            if (hilP->ord > 0) {
                 /* recurse */
-                hil_stage[hil_ord] = 7;
-                hil_ord--;
-                hil_stage[hil_ord]=0;
+                hilP->stage[hilP->ord] = 7;
+                hilP->ord--;
+                hilP->stage[hilP->ord]=0;
                 continue;
             }
         case 7:
-            temp = hil_dy;
-            hil_dy = -hil_turn * hil_dx;
-            hil_dx = hil_turn * temp;
-            hil_turn = -hil_turn;
+            temp = hilP->dy;
+            hilP->dy = -hilP->turn * hilP->dx;
+            hilP->dx = hilP->turn * temp;
+            hilP->turn = -hilP->turn;
             /* Return from a recursion */
-            if (hil_ord < hil_order)
-                hil_ord++;
+            if (hilP->ord < hilP->order)
+                hilP->ord++;
             else
-                return 0;
+                return false;
         }
     }
 }
@@ -341,6 +356,8 @@ doHilbert(FILE *       const ifP,
     tuple ** grays;
     tuple ** bits;
 
+    struct Hil hil;
+
     int end;
     int *x,*y;
     int sum;
@@ -355,7 +372,7 @@ doHilbert(FILE *       const ifP,
     MALLOCARRAY(y, clumpSize);
     if (x == NULL  || y == NULL)
         pm_error("out of memory");
-    initHilbert(graypam.width, graypam.height);
+    initHilbert(graypam.width, graypam.height, &hil);
 
     sum = 0;
     end = clumpSize;
@@ -364,7 +381,9 @@ doHilbert(FILE *       const ifP,
         unsigned int i;
         /* compute the next cluster co-ordinates along hilbert path */
         for (i = 0; i < end; i++) {
-            if (hilbert(&x[i],&y[i])==0)
+            bool gotPoint;
+            gotPoint = hilbert(&x[i], &y[i], &hil);
+            if (!gotPoint)
                 end = i;    /* we reached the end */
         }
         /* sum levels */
@@ -381,6 +400,7 @@ doHilbert(FILE *       const ifP,
                 bits[row][col][0] = 0;
         }
     }
+    free(x);    free(y); 
     pnm_writepam(&bitpam, bits);
 
     pnm_freepamarray(bits, &bitpam);
@@ -458,11 +478,11 @@ fsConvertRow(struct converter * const converterP,
             /* We've accumulated enough light (power) to justify a
                white output pixel.
             */
-            bitrow[col][0] = PAM_BW_WHITE;
+            bitrow[col] = whiteTuple;
             /* Remove from sum the power of this white output pixel */
             accum -= stateP->white;
         } else
-            bitrow[col][0] = PAM_BLACK;
+            bitrow[col] = blackTuple;
 
         /* Forward to future output pixels the power from current
            input pixel and the power forwarded from previous input
@@ -589,10 +609,6 @@ atkinsonConvertRow(struct converter * const converterP,
                    tuplen                   grayrow[],
                    tuple                    bitrow[]) {
 
-    /* See http://www.tinrocket.com/projects/programming/graphics/00158/
-       for a description of the Atkinson algorithm
-    */
-
     struct atkinsonState * const stateP = converterP->stateP;
 
     samplen ** const error = stateP->error;
@@ -607,11 +623,11 @@ atkinsonConvertRow(struct converter * const converterP,
             /* We've accumulated enough light (power) to justify a
                white output pixel.
             */
-            bitrow[col][0] = PAM_BW_WHITE;
+            bitrow[col] = whiteTuple;
             /* Remove from accum the power of this white output pixel */
             accum -= stateP->white;
         } else
-            bitrow[col][0] = PAM_BLACK;
+            bitrow[col] = blackTuple;
         
         /* Forward to future output pixels 3/4 of the power from current
            input pixel and the power forwarded from previous input
@@ -699,8 +715,8 @@ threshConvertRow(struct converter * const converterP,
 
     unsigned int col;
     for (col = 0; col < converterP->cols; ++col)
-        bitrow[col][0] =
-            grayrow[col][0] >= stateP->threshval ? PAM_BW_WHITE : PAM_BLACK;
+        bitrow[col] =
+            grayrow[col][0] >= stateP->threshval ? whiteTuple : blackTuple;
 }
 
 
@@ -754,8 +770,8 @@ clusterConvertRow(struct converter * const converterP,
     for (col = 0; col < converterP->cols; ++col) {
         float const threshold = 
             stateP->clusterMatrix[row % diameter][col % diameter];
-        bitrow[col][0] = 
-            grayrow[col][0] > threshold ? PAM_BW_WHITE : PAM_BLACK;
+        bitrow[col] =
+            grayrow[col][0] > threshold ? whiteTuple : blackTuple;
     }
 }
 
@@ -903,7 +919,7 @@ main(int argc, char *argv[]) {
         }
 
         grayrow = pnm_allocpamrown(&graypam);
-        bitrow  = pnm_allocpamrow(&bitpam);
+        MALLOCARRAY_NOFAIL(bitrow, bitpam.width);
 
         for (row = 0; row < graypam.height; ++row) {
             pnm_readpamrown(&graypam, grayrow);
@@ -912,7 +928,7 @@ main(int argc, char *argv[]) {
             
             pnm_writepamrow(&bitpam, bitrow);
         }
-        pnm_freepamrow(bitrow);
+        free(bitrow);
         pnm_freepamrow(grayrow);
 
         if (converter.destroy)
diff --git a/editor/pamenlarge.c b/editor/pamenlarge.c
index 187bfb6e..56a8c6f7 100644
--- a/editor/pamenlarge.c
+++ b/editor/pamenlarge.c
@@ -3,46 +3,132 @@
 ===============================================================================
   By Bryan Henderson 2004.09.26.  Contributed to the public domain by its
   author.
+
+  The design and code for the fast processing of PBMs is by Akira Urushibata
+  in March 2010 and substantially improved in February 2019.
 =============================================================================*/
 
+#include <stdbool.h>
+#include <assert.h>
+
 #include "netpbm/mallocvar.h"
 #include "netpbm/pm_c_util.h"
 #include "netpbm/pam.h"
 #include "netpbm/pbm.h"
+#include "netpbm/shhopt.h"
+#include "netpbm/nstring.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;  
-    unsigned int scaleFactor;
+    const char * inputFilespec;
+    unsigned int xScaleFactor;
+    unsigned int yScaleFactor;
 };
 
 
 
 static void
-parseCommandLine(int                  const argc,
-                 const char **        const argv,
-                 struct cmdlineInfo * const 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.
 -----------------------------------------------------------------------------*/
-    if (argc-1 < 1)
-        pm_error("You must specify at least one argument:  The scale factor");
-    else {
-        cmdlineP->scaleFactor = atoi(argv[1]);
-        
-        if (cmdlineP->scaleFactor < 1)
-            pm_error("Scale factor must be an integer at least 1.  "
-                     "You specified '%s'", argv[1]);
+    optStruct3 opt;  /* set by OPTENT3 */
+    optEntry * option_def;
+    unsigned int option_def_index;
+
+    unsigned int scale;
+    unsigned int xscaleSpec;
+    unsigned int yscaleSpec;
+    unsigned int scaleSpec;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0, "xscale", OPT_UINT, &cmdlineP->xScaleFactor,  &xscaleSpec, 0);
+    OPTENT3(0, "yscale", OPT_UINT, &cmdlineP->yScaleFactor,  &yscaleSpec, 0);
+    OPTENT3(0, "scale",  OPT_UINT, &scale,                   &scaleSpec, 0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = false; /* We have some 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 (scaleSpec && scale == 0)
+        pm_error("-scale must be positive.  You specified zero");
+
+    if (xscaleSpec && cmdlineP->xScaleFactor == 0)
+        pm_error("-xscale must be positive.  You specified zero");
+
+    if (yscaleSpec && cmdlineP->yScaleFactor == 0)
+        pm_error("-yscale must be positive.  You specified zero");
+
+    if (scaleSpec && xscaleSpec)
+        pm_error("You cannot specify both -scale and -xscale");
+
+    if (scaleSpec && yscaleSpec)
+        pm_error("You cannot specify both -scale and -yscale");
+
+    if (scaleSpec) {
+        cmdlineP->xScaleFactor = scale;
+        cmdlineP->yScaleFactor = scale;
+    }
+
+    if (xscaleSpec && !yscaleSpec)
+        cmdlineP->yScaleFactor = 1;
+
+    if (yscaleSpec && !xscaleSpec)
+        cmdlineP->xScaleFactor = 1;
+
+    if (scaleSpec || xscaleSpec || yscaleSpec) {
+        /* Scale options specified.  Naked scale argument not allowed */
 
-        if (argc-1 >= 2)
+        if ((argc-1) > 1)
+            pm_error("Too many arguments (%u).  With a scale option, "
+                     "the only argument is the "
+                     "optional file specification", argc-1);
+
+        if (argc-1 > 0)
+            cmdlineP->inputFilespec = argv[1];
+        else
+            cmdlineP->inputFilespec = "-";
+    } else {
+        /* scale must be specified in an argument */
+        if ((argc-1) != 1 && (argc-1) != 2)
+            pm_error("Wrong number of arguments (%d).  Without scale options, "
+                     "you must supply 1 or 2 arguments:  scale and "
+                     "optional file specification", argc-1);
+
+        {
+            const char * error;   /* error message of pm_string_to_uint */
+            unsigned int scale;
+
+            pm_string_to_uint(argv[1], &scale, &error);
+
+            if (error == NULL) {
+                if (scale == 0)
+                    pm_error("Scale argument must be positive.  "
+                             "You specified zero");
+                else
+                    cmdlineP->xScaleFactor = cmdlineP->yScaleFactor = scale;
+            } else
+                pm_error("Invalid scale factor: %s", error);
+
+        }
+        if (argc-1 > 1)
             cmdlineP->inputFilespec = argv[2];
         else
             cmdlineP->inputFilespec = "-";
     }
-}        
+    free(option_def);
+}
 
 
 
@@ -75,253 +161,571 @@ makeOutputRowMap(tuple **     const outTupleRowP,
 static void
 validateComputableDimensions(unsigned int const width,
                              unsigned int const height,
-                             unsigned int const scaleFactor) {
+                             unsigned int const xScaleFactor,
+                             unsigned int const yScaleFactor) {
 /*----------------------------------------------------------------------------
    Make sure that multiplication for output image width and height do not
    overflow.
-   See validateComputetableSize() in libpam.c
-   and pbm_readpbminitrest() in libpbm2.c
+
+   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);
+    unsigned int const greaterScaleFactor = MAX(xScaleFactor, yScaleFactor);
 
-    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);
+    if (greaterScaleFactor > maxScaleFactor)
+        pm_error("Scale factor '%u' too large.  "
+                 "The maximum for this %u x %u input image is %u.",
+                 greaterScaleFactor, width, height, maxScaleFactor);
 }
 
 
+static unsigned char const pair[7][4] = {
+    { 0x00 , 0x7F , 0x80 , 0xFF},
+    { 0x00 , 0x3F , 0xC0 , 0xFF},
+    { 0x00 , 0x1F , 0xE0 , 0xFF},
+    { 0x00 , 0x0F , 0xF0 , 0xFF},
+    { 0x00 , 0x07 , 0xF8 , 0xFF},
+    { 0x00 , 0x03 , 0xFC , 0xFF},
+    { 0x00 , 0x01 , 0xFE , 0xFF} };
+
+
 
 static void
-enlargePbmRowHorizontally(struct pam *          const inpamP,
-                          const unsigned char * const inrow,
-                          unsigned int          const inColChars,
-                          unsigned int          const outColChars,
-                          unsigned int          const scaleFactor,
-                          unsigned char *       const outrow) {
-
-    static unsigned char const dbl[16] = { 
-        0x00, 0x03, 0x0C, 0x0F, 0x30, 0x33, 0x3C, 0x3F, 
+enlargePbmRowHorizontallySmall(const unsigned char * const inrow,
+                               unsigned int          const inColChars,
+                               unsigned int          const xScaleFactor,
+                               unsigned char *       const outrow) {
+/*----------------------------------------------------------------------------
+   Fast routines for scale factors 1-13.
+
+   Using a temp value "inrowChar" makes a difference.  We know that inrow
+   and outrow don't overlap, but the compiler does not and emits code
+   which reads inrow[colChar] each time fearing that a write to outrow[x]
+   may have altered the value.  (The first "const" for inrow in the above
+   argument list is not enough for the compiler.)
+-----------------------------------------------------------------------------*/
+
+    static unsigned char const dbl[16] = {
+        0x00, 0x03, 0x0C, 0x0F, 0x30, 0x33, 0x3C, 0x3F,
         0xC0, 0xC3, 0xCC, 0xCF, 0xF0, 0xF3, 0xFC, 0xFF };
 
-    static unsigned char const trp1[8] = { 
+    static unsigned char const trp1[8] = {
         0x00, 0x03, 0x1C, 0x1F, 0xE0, 0xE3, 0xFC, 0xFF };
-        
-    static unsigned char const trp2[16] = { 
+
+    static unsigned char const trp2[16] = {
         0x00, 0x01, 0x0E, 0x0F, 0x70, 0x71, 0x7E, 0x7F,
         0x80, 0x81, 0x8E, 0x8F, 0xF0, 0xF1, 0xFE, 0xFF };
 
-    static unsigned char const trp3[8] = { 
+    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 };
 
     static unsigned char const quin4[8] = {
         0x00, 0x03, 0x7C, 0x7F, 0x80, 0x83, 0xFC, 0xFF };
 
-    static unsigned int const pair[4] = { 0x0000, 0x00FF, 0xFF00, 0xFFFF };
+    static unsigned char const * quad = pair[3];
 
     unsigned int colChar;
 
-    switch (scaleFactor) {
+    switch (xScaleFactor) {
     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) >> 0];
+
+    case 2:  /* Make outrow using prefabricated parts (same for 3, 5). */
+        for (colChar = 0; colChar < inColChars; ++colChar) {
+            unsigned char const inrowChar = inrow[colChar];
+            outrow[colChar*2]   = dbl[ inrowChar >> 4];
+            outrow[colChar*2+1] = dbl[(inrowChar & 0x0F) >> 0];
             /* Possible outrow overrun by one byte. */
         }
         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] >> 0) & 0x07];
+        for (colChar = 0; colChar < inColChars; ++colChar) {
+            unsigned char const inrowChar = inrow[colChar];
+            outrow[colChar*3]   = trp1[ inrowChar >> 5];
+            outrow[colChar*3+1] = trp2[(inrowChar >> 2) & 0x0F];
+            outrow[colChar*3+2] = trp3[(inrowChar >> 0) & 0x07];
         }
-        break;  
+        break;
 
     case 4:
         for (colChar = 0; colChar < inColChars; ++colChar) {
+            unsigned char const inrowChar = inrow[colChar];
             unsigned int i;
-            for (i = 0; i < 4; ++i) 
-                outrow[colChar*4+i]=
-                    quad[(inrow[colChar] >> (6 - 2 * i)) & 0x03]; 
+            for (i = 0; i < 4; ++i)
+                outrow[colChar*4+i] =
+                    quad[(inrowChar >> (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] >> 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; 
+        for (colChar = 0; colChar < inColChars; ++colChar) {
+            unsigned char const inrowChar = inrow[colChar];
+            outrow[colChar*5]   = pair [4][(inrowChar >> 6) & 0x03];
+            outrow[colChar*5+1] = quin2[(inrowChar >> 4) & 0x07] >> 0;
+            outrow[colChar*5+2] = quad [(inrowChar >> 3) & 0x03] >> 0;
+            outrow[colChar*5+3] = quin4[(inrowChar >> 1) & 0x07] >> 0;
+            outrow[colChar*5+4] = pair [2][(inrowChar >> 0) & 0x03];
         }
         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];
+    case 6:  /* Compound of 2 and 3 */
+        for (colChar = 0; colChar < inColChars; ++colChar) {
+            unsigned char const inrowChar = inrow[colChar];
+            unsigned char const hi = dbl[(inrowChar & 0xF0) >> 4];
+            unsigned char const lo = dbl[(inrowChar & 0x0F) >> 0];
 
-            outrow[colChar*6]   = trp1[(hi & 0xF0) >> 5];
+            outrow[colChar*6]   = trp1[hi >> 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+3] = trp1[lo >> 5];
             outrow[colChar*6+4] = trp2[(lo >> 2) & 0x0F];
             outrow[colChar*6+5] = trp3[lo & 0x07];
         }
-        break;  
+        break;
 
     case 7:
-        for (colChar = 0; colChar < inColChars; ++colChar) { 
+        /* This approach can be used for other scale values.
+           Good for architectures which provide wide registers
+           such as SSE.
+        */
+        for (colChar = 0; colChar < inColChars; ++colChar) {
+            unsigned char const inrowChar = inrow[colChar];
             uint32_t hi, lo;
 
-            hi = inrow[colChar] >> 4;
+            hi = inrowChar >> 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 = inrowChar & 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+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 8:
-        for (colChar = 0; colChar < inColChars; ++colChar) { 
+        for (colChar = 0; colChar < inColChars; ++colChar) {
+            unsigned char const inrowChar = inrow[colChar];
             unsigned int i;
             for (i = 0; i < 8; ++i) {
                 outrow[colChar*8+i] =
-                    ((inrow[colChar] >> (7-i)) & 0x01) *0xFF;
+                    ((inrowChar >> (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;
+        for (colChar = 0; colChar < inColChars; ++colChar) {
+            unsigned char const inrowChar = inrow[colChar];
+            outrow[colChar*9]   =  ((inrowChar >> 7) & 0x01) * 0xFF;
+            outrow[colChar*9+1] =  pair[0][(inrowChar >> 6) & 0x03];
+            outrow[colChar*9+2] =  pair[1][(inrowChar >> 5) & 0x03];
+            outrow[colChar*9+3] =  pair[2][(inrowChar >> 4) & 0x03];
+            outrow[colChar*9+4] =  pair[3][(inrowChar >> 3) & 0x03];
+            outrow[colChar*9+5] =  pair[4][(inrowChar >> 2) & 0x03];
+            outrow[colChar*9+6] =  pair[5][(inrowChar >> 1) & 0x03];
+            outrow[colChar*9+7] =  pair[6][(inrowChar >> 0) & 0x03];
+            outrow[colChar*9+8] =  (inrowChar & 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;
+        for (colChar = 0; colChar < inColChars; ++colChar) {
+            unsigned char const inrowChar = inrow[colChar];
+            outrow[colChar*10]   = ((inrowChar >> 7) & 0x01 ) * 0xFF;
+            outrow[colChar*10+1] = pair[1][(inrowChar >> 6) & 0x03];
+            outrow[colChar*10+2] = quad[(inrowChar >> 5) & 0x03];
+            outrow[colChar*10+3] = pair[5][(inrowChar >> 4) & 0x03];
+            outrow[colChar*10+4] = ((inrowChar >> 4) & 0x01) * 0xFF;
+            outrow[colChar*10+5] = ((inrowChar >> 3) & 0x01) * 0xFF;
+            outrow[colChar*10+6] = pair[1][(inrowChar >> 2) & 0x03];
+            outrow[colChar*10+7] = quad[(inrowChar >> 1) & 0x03];
+            outrow[colChar*10+8] = pair[5][(inrowChar >> 0) & 0x03];
+            outrow[colChar*10+9] = ((inrowChar >> 0) & 0x01) * 0xFF;
+        }
+        break;
+
+    case 11:
+        for (colChar = 0; colChar < inColChars; ++colChar) {
+            unsigned char const inrowChar = inrow[colChar];
+            outrow[colChar*11]   = ((inrowChar >> 7) & 0x01 ) * 0xFF;
+            outrow[colChar*11+1] = pair[2][(inrowChar >> 6) & 0x03];
+            outrow[colChar*11+2] = pair[5][(inrowChar >> 5) & 0x03];
+            outrow[colChar*11+3] = ((inrowChar >> 5) & 0x01) * 0xFF;
+            outrow[colChar*11+4] = pair[0][(inrowChar >> 4) & 0x03];
+            outrow[colChar*11+5] = quad[(inrowChar >> 3) & 0x03];
+            outrow[colChar*11+6] = pair[6][(inrowChar >> 2) & 0x03];
+            outrow[colChar*11+7] = ((inrowChar >> 2) & 0x01) * 0xFF;
+            outrow[colChar*11+8] = pair[1][(inrowChar >> 1) & 0x03];
+            outrow[colChar*11+9] = pair[4][(inrowChar >> 0) & 0x03];
+            outrow[colChar*11+10] = ((inrowChar >> 0) & 0x01) * 0xFF;
+        }
+        break;
+
+    case 12:
+        for (colChar = 0; colChar < inColChars; ++colChar) {
+            unsigned char const inrowChar = inrow[colChar];
+            outrow[colChar*12+ 0] = ((inrowChar >> 7) & 0x01) * 0xFF;
+            outrow[colChar*12+ 1] = quad[(inrowChar >> 6) & 0x03];
+            outrow[colChar*12+ 2] = ((inrowChar >> 6) & 0x01) * 0xFF;
+            outrow[colChar*12+ 3] = ((inrowChar >> 5) & 0x01) * 0xFF;
+            outrow[colChar*12+ 4] = quad[(inrowChar >> 4) & 0x03];
+            outrow[colChar*12+ 5] = ((inrowChar >> 4) & 0x01) * 0xFF;
+            outrow[colChar*12+ 6] = ((inrowChar >> 3) & 0x01) * 0xFF;
+            outrow[colChar*12+ 7] = quad[(inrowChar >> 2) & 0x03];
+            outrow[colChar*12+ 8] = ((inrowChar >> 2) & 0x01) * 0xFF;
+            outrow[colChar*12+ 9] = ((inrowChar >> 1) & 0x01) * 0xFF;
+            outrow[colChar*12+10] = quad[(inrowChar >> 0) & 0x03];
+            outrow[colChar*12+11] = ((inrowChar >> 0) & 0x01) * 0xFF;
+        }
+        break;
+
+    case 13:
+      /* Math quiz: 13 is the last entry here.
+         Is this an arbitrary choice?
+         Or is there something which makes 13 necessary?
+
+         If you like working on questions like this you may like
+         number/group theory.  However don't expect a straightforward
+         answer from a college math textbook.  - afu
+      */
+         for (colChar = 0; colChar < inColChars; ++colChar) {
+            unsigned char const inrowChar = inrow[colChar];
+            outrow[colChar*13+ 0] = ((inrowChar >> 7) & 0x01) * 0xFF;
+            outrow[colChar*13+ 1] = pair[4][(inrowChar >> 6) & 0x03];
+            outrow[colChar*13+ 2] = ((inrowChar >> 6) & 0x01) * 0xFF;
+            outrow[colChar*13+ 3] = pair[1][(inrowChar >> 5) & 0x03];
+            outrow[colChar*13+ 4] = pair[6][(inrowChar >> 4) & 0x03];
+            outrow[colChar*13+ 5] = ((inrowChar >> 4) & 0x01) * 0xFF;
+            outrow[colChar*13+ 6] = quad[(inrowChar >> 3) & 0x03];
+            outrow[colChar*13+ 7] = ((inrowChar >> 3) & 0x01) * 0xFF;
+            outrow[colChar*13+ 8] = pair[0][(inrowChar >> 2) & 0x03];
+            outrow[colChar*13+ 9] = pair[5][(inrowChar >> 1) & 0x03];
+            outrow[colChar*13+10] = ((inrowChar >> 1) & 0x01) * 0xFF;
+            outrow[colChar*13+11] = pair[2][(inrowChar >> 0) & 0x03];
+            outrow[colChar*13+12] = ((inrowChar >> 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.
+        pm_error("Internal error.  Invalid scale factor for "
+                 "enlargePbmRowHorizontallySmall: %u", xScaleFactor);
+    }
+}
+
+
+/*
+  General method for scale values 14 and above
+
+  First notice that all output characters are either entirely 0, entirely 1
+  or a combination with the change from 0->1 or 1->0 happening only once.
+  (Sequences like 00111000 never appear when scale value is above 8).
+
+  Let us call the chars which are entirely 0 or 1 "solid" and those which
+  may potentially contain both "transitional".   For scale values 6 - 14
+  each input character expands to output characters aligned as follows:
+
+  6 : TTTTTT
+  7 : TTTTTTT
+  8 : SSSSSSSS
+  9 : STTTTTTTS
+  10: STSTSSTSTS
+  11: STTSTTTSTTS
+  12: STSSTSSTSSTS
+  13: STSTTSTSTTSTS
+  14: STSTSTSSTSTSTS
+
+  Above 15 we insert strings of solid chars as necessary:
+
+  22: SsTSsTSsTSsSsTSsTSsTSs
+  30: SssTSssTSssTSssSssTSssTSssTSss
+  38: SsssTSsssTSsssTSsssSsssTSsssTSsssTSsss
+*/
 
-            Color changes never happen twice in a single outrow byte.
 
-            This is a generalization of above routines for scale factors
-            9 and 10.
+struct OffsetInit {
+  unsigned int scale;
+  const char * alignment;
+};
+
+
+static struct OffsetInit const offsetInit[8] = {
+  /* 0: single char copied from output of enlargePbmRowHorizontallySmall()
+     1: stretch of solid chars
+
+     Each entry is symmetrical left-right and has exactly eight '2's
+   */
+
+  {  8, "22222222" },               /* 8n+0 */
+  {  9, "21121212121212112" },      /* 8n+1 */
+  { 10, "211212112211212112" },     /* 8n+2 */
+  { 11, "2112121121211212112" },    /* 8n+3 */
+  {  4, "212212212212" },           /* 8n+4 */
+  { 13, "211211211212112112112" },  /* 8n+5 */
+  {  6, "21212122121212" },         /* 8n+6 */
+  {  7, "212121212121212" }         /* 8n+7 */
+};
+
+  /*   Relationship between 'S' 'T' in previous comment and '1' '2' here
+
+     11: STTSTTTSTTS
+     19: sSTsTsSTsTsTSsTsTSs
+         2112121121211212112           # table entry for 8n+3
+     27: ssSTssTssSTssTssTSssTssTSss
+         2*112*12*112*12*112*12*112*
+     35: sssSTsssTsssSTsssTsssTSsssTsssTSsss
+         2**112**12**112**12**112**12**112**
+     42: ssssSTssssTssssSTssssTssssTSssssTssssTSssss
+         2***112***12***112***12***112***12***112***
+  */
+
+
+struct OffsetTable {
+    unsigned int offsetSolid[8];
+    unsigned int offsetTrans[13];
+    unsigned int scale;
+    unsigned int solidChars;
+};
+
 
-            Logic works for scale factors 4, 6, 7, 8, and above (but not 5).
+
+static void
+setupOffsetTable(unsigned int         const xScaleFactor,
+                 struct OffsetTable * const offsetTableP) {
+
+    unsigned int i, j0, j1, dest;
+    struct OffsetInit const classEntry = offsetInit[xScaleFactor % 8];
+    unsigned int const scale = classEntry.scale;
+    unsigned int const solidChars = xScaleFactor / 8 - (scale > 8 ? 1 : 0);
+
+    for (i = j0 = j1 = dest = 0; classEntry.alignment[i] != '\0'; ++i) {
+      switch (classEntry.alignment[i]) {
+        case '1': offsetTableP->offsetTrans[j0++] = dest++;
+                  break;
+
+        case '2': offsetTableP->offsetSolid[j1++] = dest;
+                  dest += solidChars;
+                  break;
+
+        default:  pm_error("Internal error. Abnormal alignment value");
+        }
+    }
+
+    offsetTableP->scale = scale;
+    offsetTableP->solidChars = solidChars;
+}
+
+
+
+static void
+enlargePbmRowFractional(unsigned char         const inrowChar,
+                        unsigned int          const outColChars,
+                        unsigned int          const xScaleFactor,
+                        unsigned char       * const outrow,
+                        struct OffsetTable  * const tableP) {
+
+/*----------------------------------------------------------------------------
+  Routine called from enlargePbmRowHorizontallyGen() to process the final
+  fractional inrow char.
+
+  outrow : write position for this function (not left edge of entire row)
+----------------------------------------------------------------------------*/
+
+    unsigned int i;
+
+    /* Deploy (most) solid chars */
+
+    for (i = 0; i < 7; ++i) {
+        unsigned int j;
+        unsigned char const bit8 = (inrowChar >> (7 - i) & 0x01) * 0xFF;
+
+        if (tableP->offsetSolid[i] >= outColChars)
+            break;
+        else
+            for (j = 0; j < tableP->solidChars; ++j) {
+                outrow[j + tableP->offsetSolid[i]] = bit8;
+            }
+     }
+    /* If scale factor is a multiple of 8 we are done. */
+
+    if (tableP->scale != 8) {
+        unsigned char outrowTemp[16];
+
+        enlargePbmRowHorizontallySmall(&inrowChar, 1,
+                                       tableP->scale, outrowTemp);
+
+        for (i = 0 ; i < tableP->scale; ++i) {
+            unsigned int const offset = tableP->offsetTrans[i];
+            if (offset >= outColChars)
+                break;
+            else
+                outrow[offset] = outrowTemp[i];
+            }
+
+    }
+
+}
+
+
+
+static void
+enlargePbmRowHorizontallyGen(const unsigned char * const inrow,
+                             unsigned int          const inColChars,
+                             unsigned int          const outColChars,
+                             unsigned int          const xScaleFactor,
+                             unsigned char       * const outrow,
+                             struct OffsetTable  * const tableP) {
+
+/*----------------------------------------------------------------------------
+  We iterate through inrow.
+  Output chars are deployed according to offsetTable.
+
+  All transitional chars and some solid chars are determined by calling
+  one the fast routines in enlargePbmRowHorizontallySmall().
+----------------------------------------------------------------------------*/
+    unsigned int colChar;
+    unsigned int const fullColChars =
+        inColChars - ((inColChars * xScaleFactor == outColChars) ? 0 : 1);
+
+    for (colChar = 0; colChar < fullColChars; ++colChar) {
+        unsigned char const inrowChar = inrow[colChar];
+        char bit8[8];
+        unsigned int i;
+
+        /* Deploy most solid chars
+           Some scale factors yield uneven string lengths: in such
+           cases we don't handle the odd solid chars at this point
         */
 
-        for (colChar = 0; colChar < outColChars; ++colChar) {
-            unsigned int const mult = scaleFactor;
-            unsigned int const mod = colChar % mult;
-            unsigned int const bit = (mod*8)/mult;
-            /* source bit position, leftmost=0 */
-            unsigned int const offset = mult - (mod*8)%mult;
-            /* number of outrow bits derived from the same
-               "source" inrow bit, starting at and to the right
-               of leftmost bit of outrow byte, inclusive
-            */
-
-            if (offset >= 8)  /* Bits in outrow byte are all 1 or 0 */
-                outrow[colChar] =
-                    (inrow[colChar/mult] >> (7-bit) & 0x01) * 0xFF;
-            else           /* Two inrow bits influence this outrow byte */ 
-                outrow[colChar] = (unsigned char)
-                    (pair[inrow[colChar/mult] >> (6-bit) & 0x03] >> offset)
-                    & 0xFF;
+        for (i = 0; i < 8; ++i)
+            bit8[i] = (inrowChar >> (7 - i) & 0x01) * 0xFF;
+
+        for (i = 0; i < tableP->solidChars; ++i) {
+            unsigned int base = colChar * xScaleFactor + i;
+            outrow[base]              = bit8[0];
+            outrow[base + tableP->offsetSolid[1]] = bit8[1];
+            outrow[base + tableP->offsetSolid[2]] = bit8[2];
+            outrow[base + tableP->offsetSolid[3]] = bit8[3];
+            outrow[base + tableP->offsetSolid[4]] = bit8[4];
+            outrow[base + tableP->offsetSolid[5]] = bit8[5];
+            outrow[base + tableP->offsetSolid[6]] = bit8[6];
+            outrow[base + tableP->offsetSolid[7]] = bit8[7];
+        }
+
+        /* If scale factor is a multiple of 8 we are done */
+
+        if (tableP->scale != 8) {
+            /* Deploy transitional chars and any remaining solid chars */
+            unsigned char outrowTemp[16];
+            unsigned int base = colChar * xScaleFactor;
+
+            enlargePbmRowHorizontallySmall(&inrowChar, 1,
+                                           tableP->scale, outrowTemp);
+
+            /* There are at least 4 valid entries in offsetTrans[] */
+
+            outrow[base + tableP->offsetTrans[0]] = outrowTemp[0];
+            outrow[base + tableP->offsetTrans[1]] = outrowTemp[1];
+            outrow[base + tableP->offsetTrans[2]] = outrowTemp[2];
+            outrow[base + tableP->offsetTrans[3]] = outrowTemp[3];
+
+            for (i = 4; i < tableP->scale; ++i)
+                outrow[base + tableP->offsetTrans[i]] = outrowTemp[i];
         }
     }
+
+    /* Process the fractional final inrow byte */
+
+     if (fullColChars < inColChars) {
+        unsigned int  const start = fullColChars * xScaleFactor;
+        unsigned char const inrowLast = inrow[inColChars -1];
+
+        enlargePbmRowFractional(inrowLast, outColChars - start,
+                                xScaleFactor, &outrow[start], tableP);
+        }
+
 }
 
 
 
 static void
 enlargePbm(struct pam * const inpamP,
-           unsigned int const scaleFactor,
+           unsigned int const xScaleFactor,
+           unsigned int const yScaleFactor,
            FILE *       const ofP) {
 
-    unsigned char * inrow;
-    unsigned char * outrow;
+    enum ScaleMethod {METHOD_USEINPUT, METHOD_SMALL, METHOD_GENERAL};
+    enum ScaleMethod const scaleMethod =
+        xScaleFactor == 1  ? METHOD_USEINPUT :
+        xScaleFactor <= 13 ? METHOD_SMALL : METHOD_GENERAL;
 
-    unsigned int row;
-
-    unsigned int const outcols = inpamP->width * scaleFactor;
-    unsigned int const outrows = inpamP->height * scaleFactor;
+    unsigned int const outcols = inpamP->width * xScaleFactor;
+    unsigned int const outrows = inpamP->height * yScaleFactor;
     unsigned int const inColChars  = pbm_packed_bytes(inpamP->width);
     unsigned int const outColChars = pbm_packed_bytes(outcols);
 
+    unsigned char * inrow;
+    unsigned char * outrow;
+    unsigned int row;
+    struct OffsetTable offsetTable;
+
     inrow  = pbm_allocrow_packed(inpamP->width);
-    
-    if (scaleFactor == 1)
+
+    if (scaleMethod == METHOD_USEINPUT)
         outrow = inrow;
-    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.
+    else {
+        /* Allow writes beyond outrow data end when using the table method.
         */
-        unsigned int const rightPadding = 
-            scaleFactor > 10 ? 0 : (scaleFactor - 1) * 8;  
+        unsigned int const rightPadding =
+            scaleMethod == METHOD_GENERAL ? 0 : (xScaleFactor - 1) * 8;
+
         outrow = pbm_allocrow_packed(outcols + rightPadding);
+
+        if (scaleMethod == METHOD_GENERAL)
+            setupOffsetTable(xScaleFactor, &offsetTable);
     }
 
     pbm_writepbminit(ofP, outcols, outrows, 0);
-    
+
     for (row = 0; row < inpamP->height; ++row) {
         unsigned int i;
 
         pbm_readpbmrow_packed(inpamP->file, inrow, inpamP->width,
                               inpamP->format);
 
-        if (outcols % 8 > 0)           /* clean final partial byte */ 
+        if (outcols % 8 > 0)           /* clean final partial byte */
             pbm_cleanrowend_packed(inrow, inpamP->width);
 
-        enlargePbmRowHorizontally(inpamP, inrow, inColChars, outColChars,
-                                  scaleFactor, outrow);
+        switch (scaleMethod) {
+        case METHOD_USEINPUT:
+            /* Nothing to do */
+            break;
+        case METHOD_SMALL:
+            enlargePbmRowHorizontallySmall(inrow, inColChars,
+                                           xScaleFactor, outrow);
+            break;
+        case METHOD_GENERAL:
+            enlargePbmRowHorizontallyGen(inrow, inColChars, outColChars,
+                                         xScaleFactor, outrow,
+                                         &offsetTable);
+            break;
+        }
 
-        for (i = 0; i < scaleFactor; ++i)  
+        for (i = 0; i < yScaleFactor; ++i)
             pbm_writepbmrow_packed(ofP, outrow, outcols, 0);
     }
-    
+
     if (outrow != inrow)
         pbm_freerow(outrow);
 
@@ -332,7 +736,8 @@ enlargePbm(struct pam * const inpamP,
 
 static void
 enlargeGeneral(struct pam * const inpamP,
-               unsigned int const scaleFactor,
+               unsigned int const xScaleFactor,
+               unsigned int const yScaleFactor,
                FILE *       const ofP) {
 /*----------------------------------------------------------------------------
    Enlarge the input image described by *pamP.
@@ -342,15 +747,15 @@ enlargeGeneral(struct pam * const inpamP,
    This works on all kinds of images, but is slower than enlargePbm on
    PBM.
 -----------------------------------------------------------------------------*/
-    struct pam outpam; 
+    struct pam outpam;
     tuple * tuplerow;
     tuple * newtuplerow;
     unsigned int row;
 
-    outpam = *inpamP; 
+    outpam = *inpamP;
     outpam.file   = ofP;
-    outpam.width  = inpamP->width  * scaleFactor;
-    outpam.height = inpamP->height * scaleFactor; 
+    outpam.width  = inpamP->width  * xScaleFactor;
+    outpam.height = inpamP->height * yScaleFactor;
 
     pnm_writepaminit(&outpam);
 
@@ -360,7 +765,7 @@ enlargeGeneral(struct pam * const inpamP,
 
     for (row = 0; row < inpamP->height; ++row) {
         pnm_readpamrow(inpamP, tuplerow);
-        pnm_writepamrowmult(&outpam, newtuplerow, scaleFactor);
+        pnm_writepamrowmult(&outpam, newtuplerow, yScaleFactor);
     }
 
     free(newtuplerow);
@@ -371,10 +776,10 @@ enlargeGeneral(struct pam * const inpamP,
 
 
 int
-main(int           argc, 
-     const char ** argv) {
+main(int           argc,
+     const char ** const argv) {
 
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
     FILE * ifP;
     struct pam inpam;
 
@@ -383,16 +788,21 @@ main(int           argc,
     parseCommandLine(argc, argv, &cmdline);
 
     ifP = pm_openr(cmdline.inputFilespec);
- 
+
     pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
-    
+
+    assert(cmdline.xScaleFactor > 0);
+    assert(cmdline.yScaleFactor > 0);
+
     validateComputableDimensions(inpam.width, inpam.height,
-                                 cmdline.scaleFactor); 
-    
+                                 cmdline.xScaleFactor, cmdline.yScaleFactor);
+
     if (PNM_FORMAT_TYPE(inpam.format) == PBM_TYPE)
-        enlargePbm(&inpam, cmdline.scaleFactor, stdout);
+        enlargePbm(&inpam, cmdline.xScaleFactor, cmdline.yScaleFactor,
+                   stdout);
     else
-        enlargeGeneral(&inpam, cmdline.scaleFactor, stdout);
+        enlargeGeneral(&inpam, cmdline.xScaleFactor, cmdline.yScaleFactor,
+                       stdout);
 
     pm_close(ifP);
     pm_close(stdout);
@@ -400,3 +810,4 @@ main(int           argc,
     return 0;
 }
 
+
diff --git a/editor/pamflip/flip.h b/editor/pamflip/flip.h
index 612a7f84..ace93044 100644
--- a/editor/pamflip/flip.h
+++ b/editor/pamflip/flip.h
@@ -1,7 +1,7 @@
 #ifndef FLIP_H_INCLUDED
 #define FLIP_H_INCLUDED
 
-struct xformCore {
+struct XformCore {
     /* a b
        c d
     */
diff --git a/editor/pamflip/pamflip.c b/editor/pamflip/pamflip.c
index 149ab310..bc752208 100644
--- a/editor/pamflip/pamflip.c
+++ b/editor/pamflip/pamflip.c
@@ -13,9 +13,9 @@
 /*
    transformNonPbmChunk() is the general transformation function.
    It can transform anything, albeit slowly and expensively.
-   
+
    The following are enhancements for specific cases:
-   
+
      transformRowByRowPbm():
        PBM image with left-right or null transformation
      transformRowsBottomTopPbm()
@@ -58,6 +58,7 @@
    the source image, reading the input image through multiple times.
 */
 
+#define _DEFAULT_SOURCE 1  /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
@@ -76,12 +77,12 @@
 #include "flip.h"
 #include "pamflip_sse.h"
 
-enum xformType {LEFTRIGHT, TOPBOTTOM, TRANSPOSE};
+enum XformType {LEFTRIGHT, TOPBOTTOM, TRANSPOSE};
 
 static void
 parseXformOpt(const char *     const xformOpt,
               unsigned int  *  const xformCountP,
-              enum xformType * const xformList) {
+              enum XformType * const xformList) {
 /*----------------------------------------------------------------------------
    Translate the -xform option string into an array of transform types.
 
@@ -92,10 +93,10 @@ parseXformOpt(const char *     const xformOpt,
     char * xformOptWork;
     char * cursor;
     bool eol;
-    
+
     xformOptWork = strdup(xformOpt);
     cursor = &xformOptWork[0];
-    
+
     eol = FALSE;    /* initial value */
     xformCount = 0; /* initial value */
     while (!eol && xformCount < 10) {
@@ -124,13 +125,13 @@ parseXformOpt(const char *     const xformOpt,
 
 
 /* See transformPoint() for an explanation of the transform matrix types.  The
-   difference between xformCore and xformMatrix is that 'xformCore' is
+   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
+   transformation, while 'XformCore' is independent of the source image and
    just tells what kind of transformation.
 */
 
-struct xformMatrix {
+struct XformMatrix {
     /* a b 0
        c d 0
        e f 1
@@ -146,7 +147,7 @@ struct xformMatrix {
 
 
 static void
-leftright(struct xformCore * const xformP) {
+leftright(struct XformCore * const xformP) {
     xformP->a = - xformP->a;
     xformP->c = - xformP->c;
 }
@@ -154,7 +155,7 @@ leftright(struct xformCore * const xformP) {
 
 
 static void
-topbottom(struct xformCore * const xformP) {
+topbottom(struct XformCore * const xformP) {
     xformP->b = - xformP->b;
     xformP->d = - xformP->d;
 }
@@ -173,7 +174,7 @@ swap(int * const xP, int * const yP) {
 
 
 static void
-transpose(struct xformCore * const xformP) {
+transpose(struct XformCore * const xformP) {
     swap(&xformP->a, &xformP->b);
     swap(&xformP->c, &xformP->d);
 }
@@ -182,18 +183,18 @@ transpose(struct xformCore * const xformP) {
 
 static void
 computeXformCore(unsigned int       const xformCount,
-                 enum xformType     const xformType[],
-                 struct xformCore * const xformP) {
-    
-    struct xformCore const nullTransform = {1, 0, 0, 1};
+                 enum XformType     const XformType[],
+                 struct XformCore * const xformP) {
+
+    struct XformCore const nullTransform = {1, 0, 0, 1};
 
     unsigned int i;
 
     *xformP = nullTransform;   /* initial value */
 
     for (i = 0; i < xformCount; ++i) {
-        switch (xformType[i]) {
-        case LEFTRIGHT: 
+        switch (XformType[i]) {
+        case LEFTRIGHT:
             leftright(xformP);
             break;
         case TOPBOTTOM:
@@ -209,7 +210,7 @@ computeXformCore(unsigned int       const xformCount,
 
 
 static void
-xformDimensions(struct xformCore const xform,
+xformDimensions(struct XformCore const xform,
                 unsigned int     const inCols,
                 unsigned int     const inRows,
                 unsigned int *   const outColsP,
@@ -221,25 +222,25 @@ xformDimensions(struct xformCore const xform,
    E.g. if it's a 90 degree rotation of a 10 x 20 image, the output is
    a 20 x 10 image.
 -----------------------------------------------------------------------------*/
-    *outColsP = abs(xform.a * inCols + xform.c * inRows);
-    *outRowsP = abs(xform.b * inCols + xform.d * inRows);
+    *outColsP = abs(xform.a * (int)inCols + xform.c * (int)inRows);
+    *outRowsP = abs(xform.b * (int)inCols + xform.d * (int)inRows);
 }
 
 
 
 static void
-computeXformMatrix(struct xformMatrix * const xformP, 
+computeXformMatrix(struct XformMatrix * const xformP,
                    unsigned int         const sourceCols,
                    unsigned int         const sourceRows,
-                   struct xformCore     const xformCore) {
+                   struct XformCore     const XformCore) {
 
-    int colMax = xformCore.a * (sourceCols-1) + xformCore.c * (sourceRows-1);
-    int rowMax = xformCore.b * (sourceCols-1) + xformCore.d * (sourceRows-1);
+    int colMax = XformCore.a * (sourceCols-1) + XformCore.c * (sourceRows-1);
+    int rowMax = XformCore.b * (sourceCols-1) + XformCore.d * (sourceRows-1);
 
-    xformP->a = xformCore.a;
-    xformP->b = xformCore.b;
-    xformP->c = xformCore.c;
-    xformP->d = xformCore.d;
+    xformP->a = XformCore.a;
+    xformP->b = XformCore.b;
+    xformP->c = XformCore.c;
+    xformP->d = XformCore.d;
     xformP->e = colMax < 0 ? -colMax : 0;
     xformP->f = rowMax < 0 ? -rowMax : 0;
 }
@@ -251,7 +252,7 @@ struct cmdlineInfo {
        in a form easy for the program to use.
     */
     const char * inputFilespec;  /* Filespec of input file */
-    struct xformCore xform;
+    struct XformCore xform;
         /* Handy mathematical representation of all of transform options */
     size_t availableMemory;
     unsigned int pageSize;
@@ -271,7 +272,7 @@ interpretMemorySize(unsigned int const memsizeSpec,
     if (memsizeSpec) {
         if (memsizeOpt > sizeMax / Meg)
             pm_error("-memsize value too large: %u MiB.  Maximum this program "
-                     "can handle is %u MiB", 
+                     "can handle is %u MiB",
                      memsizeOpt, (unsigned)sizeMax / Meg);
         *availableMemoryP = memsizeOpt * Meg;
     } else
@@ -299,8 +300,8 @@ parseCommandLine(int argc, char ** const argv,
     unsigned int memsizeOpt;
     const char * xformOpt;
     unsigned int xformCount;
-        /* Number of transforms in the 'xformType' array */
-    enum xformType xformList[10];
+        /* Number of transforms in the 'XformType' array */
+    enum XformType xformList[10];
         /* Array of transforms to be applied, in order */
 
     MALLOCARRAY(option_def, 100);
@@ -322,11 +323,11 @@ parseCommandLine(int argc, char ** const argv,
     OPTENT3(0, "cw",        OPT_FLAG,    NULL, &r270,    0);
     OPTENT3(0, "null",      OPT_FLAG,    NULL, &null,    0);
     OPTENT3(0, "verbose",   OPT_FLAG,    NULL, &cmdlineP->verbose,       0);
-    OPTENT3(0, "memsize",   OPT_UINT,    &memsizeOpt, 
+    OPTENT3(0, "memsize",   OPT_UINT,    &memsizeOpt,
             &memsizeSpec,       0);
     OPTENT3(0, "pagesize",  OPT_UINT,    &cmdlineP->pageSize,
             &pagesizeSpec,      0);
-    OPTENT3(0, "xform",     OPT_STRING,  &xformOpt, 
+    OPTENT3(0, "xform",     OPT_STRING,  &xformOpt,
             &xformSpec, 0);
 
     opt.opt_table = option_def;
@@ -363,20 +364,20 @@ parseCommandLine(int argc, char ** const argv,
         } else if (null) {
             xformCount = 0;
         }
-    } else if (xformSpec) 
+    } else if (xformSpec)
         parseXformOpt(xformOpt, &xformCount, xformList);
     else
         pm_error("You must specify an option such as -topbottom to indicate "
                  "what kind of flip you want.");
 
     computeXformCore(xformCount, xformList, &cmdlineP->xform);
-    
+
     interpretMemorySize(memsizeSpec, memsizeOpt, &cmdlineP->availableMemory);
 
     if (!pagesizeSpec)
-        cmdlineP->pageSize = 4*1024;         
+        cmdlineP->pageSize = 4*1024;
 
-    if (argc-1 == 0) 
+    if (argc-1 == 0)
         cmdlineP->inputFilespec = "-";
     else if (argc-1 != 1)
         pm_error("Program takes zero or one argument (filename).  You "
@@ -390,7 +391,7 @@ parseCommandLine(int argc, char ** const argv,
 
 
 static void
-bitOrderReverse(unsigned char * const bitrow, 
+bitOrderReverse(unsigned char * const bitrow,
                 unsigned int    const cols) {
 /*----------------------------------------------------------------------------
   Reverse the bits in a packed pbm row (1 bit per pixel).  I.e. the leftmost
@@ -412,12 +413,12 @@ bitOrderReverse(unsigned char * const bitrow,
             if ((bitrow[j] | bitrow[i]) == 0) {
                 /* Both are 0x00 - skip */
             } else {
-                unsigned char const t = bitreverse[bitrow[j]]; 
+                unsigned char const t = bitreverse[bitrow[j]];
                 bitrow[j] = bitreverse[bitrow[i]];
                 bitrow[i] = t;
             }
     } else {
-        unsigned int const m = cols % 8; 
+        unsigned int const m = cols % 8;
 
         unsigned int i, j;
             /* Cursors into bitrow[].  i moves from left to center;
@@ -430,7 +431,7 @@ bitOrderReverse(unsigned char * const bitrow,
                 /* Skip if both are 0x00 */
                 th = bitreverse[bitrow[i]];
                 bitrow[i] =
-                    bitreverse[0xff & ((bitrow[j-1] << 8 
+                    bitreverse[0xff & ((bitrow[j-1] << 8
                                         | bitrow[j]) >> (8-m))];
                 bitrow[j] = 0xff & ((th << 8 | tl) >> m);
                 tl = th;
@@ -449,7 +450,7 @@ bitOrderReverse(unsigned char * const bitrow,
 
 
 static void
-transformRowByRowPbm(struct pam * const inpamP, 
+transformRowByRowPbm(struct pam * const inpamP,
                      struct pam * const outpamP,
                      bool         const reverse) {
 /*----------------------------------------------------------------------------
@@ -463,7 +464,7 @@ transformRowByRowPbm(struct pam * const inpamP,
     unsigned char * bitrow;
     unsigned int row;
 
-    bitrow = pbm_allocrow_packed(outpamP->width); 
+    bitrow = pbm_allocrow_packed(outpamP->width);
 
     for (row = 0; row < inpamP->height; ++row) {
         pbm_readpbmrow_packed(inpamP->file,  bitrow, inpamP->width,
@@ -480,7 +481,7 @@ transformRowByRowPbm(struct pam * const inpamP,
 
 
 static void
-transformRowByRowNonPbm(struct pam * const inpamP, 
+transformRowByRowNonPbm(struct pam * const inpamP,
                         struct pam * const outpamP,
                         bool         const reverse) {
 /*----------------------------------------------------------------------------
@@ -499,20 +500,20 @@ transformRowByRowNonPbm(struct pam * const inpamP,
            itself.
         */
     tuple * scratchTuplerow;
-    
+
     unsigned int row;
-    
+
     tuplerow = pnm_allocpamrow(inpamP);
-    
+
     if (reverse) {
         /* Set up newtuplerow[] to point to the tuples of tuplerow[] in
            reverse order.
         */
         unsigned int col;
-        
+
         MALLOCARRAY_NOFAIL(scratchTuplerow, inpamP->width);
 
-        for (col = 0; col < inpamP->width; ++col) 
+        for (col = 0; col < inpamP->width; ++col)
             scratchTuplerow[col] = tuplerow[inpamP->width - col - 1];
         newtuplerow = scratchTuplerow;
     } else {
@@ -523,7 +524,7 @@ transformRowByRowNonPbm(struct pam * const inpamP,
         pnm_readpamrow(inpamP, tuplerow);
         pnm_writepamrow(outpamP, newtuplerow);
     }
-    
+
     if (scratchTuplerow)
         free(scratchTuplerow);
     pnm_freepamrow(tuplerow);
@@ -534,7 +535,7 @@ transformRowByRowNonPbm(struct pam * const inpamP,
 static void
 transformRowsBottomTopPbm(struct pam * const inpamP,
                           struct pam * const outpamP,
-                          bool         const reverse) { 
+                          bool         const reverse) {
 /*----------------------------------------------------------------------------
   Flip a PBM raster top for bottom; read the raster from *inpamP;
   write the flipped raster to *outpamP.
@@ -546,15 +547,15 @@ transformRowsBottomTopPbm(struct pam * const inpamP,
 
     unsigned char ** bitrow;
     unsigned int row;
-        
+
     bitrow = pbm_allocarray_packed(outpamP->width, outpamP->height);
-        
+
     for (row = 0; row < rows; ++row)
-        pbm_readpbmrow_packed(inpamP->file, bitrow[row], 
+        pbm_readpbmrow_packed(inpamP->file, bitrow[row],
                               inpamP->width, inpamP->format);
 
     for (row = 0; row < rows; ++row) {
-        if (reverse) 
+        if (reverse)
             bitOrderReverse(bitrow[rows-row-1], inpamP->width);
 
         pbm_writepbmrow_packed(outpamP->file, bitrow[rows - row - 1],
@@ -566,7 +567,7 @@ transformRowsBottomTopPbm(struct pam * const inpamP,
 
 
 static void
-transformRowsBottomTopNonPbm(struct pam * const inpamP, 
+transformRowsBottomTopNonPbm(struct pam * const inpamP,
                              struct pam * const outpamP) {
 /*----------------------------------------------------------------------------
   Do a simple vertical flip.  Read the raster from *inpamP; write the
@@ -592,17 +593,17 @@ transformRowsBottomTopNonPbm(struct pam * const inpamP,
 
         pnm_writepamrow(outpamP, tuplerow);
     }
-    
+
     pnm_freepamarray(tuplerows, outpamP);
 }
 
 
 
 static void __inline__
-transformPoint(int                const col, 
-               int                const row, 
-               struct xformMatrix const xform, 
-               unsigned int *     const newcolP, 
+transformPoint(int                const col,
+               int                const row,
+               struct XformMatrix const xform,
+               unsigned int *     const newcolP,
                unsigned int *     const newrowP ) {
 /*----------------------------------------------------------------------------
    Compute the location in the output of a pixel that is at row 'row',
@@ -612,7 +613,7 @@ transformPoint(int                const col,
    Return the output image location of the pixel as *newcolP and *newrowP.
 -----------------------------------------------------------------------------*/
     /* The transformation is:
-     
+
                  [ a b 0 ]
        [ x y 1 ] [ c d 0 ] = [ x2 y2 1 ]
                  [ e f 1 ]
@@ -652,9 +653,9 @@ writeRaster(struct pam *    const pamP,
 static void
 transformPbmGen(struct pam *     const inpamP,
                 struct pam *     const outpamP,
-                struct xformCore const xformCore) { 
+                struct XformCore const XformCore) {
 /*----------------------------------------------------------------------------
-   This is the same as transformGen, except that it uses less 
+   This is the same as transformGen, except that it uses less
    memory, since the PBM buffer format uses one bit per pixel instead
    of twelve bytes + pointer space
 
@@ -664,28 +665,28 @@ transformPbmGen(struct pam *     const inpamP,
 -----------------------------------------------------------------------------*/
     bit * bitrow;
     bit ** newbits;
-    struct xformMatrix xform;
+    struct XformMatrix xform;
     unsigned int row;
-            
-    computeXformMatrix(&xform, inpamP->width, inpamP->height, xformCore);
-    
+
+    computeXformMatrix(&xform, inpamP->width, inpamP->height, XformCore);
+
     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; 
+        for (col = 0; col < pbm_packed_bytes(outpamP->width); ++col)
+            newbits[row][col] = 0;
     }
-    
+
     for (row = 0; row < inpamP->height; ++row) {
         unsigned int col;
 
         pbm_readpbmrow_packed(inpamP->file, bitrow,
                               inpamP->width, inpamP->format);
         for (col = 0; col < inpamP->width; ) {
-            if (bitrow[col/8] == 0x00) 
+            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);
@@ -694,7 +695,7 @@ transformPbmGen(struct pam *     const inpamP,
                 for (i = 0; col < colLimit; ++i, ++col) {
                     bool const bitIsOne = (bitrow[col/8] >> (7-i)) & 0x01;
                     if (bitIsOne) {
-                        /* Write in only the one bits. */  
+                        /* Write in only the one bits. */
                         unsigned int newcol, newrow;
                         transformPoint(col, row, xform, &newcol, &newrow);
                         newbits[newrow][newcol/8] |= 0x01 << (7 - newcol % 8);
@@ -709,7 +710,7 @@ transformPbmGen(struct pam *     const inpamP,
 
     for (row = 0; row < outpamP->height; ++row)
         pbm_writepbmrow_packed(outpamP->file, newbits[row], outpamP->width, 0);
-    
+
     pbm_freearray(newbits, outpamP->height);
     pbm_freerow(bitrow);
 }
@@ -719,7 +720,7 @@ transformPbmGen(struct pam *     const inpamP,
 static void
 transformNonPbmWhole(struct pam *     const inpamP,
                      struct pam *     const outpamP,
-                     struct xformCore const xformCore,
+                     struct XformCore const XformCore,
                      bool             const verbose) {
 /*----------------------------------------------------------------------------
   Do the transform using "pam" library functions, as opposed to "pbm"
@@ -737,18 +738,18 @@ transformNonPbmWhole(struct pam *     const inpamP,
 -----------------------------------------------------------------------------*/
     tuple * tuplerow;
     tuple ** newtuples;
-    struct xformMatrix xform;
+    struct XformMatrix xform;
     unsigned int row;
 
-    computeXformMatrix(&xform, inpamP->width, inpamP->height, xformCore);
-    
+    computeXformMatrix(&xform, inpamP->width, inpamP->height, XformCore);
+
     tuplerow = pnm_allocpamrow(inpamP);
     newtuples = pnm_allocpamarray(outpamP);
-    
+
     for (row = 0; row < inpamP->height; ++row) {
         unsigned int col;
         pnm_readpamrow(inpamP, tuplerow);
-        
+
         for (col = 0; col < inpamP->width; ++col) {
             unsigned int newcol, newrow;
 
@@ -761,9 +762,9 @@ transformNonPbmWhole(struct pam *     const inpamP,
                             tuplerow[col]);
         }
     }
-    
+
     writeRaster(outpamP, newtuples);
-    
+
     pnm_freepamarray(newtuples, outpamP);
     pnm_freepamrow(tuplerow);
 }
@@ -797,7 +798,7 @@ initOutCell(struct pam *     const outCellPamP,
             unsigned int     const inCellWidth,
             unsigned int     const inCellHeight,
             struct pam *     const inpamP,
-            struct xformCore const xformCore) {
+            struct XformCore const XformCore) {
 /*----------------------------------------------------------------------------
    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
@@ -811,7 +812,7 @@ initOutCell(struct pam *     const outCellPamP,
 
     outCellPamP->file = pm_tmpfile();
 
-    xformDimensions(xformCore, inCellWidth, inCellHeight,
+    xformDimensions(XformCore, inCellWidth, inCellHeight,
                     &outCellFileCt, &outCellRankCt);
 
     outCellPamP->width = outCellFileCt;
@@ -823,8 +824,8 @@ initOutCell(struct pam *     const outCellPamP,
 static outputMap *
 createOutputMap(struct pam *       const inpamP,
                 unsigned int       const maxRows,
-                struct xformMatrix const cellXform,
-                struct xformCore   const xformCore) {
+                struct XformMatrix const cellXform,
+                struct XformCore   const XformCore) {
 /*----------------------------------------------------------------------------
    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
@@ -852,7 +853,7 @@ createOutputMap(struct pam *       const inpamP,
 
     MALLOCVAR_NOFAIL(mapP);
 
-    xformDimensions(xformCore, inCellFileCt, inCellRankCt,
+    xformDimensions(XformCore, inCellFileCt, inCellRankCt,
                     &mapP->fileCt, &mapP->rankCt);
 
     MALLOCARRAY(mapP->pam, mapP->rankCt);
@@ -878,14 +879,14 @@ createOutputMap(struct pam *       const inpamP,
         unsigned int outCellFile, outCellRank;
         transformPoint(inCellFile, inCellRank, cellXform,
                        &outCellFile, &outCellRank);
-    
+
         initOutCell(&mapP->pam[outCellRank][outCellFile],
                     inpamP->width, inCellRowCt,
-                    inpamP, xformCore);
+                    inpamP, XformCore);
     }
     return mapP;
 }
-                
+
 
 
 static void
@@ -934,14 +935,14 @@ closeCellFiles(outputMap * const outputMapP) {
 static void
 transformCell(struct pam *     const inpamP,
               struct pam *     const outpamP,
-              struct xformCore const xformCore) {
+              struct XformCore const XformCore) {
 
-    struct xformMatrix xform;
+    struct XformMatrix xform;
     tuple * inTupleRow;
     tuple ** outTuples;
     unsigned int inRow;
 
-    computeXformMatrix(&xform, inpamP->width, inpamP->height, xformCore);
+    computeXformMatrix(&xform, inpamP->width, inpamP->height, XformCore);
 
     inTupleRow = pnm_allocpamrow(inpamP);
 
@@ -951,7 +952,7 @@ transformCell(struct pam *     const inpamP,
         unsigned int inCol;
 
         pnm_readpamrow(inpamP, inTupleRow);
-        
+
         for (inCol = 0; inCol < inpamP->width; ++inCol) {
             unsigned int outCol, outRow;
 
@@ -995,7 +996,7 @@ stitchCellsToOutput(outputMap *  const outputMapP,
             outCol = 0;
 
             for (outFile = 0; outFile < outputMapP->fileCt; ++outFile) {
-                struct pam * const outCellPamP = 
+                struct pam * const outCellPamP =
                     &outputMapP->pam[outRank][outFile];
 
                 assert(outCellPamP->height == cellRows);
@@ -1019,7 +1020,7 @@ stitchCellsToOutput(outputMap *  const outputMapP,
 static void
 transformNonPbmChunk(struct pam *     const inpamP,
                      struct pam *     const outpamP,
-                     struct xformCore const xformCore,
+                     struct XformCore const XformCore,
                      unsigned int     const maxRows,
                      bool             const verbose) {
 /*----------------------------------------------------------------------------
@@ -1031,11 +1032,11 @@ 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 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
+  matrix according to 'XformCore'.  The resulting cell matrix is the
   target image.
 -----------------------------------------------------------------------------*/
     /* The cells of the source image ("inCell") are in a 1-column matrix.
@@ -1045,7 +1046,7 @@ transformNonPbmChunk(struct pam *     const inpamP,
     unsigned int const inCellFileCt = 1;
     unsigned int const inCellRankCt = (inpamP->height + maxRows - 1) / maxRows;
 
-    struct xformMatrix cellXform;
+    struct XformMatrix cellXform;
     unsigned int inCellRank;
     outputMap * outputMapP;
 
@@ -1053,9 +1054,9 @@ transformNonPbmChunk(struct pam *     const inpamP,
         pm_message("Transforming in %u chunks, using temp files",
                    inCellRankCt);
 
-    computeXformMatrix(&cellXform, inCellFileCt, inCellRankCt, xformCore);
+    computeXformMatrix(&cellXform, inCellFileCt, inCellRankCt, XformCore);
 
-    outputMapP = createOutputMap(inpamP, maxRows, cellXform, xformCore);
+    outputMapP = createOutputMap(inpamP, maxRows, cellXform, XformCore);
 
     for (inCellRank = 0; inCellRank < inCellRankCt; ++inCellRank) {
         unsigned int const inCellFile = 0;
@@ -1069,15 +1070,15 @@ transformNonPbmChunk(struct pam *     const inpamP,
 
         transformPoint(inCellFile, inCellRank, cellXform,
                        &outCellFile, &outCellRank);
-    
+
         outCellPamP = &outputMapP->pam[outCellRank][outCellFile];
 
         /* Input cell is just the next 'inCellRows' rows of input image */
         inCellPam = *inpamP;
         inCellPam.height = inCellRows;
 
-        transformCell(&inCellPam, outCellPamP, xformCore);
-    }    
+        transformCell(&inCellPam, outCellPamP, XformCore);
+    }
 
     rewindCellFiles(outputMapP);
 
@@ -1121,7 +1122,7 @@ maxRowsThatFit(struct pam * const pamP,
 static void
 transformPbm(struct pam *     const inpamP,
              struct pam *     const outpamP,
-             struct xformCore const xform,
+             struct XformCore const xform,
              bool             const verbose) {
 
     if (xform.b == 0 && xform.c == 0) {
@@ -1154,7 +1155,7 @@ transformPbm(struct pam *     const inpamP,
 static void
 transformNonPbm(struct pam *     const inpamP,
                 struct pam *     const outpamP,
-                struct xformCore const xform,
+                struct XformCore const xform,
                 unsigned int     const availableMemory,
                 bool             const verbose) {
 
@@ -1211,7 +1212,7 @@ main(int argc, char * argv[]) {
         ifP = pm_openr_seekable(cmdline.inputFilespec);
     else
         ifP = pm_openr(cmdline.inputFilespec);
-    
+
     pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
 
     outpam = inpam;  /* initial value */
@@ -1232,6 +1233,6 @@ main(int argc, char * argv[]) {
     }
     pm_close(inpam.file);
     pm_close(outpam.file);
-    
+
     return 0;
 }
diff --git a/editor/pamflip/pamflip_sse.c b/editor/pamflip/pamflip_sse.c
index e0929f65..c4e51751 100644
--- a/editor/pamflip/pamflip_sse.c
+++ b/editor/pamflip/pamflip_sse.c
@@ -37,7 +37,7 @@
 
 /*----------------------------------------------------------------------------
    This is a specialized routine for row-for-column PBM transformations.
-   (-cw, -ccw, -xy).  It requires GCC (>= v. 4.2.0) and SSE2. 
+   (-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
@@ -70,7 +70,7 @@
    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
@@ -149,7 +149,7 @@ transpose16Bitrows(unsigned int const cols,
 
         register v16qi vReg = {
             block[0][col8],  block[1][col8],
-            block[2][col8],  block[3][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],
@@ -163,14 +163,14 @@ transpose16Bitrows(unsigned int const cols,
         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 */  
+                /* GCC (>=4.2) automatically unrolls this loop */
                 outplane[outrow++][outcol16] =
                     _mm_movemask_epi8((__m128i)vReg);
                 vReg = (v16qi)_mm_slli_epi32((__m128i)vReg, 1);
@@ -197,7 +197,7 @@ analyzeBlock(const struct pam * const inpamP,
   "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
@@ -227,7 +227,7 @@ analyzeBlock(const struct pam * const inpamP,
                 : block[i];
         }
         *topOfFullBlockP = inpamP->height % 16;
-    
+
         if (inpamP->height >= 16) {
             *outcol16P = inpamP->height/16 - 1;
         } else
@@ -243,7 +243,7 @@ doPartialBlockTop(const struct pam * const inpamP,
                   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;
@@ -267,7 +267,7 @@ doPartialBlockTop(const struct pam * const inpamP,
                            outplane, inpamP->height /16);
             /* Transpose partial rows on top of input.  Place on right edge of
                output.
-            */ 
+            */
     }
 }
 
@@ -303,7 +303,7 @@ doFullBlocks(const struct pam * const inpamP,
         ++modrow;
         if (modrow == 16) {
             /* 16 row buffer is full.  Transpose. */
-            modrow = 0; 
+            modrow = 0;
 
             transpose16Bitrows(inpamP->width, inpamP->height,
                                block, outplane, outcol16);
@@ -320,7 +320,7 @@ doPartialBlockBottom(const struct pam * const inpamP,
                      int                const xdir,
                      const bit *        const blockPartial[16],
                      uint16_t **        const outplane) {
-    
+
     if (xdir > 0 && inpamP->height % 16 > 0) {
         unsigned int colChar;
 
@@ -331,7 +331,7 @@ doPartialBlockBottom(const struct pam * const inpamP,
                            outplane, inpamP->height/16);
             /* Transpose partial rows on bottom of input.  Place on right edge
                of output.
-            */ 
+            */
     }
 }
 
@@ -341,7 +341,7 @@ 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) {
@@ -357,23 +357,23 @@ writeOut(const struct pam * const outpamP,
 
 static void
 clearOutplane(const struct pam * const outpamP,
-              uint16_t **        const outplane) { 
-    
+              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) { 
+                                     struct XformCore   const xformCore) {
 /*----------------------------------------------------------------------------
   This is a specialized routine for row-for-column PBM transformations.
   (-cw, -ccw, -xy).
@@ -397,11 +397,11 @@ pamflip_transformRowsToColumnsPbmSse(const struct pam * const inpamP,
         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. */  
+    /* We write to the output array in 16 bit units.  Add margin. */
 
     clearOutplane(outpamP, outplane);
 
-    analyzeBlock(inpamP, inrow, xdir, block, blockPartial, 
+    analyzeBlock(inpamP, inrow, xdir, block, blockPartial,
                  &topOfFullBlock, &outcol16);
 
     doPartialBlockTop(inpamP, inrow, blockPartial, topOfFullBlock, outplane);
@@ -421,9 +421,9 @@ pamflip_transformRowsToColumnsPbmSse(const struct pam * const inpamP,
 void
 pamflip_transformRowsToColumnsPbmSse(const struct pam * const inpamP,
                                      const struct pam * const outpamP,
-                                     struct xformCore   const xformCore) { 
+                                     struct XformCore   const xformCore) {
 
     /* Nobody is supposed to call this */
     assert(false);
 }
-#endif 
+#endif
diff --git a/editor/pamflip/pamflip_sse.h b/editor/pamflip/pamflip_sse.h
index 59e7c026..cd1267b7 100644
--- a/editor/pamflip/pamflip_sse.h
+++ b/editor/pamflip/pamflip_sse.h
@@ -7,6 +7,6 @@ struct pam;
 void
 pamflip_transformRowsToColumnsPbmSse(const struct pam *     const inpamP,
                                      const struct pam *     const outpamP,
-                                     struct xformCore       const xformCore);
+                                     struct XformCore       const xformCore);
 
 #endif
diff --git a/editor/pamhue.c b/editor/pamhue.c
new file mode 100644
index 00000000..7dddedd8
--- /dev/null
+++ b/editor/pamhue.c
@@ -0,0 +1,209 @@
+/*=============================================================================
+                                  pamhue
+===============================================================================
+  Change the hue, the Hue-Saturation-Value model, every pixel in an image
+  by a specified angle.
+=============================================================================*/
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <math.h>
+#include "mallocvar.h"
+#include "shhopt.h"
+#include "pam.h"
+
+
+
+struct CmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * inputFileName;  /* '-' if stdin */
+    float        huechange;
+};
+
+
+
+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 huechangeSpec;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0,  "huechange",          OPT_FLOAT,
+            &cmdlineP->huechange,           &huechangeSpec,             0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = false;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = false;    /* No negative arguments */
+
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (!huechangeSpec)
+        pm_error("You must specify -huechange");
+
+    if (argc-1 < 1)
+        cmdlineP->inputFileName = "-";
+    else if (argc-1 == 1)
+        cmdlineP->inputFileName = argv[1];
+    else
+        pm_error("Program takes at most one argument:  file specification");
+}
+
+
+
+static float
+positiveMod(float const arg,
+            float const modulus) {
+/*----------------------------------------------------------------------------
+   'arg' mod 'modulus', but positive (i.e. in the range 0.0 - 'modulus').
+-----------------------------------------------------------------------------*/
+    float const mod = fmodf(arg, modulus);
+
+    return mod >= 0.0 ? mod : 360 + mod;
+}
+
+
+
+static void
+changeHue(tuple   const tupleval,
+          float   const huechange,
+          sample  const maxval) {
+
+    pixel oldRgb, newRgb;
+    struct hsv oldHsv, newHsv;
+
+    PPM_PUTR(oldRgb, tupleval[PAM_RED_PLANE]);
+    PPM_PUTG(oldRgb, tupleval[PAM_GRN_PLANE]);
+    PPM_PUTB(oldRgb, tupleval[PAM_BLU_PLANE]);
+
+    oldHsv = ppm_hsv_from_color(oldRgb, maxval);
+
+    newHsv.h = positiveMod(oldHsv.h + huechange, 360.0);
+    newHsv.s = oldHsv.s;
+    newHsv.v = oldHsv.v;
+
+    newRgb = ppm_color_from_hsv(newHsv, maxval);
+
+    tupleval[PAM_RED_PLANE] = PPM_GETR(newRgb);
+    tupleval[PAM_GRN_PLANE] = PPM_GETG(newRgb);
+    tupleval[PAM_BLU_PLANE] = PPM_GETB(newRgb);
+}
+
+
+
+static void
+convertRow(tuple *            const tuplerow,
+           float              const huechange,
+           const struct pam * const pamP) {
+
+    unsigned int col;
+
+    for (col = 0; col < pamP->width; ++col)  {
+        if ((pamP->format == PPM_FORMAT) || (pamP->format == RPPM_FORMAT) ||
+                 ((pamP->format == PAM_FORMAT) && (pamP->depth >= 3))) {
+            /* It's a color image, so there is a hue to change */
+
+            changeHue(tuplerow[col], huechange, pamP->maxval);
+        } else {
+            /* It's black and white or grayscale, which means fully
+               desaturated, so hue is meaningless.  Nothing to change.
+            */
+        }
+    }
+}
+
+
+
+static void
+pamhue(struct CmdlineInfo const cmdline,
+       FILE *             const ifP,
+       FILE *             const ofP) {
+
+    struct pam inpam, outpam;
+    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);
+
+        convertRow(tuplerow, cmdline.huechange, &inpam);
+
+        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);
+
+    pamhue(cmdline, ifP, stdout);
+
+    pm_close(ifP);
+    pm_close(stdout);
+
+    return 0;
+}
+
+
+
+/* This was derived by Bryan Henderson from code by Willem van Schaik
+   (willem@schaik.com), which was derived from Ppmbrighten code written by Jef
+   Poskanzer and Brian Moffet.
+
+   Copyright (C) 1989 by Jef Poskanzer.
+   Copyright (C) 1990 by Brian Moffet.
+   Copyright (C) 2019 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.
+
+   Bryan contributes his work to the public domain.
+*/
diff --git a/editor/pamlevels.c b/editor/pamlevels.c
new file mode 100644
index 00000000..fbbb2c0b
--- /dev/null
+++ b/editor/pamlevels.c
@@ -0,0 +1,515 @@
+#include <stdbool.h>
+#include <math.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "netpbm/pam.h"
+#include "netpbm/pm_system.h"
+#include "netpbm/pm_gamma.h"
+#include "netpbm/nstring.h"
+#include "netpbm/ppm.h"
+
+#include "shhopt.h"
+#include "mallocvar.h"
+
+/* ----------------------------- Type aliases ------------------------------ */
+
+typedef unsigned char uchar;
+typedef unsigned int  uint;
+typedef struct   pam  pam;
+
+typedef struct {
+/*----------------------------------------------------------------------------
+  An RGB triple, in linear intensity or linear brightness; user's choice.
+-----------------------------------------------------------------------------*/
+    double _[3];
+} Rgb;
+
+typedef struct {
+/*----------------------------------------------------------------------------
+  A quadratic polynomial
+-----------------------------------------------------------------------------*/
+    double coeff[3];
+} Polynomial;
+
+typedef struct {
+/*----------------------------------------------------------------------------
+  A set of source or target sample values, in some plane.
+
+  These are either intensity-linear or brightness-linear; user's choice.
+
+  There could be two or three values; user must know which.
+-----------------------------------------------------------------------------*/
+    double _[3];
+} SampleSet;
+
+/* ------------------------- Parse transformations ------------------------- */
+
+typedef struct {
+/*----------------------------------------------------------------------------
+  A mapping of one source color to one target color, encoded in linear RGB
+-----------------------------------------------------------------------------*/
+    tuplen from;
+    tuplen to;
+} Trans;
+
+typedef struct {
+    const char * from;  /* color specifications  */
+    const char * to;    /*   as they appear on commandline */
+    unsigned int  hasFrom;      /* "from" part is present */
+    unsigned int  hasTo;        /* "to" part is present */
+    char  nameFromS[3]; /* short option name */
+    char  nameToS  [3];
+    char  nameFromL[6]; /* long option name */
+    char  nameToL  [6];
+} TransArg;
+
+typedef struct {
+    TransArg _[3];
+} TransArgSet;
+
+typedef struct {
+    unsigned int n;
+        /* Number of elements in 't', 2 for linear transformation; 3 for
+           quadratic.
+        */
+    Trans t[3];
+} TransSet;
+
+typedef struct {
+    unsigned int linear;
+    unsigned int fitbrightness;
+    TransSet     xlats; /* color mappings (-from1, -to1, etc.) */
+    const char * inputFileName;  /* the input file name, "-" for stdin     */
+} CmdlineInfo;
+
+
+
+static void
+optAddTrans (optEntry *     const option_def,
+             unsigned int * const option_def_indexP,
+             TransArg *     const xP,
+             char           const index) {
+
+    char indexc;
+    uint option_def_index;
+
+    option_def_index = *option_def_indexP;
+
+    indexc = '0' + index;
+
+    STRSCPY(xP->nameFromL, "from "); xP->nameFromL[4] = indexc;
+    STRSCPY(xP->nameToL,   "to "  ); xP->nameToL  [2] = indexc;
+    STRSCPY(xP->nameFromS, "f "   ); xP->nameFromS[1] = indexc;
+    STRSCPY(xP->nameToS,   "t "   ); xP->nameToS  [1] = indexc;
+
+    OPTENT3(0, xP->nameFromL, OPT_STRING, &xP->from, &xP->hasFrom, 0);
+    OPTENT3(0, xP->nameFromS, OPT_STRING, &xP->from, &xP->hasFrom, 0);
+    OPTENT3(0, xP->nameToL,   OPT_STRING, &xP->to,   &xP->hasTo,   0);
+    OPTENT3(0, xP->nameToS,   OPT_STRING, &xP->to,   &xP->hasTo,   0);
+
+    *option_def_indexP = option_def_index;
+}
+
+
+
+static void
+parseColor(const char * const text,
+           tuplen *     const colorP) {
+/*----------------------------------------------------------------------------
+  Parses color secification in <text>, converts it into linear RGB,
+  and stores the result in <colorP>.
+-----------------------------------------------------------------------------*/
+    const char * const lastsc = strrchr(text, ':');
+
+    const char * colorname;
+    double mul;
+    tuplen unmultipliedColor;
+    tuplen color;
+
+    if (lastsc) {
+        /* Specification contains a colon.  It might be the colon that
+           introduces the optional multiplier, or it might just be the colon
+           after the type specifier, e.g. "rgbi:...".
+        */
+
+        if (strstr(text, "rgb") == text && strchr(text, ':') == lastsc) {
+            /* The only colon present is the one on the type specifier.
+               So there is no multiplier.
+            */
+            mul = 1.0;
+            colorname = pm_strdup(text);
+        } else {
+            /* There is a multiplier (possibly invalid, though). */
+            const char * const mulstart = lastsc + 1;
+
+            char * endP;
+            char colorbuf[50];
+
+            errno = 0;
+            mul = strtod(mulstart, &endP);
+            if (errno != 0 || endP == mulstart)
+                pm_error("Invalid sample multiplier: '%s'", mulstart);
+
+            strncpy(colorbuf, text, lastsc - text);
+            colorbuf[lastsc - text] = '\0';
+            colorname = pm_strdup(colorbuf);
+        }
+    } else {
+        mul = 1.0;
+        colorname = pm_strdup(text);
+    }
+
+    unmultipliedColor = pnm_parsecolorn(colorname);
+
+    pm_strfree(colorname);
+
+    MALLOCARRAY_NOFAIL(color, 3);
+
+    {
+        /* Linearize and apply multiplier */
+        unsigned int i;
+        for (i = 0; i < 3; ++i)
+            color[i] = pm_ungamma709(unmultipliedColor[i]) * mul;
+    }
+    free(unmultipliedColor);
+
+    *colorP = color;
+}
+
+
+
+static void
+parseTran (TransArg const transArg,
+           Trans *  const rP) {
+
+    parseColor(transArg.from, &rP->from);
+    parseColor(transArg.to,   &rP->to);
+}
+
+
+
+static void
+calcTrans(TransArgSet   const transArgs,
+          TransSet *    const transP) {
+/*----------------------------------------------------------------------------
+   Interpret transformation option (-from1, etc.) values 'transArg'
+   as transformations, *transP.
+-----------------------------------------------------------------------------*/
+    unsigned int xi;
+
+    for (transP->n = 0, xi = 0; xi < 3; ++xi) {
+        const TransArg * const xformP = &transArgs._[xi];
+
+        if (xformP->hasFrom || xformP->hasTo) {
+            if (!xformP->hasFrom || !xformP->hasTo)
+                pm_error("Mapping %u incompletely specified - "
+                         "you specified -fromN or -toN but not the other",
+                    xi + 1);
+            parseTran(*xformP, &transP->t[transP->n++]);
+        }
+    }
+    if (transP->n < 2)
+        pm_error("You must specify at least two mappings with "
+                 "-from1, -to1, etc.  You specified %u", transP->n);
+}
+
+
+
+static void
+parseCommandLine(int                 argc,
+                 const char **       argv,
+                 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;
+
+    TransArgSet xlations; /* color mapping as read from command line */
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;  /* incremented by OPTENT3 */
+
+    OPTENT3(0, "fitbrightness",          OPT_FLAG, NULL,
+            &cmdlineP->fitbrightness, 0);
+    OPTENT3(0, "linear",                 OPT_FLAG, NULL,
+            &cmdlineP->linear,        0);
+
+    {
+        unsigned int i;
+        for (i = 0; i < 3; ++i)
+            optAddTrans(option_def, &option_def_index,
+                        &xlations._[i], i + 1);
+    }
+
+    opt.opt_table     = option_def;
+    opt.short_allowed = 0;
+    opt.allowNegNum   = 0;
+
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+
+    if (cmdlineP->linear && cmdlineP->fitbrightness) {
+        pm_error("You cannot use -linear and -fitbrightness together");
+        /* Note: It actually makes sense to use them together; we're just not
+           willing to put the effort into something it's unlikely anyone will
+           want.
+        */
+    }
+
+    calcTrans(xlations, &cmdlineP->xlats);
+
+    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 void
+errResolve(void) {
+    pm_error( "Cannot resolve the transformations");
+}
+
+
+
+static double
+sqr(double const x) {
+    return x * x;
+}
+
+
+
+static void
+solveOnePlane(SampleSet    const f,
+              SampleSet    const t,
+              unsigned int const n,
+              Polynomial * const solutionP) {
+/*----------------------------------------------------------------------------
+  Find the transformation that maps f[i] to t[i] for 0 <= i < n.
+-----------------------------------------------------------------------------*/
+    double const eps = 0.00001;
+
+    double a, b, c;
+
+    /* I have decided against generic methods of solving systems of linear
+       equations in favour of simple explicit formulas, with no memory
+       allocation and tedious matrix processing.
+    */
+
+    switch (n) {
+    case 3: {
+        double const aDenom =
+            sqr( f._[0] ) * ( f._[1] - f._[2] ) -
+            sqr( f._[2] ) * ( f._[1] - f._[0] ) -
+            sqr( f._[1] ) * ( f._[0] - f._[2] );
+
+        if (fabs(aDenom) < eps)
+            errResolve();
+
+        a = (t._[1] * (f._[2] - f._[0]) - t._[0] * (f._[2] - f._[1]) -
+             t._[2] * (f._[1] - f._[0]))
+            / aDenom;
+    } break;
+    case 2:
+        a = 0.0;
+        break;
+    default:
+        a = 0.0; /* to avoid a warning that <a> "may be uninitialized". */
+        pm_error("INTERNAL ERROR: solve(): impossible value of n: %u", n);
+    }
+
+    {
+        double const bDenom = f._[1] - f._[0];
+
+        if (fabs(bDenom) < eps)
+            errResolve();
+
+        b = (t._[1] - t._[0] + a * (sqr(f._[0]) - sqr(f._[1]))) / bDenom;
+    }
+
+    c = -a * sqr(f._[0]) - b * f._[0] + t._[0];
+
+    solutionP->coeff[0] = a; solutionP->coeff[1] = b; solutionP->coeff[2] = c;
+}
+
+
+
+static void
+chanData(TransSet     const ta,
+         bool         const fittingBrightness,
+         unsigned int const plane,
+         SampleSet *  const fromP,
+         SampleSet *  const toP) {
+/*----------------------------------------------------------------------------
+  Collate transformations from 'ta' for plane 'plane'.
+-----------------------------------------------------------------------------*/
+    unsigned int i;
+
+    for (i = 0; i < ta.n; ++i) {
+        if (fittingBrightness) { /* working with gamma-compressed values */
+            fromP->_[i] = pm_gamma709(ta.t[i].from[plane]);
+            toP->  _[i] = pm_gamma709(ta.t[i].to  [plane]);
+        } else { /* working in linear RGB */
+            fromP->_[i] = ta.t[i].from[plane];
+            toP->  _[i] = ta.t[i].to  [plane];
+        }
+    }
+}
+
+
+
+typedef struct {
+    Polynomial _[3];  /* One per plane */
+} Solution;
+
+
+
+static void
+solveFmCmdlineOpts(CmdlineInfo  const cmdline,
+                   unsigned int const depth,
+                   Solution *   const solutionP) {
+/*----------------------------------------------------------------------------
+   Compute the function that will transform the tuples, based on what the user
+   requested ('cmdline').
+
+   The function takes intensity-linear tuples for the normal levels function,
+   or brightness-linear for the brightness approximation levels function.
+
+   The transformed image has 'depth' planes.
+-----------------------------------------------------------------------------*/
+    unsigned int plane;
+    SampleSet from, to;
+    /* This initialization to bypass the "may be uninitialized" warning: */
+    to  ._[0] = 0; to.  _[1] = 0; to  ._[2] = 0;
+    from._[0] = 1; from._[1] = 0; from._[2] = 0;
+
+    for (plane = 0; plane < depth; ++plane) {
+
+        chanData(cmdline.xlats, cmdline.fitbrightness, plane, &from, &to);
+        solveOnePlane(from, to, cmdline.xlats.n, &solutionP->_[plane]);
+    }
+}
+
+
+
+static samplen
+xformedSample(samplen    const value,
+              Polynomial const polynomial) {
+/*----------------------------------------------------------------------------
+  'sample' transformed by 'polynomial'.
+-----------------------------------------------------------------------------*/
+    double const res =
+        (polynomial.coeff[0] * value + polynomial.coeff[1]) * value +
+        polynomial.coeff[2];
+
+    return MAX(0.0f, MIN(1.0f, res));
+}
+
+
+
+static void
+pamlevels(CmdlineInfo const cmdline) {
+
+    unsigned int row;
+    pam      inPam, outPam;
+    Solution solution;
+    tuplen * tuplerown;
+    FILE   * ifP;
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    pnm_readpaminit(ifP, &inPam, PAM_STRUCT_SIZE(tuple_type));
+
+    outPam = inPam;
+    outPam.file = stdout;
+
+    solveFmCmdlineOpts(cmdline, inPam.depth, &solution);
+
+    tuplerown = pnm_allocpamrown(&inPam);
+
+    pnm_writepaminit(&outPam);
+
+    for (row = 0; row < inPam.height; ++row) {
+        unsigned int col;
+
+        pnm_readpamrown(&inPam, tuplerown);
+
+        if (!cmdline.linear && !cmdline.fitbrightness)
+            pnm_ungammarown(&inPam, tuplerown);
+
+        for (col = 0; col < inPam.width; ++col) {
+            unsigned int plane;
+
+            for (plane = 0; plane < inPam.depth; ++plane) {
+                tuplerown[col][plane] =
+                    xformedSample(tuplerown[col][plane], solution._[plane]);
+            }
+        }
+        if (!cmdline.linear && !cmdline.fitbrightness)
+            pnm_gammarown(&inPam, tuplerown);
+
+        pnm_writepamrown(&outPam, tuplerown);
+    }
+    pnm_freepamrown(tuplerown);
+    pm_close(ifP);
+}
+
+
+
+static void
+freeCmdLineInfo(CmdlineInfo cmdline) {
+/*----------------------------------------------------------------------------
+  Free any memory that has been dynamically allcoated in <cmdline>.
+-----------------------------------------------------------------------------*/
+    TransSet * const xxP = &cmdline.xlats;
+
+    uint x;
+
+    for (x = 0; x < xxP->n; ++x) {
+        free(xxP->t[x].from);
+        free(xxP->t[x].to);
+    }
+}
+
+
+
+int main(int    argc, const char * argv[]) {
+
+    CmdlineInfo cmdline;
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    pamlevels(cmdline);
+
+    freeCmdLineInfo(cmdline);
+
+    return 0;
+}
+
+
+
diff --git a/editor/pammixmulti.c b/editor/pammixmulti.c
new file mode 100644
index 00000000..d063d96c
--- /dev/null
+++ b/editor/pammixmulti.c
@@ -0,0 +1,543 @@
+/*************************************************
+ * Blend multiple Netpbm files into a single one *
+ *                                               *
+ * By Scott Pakin <scott+pbm@pakin.org>          *
+ *************************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <float.h>
+#include <math.h>
+#include "pam.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+#include "nstring.h"
+
+typedef enum {
+    BLEND_AVERAGE,   /* Take the average color of all pixels */
+    BLEND_RANDOM,    /* Take each pixel color from a randomly selected image */
+    BLEND_MASK   /* Take each pixel color from the image indicated by a mask */
+} BlendType;
+
+static unsigned int const randSamples = 1024;
+    /* Random samples to draw per file */
+
+struct ProgramState {
+    unsigned int     inFileCt;      /* Number of input files */
+    struct pam *     inPam;         /* List of input-file PAM structures */
+    tuple **         inTupleRows;   /* Current row from each input file */
+    struct pam       outPam;        /* Output-file PAM structure */
+    tuple *          outTupleRow;   /* Row to write to the output file */
+    const char *     maskFileName;  /* Name of the image-mask file */
+    struct pam       maskPam;       /* PAM structure for the image mask */
+    tuple *          maskTupleRow;  /* Row to read from the mask file */
+    double           sigma;
+        /* Standard deviation when selecting images via a mask */
+    unsigned long ** imageWeights;
+        /* Per-image weights as a function of grayscale level */
+};
+
+
+
+struct CmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    BlendType        blend;
+    const char *     maskfile;
+    float            stdev;
+    unsigned int     randomseed;
+    unsigned int     randomseedSpec;
+    unsigned int     inFileNameCt;  /* Number of input files */
+    const char **    inFileName;    /* Name of each input file */
+};
+
+
+
+static void
+freeCmdline(struct CmdlineInfo * const cmdlineP) {
+
+    free(cmdlineP->inFileName);
+}
+
+
+
+static void
+parseCommandLine(int argc, const char ** argv,
+                 struct CmdlineInfo * const cmdlineP) {
+
+    optStruct3 opt;
+    unsigned int option_def_index = 0;
+    optEntry * option_def;
+    unsigned int blendSpec, maskfileSpec, stdevSpec;
+    const char * blendOpt;
+
+    /* Define the allowed command-line options. */
+    MALLOCARRAY(option_def, 100);
+    OPTENT3(0, "blend",     OPT_STRING,     &blendOpt,
+            &blendSpec,     0);
+    OPTENT3(0, "maskfile",  OPT_STRING,     &cmdlineP->maskfile,
+            &maskfileSpec,  0);
+    OPTENT3(0, "stdev",      OPT_FLOAT,     &cmdlineP->stdev,
+            &stdevSpec,     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 (blendSpec) {
+        if (streq(blendOpt, "average"))
+            cmdlineP->blend = BLEND_AVERAGE;
+        else if (streq(blendOpt, "random"))
+            cmdlineP->blend = BLEND_RANDOM;
+        else if (streq(blendOpt, "mask"))
+            cmdlineP->blend = BLEND_MASK;
+        else
+            pm_error("Unrecognized -blend value '%s'.  "
+                     "We recognize 'average', 'random', and 'mask'", blendOpt);
+    } else
+        cmdlineP->blend = BLEND_AVERAGE;
+
+    if (cmdlineP->blend == BLEND_MASK) {
+        if (!maskfileSpec)
+            pm_error("Because you specified -blend=mask, "
+                     "you must also specify -maskfile");
+    } else {
+        if (maskfileSpec)
+            pm_message("Ignoring -maskfile because -blend=mask "
+                       "is not specified");
+        if (stdevSpec)
+            pm_message("Ignoring -stdev because -blend=mask "
+                       "is not specified");
+    }
+    if (!stdevSpec)
+        cmdlineP->stdev = 0.25;
+
+    if (argc-1 < 1)
+        pm_error("You must specify the names of the files to blend together "
+                 "as arguments");
+
+    cmdlineP->inFileNameCt = argc-1;
+    MALLOCARRAY(cmdlineP->inFileName, argc-1);
+    if (!cmdlineP->inFileName)
+        pm_error("Unable to allocate space for %u file names", argc-1);
+    {
+        unsigned int i;
+        for (i = 0; i < argc-1; ++i)
+            cmdlineP->inFileName[i] = argv[1+i];
+    }
+    free(option_def);
+}
+
+static void
+openInputFiles(unsigned int          const inFileCt,
+               const char **         const inFileName,
+               struct ProgramState * const stateP) {
+/*----------------------------------------------------------------------------
+  Open all of the input files.
+
+  Abort if the input files don't all have the same size and format.
+-----------------------------------------------------------------------------*/
+    struct pam * inPam;
+    unsigned int i;
+
+    MALLOCARRAY(inPam, inFileCt);
+    if (!inPam)
+        pm_error("Failed to allocated memory for PAM structures for %u "
+                 "input files", inFileCt);
+    MALLOCARRAY(stateP->inTupleRows, inFileCt);
+    if (!stateP->inTupleRows)
+        pm_error("Failed to allocated memory for PAM structures for %u "
+                 "input rasters", inFileCt);
+
+    for (i = 0; i < inFileCt; ++i) {
+        FILE * const ifP = pm_openr(inFileName[i]);
+        pnm_readpaminit(ifP, &inPam[i], PAM_STRUCT_SIZE(tuple_type));
+        if (inPam[i].width != inPam[0].width ||
+            inPam[i].height != inPam[0].height)
+            pm_error("Input image %u has different dimensions from "
+                     "earlier input images", i);
+        if (inPam[i].depth != inPam[0].depth)
+            pm_error("Input image %u has different depth from "
+                     "earlier input images", i);
+        if (inPam[i].maxval != inPam[0].maxval)
+            pm_error("Input image %u has different maxval from "
+                     "earlier input images", i);
+        if (!streq(inPam[i].tuple_type, inPam[0].tuple_type))
+            pm_error("Input image %u has different tuple type from "
+                     "earlier input images", i);
+    }
+
+    for (i = 0; i < inFileCt; ++i)
+        stateP->inTupleRows[i] = pnm_allocpamrow(&inPam[i]);
+
+    stateP->inPam    = inPam;
+    stateP->inFileCt = inFileCt;
+}
+
+
+static void
+initMask(const char *          const maskFileName,
+         struct ProgramState * const stateP) {
+
+    struct pam * const maskPamP = &stateP->maskPam;
+
+    FILE * const mfP = pm_openr(maskFileName);
+
+    pnm_readpaminit(mfP, maskPamP, PAM_STRUCT_SIZE(tuple_type));
+
+    if (maskPamP->width != stateP->inPam[0].width ||
+        maskPamP->height != stateP->inPam[0].height) {
+
+        pm_error("The mask image does not have have the same dimensions "
+                 "as the input images");
+    }
+    if (maskPamP->depth > 1)
+        pm_message("Ignoring all but the first channel of the mask image");
+
+    stateP->maskTupleRow = pnm_allocpamrow(maskPamP);
+}
+
+
+
+static void
+termMask(struct ProgramState * const stateP) {
+
+    unsigned int i;
+
+    for (i = 0; i <= stateP->maskPam.maxval; ++i)
+        free(stateP->imageWeights[i]);
+
+    free(stateP->imageWeights);
+
+    pnm_freepamrow(stateP->maskTupleRow);
+
+    pm_close(stateP->maskPam.file);
+}
+
+
+
+static void
+initOutput(FILE *                const ofP,
+           struct ProgramState * const stateP) {
+
+    stateP->outPam      = stateP->inPam[0];
+    stateP->outPam.file = ofP;
+    stateP->outTupleRow = pnm_allocpamrow(&stateP->outPam);
+
+    pnm_writepaminit(&stateP->outPam);
+}
+
+
+
+static void
+blendTuplesRandom(struct ProgramState * const stateP,
+                  unsigned int          const col,
+                  sample *              const outSamps) {
+/*----------------------------------------------------------------------------
+  Blend one tuple of the input images into a new tuple by selecting a tuple
+  from a random input image.
+-----------------------------------------------------------------------------*/
+    unsigned int const depth = stateP->inPam[0].depth;
+    unsigned int const img = (unsigned int) (random() % stateP->inFileCt);
+
+    unsigned int samp;
+
+    for (samp = 0; samp < depth; ++samp)
+        outSamps[samp] = ((sample *)stateP->inTupleRows[img][col])[samp];
+}
+
+
+
+static void
+blendTuplesAverage(struct ProgramState * const stateP,
+                   unsigned int          const col,
+                   sample *              const outSamps) {
+/*----------------------------------------------------------------------------
+  Blend one tuple of the input images into a new tuple by averaging all input
+  tuples.
+-----------------------------------------------------------------------------*/
+    unsigned int const depth = stateP->inPam[0].depth;
+
+    unsigned int samp;
+
+    for (samp = 0; samp < depth; ++samp) {
+        unsigned int img;
+
+        for (img = 0, outSamps[samp] = 0; img < stateP->inFileCt; ++img)
+            outSamps[samp] += ((sample *)stateP->inTupleRows[img][col])[samp];
+        outSamps[samp] /= stateP->inFileCt;
+    }
+}
+
+
+
+static void
+randomNormal2(double * const r1P,
+              double * const r2P) {
+/*----------------------------------------------------------------------------
+  Return two normally distributed random numbers.
+-----------------------------------------------------------------------------*/
+    double u1, u2;
+
+    do {
+        u1 = drand48();
+        u2 = drand48();
+    }
+    while (u1 <= DBL_EPSILON);
+
+    *r1P = sqrt(-2.0*log(u1)) * cos(2.0*M_PI*u2);
+    *r2P = sqrt(-2.0*log(u1)) * sin(2.0*M_PI*u2);
+}
+
+
+
+static void
+precomputeImageWeights(struct ProgramState * const stateP,
+                       double                const sigma) {
+/*----------------------------------------------------------------------------
+  Precompute the weight to give to each image as a function of grayscale
+  level.
+-----------------------------------------------------------------------------*/
+    unsigned int const maxGray = (unsigned int) stateP->maskPam.maxval;
+
+    unsigned int i;
+
+    MALLOCARRAY(stateP->imageWeights, maxGray + 1);
+    if (!stateP->imageWeights)
+        pm_error("Unable to allocate memory for image weights for %u "
+                 "gray levels", maxGray);
+
+    for (i = 0; i <= maxGray; ++i) {
+        unsigned int j;
+        MALLOCARRAY(stateP->imageWeights[i], stateP->inFileCt);
+        if (!stateP->imageWeights[i])
+            pm_error("Unable to allocate memory for image weights for %u "
+                     "images for gray level %u", stateP->inFileCt, i);
+        for (j = 0; j < stateP->inFileCt; ++j)
+            stateP->imageWeights[i][j] = 0;
+    }
+
+    /* Populate the image-weight arrays. */
+    for (i = 0; i <= maxGray; ++i) {
+        double const pctGray = i / (double)maxGray;
+
+        unsigned int j;
+
+        for (j = 0; j < stateP->inFileCt * randSamples; ) {
+            double r[2];
+            unsigned int k;
+
+            randomNormal2(&r[0], &r[1]);
+            for (k = 0; k < 2; ++k) {
+                int const img =
+                    r[k] * sigma + pctGray * stateP->inFileCt * 0.999999;
+                    /* Scale [0, 1] to [0, 1) (sort of). */
+                if (img >= 0 && img < (int)stateP->inFileCt) {
+                    ++stateP->imageWeights[i][img];
+                    ++j;
+                }
+            }
+        }
+    }
+}
+
+
+
+static void
+blendTuplesMask(struct ProgramState * const stateP,
+                unsigned int          const col,
+                sample *              const outSamps) {
+/*----------------------------------------------------------------------------
+  Blend one tuple of the input images into a new tuple according to the gray
+  levels specified in a mask file.
+-----------------------------------------------------------------------------*/
+    unsigned int const depth = stateP->inPam[0].depth;
+    sample const grayLevel = ((sample *)stateP->maskTupleRow[col])[0];
+
+    unsigned int img;
+
+    /* Initialize outSamps[] to zeroes */
+    {
+        unsigned int samp;
+
+        for (samp = 0; samp < depth; ++samp)
+            outSamps[samp] = 0;
+    }
+
+    /* Accumulate to outSamps[] */
+    for (img = 0; img < stateP->inFileCt; ++img) {
+        unsigned long weight = stateP->imageWeights[grayLevel][img];
+
+        if (weight != 0) {
+            unsigned int samp;
+
+            for (samp = 0; samp < depth; ++samp)
+                outSamps[samp] +=
+                    ((sample *)stateP->inTupleRows[img][col])[samp] * weight;
+        }
+    }
+    /* Scale all outSamps[] */
+    {
+        unsigned int samp;
+
+        for (samp = 0; samp < depth; ++samp)
+            outSamps[samp] /= randSamples * stateP->inFileCt;
+    }
+}
+
+
+
+static void
+blendImageRow(BlendType             const blend,
+              struct ProgramState * const stateP) {
+/*----------------------------------------------------------------------------
+  Blend one row of input images into a new row.
+-----------------------------------------------------------------------------*/
+    unsigned int const width = stateP->inPam[0].width;
+
+    unsigned int col;
+
+    for (col = 0; col < width; ++col) {
+        sample * const outSamps = stateP->outTupleRow[col];
+
+        switch (blend) {
+        case BLEND_RANDOM:
+            /* Take each pixel from a different, randomly selected image. */
+            blendTuplesRandom(stateP, col, outSamps);
+            break;
+
+        case BLEND_AVERAGE:
+            /* Average each sample across all the images. */
+            blendTuplesAverage(stateP, col, outSamps);
+            break;
+
+        case BLEND_MASK:
+            /* Take each pixel from the image specified by the mask image. */
+            blendTuplesMask(stateP, col, outSamps);
+            break;
+        }
+    }
+}
+
+
+
+static void
+blendImages(BlendType             const blend,
+            struct ProgramState * const stateP) {
+/*----------------------------------------------------------------------------
+  Blend the images row-by-row into a new image.
+-----------------------------------------------------------------------------*/
+    unsigned int const nRows = stateP->inPam[0].height;
+
+    unsigned int row;
+
+    for (row = 0; row < nRows; ++row) {
+        unsigned int img;
+
+        for (img = 0; img < stateP->inFileCt; ++img)
+            pnm_readpamrow(&stateP->inPam[img], stateP->inTupleRows[img]);
+
+        if (blend == BLEND_MASK)
+            pnm_readpamrow(&stateP->maskPam, stateP->maskTupleRow);
+
+        blendImageRow(blend, stateP);
+
+        pnm_writepamrow(&stateP->outPam, stateP->outTupleRow);
+    }
+}
+
+
+
+static void
+termState(struct ProgramState * const stateP) {
+/*----------------------------------------------------------------------------
+  Deallocate all of the resources we allocated.
+-----------------------------------------------------------------------------*/
+    unsigned int i;
+
+    for (i = 0; i < stateP->inFileCt; ++i) {
+        pnm_freepamrow(stateP->inTupleRows[i]);
+        pm_close(stateP->inPam[i].file);
+    }
+
+    free(stateP->outTupleRow);
+    free(stateP->inTupleRows);
+    free(stateP->inPam);
+    pm_close(stateP->outPam.file);
+}
+
+
+
+int
+main(int argc, const char * argv[]) {
+
+    struct CmdlineInfo cmdline;
+    struct ProgramState state;
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    srand(cmdline.randomseedSpec ? cmdline.randomseed : pm_randseed());
+
+    openInputFiles(cmdline.inFileNameCt, cmdline.inFileName, &state);
+
+    if (cmdline.blend == BLEND_MASK)
+        initMask(cmdline.maskfile, &state);
+
+    initOutput(stdout, &state);
+
+    if (cmdline.blend == BLEND_MASK)
+        precomputeImageWeights(&state, cmdline.stdev);
+
+    blendImages(cmdline.blend, &state);
+
+    if (cmdline.blend == BLEND_MASK)
+        termMask(&state);
+
+    termState(&state);
+
+    freeCmdline(&cmdline);
+
+    return 0;
+}
+
+
+/*  COPYRIGHT LICENSE and WARRANTY DISCLAIMER
+
+Copyright (c) 2018 Scott Pakin
+All rights reserved
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted (subject to the limitations in the disclaimer
+below) provided that the following conditions are met:
+
+  Redistributions of source code must retain the above copyright notice,
+  this list of conditions and the following disclaimer.
+
+  Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+
+  Neither the names of the oopyright owners nor the names of its contributors
+  may be used to endorse or promote products derived from this software
+  without specific prior written permission.
+
+  NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY
+  THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+  CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
+  NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+  PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
diff --git a/editor/pamperspective.c b/editor/pamperspective.c
index 16715c2e..a206b57f 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 _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE   /* Make sure strdup is int string.h */
 
diff --git a/editor/pamrecolor.c b/editor/pamrecolor.c
index 6937fd8d..8c5bce12 100644
--- a/editor/pamrecolor.c
+++ b/editor/pamrecolor.c
@@ -404,7 +404,7 @@ parseCommandLine(int argc, const char ** const argv,
             cmdlineP->color2gray.rfrac +
             cmdlineP->color2gray.gfrac +
             cmdlineP->color2gray.bfrac;
-        if (fabsf(1.0 - maxLuminance) > REAL_EPSILON)
+        if (fabsf(1.0f - maxLuminance) > REAL_EPSILON)
             pm_error("The values given for --rmult, --gmult, and --bmult must "
                      "sum to 1.0, not %.10g", maxLuminance);
     } else if (csSpec)
diff --git a/editor/pamrubber.c b/editor/pamrubber.c
index 4378c340..e7abd789 100644
--- a/editor/pamrubber.c
+++ b/editor/pamrubber.c
@@ -1405,7 +1405,7 @@ main(int argc, const char ** const argv) {
 
     setGlobalCP(cmdline);
 
-    srand(cmdline.randseedSpec ? cmdline.randseed : time(NULL));
+    srand(cmdline.randseedSpec ? cmdline.randseed : pm_randseed());
 
     ifP = pm_openr(cmdline.fileName);
 
diff --git a/editor/pamscale.c b/editor/pamscale.c
index 7c6ee256..d8436689 100644
--- a/editor/pamscale.c
+++ b/editor/pamscale.c
@@ -11,9 +11,9 @@
    people contributed code changes over the years.
 
    Copyright (C) 2003 by Michael Reinelt <reinelt@eunet.at>
-  
+
    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
@@ -22,8 +22,9 @@
    implied warranty.
 */
 
-#define _XOPEN_SOURCE   /* get M_PI in math.h */
+#define _XOPEN_SOURCE 500  /* get M_PI in math.h */
 
+#include <stdbool.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <math.h>
@@ -46,7 +47,7 @@
 ** (sinc, bessel) are IIR (infinite impulse respone).
 ** They should be windowed with hanning, hamming, blackman or
 ** kaiser window.
-** For sinc and bessel the blackman window will be used per default.
+** For sinc and bessel the blackman window will be used by default.
 */
 
 #define EPSILON 1e-7
@@ -54,14 +55,14 @@
 
 
 /* x^2 and x^3 helper functions */
-static __inline__ double 
+static __inline__ double
 pow2(double const x) {
     return x * x;
 }
 
 
 
-static __inline__ double 
+static __inline__ double
 pow3(double const x) {
     return x * x * x;
 }
@@ -69,13 +70,13 @@ pow3(double const x) {
 
 
 /* box, pulse, Fourier window, */
-/* box function also know as rectangle function */
+/* box function also known as rectangle function */
 /* 1st order (constant) b-spline */
 
 #define radius_point (0.0)
 #define radius_box (0.5)
 
-static double 
+static double
 filter_box(double const x) {
 
     double const absx = x < 0.0 ? -x : x;
@@ -91,7 +92,7 @@ filter_box(double const x) {
 
 #define radius_triangle (1.0)
 
-static double 
+static double
 filter_triangle(double const x) {
 
     double const absx = x < 0.0 ? -x : x;
@@ -105,7 +106,7 @@ filter_triangle(double const x) {
 
 #define radius_quadratic (1.5)
 
-static double 
+static double
 filter_quadratic(double const x) {
 
     double const absx = x < 0.0 ? -x : x;
@@ -122,7 +123,7 @@ filter_quadratic(double const x) {
 
 #define radius_cubic (2.0)
 
-static double 
+static double
 filter_cubic(double const x) {
 
     double const absx = x < 0.0 ? -x : x;
@@ -139,7 +140,7 @@ filter_cubic(double const x) {
 
 #define radius_catrom (2.0)
 
-static double 
+static double
 filter_catrom(double const x) {
 
     double const absx = x < 0.0 ? -x : x;
@@ -158,7 +159,7 @@ filter_catrom(double const x) {
 
 #define radius_mitchell (2.0)
 
-static double 
+static double
 filter_mitchell(double x)
 {
 
@@ -187,7 +188,7 @@ filter_mitchell(double x)
 
 #define radius_gauss (1.25)
 
-static double 
+static double
 filter_gauss(double const x) {
 
     return exp(-2.0*pow2(x)) * sqrt(2.0/M_PI);
@@ -199,14 +200,14 @@ filter_gauss(double const x) {
 
 #define radius_sinc (4.0)
 
-static double 
+static double
 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).
     */
-    return 
+    return
         x == 0.0 ? 1.0 :
         sin(M_PI*x)/(M_PI*x);
 }
@@ -218,10 +219,10 @@ filter_sinc(double const x) {
 
 #define radius_bessel (3.2383)
 
-static double 
+static double
 filter_bessel(double const x) {
 
-    return 
+    return
         x == 0.0 ? M_PI/4.0 :
         j1(M_PI * x) / (2.0 * x);
 }
@@ -232,7 +233,7 @@ filter_bessel(double const x) {
 
 #define radius_hanning (1.0)
 
-static double 
+static double
 filter_hanning(double const x) {
 
     return 0.5 * cos(M_PI * x) + 0.5;
@@ -244,7 +245,7 @@ filter_hanning(double const x) {
 
 #define radius_hamming (1.0)
 
-static double 
+static double
 filter_hamming(double const x) {
     return 0.46 * cos(M_PI * x) + 0.54;
 }
@@ -255,7 +256,7 @@ filter_hamming(double const x) {
 
 #define radius_blackman (1.0)
 
-static double 
+static double
 filter_blackman(double const x) {
     return 0.5 * cos(M_PI * x) + 0.08 * cos(2.0 * M_PI * x) + 0.42;
 }
@@ -268,12 +269,12 @@ filter_blackman(double const x) {
 #define radius_kaiser (1.0)
 
 /* modified zeroth order Bessel function of the first kind. */
-static double 
+static double
 bessel_i0(double const x) {
-  
+
     int i;
     double sum, y, t;
-  
+
     sum = 1.0;
     y = pow2(x)/4.0;
     t = y;
@@ -286,15 +287,15 @@ bessel_i0(double const x) {
 
 
 
-static double 
+static double
 filter_kaiser(double const x) {
     /* typically 4 < a < 9 */
     /* param a trades off main lobe width (sharpness) */
     /* for side lobe amplitude (ringing) */
-  
+
     double const a   = 6.5;
     double const i0a = 1.0/bessel_i0(a);
-  
+
     return i0a * bessel_i0(a * sqrt(1.0-pow2(x)));
 }
 
@@ -305,7 +306,7 @@ filter_kaiser(double const x) {
 
 #define radius_normal (1.0)
 
-static double 
+static double
 filter_normal(double const x) {
     return exp(-pow2(x)/2.0) / sqrt(2.0*M_PI);
 }
@@ -316,7 +317,7 @@ filter_normal(double const x) {
 
 #define radius_hermite  (1.0)
 
-static double 
+static double
 filter_hermite(double const x) {
     /* f(x) = 2|x|^3 - 3|x|^2 + 1, -1 <= x <= 1 */
 
@@ -333,7 +334,7 @@ filter_hermite(double const x) {
 
 #define radius_lanczos (3.0)
 
-static double 
+static double
 filter_lanczos(double const x) {
 
     double const absx = x < 0.0 ? -x : x;
@@ -352,30 +353,30 @@ typedef struct {
         /* This is how far from the Y axis (on either side) the
            function has significant value.  (You can use this to limit
            how much of your domain you bother to compute the function
-           over).  
+           over).
         */
     bool windowed;
 } filter;
 
 
 static filter Filters[] = {
-    { "point",     filter_box,       radius_point,     FALSE },
-    { "box",       filter_box,       radius_box,       FALSE },
-    { "triangle",  filter_triangle,  radius_triangle,  FALSE },
-    { "quadratic", filter_quadratic, radius_quadratic, FALSE },
-    { "cubic",     filter_cubic,     radius_cubic,     FALSE },
-    { "catrom",    filter_catrom,    radius_catrom,    FALSE },
-    { "mitchell",  filter_mitchell,  radius_mitchell,  FALSE },
-    { "gauss",     filter_gauss,     radius_gauss,     FALSE },
-    { "sinc",      filter_sinc,      radius_sinc,      TRUE  },
-    { "bessel",    filter_bessel,    radius_bessel,    TRUE  },
-    { "hanning",   filter_hanning,   radius_hanning,   FALSE },
-    { "hamming",   filter_hamming,   radius_hamming,   FALSE },
-    { "blackman",  filter_blackman,  radius_blackman,  FALSE },
-    { "kaiser",    filter_kaiser,    radius_kaiser,    FALSE },
-    { "normal",    filter_normal,    radius_normal,    FALSE },
-    { "hermite",   filter_hermite,   radius_hermite,   FALSE },
-    { "lanczos",   filter_lanczos,   radius_lanczos,   FALSE },
+    { "point",     filter_box,       radius_point,     false },
+    { "box",       filter_box,       radius_box,       false },
+    { "triangle",  filter_triangle,  radius_triangle,  false },
+    { "quadratic", filter_quadratic, radius_quadratic, false },
+    { "cubic",     filter_cubic,     radius_cubic,     false },
+    { "catrom",    filter_catrom,    radius_catrom,    false },
+    { "mitchell",  filter_mitchell,  radius_mitchell,  false },
+    { "gauss",     filter_gauss,     radius_gauss,     false },
+    { "sinc",      filter_sinc,      radius_sinc,      true  },
+    { "bessel",    filter_bessel,    radius_bessel,    true  },
+    { "hanning",   filter_hanning,   radius_hanning,   false },
+    { "hamming",   filter_hamming,   radius_hamming,   false },
+    { "blackman",  filter_blackman,  radius_blackman,  false },
+    { "kaiser",    filter_kaiser,    radius_kaiser,    false },
+    { "normal",    filter_normal,    radius_normal,    false },
+    { "hermite",   filter_hermite,   radius_hermite,   false },
+    { "lanczos",   filter_lanczos,   radius_lanczos,   false },
    { NULL },
 };
 
@@ -416,29 +417,30 @@ struct CmdlineInfo {
      * in a form easy for the program to use.
      */
     const char * inputFileName;  /* Filespec of input file */
+    unsigned int reportonly;
     unsigned int nomix;
     basicFunction_t filterFunction; /* NULL if not using resample method */
     basicFunction_t windowFunction;
         /* Meaningful only when filterFunction != NULL */
-    double filterRadius;           
+    double filterRadius;
         /* Meaningful only when filterFunction != NULL */
     enum scaleType scaleType;
     /* 'xsize' and 'ysize' are numbers of pixels.  Their meaning depends upon
-       'scaleType'.  for SCALE_BOXFIT and SCALE_BOXFILL, they are the box 
+       'scaleType'.  for SCALE_BOXFIT and SCALE_BOXFILL, they are the box
        dimensions.  For SCALE_SEPARATE, they are the separate dimensions, or
        zero to indicate unspecified.  For SCALE_PIXELMAX, they are
        meaningless.
     */
     unsigned int xsize;
     unsigned int ysize;
-    /* 'xscale' and 'yscale' are meaningful only for scaleType == 
+    /* 'xscale' and 'yscale' are meaningful only for scaleType ==
        SCALE_SEPARATE and only where the corresponding xsize/ysize is
        unspecified.  0.0 means unspecified.
     */
     float xscale;
     float yscale;
     /* 'pixels' is meaningful only for scaleType == SCALE_PIXELMAX */
-    unsigned int pixels; 
+    unsigned int pixels;
     unsigned int linear;
     unsigned int verbose;
 };
@@ -452,12 +454,12 @@ lookupFilterByName(const char * const filtername,
     unsigned int i;
     bool found;
 
-    found = FALSE;  /* initial assumption */
+    found = false;  /* initial assumption */
 
     for (i=0; Filters[i].name; ++i) {
         if (strcmp(filtername, Filters[i].name) == 0) {
             *filterP = Filters[i];
-            found = TRUE;
+            found = true;
         }
     }
     if (!found) {
@@ -466,7 +468,7 @@ lookupFilterByName(const char * const filtername,
         strcpy(known_filters, "");
         for (i = 0; Filters[i].name; ++i) {
             const char * const name = Filters[i].name;
-            if (strlen(known_filters) + strlen(name) + 1 + 1 < 
+            if (strlen(known_filters) + strlen(name) + 1 + 1 <
                 sizeof(known_filters)) {
                 strcat(known_filters, name);
                 strcat(known_filters, " ");
@@ -489,13 +491,13 @@ processFilterOptions(unsigned int const         filterSpec,
     if (filterSpec) {
         filter baseFilter;
         lookupFilterByName(filterOpt, &baseFilter);
-        cmdlineP->filterFunction = baseFilter.function; 
+        cmdlineP->filterFunction = baseFilter.function;
         cmdlineP->filterRadius   = baseFilter.radius;
 
         if (windowSpec) {
             filter windowFilter;
             lookupFilterByName(windowOpt, &windowFilter);
-            
+
             if (cmdlineP->windowFunction == filter_box)
                 cmdlineP->windowFunction = NULL;
             else
@@ -526,28 +528,28 @@ parseSizeParm(const char *   const sizeString,
 
     sizeLong = strtol(sizeString, &endptr, 10);
     if (strlen(sizeString) > 0 && *endptr != '\0')
-        pm_error("%s size argument not an integer: '%s'", 
+        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", 
+                 "for computations: %ld",
                  description, sizeLong);
     else if (sizeLong <= 0)
-        pm_error("%s size argument is not positive: %ld", 
+        pm_error("%s size argument is not positive: %ld",
                  description, sizeLong);
     else
         *sizeP = (unsigned int) sizeLong;
-}        
+}
 
 
 
 static void
-parseXyParms(int                  const argc, 
+parseXyParms(int                  const argc,
              const char **        const argv,
              struct CmdlineInfo * const cmdlineP) {
 
     /* parameters are box width (columns), box height (rows), and
-       optional filespec 
+       optional filespec
     */
     if (argc-1 < 2)
         pm_error("You must supply at least two parameters with "
@@ -571,7 +573,7 @@ parseXyParms(int                  const argc,
 
 
 static void
-parseScaleParms(int                   const argc, 
+parseScaleParms(int                   const argc,
                 const char **         const argv,
                 struct CmdlineInfo  * const cmdlineP) {
 /*----------------------------------------------------------------------------
@@ -583,7 +585,7 @@ parseScaleParms(int                   const argc,
                  "one parameter: the scale factor.");
     else {
         cmdlineP->xscale = cmdlineP->yscale = atof(argv[1]);
-        
+
         if (cmdlineP->xscale <= 0.0)
             pm_error("The scale parameter %s is not a positive number.",
                      argv[1]);
@@ -592,7 +594,7 @@ parseScaleParms(int                   const argc,
                 cmdlineP->inputFileName = "-";
             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: "
@@ -606,7 +608,7 @@ parseScaleParms(int                   const argc,
 
 
 static void
-parseFilespecOnlyParms(int                   const argc, 
+parseFilespecOnlyParms(int                   const argc,
                        const char **         const argv,
                        struct CmdlineInfo  * const cmdlineP) {
 
@@ -618,13 +620,13 @@ parseFilespecOnlyParms(int                   const argc,
 }
 
 
-static void 
-parseCommandLine(int argc, 
-                 const char ** argv, 
+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.  
+   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.
@@ -635,13 +637,14 @@ parseCommandLine(int argc,
     optEntry * option_def;
     optStruct3 opt;
         /* Instructions to pm_optParseOptions3 on how to parse our options. */
-  
+
     unsigned int option_def_index;
     unsigned int xyfit, xyfill;
     int xsize, ysize, pixels;
     int reduce;
     float xscale, yscale;
-    const char *filterOpt, *window;
+    const char * filterOpt;
+    const char * window;
     unsigned int filterSpec, windowSpec;
     unsigned int xscaleSpec, yscaleSpec, xsizeSpec, ysizeSpec;
     unsigned int pixelsSpec, reduceSpec;
@@ -665,15 +668,17 @@ parseCommandLine(int argc,
     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, "reportonly",        OPT_FLAG,    NULL,
+            &cmdlineP->reportonly,    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 */
+    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 (cmdlineP->nomix && filterSpec) 
+    if (cmdlineP->nomix && filterSpec)
         pm_error("You cannot specify both -nomix and -filter.");
 
     processFilterOptions(filterSpec, filterOpt, windowSpec, window,
@@ -694,19 +699,19 @@ parseCommandLine(int argc,
         pm_error("Cannot specify both -xsize/width and -xscale.");
     if (ysizeSpec && yscaleSpec)
         pm_error("Cannot specify both -ysize/height and -yscale.");
-    
+
     if ((xyfit || xyfill) &&
-        (xsizeSpec || xscaleSpec || ysizeSpec || yscaleSpec || 
+        (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 (pixelsSpec && 
+    if (pixelsSpec &&
         (xsizeSpec || xscaleSpec || ysizeSpec || yscaleSpec ||
          reduceSpec) )
         pm_error("Cannot specify -pixels with other dimension options.");
-    if (reduceSpec && 
+    if (reduceSpec &&
         (xsizeSpec || xscaleSpec || ysizeSpec || yscaleSpec) )
         pm_error("Cannot specify -reduce with other dimension options.");
 
@@ -722,9 +727,9 @@ parseCommandLine(int argc,
         cmdlineP->scaleType = SCALE_SEPARATE;
         parseFilespecOnlyParms(argc, argv, cmdlineP);
         cmdlineP->xsize = cmdlineP->ysize = 0;
-        cmdlineP->xscale = cmdlineP->yscale = 
+        cmdlineP->xscale = cmdlineP->yscale =
             ((double) 1.0) / ((double) reduce);
-        pm_message("reducing by %d gives scale factor of %f.", 
+        pm_message("reducing by %d gives scale factor of %f.",
                    reduce, cmdlineP->xscale);
     } else if (pixelsSpec) {
         cmdlineP->scaleType = SCALE_PIXELMAX;
@@ -746,12 +751,12 @@ parseCommandLine(int argc,
 
 
 
-static void 
-computeOutputDimensions(struct CmdlineInfo  const cmdline, 
-                        unsigned int        const cols, 
-                        unsigned int        const rows, 
+static void
+computeOutputDimensions(struct CmdlineInfo  const cmdline,
+                        unsigned int        const cols,
+                        unsigned int        const rows,
                         int *               const newcolsP,
-                        int *               const newrowsP) { 
+                        int *               const newrowsP) {
 
     double newcolsD, newrowsD;
         /* Intermediate calculation of the output dimensions, in double
@@ -775,10 +780,10 @@ computeOutputDimensions(struct CmdlineInfo  const cmdline,
     case SCALE_BOXFIT:
     case SCALE_BOXFILL: {
         double const aspect_ratio = (float) cols / (float) rows;
-        double const box_aspect_ratio = 
+        double const box_aspect_ratio =
             (float) cmdline.xsize / (float) cmdline.ysize;
-        
-        if ((box_aspect_ratio > aspect_ratio && 
+
+        if ((box_aspect_ratio > aspect_ratio &&
              cmdline.scaleType == SCALE_BOXFIT) ||
             (box_aspect_ratio < aspect_ratio &&
              cmdline.scaleType == SCALE_BOXFILL)) {
@@ -809,7 +814,7 @@ computeOutputDimensions(struct CmdlineInfo  const cmdline,
             newrowsD = rows;
     }
     }
-    
+
     /* 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
@@ -898,9 +903,9 @@ typedef struct {
            at index 0.
         */
     unsigned int allocWeight;
-        /* Number of allocated frames in 'Weight' */ 
+        /* Number of allocated frames in 'Weight' */
     WEIGHT *Weight;
-        /* The terms of the linear combination.  Has 'nWeight' elements. 
+        /* The terms of the linear combination.  Has 'nWeight' elements.
            The coefficients (weights) of each add up to unity.
         */
 } WLIST;
@@ -911,7 +916,7 @@ typedef struct {
         /* The row number in the input image of the row.
            -1 means no row.
         */
-    tuple *tuplerow;  
+    tuple *tuplerow;
         /* The tuples of the row.
            If rowNumber = -1, these are arbitrary, but allocated, tuples.
         */
@@ -957,7 +962,7 @@ appendWeight(WLIST * const WList,
         unsigned int const n = WList->nWeight;
 
         assert(WList->allocWeight >= n+1);
-        
+
         WList->Weight[n].position = index;
         WList->Weight[n].weight   = weight;
         ++WList->nWeight;
@@ -977,7 +982,7 @@ unnormalize(double const normalized,
        wrong thing in actual practice, EG on Darwin PowerPC (my iBook
        running OS X) negative values clamp to maxval.  We get negative
        values because some of the filters (EG catrom) have negative
-       weights.  
+       weights.
     */
 
     return MIN(maxval, (sample)(MAX(0.0, (normalized*maxval + 0.5))));
@@ -1007,10 +1012,10 @@ createWeightList(unsigned int          const targetPos,
                  WLIST *               const weightListP) {
 /*----------------------------------------------------------------------------
    Create a weight list for computing target pixel number 'targetPos' from
-   a set of source pixels.  These pixels are a line of pixels either 
+   a set of source pixels.  These pixels are a line of pixels either
    horizontally or vertically.  The weight list is a list of weights to give
    each source pixel in the set.
-   
+
    The source pixel set is a window of source pixels centered on some
    point.  The weights are defined by the function 'filter' of
    the position within the window, and normalized to add up to 1.0.
@@ -1034,7 +1039,7 @@ createWeightList(unsigned int          const targetPos,
 
    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
-   height of the filter function over each source pixel region.  But 
+   height of the filter function over each source pixel region.  But
    that's too hard.  So we approximate by assuming that the filter function
    is constant within each region, at the value the function has at the
    _center_ of the region.
@@ -1064,12 +1069,12 @@ createWeightList(unsigned int          const targetPos,
        'leftPixel' and 'rightPixel' are the pixel positions of the
        pixels at the edges of that window.  Note that if we're
        doing vertical weights, "left" and "right" mean top and
-       bottom.  
+       bottom.
     */
     double const windowCenter = ((double)targetPos + 0.5) / scale;
     double left = MAX(0.0, windowCenter - filter.radius - EPSILON);
     unsigned int const leftPixel = floor(left);
-    double right = MIN((double)sourceSize - EPSILON, 
+    double right = MIN((double)sourceSize - EPSILON,
                        windowCenter + filter.radius + EPSILON);
     unsigned int const rightPixel = floor(right);
 
@@ -1082,7 +1087,7 @@ createWeightList(unsigned int          const targetPos,
     norm = 0.0;  /* initial value */
 
     for (j = leftPixel; j <= rightPixel; ++j) {
-        /* Calculate the weight that source pixel 'j' will have in the 
+        /* Calculate the weight that source pixel 'j' will have in the
            value of target pixel 'targetPos'.
         */
         double const regionLeft   = MAX(left, (double)j);
@@ -1132,7 +1137,7 @@ createWeightListSet(unsigned int          const sourceSize,
    pixels in a region to effect a resampling.  Multiplying by these
    factors effects all of the following transformations on the
    original pixels:
-   
+
    1) Filter out any frequencies that are artifacts of the
       original sampling.  We assume a perfect sampling was done,
       which means the original continuous dataset had a maximum
@@ -1140,12 +1145,12 @@ createWeightListSet(unsigned int          const sourceSize,
       above that is an artifact of the sampling.  So we filter out
       anything above 1/2 of the original sample rate (sample rate
       == pixel resolution).
-      
+
    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 because of low
       sample rate.
-      
+
    3) Sample the result at the new sample rate.
 
    We do all three of these steps in a single convolution of the
@@ -1154,7 +1159,7 @@ createWeightListSet(unsigned int          const sourceSize,
    rectangle function is a pixel domain sinc function, which is
    what we assume 'filterFunction' is.  We get Step 3 by computing
    the convolution only at the new sample points.
-   
+
    I don't know what any of this means when 'filterFunction' is
    not sinc.  Maybe it just means some approximation or additional
    filtering steps are happening.
@@ -1164,7 +1169,7 @@ createWeightListSet(unsigned int          const sourceSize,
     unsigned int targetPos;
 
     MALLOCARRAY_NOFAIL(weightListSet, targetSize);
-    
+
     for (targetPos = 0; targetPos < targetSize; ++targetPos)
         createWeightList(targetPos, sourceSize, scale, filterFunction,
                          &weightListSet[targetPos]);
@@ -1210,7 +1215,7 @@ makeFilterFunction(double          const scale,
 
     retval.basicFunction = basicFunction;
     retval.windowFunction = windowFunction;
-    
+
     retval.horizontalScaler = freqLimit;
 
     /* Our 'windowFunction' argument is a function normalized to the
@@ -1235,11 +1240,11 @@ makeFilterFunction(double          const scale,
 
     return retval;
 }
-                   
 
-                   
+
+
 static void
-destroyWeightListSet(WLIST *      const weightListSet, 
+destroyWeightListSet(WLIST *      const weightListSet,
                      unsigned int const size) {
 
     unsigned int i;
@@ -1264,14 +1269,14 @@ createScanBuf(struct pam * const pamP,
     scanbuf.width = pamP->width;
     scanbuf.height = maxRowWeights;
     MALLOCARRAY_NOFAIL(scanbuf.line, scanbuf.height);
-  
+
     for (lineNumber = 0; lineNumber < scanbuf.height; ++lineNumber) {
         scanbuf.line[lineNumber].rowNumber = -1;
         scanbuf.line[lineNumber].tuplerow = pnm_allocpamrow(pamP);
     }
-  
+
     if (verbose)
-        pm_message("scanline buffer: %d lines of %d pixels", 
+        pm_message("scanline buffer: %d lines of %d pixels",
                    scanbuf.height, scanbuf.width);
 
     *scanbufP = scanbuf;
@@ -1296,10 +1301,10 @@ static void
 resampleDimensionMessage(struct pam * const inpamP,
                          struct pam * const outpamP) {
 
-    pm_message ("resampling from %d*%d to %d*%d (%f, %f)", 
-                inpamP->width, inpamP->height, 
+    pm_message ("resampling from %d*%d to %d*%d (%f, %f)",
+                inpamP->width, inpamP->height,
                 outpamP->width, outpamP->height,
-                (double)outpamP->width/inpamP->width, 
+                (double)outpamP->width/inpamP->width,
                 (double)outpamP->height/inpamP->height);
 }
 
@@ -1325,12 +1330,12 @@ addInPixel(const struct pam * const pamP,
     for (plane = 0; plane < pamP->depth; ++plane) {
         double const normalizedSample = (double)tuple[plane]/pamP->maxval;
         double opacityAdjustment;
-        
+
         if (haveOpacity && plane != opacityPlane)
             opacityAdjustment = (double)tuple[opacityPlane]/pamP->maxval;
         else
             opacityAdjustment = 1;
-        
+
         accum[plane] += opacityAdjustment * normalizedSample * weight;
     }
 }
@@ -1340,7 +1345,7 @@ addInPixel(const struct pam * const pamP,
 static void
 generateOutputTuple(const struct pam * const pamP,
                     double             const accum[],
-                    bool               const haveOpacity, 
+                    bool               const haveOpacity,
                     unsigned int       const opacityPlane,
                     tuple *            const tupleP) {
 /*----------------------------------------------------------------------------
@@ -1358,7 +1363,7 @@ generateOutputTuple(const struct pam * const pamP,
         if (haveOpacity && plane != opacityPlane) {
             if (accum[opacityPlane] < EPSILON) {
                 opacityAdjustedSample = 0.0;
-            } else 
+            } else
                 opacityAdjustedSample = accum[plane] / accum[opacityPlane];
         } else
             opacityAdjustedSample = accum[plane];
@@ -1377,7 +1382,7 @@ outputOneResampledRow(const struct pam * const outpamP,
                       tuple *            const line,
                       double *           const accum) {
 /*----------------------------------------------------------------------------
-   From the data in 'scanbuf' and weights in 'YW' and 'XWeight', 
+   From the data in 'scanbuf' and weights in 'YW' and 'XWeight',
    generate one output row for the image described by *outpamP and
    output it.
 
@@ -1411,24 +1416,24 @@ outputOneResampledRow(const struct pam * const outpamP,
             for (plane = 0; plane < outpamP->depth; ++plane)
                 accum[plane] = 0.0;
         }
-        
+
         for (i = 0; i < YW.nWeight; ++i) {
             int   const yp   = YW.Weight[i].position;
             float const yw   = YW.Weight[i].weight;
             int   const slot = yp % scanbuf.height;
 
             unsigned int j;
-            
+
             for (j = 0; j < XW.nWeight; ++j) {
                 int   const xp    = XW.Weight[j].position;
                 tuple const tuple = scanbuf.line[slot].tuplerow[xp];
-                
-                addInPixel(outpamP, tuple, yw * XW.Weight[j].weight, 
+
+                addInPixel(outpamP, tuple, yw * XW.Weight[j].weight,
                            haveOpacity, opacityPlane,
                            accum);
             }
         }
-        generateOutputTuple(outpamP, accum, haveOpacity, opacityPlane, 
+        generateOutputTuple(outpamP, accum, haveOpacity, opacityPlane,
                             &line[col]);
     }
     pnm_writepamrow(outpamP, line);
@@ -1440,15 +1445,15 @@ static bool
 scanbufContainsTheRows(SCAN  const scanbuf,
                        WLIST const rowWeights) {
 /*----------------------------------------------------------------------------
-   Return TRUE iff scanbuf 'scanbuf' contains every row mentioned in
+   Return true iff scanbuf 'scanbuf' contains every row mentioned in
    'rowWeights'.
 
    It might contain additional rows besides.
 -----------------------------------------------------------------------------*/
     bool missingRow;
     unsigned int i;
-    
-    for (i = 0, missingRow = FALSE;
+
+    for (i = 0, missingRow = false;
          i < rowWeights.nWeight && !missingRow;
         ++i) {
         unsigned int const inputRow = rowWeights.Weight[i].position;
@@ -1461,7 +1466,7 @@ scanbufContainsTheRows(SCAN  const scanbuf,
             /* Nope, this slot has some other row or no row at all.
                So the row we're looking for isn't in the scanbuf.
             */
-            missingRow = TRUE;
+            missingRow = true;
         }
     }
     return !missingRow;
@@ -1481,7 +1486,7 @@ createWeightLists(struct pam *     const inpamP,
 /*----------------------------------------------------------------------------
    This is the function that actually does the resampling.  Note that it
    does it without ever looking at the source or target pixels!  It produces
-   a simple set of numbers that Caller can blindly apply to the source 
+   a simple set of numbers that Caller can blindly apply to the source
    pixels to get target pixels.
 -----------------------------------------------------------------------------*/
     struct filterFunction horizFilter, vertFilter;
@@ -1490,14 +1495,14 @@ createWeightLists(struct pam *     const inpamP,
         (double)outpamP->width/inpamP->width,
         filterFunction, filterRadius, windowFunction);
 
-    createWeightListSet(inpamP->width, outpamP->width, horizFilter, 
+    createWeightListSet(inpamP->width, outpamP->width, horizFilter,
                         horizWeightP);
-    
+
     vertFilter = makeFilterFunction(
         (double)outpamP->height/inpamP->height,
         filterFunction, filterRadius, windowFunction);
 
-    createWeightListSet(inpamP->height, outpamP->height, vertFilter, 
+    createWeightListSet(inpamP->height, outpamP->height, vertFilter,
                         vertWeightP);
 
     *maxRowWeightsP = ceil(2.0*(vertFilter.radius+EPSILON) + 1 + EPSILON);
@@ -1516,12 +1521,12 @@ resample(struct pam *     const inpamP,
 /*---------------------------------------------------------------------------
   Resample the image in the input file, described by *inpamP,
   so as to create the image in the output file, described by *outpamP.
-  
+
   Input and output differ by height, width, and maxval only.
 
   Use the resampling filter function 'filterFunction', applied over
   radius 'filterRadius'.
-  
+
   The input file is positioned past the header, to the beginning of the
   raster.  The output file is too.
 ---------------------------------------------------------------------------*/
@@ -1539,7 +1544,7 @@ resample(struct pam *     const inpamP,
     if (linear)
         pm_error("You cannot use the resampling scaling method on "
                  "linear input.");
-  
+
     createWeightLists(inpamP, outpamP, filterFunction, filterRadius,
                       windowFunction, &horizWeight, &vertWeight,
                       &maxRowWeights);
@@ -1569,21 +1574,21 @@ resample(struct pam *     const inpamP,
         /* Output all the rows we can make out of the current contents of
            the scanbuf.  Might be none.
         */
-        needMoreInput = FALSE;  /* initial assumption */
+        needMoreInput = false;  /* initial assumption */
         while (outputRow < outpamP->height && !needMoreInput) {
             WLIST const rowWeights = vertWeight[outputRow];
                 /* The description of what makes up our current output row;
                    i.e. what fractions of which input rows combine to create
                    this output row.
                 */
-            assert(rowWeights.nWeight <= scanbuf.height); 
+            assert(rowWeights.nWeight <= scanbuf.height);
 
             if (scanbufContainsTheRows(scanbuf, rowWeights)) {
-                outputOneResampledRow(outpamP, scanbuf, rowWeights, 
+                outputOneResampledRow(outpamP, scanbuf, rowWeights,
                                       horizWeight, line, weight);
                 ++outputRow;
             } else
-                needMoreInput = TRUE;
+                needMoreInput = true;
         }
     }
 
@@ -1609,7 +1614,7 @@ resample(struct pam *     const inpamP,
 static void
 zeroNewRow(struct pam * const pamP,
            tuplen *     const tuplenrow) {
-    
+
     unsigned int col;
 
     for (col = 0; col < pamP->width; ++col) {
@@ -1625,7 +1630,7 @@ zeroNewRow(struct pam * const pamP,
 static void
 accumOutputCol(struct pam * const pamP,
                tuplen       const intuplen,
-               float        const fraction, 
+               float        const fraction,
                tuplen       const accumulator) {
 /*----------------------------------------------------------------------------
    Add fraction 'fraction' of the pixel indicated by 'intuplen' to the
@@ -1645,7 +1650,7 @@ accumOutputCol(struct pam * const pamP,
 
 
 static void
-horizontalScale(tuplen *     const inputtuplenrow, 
+horizontalScale(tuplen *     const inputtuplenrow,
                 tuplen *     const newtuplenrow,
                 struct pam * const inpamP,
                 struct pam * const outpamP,
@@ -1683,7 +1688,7 @@ horizontalScale(tuplen *     const inputtuplenrow,
             /* Generate one output pixel in 'newtuplerow'.  It will
                consist of anything accumulated from prior input pixels
                in accumulator[], plus a fraction of the current input
-               pixel.  
+               pixel.
             */
             assert(newcol < outpamP->width);
             accumOutputCol(inpamP, inputtuplenrow[col], fraccoltofill,
@@ -1695,7 +1700,7 @@ horizontalScale(tuplen *     const inputtuplenrow,
             ++newcol;
             fraccoltofill = 1.0;
         }
-        /* There's not enough left in the current input pixel to fill up 
+        /* 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.  Because of rounding, we may
            have a tiny bit of pixel left and have run out of output pixels.
@@ -1714,17 +1719,17 @@ horizontalScale(tuplen *     const inputtuplenrow,
                  newcol, outpamP->width-1);
 
     if (newcol < outpamP->width) {
-        /* We were still working on the last output column when we 
+        /* We were still working on the last output column when we
            ran out of input columns.  This would be because of rounding
            down, and we should be missing only a tiny fraction of that
            last output column.  Just fill in the missing color with the
            color of the rightmost input pixel.
         */
-        accumOutputCol(inpamP, inputtuplenrow[inpamP->width-1], 
+        accumOutputCol(inpamP, inputtuplenrow[inpamP->width-1],
                        fraccoltofill, newtuplenrow[newcol]);
-        
+
         *stretchP = fraccoltofill;
-    } else 
+    } else
         *stretchP = 0.0;
 }
 
@@ -1747,11 +1752,11 @@ zeroAccum(struct pam * const pamP,
 
 static void
 accumOutputRow(struct pam * const pamP,
-               tuplen *     const tuplenrow, 
-               float        const fraction, 
+               tuplen *     const tuplenrow,
+               float        const fraction,
                tuplen *     const accumulator) {
 /*----------------------------------------------------------------------------
-   Take 'fraction' times the samples in row 'tuplenrow' and add it to 
+   Take 'fraction' times the samples in row 'tuplenrow' and add it to
    'accumulator' in the same way as accumOutputCol().
 
    'fraction' is less than 1.0.
@@ -1838,7 +1843,7 @@ writeARow(struct pam *             const pamP,
 
 
 static void
-issueStretchWarning(bool   const verbose, 
+issueStretchWarning(bool   const verbose,
                     double const fracrowtofill) {
 
     /* We need another input row to fill up this
@@ -1847,11 +1852,11 @@ issueStretchWarning(bool   const verbose,
        scaling arithmetic.  So we go ahead with
        the data from the last row we read, which
        amounts to stretching out the last output
-       row.  
+       row.
     */
     if (verbose)
         pm_message("%f of bottom row stretched because of "
-                   "arithmetic imprecision", 
+                   "arithmetic imprecision",
                    fracrowtofill);
 }
 
@@ -1873,21 +1878,21 @@ scaleHorizontallyAndOutputRow(struct pam *             const inpamP,
    'newtuplenrow' is work space Caller provides us.  It is at least
    wide enough to hold one output row.
 -----------------------------------------------------------------------------*/
-    if (outpamP->width == inpamP->width)    
+    if (outpamP->width == inpamP->width)
         /* shortcut X scaling */
         writeARow(outpamP, rowAccumulator, transform);
             /* This destroys 'rowAccumulator' */
     else {
         float stretch;
-            
+
         horizontalScale(rowAccumulator, newtuplenrow, inpamP, outpamP,
                         xscale, &stretch);
-            
+
         if (verbose && row == 0)
             pm_message("%f of right column stretched because of "
-                       "arithmetic imprecision", 
+                       "arithmetic imprecision",
                        stretch);
-            
+
         writeARow(outpamP, newtuplenrow, transform);
             /* This destroys 'newtuplenrow' */
     }
@@ -1919,7 +1924,7 @@ destroyTransforms(const pnm_transformMap * const inputTransform,
 
     if (inputTransform)
         free((void*)inputTransform);
-    
+
     if (outputTransform)
         free((void*)outputTransform);
 }
@@ -1929,7 +1934,7 @@ destroyTransforms(const pnm_transformMap * const inputTransform,
 static void
 scaleWithMixing(struct pam * const inpamP,
                 struct pam * const outpamP,
-                float        const xscale, 
+                float        const xscale,
                 float        const yscale,
                 bool         const assumeLinear,
                 bool         const verbose) {
@@ -1950,8 +1955,8 @@ scaleWithMixing(struct pam * const inpamP,
   approximate but fast results).
 
 -----------------------------------------------------------------------------*/
-    /* Here's how we think of the color mixing scaling operation:  
-       
+    /* Here's how we think of the color mixing scaling operation:
+
        First, I'll describe scaling in one dimension.  Assume we have
        a one row image.  A raster row is ordinarily a sequence of
        discrete pixels which have no width and no distance between
@@ -1973,7 +1978,7 @@ scaleWithMixing(struct pam * const inpamP,
        look the same.
 
        This works for all scale factors, both scaling up and scaling down.
-       
+
        For images with an opacity plane, imagine Input Pixel 0's
        foreground is fully opaque red (1,0,0,1), and Input Pixel 1 is
        fully transparent (foreground irrelevant) (0,0,0,0).  We make
@@ -1994,14 +1999,14 @@ scaleWithMixing(struct pam * const inpamP,
        stretch the image vertically first (same process as above, but
        in place of a single-color pixels, we have a vector of colors).
        Then we take each row this vertical stretching generates and
-       stretch it horizontally.  
+       stretch it horizontally.
     */
 
     tuplen * tuplenrow;     /* An input row */
     tuplen * newtuplenrow;  /* Working space */
     float rowsleft;
     /* The number of rows of output that need to be formed from the
-       current input row (the one in tuplerow[]), less the number that 
+       current input row (the one in tuplerow[]), less the number that
        have already been formed (either in accumulator[]
        or output to the file).  This can be fractional because of the
        way we define rows as having height.
@@ -2021,8 +2026,8 @@ scaleWithMixing(struct pam * const inpamP,
     int row;
     const pnm_transformMap * inputTransform;
     const pnm_transformMap * outputTransform;
-    
-    tuplenrow = pnm_allocpamrown(inpamP); 
+
+    tuplenrow = pnm_allocpamrown(inpamP);
     rowAccumulator = pnm_allocpamrown(inpamP);
 
     rowsread = 0;
@@ -2087,7 +2092,7 @@ scaleWithMixing(struct pam * const inpamP,
 static void
 scaleWithoutMixing(const struct pam * const inpamP,
                    const struct pam * const outpamP,
-                   float              const xscale, 
+                   float              const xscale,
                    float              const yscale) {
 /*----------------------------------------------------------------------------
   Scale the image described by *inpamP by xscale horizontally and
@@ -2096,9 +2101,9 @@ scaleWithoutMixing(const struct pam * const inpamP,
 
   The input file is positioned past the header, to the beginning of the
   raster.  The output file is too.
-  
+
   Don't mix colors from different input pixels together in the output
-  pixels.  Each output pixel is an exact copy of some corresponding 
+  pixels.  Each output pixel is an exact copy of some corresponding
   input pixel.
 -----------------------------------------------------------------------------*/
     tuple * tuplerow;  /* An input row */
@@ -2109,22 +2114,22 @@ scaleWithoutMixing(const struct pam * const inpamP,
     assert(outpamP->maxval == inpamP->maxval);
     assert(outpamP->depth  == inpamP->depth);
 
-    tuplerow = pnm_allocpamrow(inpamP); 
+    tuplerow = pnm_allocpamrow(inpamP);
     rowInInput = -1;
 
     newtuplerow = pnm_allocpamrow(outpamP);
 
     for (row = 0; row < outpamP->height; ++row) {
         int col;
-        
+
         int const inputRow = (int) (row / yscale);
 
-        for (; rowInInput < inputRow; ++rowInInput) 
+        for (; rowInInput < inputRow; ++rowInInput)
             pnm_readpamrow(inpamP, tuplerow);
-        
+
         for (col = 0; col < outpamP->width; ++col) {
             int const inputCol = (int) (col / xscale);
-            
+
             pnm_assigntuple(inpamP, newtuplerow[col], tuplerow[inputCol]);
         }
 
@@ -2135,12 +2140,58 @@ scaleWithoutMixing(const struct pam * const inpamP,
 }
 
 
+static void
+skipImage(struct pam * const pamP) {
+
+        tuple * tuplerow;
+        unsigned int row;
+
+        tuplerow = pnm_allocpamrow(pamP);
+
+        for (row = 0; row < pamP->height; ++row)
+            pnm_readpamrow(pamP, tuplerow);
+
+        pnm_freepamrow(tuplerow);
+}
+
+
+
+static void
+scale(FILE *             const ifP,
+      struct pam *       const inpamP,
+      struct pam *       const outpamP,
+      float              const xscale,
+      float              const yscale,
+      struct CmdlineInfo const cmdline) {
+
+    pnm_writepaminit(outpamP);
+
+    if (cmdline.nomix) {
+        if (cmdline.verbose)
+            pm_message("Using nomix method");
+        scaleWithoutMixing(inpamP, outpamP, xscale, yscale);
+    } else if (!cmdline.filterFunction) {
+        if (cmdline.verbose)
+            pm_message("Using simple pixel mixing rescaling method");
+        scaleWithMixing(inpamP, outpamP, xscale, yscale,
+                        cmdline.linear, cmdline.verbose);
+    } else {
+        if (cmdline.verbose)
+            pm_message("Using general filter method");
+        resample(inpamP, outpamP,
+                 cmdline.filterFunction, cmdline.filterRadius,
+                 cmdline.windowFunction, cmdline.verbose,
+                 cmdline.linear);
+    }
+}
+
+
 
 static void
 pamscale(FILE *             const ifP,
          FILE *             const ofP,
          struct CmdlineInfo const cmdline) {
-    
+
     struct pam inpam, outpam;
     float xscale, yscale;
 
@@ -2158,44 +2209,31 @@ pamscale(FILE *             const ifP,
         outpam.maxval = inpam.maxval;
     }
 
-    computeOutputDimensions(cmdline, inpam.width, inpam.height, 
+    computeOutputDimensions(cmdline, inpam.width, inpam.height,
                             &outpam.width, &outpam.height);
 
     xscale = (float) outpam.width / inpam.width;
     yscale = (float) outpam.height / inpam.height;
 
     if (cmdline.verbose) {
-        pm_message("Scaling by %f horizontally to %d columns.", 
+        pm_message("Scaling by %f horizontally to %d columns.",
                    xscale, outpam.width);
-        pm_message("Scaling by %f vertically to %d rows.", 
+        pm_message("Scaling by %f vertically to %d rows.",
                    yscale, outpam.height);
     }
 
     if (xscale * inpam.width < outpam.width - 1 ||
-        yscale * inpam.height < outpam.height - 1) 
+        yscale * inpam.height < outpam.height - 1)
         pm_error("Arithmetic precision of this program is inadequate to "
                  "do the specified scaling.  Use a smaller input image "
                  "or a slightly different scale factor.");
 
-    pnm_writepaminit(&outpam);
-
-    if (cmdline.nomix) {
-        if (cmdline.verbose)
-            pm_message("Using nomix method");
-        scaleWithoutMixing(&inpam, &outpam, xscale, yscale);
-    } else if (!cmdline.filterFunction) {
-        if (cmdline.verbose)
-            pm_message("Using simple pixel mixing rescaling method");
-        scaleWithMixing(&inpam, &outpam, xscale, yscale, 
-                        cmdline.linear, cmdline.verbose);
-    } else {
-        if (cmdline.verbose)
-            pm_message("Using general filter method");
-        resample(&inpam, &outpam,
-                 cmdline.filterFunction, cmdline.filterRadius,
-                 cmdline.windowFunction, cmdline.verbose,
-                 cmdline.linear);
-    }
+    if (cmdline.reportonly) {
+        printf("%d %d %f %f %d %d\n", inpam.width, inpam.height,
+               xscale, yscale, outpam.width, outpam.height);
+        skipImage(&inpam);
+    } else
+        scale(ifP, &inpam, &outpam, xscale, yscale, cmdline);
 }
 
 
@@ -2213,7 +2251,7 @@ main(int argc, const char **argv ) {
 
     ifP = pm_openr(cmdline.inputFileName);
 
-    eof = FALSE;
+    eof = false;
     while (!eof) {
         pamscale(ifP, stdout, cmdline);
         pnm_nextimage(ifP, &eof);
@@ -2221,6 +2259,6 @@ main(int argc, const char **argv ) {
 
     pm_close(ifP);
     pm_close(stdout);
-    
+
     return 0;
 }
diff --git a/editor/pamstretch-gen b/editor/pamstretch-gen
index ba0e8188..fec4469c 100755
--- a/editor/pamstretch-gen
+++ b/editor/pamstretch-gen
@@ -1,80 +1,129 @@
 #!/bin/sh
+###############################################################################
+#                              pamstretch-gen
+###############################################################################
+# A generalized version of pamstretch that can do non-integer scale factors.
 #
-# pamstretch-gen - a shell script which acts a little like a general
-# form of pamstretch, by scaling up with pamstretch then scaling
-# down with pamscale.
+# It works by scaling up with pamstretch then scaling down with pamscale.
 #
 # it also copes with N<1, but then it just uses pamscale. :-)
 #
 # Formerly named 'pnminterp-gen' and 'pnmstretch-gen'.
 #
-# Copyright (C) 1998,2000 Russell Marks.
-# 
-# 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-# 
+###############################################################################
 
+# Scan command line arguments
 
-if [ "$1" = "" ]; then
-  echo 'usage: pamstretch-gen N [pnmfile]'
+while true ; do
+    case "$1" in
+        -version|--version )
+        pamstretch --version; exit $?;
+        ;;
+        -p|-pl|-pla|-plai|-plain|--p|--pl|--pla|--plai|--plain )
+        plainopt="-plain"
+        shift
+        ;;
+        -q|-qu|-qui|-quie|-quiet|--q|--qu|--qui|--quie|--quiet )
+        quietopt="-plain"
+        shift
+        ;;
+        -q|-qu|-qui|-quie|-quiet|--q|--qu|--qui|--quie|--quiet )
+        quietopt="-quiet"
+        shift
+        ;;
+        -verb|-verbo|-verbos|-verbose|--verb|--verbo|--verbos|--verbose )
+        verboseopt="-verbose"
+        shift
+        ;;
+        -* )
+        echo 'usage: pamstretch-gen N [pnmfile]' 1>&2
+        exit 1
+        ;;
+        * )
+        break
+        ;;
+    esac
+done
+ 
+tempfile=$(mktemp "${TMPDIR:-/tmp}/netpbm.XXXXXXXX")
+if [ $? -ne 0 -o ! -e $tempfile ]; then
+  echo "Could not create temporary file. Exiting." 1>&2
   exit 1
 fi
+trap 'rm -rf $tempfile' 0 1 3 15
 
-tempdir="${TMPDIR-/tmp}/pamstretch-gen.$$"
-mkdir -m 0700 $tempdir || \
-  { echo "Could not create temporary file. Exiting."; exit 1;}
-trap 'rm -rf $tempdir' 0 1 3 15
+case "$#" in
+    0)
+    echo "pamstretch-gen: too few arguments" 1>&2
+    exit 1
+    ;;   
+    1 )
+    if ! cat > $tempfile; then
+    echo "pamstretch-gen: error reading input" 1>&2
+    exit 1
+    fi
+    ;;
+    2 )
+    if ! cat $2 > $tempfile; then
+    echo "pamstretch-gen: error reading file "$2 1>&2
+    exit 1
+    fi
+    ;;
+    * )
+    echo "pamstretch-gen: misaligned arguments or too many arguments" 1>&2
+    exit 1
+    ;;
+esac
 
-tempfile=$tempdir/pnmig
+# Calculate pamstretch scale factor (="iscale") and output width and
+# height.  Usually "int(scale) + 1" is sufficient for iscale but
+# in some exceptional cases adjustment is necessary because of
+# "-dropedge".
 
-if ! cat $2 >$tempfile 2>/dev/null; then
-  echo 'pamstretch-gen: error reading file' 1>&2
+report=$(pamscale -reportonly $1 $tempfile)
+if [ $? -ne 0 ]; then
+  echo "pamstretch-gen: pamscale -reportonly $1 (file) failed" 1>&2
   exit 1
 fi
 
-if ! pnmfile $tempfile 1>/dev/null 2>/dev/null; then
-  echo 'Not valid pnm input'
-  exit 1
-fi
+iscale_width_height=$(echo $report |\
+  awk 'NF!=6 || $1<=0 || $2<=0 || $3<=0 || $5<=0 || $6<=0  { exit 1 }
+           { if ($3 > 1.0)  { iscale = int($3) + 1;
+                              if (iscale * ($1-1) < $5 ||
+                                  iscale * ($2-1) < $6 )
+                                     ++iscale;            }
+             else { iscale = 1 }  # $3 <= 1.0
+       }
+       { print iscale, "-width="$5, "-height="$6}' )
 
-# we use the width as indication of how much to scale; width and
-# height are being scaled equally, so this should be ok.
-width=`pnmfile $tempfile 2>/dev/null|cut -d " " -f 3`
+# Note that $1, $2, ..., $6 here are fields of the input line fed to awk,
+# not shell positional parameters.
 
-if [ "$width" = "" ]; then
-  echo 'pamstretch-gen: not a PNM file' 1>&2
-  exit 1
-fi
+iscale=${iscale_width_height% -width=* -height=*}
+width_height=${iscale_width_height#* }
+if [ -n "$verboseopt" ]; then
+    echo "pamstretch-gen: rounded scale factor=$iscale $width_height" 1>&2
+fi 
 
-# should really use dc for maths, but awk is less painful :-)
-target_width=`awk 'BEGIN{printf("%d",'0.5+"$width"*"$1"')}'`
+pamstretch -dropedge $quietopt $iscale $tempfile |\
+  pamscale $verboseopt $quietopt $plainopt $width_height
 
-# work out how far we have to scale it up with pamstretch so that the
-# new width is >= the target width.
-int_scale=`awk '
-BEGIN {
-int_scale=1;int_width='"$width"'
-while(int_width<'"$target_width"')
-  {
-  int_scale++
-  int_width+='"$width"'
-  }
-print int_scale
-}'`
 
-if [ "$int_scale" -eq 1 ]; then
-  pamscale "$1" $tempfile
-else
-  pamstretch "$int_scale" $tempfile | pnmscale -xsi "$target_width"
-fi
+# Copyright (C) 1998,2000 Russell Marks.
+# Modifications for "pamscale -reportonly" "pamstretch -dropedge" by
+# Akira Urushibata (Jan. 2019)
+#
+# 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
diff --git a/editor/pamstretch.c b/editor/pamstretch.c
index 04883c35..5d24e437 100644
--- a/editor/pamstretch.c
+++ b/editor/pamstretch.c
@@ -1,5 +1,5 @@
 /* pamstretch - scale up portable anymap by interpolating between pixels.
- * 
+ *
  * This program is based on 'pnminterp' by Russell Marks, rename
  * pnmstretch for inclusion in Netpbm, then rewritten and renamed to
  * pamstretch by Bryan Henderson in December 2001.
@@ -10,12 +10,12 @@
  * 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
@@ -27,33 +27,35 @@
 #include <limits.h>
 
 #include "pm_c_util.h"
-#include "pam.h"
+#include "mallocvar.h"
+#include "nstring.h"
 #include "shhopt.h"
+#include "pam.h"
 
-enum an_edge_mode {
+enum EdgeMode {
     EDGE_DROP,
         /* drop one (source) pixel at right/bottom edges. */
     EDGE_INTERP_TO_BLACK,
         /* interpolate right/bottom edge pixels to black. */
     EDGE_NON_INTERP
-        /* don't interpolate right/bottom edge pixels 
+        /* don't interpolate right/bottom edge pixels
            (default, and what zgv does). */
 };
 
 
-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 */
-    enum an_edge_mode edge_mode;
+    const char * inputFileName;  /* Filespecs of input files */
+    enum EdgeMode edgeMode;
     unsigned int xscale;
     unsigned int yscale;
 };
 
 
 
-tuple blackTuple;  
+tuple blackTuple;
    /* A "black" tuple.  Unless our input image is PBM, PGM, or PPM, we
       don't really know what "black" means, so this is just something
       arbitrary in that case.
@@ -61,116 +63,125 @@ tuple blackTuple;
 
 
 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
+   Note that the file name 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 blackedge;
     unsigned int dropedge;
-    unsigned int xscale_spec;
-    unsigned int yscale_spec;
+    unsigned int xscaleSpec;
+    unsigned int yscaleSpec;
+
+    MALLOCARRAY(option_def, 100);
 
     option_def_index = 0;   /* incremented by OPTENTRY */
     OPTENT3('b', "blackedge",    OPT_FLAG, NULL, &blackedge,            0);
     OPTENT3('d', "dropedge",     OPT_FLAG, NULL, &dropedge,             0);
-    OPTENT3(0,   "xscale",       OPT_UINT, 
-            &cmdline_p->xscale, &xscale_spec, 0);
-    OPTENT3(0,   "yscale",       OPT_UINT, 
-            &cmdline_p->yscale, &yscale_spec, 0);
+    OPTENT3(0,   "xscale",       OPT_UINT,
+            &cmdlineP->xscale, &xscaleSpec, 0);
+    OPTENT3(0,   "yscale",       OPT_UINT,
+            &cmdlineP->yscale, &yscaleSpec, 0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE; /* We have some short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    pm_optParseOptions3(&argc, argv, opt, sizeof(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 (blackedge && dropedge) 
+    if (blackedge && dropedge)
         pm_error("Can't specify both -blackedge and -dropedge options.");
     else if (blackedge)
-        cmdline_p->edge_mode = EDGE_INTERP_TO_BLACK;
+        cmdlineP->edgeMode = EDGE_INTERP_TO_BLACK;
     else if (dropedge)
-        cmdline_p->edge_mode = EDGE_DROP;
+        cmdlineP->edgeMode = EDGE_DROP;
     else
-        cmdline_p->edge_mode = EDGE_NON_INTERP;
+        cmdlineP->edgeMode = EDGE_NON_INTERP;
 
-    if (xscale_spec && cmdline_p->xscale == 0)
+    if (xscaleSpec && cmdlineP->xscale == 0)
         pm_error("You specified zero for the X scale factor.");
-    if (yscale_spec && cmdline_p->yscale == 0)
+    if (yscaleSpec && cmdlineP->yscale == 0)
         pm_error("You specified zero for the Y scale factor.");
 
-    if (xscale_spec && !yscale_spec)
-        cmdline_p->yscale = 1;
-    if (yscale_spec && !xscale_spec)
-        cmdline_p->xscale = 1;
+    if (xscaleSpec && !yscaleSpec)
+        cmdlineP->yscale = 1;
+    if (yscaleSpec && !xscaleSpec)
+        cmdlineP->xscale = 1;
 
-    if (!(xscale_spec || yscale_spec)) {
+    if (!(xscaleSpec || yscaleSpec)) {
         /* scale must be specified in an argument */
-        if ((argc-1) != 1 && (argc-1) != 2)
+        if (argc-1 != 1 && argc-1 != 2)
             pm_error("Wrong number of arguments (%d).  Without scale options, "
                      "you must supply 1 or 2 arguments:  scale and "
                      "optional file specification", argc-1);
-        
+
         {
-            char *endptr;   /* ptr to 1st invalid character in scale arg */
+            const char * error;   /* error message of pm_string_to_uint */
             unsigned int scale;
-            
-            scale = strtol(argv[1], &endptr, 10);
-            if (*argv[1] == '\0') 
-                pm_error("Scale argument is a null string.  "
-                         "Must be a number.");
-            else if (*endptr != '\0')
-                pm_error("Scale argument contains non-numeric character '%c'.",
-                         *endptr);
-            else if (scale < 2)
-                pm_error("Scale argument must be at least 2.  "
-                         "You specified %d", scale);
-            cmdline_p->xscale = scale;
-            cmdline_p->yscale = scale;
+
+            pm_string_to_uint(argv[1], &scale, &error);
+
+            if (error)
+                pm_error("Invalid scale factor: %s", error);
+            else {
+                if (scale < 1)
+                    pm_error("Scale argument must be at least 1.  "
+                             "You specified %d", scale);
+                cmdlineP->xscale = scale;
+                cmdlineP->yscale = scale;
+            }
         }
-        if (argc-1 > 1) 
-            cmdline_p->input_filespec = argv[2];
+
+        if (argc-1 > 1)
+            cmdlineP->inputFileName = argv[2];
         else
-            cmdline_p->input_filespec = "-";
+            cmdlineP->inputFileName = "-";
     } else {
         /* No scale argument allowed */
-        if ((argc-1) > 1)
+        if (argc-1 > 1)
             pm_error("Too many arguments (%d).  With a scale option, "
                      "the only argument is the "
                      "optional file specification", argc-1);
-        if (argc-1 > 0) 
-            cmdline_p->input_filespec = argv[1];
-        else
-            cmdline_p->input_filespec = "-";
+        else {
+            if (argc-1 > 0)
+                cmdlineP->inputFileName = argv[1];
+            else
+                cmdlineP->inputFileName = "-";
+        }
     }
 }
 
 
 
 static void
-stretch_line(struct pam * const inpamP, 
-             const tuple * const line, const tuple * const line_stretched, 
-             unsigned int const scale, enum an_edge_mode const edge_mode) {
+stretchLine(struct pam *  const inpamP,
+            const tuple * const line,
+            const tuple * const lineStretched,
+            unsigned int  const scale,
+            enum EdgeMode const edgeMode) {
 /*----------------------------------------------------------------------------
    Stretch the line of tuples 'line' into the output buffer 'line_stretched',
    by factor 'scale'.
 -----------------------------------------------------------------------------*/
+    enum EdgeMode const horizontalEdgeMode =
+        (scale == 1) ? EDGE_NON_INTERP : edgeMode;
+
     int scaleincr;
-    int sisize;   
+    int sisize;
         /* normalizing factor to make fractions representable as integers.
            E.g. if sisize = 100, one half is represented as 50.
         */
     unsigned int col;
     unsigned int outcol;
-    
+
     sisize=0;
-    while (sisize<256) 
+    while (sisize<256)
         sisize += scale;
     scaleincr = sisize/scale;  /* (1/scale, normalized) */
 
@@ -185,7 +196,7 @@ stretch_line(struct pam * const inpamP,
             /* We're at the edge.  There is no column to the right with which
                to interpolate.
             */
-            switch(edge_mode) {
+            switch(horizontalEdgeMode) {
             case EDGE_DROP:
                 /* No output column needed for this input column */
                 break;
@@ -194,32 +205,30 @@ stretch_line(struct pam * const inpamP,
                 for (pos = 0; pos < sisize; pos += scaleincr) {
                     unsigned int plane;
                     for (plane = 0; plane < inpamP->depth; ++plane)
-                        line_stretched[outcol][plane] = 
+                        lineStretched[outcol][plane] =
                             (line[col][plane] * (sisize-pos)) / sisize;
                     ++outcol;
                 }
-            }
-            break;
+            } break;
             case EDGE_NON_INTERP: {
                 unsigned int pos;
                 for (pos = 0; pos < sisize; pos += scaleincr) {
                     unsigned int plane;
                     for (plane = 0; plane < inpamP->depth; ++plane)
-                        line_stretched[outcol][plane] = line[col][plane];
+                        lineStretched[outcol][plane] = line[col][plane];
                     ++outcol;
                 }
-            }
-            break;
-            default: 
-                pm_error("INTERNAL ERROR: invalid value for edge_mode");
+            } break;
+            default:
+                pm_error("INTERNAL ERROR: invalid value for edgeMode");
             }
         } else {
             /* Interpolate with the next input column to the right */
             for (pos = 0; pos < sisize; pos += scaleincr) {
                 unsigned int plane;
                 for (plane = 0; plane < inpamP->depth; ++plane)
-                    line_stretched[outcol][plane] = 
-                        (line[col][plane] * (sisize-pos) 
+                    lineStretched[outcol][plane] =
+                        (line[col][plane] * (sisize-pos)
                          +  line[col+1][plane] * pos) / sisize;
                 ++outcol;
             }
@@ -229,31 +238,33 @@ stretch_line(struct pam * const inpamP,
 
 
 
-static void 
-write_interp_rows(struct pam *      const outpamP,
-                  const tuple *     const curline,
-                  const tuple *     const nextline, 
-                  tuple *           const outbuf,
-                  int               const scale) {
+static void
+writeInterpRows(struct pam *      const outpamP,
+                const tuple *     const curline,
+                const tuple *     const nextline,
+                tuple *           const outbuf,
+                int               const scale) {
 /*----------------------------------------------------------------------------
-   Write out 'scale' rows, being 'curline' followed by rows that are 
+   Write out 'scale' rows, being 'curline' followed by rows that are
    interpolated between 'curline' and 'nextline'.
 -----------------------------------------------------------------------------*/
-    unsigned int scaleincr;
-    unsigned int sisize;
+    unsigned int scaleIncr;
+    unsigned int siSize;
     unsigned int pos;
 
-    sisize=0;
-    while(sisize<256) sisize+=scale;
-    scaleincr=sisize/scale;
+    for (siSize = 0; siSize < 256; siSize += scale);
+
+    scaleIncr = siSize / scale;
 
-    for (pos = 0; pos < sisize; pos += scaleincr) {
+    for (pos = 0; pos < siSize; pos += scaleIncr) {
         unsigned int col;
+
         for (col = 0; col < outpamP->width; ++col) {
             unsigned int plane;
-            for (plane = 0; plane < outpamP->depth; ++plane) 
-                outbuf[col][plane] = (curline[col][plane] * (sisize-pos)
-                    + nextline[col][plane] * pos) / sisize;
+
+            for (plane = 0; plane < outpamP->depth; ++plane)
+                outbuf[col][plane] = (curline[col][plane] * (siSize - pos)
+                    + nextline[col][plane] * pos) / siSize;
         }
         pnm_writepamrow(outpamP, outbuf);
     }
@@ -262,10 +273,11 @@ write_interp_rows(struct pam *      const outpamP,
 
 
 static void
-swap_buffers(tuple ** const buffer1P, tuple ** const buffer2P) {
-    /* Advance "next" line to "current" line by switching
-       line buffers 
-    */
+swapBuffers(tuple ** const buffer1P,
+            tuple ** const buffer2P) {
+/*----------------------------------------------------------------------------
+  Advance "next" line to "current" line by switching line buffers.
+-----------------------------------------------------------------------------*/
     tuple *tmp;
 
     tmp = *buffer1P;
@@ -274,110 +286,142 @@ swap_buffers(tuple ** const buffer1P, tuple ** const buffer2P) {
 }
 
 
-static void 
-stretch(struct pam * const inpamP, struct pam * const outpamP,
-        int const xscale, int const yscale,
-        enum an_edge_mode const edge_mode) {
+
+static void
+stretch(struct pam *  const inpamP,
+        struct pam *  const outpamP,
+        unsigned int  const xscale,
+        unsigned int  const yscale,
+        enum EdgeMode const edgeMode) {
+
+    enum EdgeMode const verticalEdgeMode =
+        (yscale == 1) ? EDGE_NON_INTERP : edgeMode;
 
     tuple *linebuf1, *linebuf2;  /* Input buffers for two rows at a time */
     tuple *curline, *nextline;   /* Pointers to one of the two above buffers */
     /* And the stretched versions: */
-    tuple *stretched_linebuf1, *stretched_linebuf2;
-    tuple *curline_stretched, *nextline_stretched;
+    tuple *stretchedLinebuf1, *stretchedLinebuf2;
+    tuple *curlineStretched, *nextlineStretched;
 
     tuple *outbuf;   /* One-row output buffer */
     unsigned int row;
-    unsigned int rowsToStretch;
-    
-    linebuf1 =           pnm_allocpamrow(inpamP);
-    linebuf2 =           pnm_allocpamrow(inpamP);
-    stretched_linebuf1 = pnm_allocpamrow(outpamP);
-    stretched_linebuf2 = pnm_allocpamrow(outpamP);
-    outbuf =             pnm_allocpamrow(outpamP);
+    unsigned int nRowsToStretch;
+
+    linebuf1          = pnm_allocpamrow(inpamP);
+    linebuf2          = pnm_allocpamrow(inpamP);
+    stretchedLinebuf1 = pnm_allocpamrow(outpamP);
+    stretchedLinebuf2 = pnm_allocpamrow(outpamP);
+    outbuf            = pnm_allocpamrow(outpamP);
 
     curline = linebuf1;
-    curline_stretched = stretched_linebuf1;
+    curlineStretched = stretchedLinebuf1;
     nextline = linebuf2;
-    nextline_stretched = stretched_linebuf2;
+    nextlineStretched = stretchedLinebuf2;
 
     pnm_readpamrow(inpamP, curline);
-    stretch_line(inpamP, curline, curline_stretched, xscale, edge_mode);
+    stretchLine(inpamP, curline, curlineStretched, xscale, edgeMode);
 
-    if (edge_mode == EDGE_DROP) 
-        rowsToStretch = inpamP->height - 1;
+    if (verticalEdgeMode == EDGE_DROP)
+        nRowsToStretch = inpamP->height - 1;
     else
-        rowsToStretch = inpamP->height;
-    
-    for (row = 0; row < rowsToStretch; row++) {
-        if (row == inpamP->height-1) {
+        nRowsToStretch = inpamP->height;
+
+    for (row = 0; row < nRowsToStretch; ++row) {
+        if (row == inpamP->height - 1) {
             /* last line is about to be output. there is no further
              * `next line'.  if EDGE_DROP, we stop here, with output
              * of rows-1 rows.  if EDGE_INTERP_TO_BLACK we make next
              * line black.  if EDGE_NON_INTERP (default) we make it a
-             * copy of the current line.  
+             * copy of the current line.
              */
-            switch (edge_mode) {
+            switch (verticalEdgeMode) {
             case EDGE_INTERP_TO_BLACK: {
-                int col;
-                for (col = 0; col < outpamP->width; col++)
-                    nextline_stretched[col] = blackTuple;
-            } 
+                unsigned int col;
+                for (col = 0; col < outpamP->width; ++col)
+                    nextlineStretched[col] = blackTuple;
+            }
             break;
             case EDGE_NON_INTERP: {
                 /* EDGE_NON_INTERP */
-                int col;
-                for (col = 0; col < outpamP->width; col++)
-                    nextline_stretched[col] = curline_stretched[col];
+                unsigned int col;
+                for (col = 0; col < outpamP->width; ++col)
+                    nextlineStretched[col] = curlineStretched[col];
             }
             break;
-            case EDGE_DROP: 
+            case EDGE_DROP:
                 pm_error("INTERNAL ERROR: processing last row, but "
-                         "edge_mode is EDGE_DROP.");
+                         "edgeMode is EDGE_DROP.");
             }
         } else {
             pnm_readpamrow(inpamP, nextline);
-            stretch_line(inpamP, nextline, nextline_stretched, xscale,
-                         edge_mode);
+            stretchLine(inpamP, nextline, nextlineStretched, xscale, edgeMode);
         }
-        
+
         /* interpolate curline towards nextline into outbuf */
-        write_interp_rows(outpamP, curline_stretched, nextline_stretched,
-                          outbuf, yscale);
+        writeInterpRows(outpamP, curlineStretched, nextlineStretched,
+                        outbuf, yscale);
 
-        swap_buffers(&curline, &nextline);
-        swap_buffers(&curline_stretched, &nextline_stretched);
+        swapBuffers(&curline, &nextline);
+        swapBuffers(&curlineStretched, &nextlineStretched);
     }
     pnm_freerow(outbuf);
-    pnm_freerow(stretched_linebuf2);
-    pnm_freerow(stretched_linebuf1);
+    pnm_freerow(stretchedLinebuf2);
+    pnm_freerow(stretchedLinebuf1);
     pnm_freerow(linebuf2);
     pnm_freerow(linebuf1);
 }
 
 
+static void
+computeOutputWidthHeight(int           const inWidth,
+                         int           const inHeight,
+                         unsigned int  const xScale,
+                         unsigned int  const yScale,
+                         enum EdgeMode const edgeMode,
+                         int *         const outWidthP,
+                         int *         const outHeightP) {
+
+    unsigned int const xDropped =
+        (edgeMode == EDGE_DROP && xScale != 1) ? 1 : 0;
+    unsigned int const yDropped =
+        (edgeMode == EDGE_DROP && yScale != 1) ? 1 : 0;
+    double const width  = (inWidth  - xDropped) * xScale;
+    double const height = (inHeight - yDropped) * yScale;
+
+    if (width > INT_MAX - 2)
+        pm_error("output image width (%f) too large for computations",
+                 width);
+    if (height > INT_MAX - 2)
+        pm_error("output image height (%f) too large for computation",
+                 height);
+
+    *outWidthP  = (unsigned int)width;
+    *outHeightP = (unsigned int)height;
+}
+
+
 
-int 
-main(int argc,char *argv[]) {
+int
+main(int argc, const char ** argv) {
 
-    FILE *ifp;
+    FILE * ifP;
 
-    struct cmdline_info cmdline; 
+    struct CmdlineInfo cmdline;
     struct pam inpam, outpam;
-    
-    pnm_init(&argc, argv);
 
-    parse_command_line(argc, argv, &cmdline);
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
 
-    ifp = pm_openr(cmdline.input_filespec);
+    ifP = pm_openr(cmdline.inputFileName);
 
-    pnm_readpaminit(ifp, &inpam, PAM_STRUCT_SIZE(tuple_type));
+    pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
 
     if (inpam.width < 2)
         pm_error("Image is too narrow.  Must be at least 2 columns.");
     if (inpam.height < 2)
         pm_error("Image is too short.  Must be at least 2 lines.");
 
-
     outpam = inpam;  /* initial value */
     outpam.file = stdout;
 
@@ -388,30 +432,18 @@ main(int argc,char *argv[]) {
     } else {
         outpam.format = inpam.format;
     }
-    {
-        unsigned int const dropped = cmdline.edge_mode == EDGE_DROP ? 1 : 0;
-        double const width  = (inpam.width  - dropped) * cmdline.xscale;
-        double const height = (inpam.height - dropped) * cmdline.yscale;
-
-        if (width > INT_MAX - 2)
-            pm_error("output image width (%f) too large for computations",
-                     width);
-        if (height > INT_MAX - 2)
-            pm_error("output image height (%f) too large for computation",
-                     height);
- 
-        outpam.width  = width;
-        outpam.height = height;
-
-        pnm_writepaminit(&outpam);
-    }
+    computeOutputWidthHeight(inpam.width, inpam.height,
+                             cmdline.xscale, cmdline.yscale, cmdline.edgeMode,
+                             &outpam.width, &outpam.height);
+
+    pnm_writepaminit(&outpam);
 
     pnm_createBlackTuple(&outpam, &blackTuple);
 
-    stretch(&inpam, &outpam, 
-            cmdline.xscale, cmdline.yscale, cmdline.edge_mode);
+    stretch(&inpam, &outpam,
+            cmdline.xscale, cmdline.yscale, cmdline.edgeMode);
 
-    pm_close(ifp);
+    pm_close(ifP);
 
     exit(0);
 }
diff --git a/editor/pamundice.c b/editor/pamundice.c
index 9a80e46d..dbe0a8df 100644
--- a/editor/pamundice.c
+++ b/editor/pamundice.c
@@ -271,12 +271,11 @@ computeInputFileName(const char *  const pattern,
                      const char ** const fileNameP) {
 
     struct buffer buffer;
-    unsigned int inCursor, outCursor;
+    unsigned int inCursor;
 
     buffer_init(&buffer);
 
     inCursor = 0;
-    outCursor = 0;
 
     while (pattern[inCursor] != '\0') {
         if (pattern[inCursor] == '%') {
diff --git a/editor/pbmclean.c b/editor/pbmclean.c
index 46e7dee6..08f410c0 100644
--- a/editor/pbmclean.c
+++ b/editor/pbmclean.c
@@ -6,13 +6,14 @@
 =============================================================================*/
 #include <assert.h>
 #include <stdio.h>
+#include <stdbool.h>
 
 #include "pm_c_util.h"
 #include "mallocvar.h"
 #include "shhopt.h"
 #include "pbm.h"
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
@@ -28,7 +29,7 @@ struct cmdlineInfo {
 
 static void
 parseCommandLine(int argc, const char ** argv,
-                 struct cmdlineInfo *cmdlineP) {
+                 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.
@@ -46,13 +47,13 @@ parseCommandLine(int argc, const char ** argv,
     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, 
+    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 */
+    opt.short_allowed = false;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = true;  /* We sort of allow negative numbers as parms */
 
     pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
@@ -64,8 +65,8 @@ parseCommandLine(int argc, const char ** argv,
             pm_error("With -extended, you cannot specify both "
                      "-black and -white");
         else if (!black & !white) {
-            cmdlineP->flipBlack = TRUE;
-            cmdlineP->flipWhite = FALSE;
+            cmdlineP->flipBlack = true;
+            cmdlineP->flipWhite = false;
         } else {
             cmdlineP->flipBlack = !!black;
             cmdlineP->flipWhite = !!white;
@@ -77,7 +78,7 @@ parseCommandLine(int argc, const char ** argv,
         } else {
             cmdlineP->flipBlack = !!black;
             cmdlineP->flipWhite = !!white;
-        }    
+        }
     }
     if (!minneighborsSpec) {
         /* Now we do a sleazy tour through the parameters to see if
@@ -86,7 +87,7 @@ parseCommandLine(int argc, const char ** argv,
            unconventional syntax where a -N option was used instead of
            the current -minneighbors option.  The only reason -N didn't
            get processed by pm_pm_optParseOptions3() is that it looked
-           like a negative number parameter instead of an option.  
+           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;
@@ -109,7 +110,7 @@ parseCommandLine(int argc, const char ** argv,
             --argc;
     }
 
-    if (argc-1 < 1) 
+    if (argc-1 < 1)
         cmdlineP->inputFileName = "-";
     else if (argc-1 == 1)
         cmdlineP->inputFileName = argv[1];
@@ -160,7 +161,7 @@ bitpop24(uint32_t const w){
 -----------------------------------------------------------------------------*/
     return (bitpop8((w >> 16) & 0xff) +
             bitpop8((w >>  8) & 0xff) +
-            bitpop8((w >>  0) & 0xff));  
+            bitpop8((w >>  0) & 0xff));
 }
 
 
@@ -204,11 +205,11 @@ and written to outrow at the byte boundary.
 
 
 static unsigned int
-likeNeighbors(uint32_t     const blackSample, 
+likeNeighbors(uint32_t     const blackSample,
               unsigned int const offset) {
 
     bool const thispoint = ( blackSample >> (18-offset) ) & 0x01;
-    uint32_t const sample = (thispoint == PBM_BLACK ) 
+    uint32_t const sample = (thispoint == PBM_BLACK )
                           ?   blackSample
                           : ~ blackSample ;
     uint32_t const selection = 0x701407;
@@ -238,7 +239,7 @@ setSample(const bit *  const prevrow,
         ((nextrow[col8 - 1] & 0x01)  <<  9) |
         ((nextrow[col8]           )  <<  1) |
         ((nextrow[col8 + 1] & 0x80)  >>  7);
-    
+
     return sample;
 }
 
@@ -277,7 +278,7 @@ cleanrow(const bit *    const prevrow,
 /* ----------------------------------------------------------------------
   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;
@@ -350,9 +351,9 @@ setupInputBuffers(FILE *       const ifP,
 
     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 */ 
+        /* Add blank (all white) bytes beside the edges */
         buffer[i][0] = buffer[i][ pbm_packed_bytes( cols +16 ) - 1] = 0x00;
     }
     nextRow = &buffer[0][1];
@@ -374,7 +375,7 @@ setupInputBuffers(FILE *       const ifP,
 static void
 cleanSimple(FILE *             const ifP,
             FILE *             const ofP,
-            struct cmdlineInfo const cmdline,
+            struct CmdlineInfo const cmdline,
             double *           const nFlippedP) {
 /*----------------------------------------------------------------------------
    Do the traditional clean where you look only at the immediate neighboring
@@ -412,7 +413,7 @@ cleanSimple(FILE *             const ifP,
         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 
+               with the help of a temporary.  This provision is for proper
                handling of the initial edgerow.
             */
             pbm_readpbmrow_packed(ifP, nextRow, cols, format);
@@ -423,7 +424,7 @@ cleanSimple(FILE *             const ifP,
 
         cleanrow(prevRow, thisRow, nextRow, outRow, cols, cmdline.minneighbors,
                  cmdline.flipWhite, cmdline.flipBlack, &nFlipped);
-        
+
         *nFlippedP += nFlipped;
 
         pbm_writepbmrow_packed(ofP, outRow, cols, 0) ;
@@ -446,7 +447,7 @@ typedef struct {
    A queue of pixel locations.
 -----------------------------------------------------------------------------*/
     unsigned int size;
-    
+
     struct PixQueueElt * headP;
     struct PixQueueElt * tailP;
 } PixQueue;
@@ -495,7 +496,7 @@ pixQueue_push(PixQueue *    const queueP,
     newEltP->nextP = NULL;
     if (queueP->tailP)
         queueP->tailP->nextP = newEltP;
-    
+
     queueP->tailP = newEltP;
 
     if (!queueP->headP)
@@ -538,7 +539,7 @@ pixQueue_term(PixQueue * const queueP) {
 
     struct PixQueueElt * p;
     struct PixQueueElt * nextP;
-    
+
     for (p = queueP->headP; p; p = nextP) {
         nextP = p->nextP;
         free(p);
@@ -720,9 +721,9 @@ cleanPixels(bit **       const pixels,
         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);
 
@@ -772,7 +773,7 @@ cleanExtended(FILE *             const ifP,
 static void
 pbmclean(FILE *             const ifP,
          FILE *             const ofP,
-         struct cmdlineInfo const cmdline,
+         struct CmdlineInfo const cmdline,
          double *           const nFlippedP) {
 
     if (cmdline.extended) {
@@ -791,7 +792,7 @@ pbmclean(FILE *             const ifP,
 int
 main(int argc, const char *argv[]) {
 
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
     FILE * ifP;
     double nFlipped;
         /* Number of pixels we have flipped so far.  Use type double to
diff --git a/editor/pbmmask.c b/editor/pbmmask.c
index 25c71226..0be10435 100644
--- a/editor/pbmmask.c
+++ b/editor/pbmmask.c
@@ -10,13 +10,57 @@
 ** implied warranty.
 */
 
+#include <stdbool.h>
+#include <assert.h>
+
 #include "pbm.h"
+#include "shhopt.h"
 #include "mallocvar.h"
 
-static bit ** bits;
-static bit ** mask;
-static bit backcolor;
-static int rows, cols;
+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 expand;
+};
+
+
+
+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.
+-----------------------------------------------------------------------------*/
+    optStruct3 opt;  /* set by OPTENT3 */
+    optEntry * option_def;
+    unsigned int option_def_index;
+
+    MALLOCARRAY(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0,   "expand",          OPT_FLAG, NULL, &cmdlineP->expand, 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 */
+
+    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 < 1)
+        cmdlineP->inputFileName = "-";
+    else if (argc-1 == 1)
+        cmdlineP->inputFileName = argv[1];
+    else
+        pm_error("You specified too many arguments (%u).  The only "
+                 "possible argument is the optional input file specification.",
+                 argc-1);
+}
 
 
 
@@ -28,25 +72,78 @@ static int fstackp = 0;
 
 
 static void
-addflood(int const col,
-         int const row) {
+clearMask(bit ** const mask,
+          unsigned int const cols,
+          unsigned int const rows) {
+
+    /* Clear out the mask. */
+    unsigned int row;
 
-    if ( bits[row][col] == backcolor && mask[row][col] == PBM_BLACK ) {
-        if ( fstackp >= fstacksize ) {
-            if ( fstacksize == 0 ) {
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+
+        for (col = 0; col < cols; ++col)
+            mask[row][col] = PBM_BLACK;
+    }
+}
+
+
+
+static bit
+backcolorFmImage(bit **       const bits,
+                 unsigned int const cols,
+                 unsigned int const rows) {
+
+    /* Figure out the background color, by counting along the edge. */
+
+    unsigned int row;
+    unsigned int col;
+    unsigned int wcount;
+
+    assert(cols > 0); assert(rows > 0);
+
+    wcount = 0;
+    for (row = 0; row < rows; ++row) {
+        if (bits[row][0] == PBM_WHITE)
+            ++wcount;
+        if (bits[row][cols - 1] == PBM_WHITE)
+            ++wcount;
+    }
+    for (col = 1; col < cols - 1; ++col) {
+        if (bits[0][col] == PBM_WHITE)
+            ++wcount;
+        if (bits[rows - 1][col] == PBM_WHITE)
+            ++wcount;
+    }
+
+    return (wcount >= rows + cols - 2) ? PBM_WHITE : PBM_BLACK;
+}
+
+
+
+static void
+addflood(bit **       const bits,
+         bit **       const mask,
+         unsigned int const col,
+         unsigned int const row,
+         bit          const backcolor) {
+
+    if (bits[row][col] == backcolor && mask[row][col] == PBM_BLACK) {
+        if (fstackp >= fstacksize) {
+            if (fstacksize == 0) {
                 fstacksize = 1000;
                 MALLOCARRAY(fcols, fstacksize);
                 MALLOCARRAY(frows, fstacksize);
-                if ( fcols == NULL || frows == NULL )
-                    pm_error( "out of memory" );
+                if (fcols == NULL || frows == NULL)
+                    pm_error("out of memory");
             } else {
                 fstacksize *= 2;
                 fcols = (short*) realloc(
-                    (char*) fcols, fstacksize * sizeof(short) );
+                    (char*) fcols, fstacksize * sizeof(short));
                 frows = (short*) realloc(
-                    (char*) frows, fstacksize * sizeof(short) );
-                if ( fcols == (short*) 0 || frows == (short*) 0 )
-                    pm_error( "out of memory" );
+                    (char*) frows, fstacksize * sizeof(short));
+                if (fcols == (short*) 0 || frows == (short*)0)
+                    pm_error("out of memory");
             }
         }
         fcols[fstackp] = col;
@@ -58,46 +155,81 @@ addflood(int const col,
 
 
 static void
-flood(void) {
+floodEdge(bit **       const bits,
+          unsigned int const cols,
+          unsigned int const rows,
+          bit          const backcolor,
+          bit **       const mask) {
+
+    int col;
+    int row;
+
+    /* Flood the entire edge.  Probably the first call will be enough, but
+       might as well be sure.
+    */
+    assert(cols > 0); assert(rows > 0);
+
+    for (col = cols - 3; col >= 2; col -= 2) {
+        addflood(bits, mask, col, rows - 1, backcolor);
+        addflood(bits, mask, col, 0, backcolor);
+    }
+    for (row = rows - 1; row >= 0; row -= 2) {
+        addflood(bits, mask, cols - 1, row, backcolor);
+        addflood(bits, mask, 0, row, backcolor);
+    }
+}
+
+
+
+static void
+flood(bit **       const bits,
+      unsigned int const cols,
+      unsigned int const rows,
+      bit          const backcolor,
+      bit **       const mask) {
+
+    assert(cols > 0); assert(rows > 0);
 
-    while ( fstackp > 0 ) {
+    floodEdge(bits, cols, rows, backcolor, mask);
+
+    while (fstackp > 0) {
         int col, row;
         --fstackp;
         col = fcols[fstackp];
         row = frows[fstackp];
-        if ( bits[row][col] == backcolor && mask[row][col] == PBM_BLACK ) {
+        if (bits[row][col] == backcolor && mask[row][col] == PBM_BLACK) {
             int c;
             mask[row][col] = PBM_WHITE;
-            if ( row - 1 >= 0 )
-                addflood( col, row - 1 );
-            if ( row + 1 < rows )
-                addflood( col, row + 1 );
-            for ( c = col + 1; c < cols; ++c ) {
-                if ( bits[row][c] == backcolor && mask[row][c] == PBM_BLACK ) {
+            if (row - 1 >= 0)
+                addflood(bits, mask, col, row - 1, backcolor);
+            if (row + 1 < rows)
+                addflood(bits, mask, col, row + 1, backcolor);
+            for (c = col + 1; c < cols; ++c) {
+                if (bits[row][c] == backcolor && mask[row][c] == PBM_BLACK) {
                     mask[row][c] = PBM_WHITE;
-                    if ( row - 1 >= 0 && 
-                         ( bits[row - 1][c - 1] != backcolor || 
-                           mask[row - 1][c - 1] != PBM_BLACK ) )
-                        addflood( c, row - 1 );
-                    if ( row + 1 < rows && 
-                         ( bits[row + 1][c - 1] != backcolor || 
-                           mask[row + 1][c - 1] != PBM_BLACK ) )
-                        addflood( c, row + 1 );
+                    if (row - 1 >= 0 &&
+                        (bits[row - 1][c - 1] != backcolor ||
+                         mask[row - 1][c - 1] != PBM_BLACK))
+                        addflood(bits, mask, c, row - 1, backcolor);
+                    if (row + 1 < rows &&
+                         (bits[row + 1][c - 1] != backcolor ||
+                          mask[row + 1][c - 1] != PBM_BLACK))
+                        addflood(bits, mask, c, row + 1, backcolor);
                 }
                 else
                     break;
             }
-            for ( c = col - 1; c >= 0; --c ) {
-                if ( bits[row][c] == backcolor && mask[row][c] == PBM_BLACK ) {
+            for (c = col - 1; c >= 0; --c) {
+                if (bits[row][c] == backcolor && mask[row][c] == PBM_BLACK) {
                     mask[row][c] = PBM_WHITE;
-                    if ( row - 1 >= 0 && 
-                         ( bits[row - 1][c + 1] != backcolor || 
-                           mask[row - 1][c + 1] != PBM_BLACK ) )
-                        addflood( c, row - 1 );
-                    if ( row + 1 < rows && 
-                         ( bits[row + 1][c + 1] != backcolor || 
-                           mask[row + 1][c + 1] != PBM_BLACK ) )
-                        addflood( c, row + 1 );
+                    if (row - 1 >= 0 &&
+                        (bits[row - 1][c + 1] != backcolor ||
+                         mask[row - 1][c + 1] != PBM_BLACK))
+                        addflood(bits, mask, c, row - 1, backcolor);
+                    if (row + 1 < rows &&
+                         (bits[row + 1][c + 1] != backcolor ||
+                          mask[row + 1][c + 1] != PBM_BLACK))
+                        addflood(bits, mask, c, row + 1, backcolor);
                 } else
                     break;
             }
@@ -107,121 +239,103 @@ flood(void) {
 
 
 
-int
-main(int argc, char * argv[]) {
-
-    FILE* ifp;
-    int argn, expand, wcount;
-    register int row, col;
-    const char* const usage = "[-expand] [pbmfile]";
-
-    pbm_init( &argc, argv );
-
-    argn = 1;
-    expand = 0;
-
-    if ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
-    {
-        if ( pm_keymatch( argv[argn], "-expand", 2 ) )
-            expand = 1;
-        else if ( pm_keymatch( argv[argn], "-noexpand", 2 ) )
-            expand = 0;
-        else
-            pm_usage( usage );
-        ++argn;
-    }
+static bit **
+expandedByOnePixel(bit **       const mask,
+                   unsigned int const cols,
+                   unsigned int const rows) {
 
-    if ( argn == argc )
-        ifp = stdin;
-    else
-    {
-        ifp = pm_openr( argv[argn] );
-        ++argn;
+    /* Expand by one pixel. */
+
+    bit ** const emask = pbm_allocarray(cols, rows);
+
+    unsigned int row;
+
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+        for (col = 0; col < cols; ++col)
+            if (mask[row][col] == PBM_BLACK)
+                emask[row][col] = PBM_BLACK;
+            else {
+                unsigned int srow;
+
+                emask[row][col] = PBM_WHITE;
+
+                for (srow = row - 1; srow <= row + 1; ++srow) {
+                    unsigned int scol;
+
+                    for (scol = col - 1; scol <= col + 1; ++scol) {
+                        if (srow >= 0 && srow < rows &&
+                            scol >= 0 && scol < cols &&
+                            mask[srow][scol] == PBM_BLACK) {
+
+                            emask[row][col] = PBM_BLACK;
+                            break;
+                        }
+                    }
+                }
+            }
     }
+    return emask;
+}
+
+
 
-    if ( argn != argc )
-        pm_usage( usage );
+static void
+pbmmask(FILE *             const ifP,
+        FILE *             const ofP,
+        struct CmdlineInfo const cmdline) {
+
+    int cols, rows;
+    bit ** mask;
+    bit ** bits;
+    bit backcolor;
 
-    bits = pbm_readpbm( ifp, &cols, &rows );
+    bits = pbm_readpbm(ifP, &cols, &rows);
 
     if (cols == 0 || rows == 0)
         pm_error("Image contains no pixels, so there is no such thing "
                  "as background and foreground");
 
-    pm_close( ifp );
-    mask = pbm_allocarray( cols, rows );
+    mask = pbm_allocarray(cols, rows);
 
-    /* Clear out the mask. */
-    for ( row = 0; row < rows; ++row )
-        for ( col = 0; col < cols; ++col )
-            mask[row][col] = PBM_BLACK;
+    clearMask(mask, cols, rows);
 
-    /* Figure out the background color, by counting along the edge. */
-    wcount = 0;
-    for ( row = 0; row < rows; ++row ) {
-        if ( bits[row][0] == PBM_WHITE )
-            ++wcount;
-        if ( bits[row][cols - 1] == PBM_WHITE )
-            ++wcount;
-    }
-    for ( col = 1; col < cols - 1; ++col ) {
-        if ( bits[0][col] == PBM_WHITE )
-            ++wcount;
-        if ( bits[rows - 1][col] == PBM_WHITE )
-            ++wcount;
-    }
-    if ( wcount >= rows + cols - 2 )
-        backcolor = PBM_WHITE;
-    else
-        backcolor = PBM_BLACK;
+    backcolor = backcolorFmImage(bits, cols, rows);
 
-    /* Flood the entire edge.  Probably the first call will be enough, but
-       might as well be sure.
-    */
-    for ( col = cols - 3; col >= 2; col -= 2 ) {
-        addflood( col, rows - 1 );
-        addflood( col, 0 );
-    }
-    for ( row = rows - 1; row >= 0; row -= 2 ) {
-        addflood( cols - 1, row );
-        addflood( 0, row );
-    }
-    flood( );
+    flood(bits, cols, rows, backcolor, mask);
 
-    if ( ! expand )
+    if (!cmdline.expand) {
         /* Done. */
-        pbm_writepbm( stdout, mask, cols, rows, 0 );
-    else {
-        /* Expand by one pixel. */
-        int srow, scol;
-        unsigned int row;
-        bit ** emask;
-
-        emask = pbm_allocarray( cols, rows );
-
-        for ( row = 0; row < rows; ++row ) {
-            unsigned int col;
-            for ( col = 0; col < cols; ++col )
-                if ( mask[row][col] == PBM_BLACK )
-                    emask[row][col] = PBM_BLACK;
-                else {
-                    emask[row][col] = PBM_WHITE;
-                    for ( srow = row - 1; srow <= row + 1; ++srow )
-                        for ( scol = col - 1; scol <= col + 1; ++scol )
-                            if ( srow >= 0 && srow < rows &&
-                                 scol >= 0 && scol < cols &&
-                                 mask[srow][scol] == PBM_BLACK ) {
-
-                                emask[row][col] = PBM_BLACK;
-                                break;
-                            }
-                }
-        }
-        pbm_writepbm( stdout, emask, cols, rows, 0 );
+        pbm_writepbm(stdout, mask, cols, rows, 0);
+    } else {
+        bit ** const emask = expandedByOnePixel(mask, cols, rows);
+
+        pbm_writepbm(stdout, emask, cols, rows, 0);
+
+        pbm_freearray(emask, rows);
     }
+}
+
+
+
+int
+main(int argc, const char ** argv) {
 
-    pm_close( stdout );
+    struct CmdlineInfo cmdline;
+    FILE * ifP;
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    pbmmask(ifP, stdout, cmdline);
+
+    pm_close(ifP);
+    pm_close(stdout);
 
     return 0;
 }
 
+
diff --git a/editor/pbmreduce.c b/editor/pbmreduce.c
index ee4a4fbd..3a0968fe 100644
--- a/editor/pbmreduce.c
+++ b/editor/pbmreduce.c
@@ -10,203 +10,345 @@
 ** implied warranty.
 */
 
-#include <limits.h>
+#include "pm_c_util.h"
 #include "pbm.h"
 #include "mallocvar.h"
+#include "shhopt.h"
+#include <assert.h>
 
-int
-main( argc, argv )
-    int argc;
-    char* argv[];
-    {
-    FILE* ifp;
-    register bit** bitslice;
-    register bit* newbitrow;
-    register bit* nbP;
-    int argn, n, rows, cols, format, newrows, newcols;
-    int row, col, limitcol, subrow, subcol, count, direction;
-    const char* const usage = "[-floyd|-fs | -threshold] [-value <val>] N [pbmfile]";
-    int halftone;
-#define QT_FS 1
-#define QT_THRESH 2
 #define SCALE 1024
 #define HALFSCALE 512
-    long threshval, sum;
-    long* thiserr;  /* used for Floyd-Steinberg stuff */
-    long* nexterr;  /* used for Floyd-Steinberg stuff */
-    long* temperr;
-
-
-    pbm_init( &argc, argv );
-
-    argn = 1;
-    halftone = QT_FS;
-    threshval = HALFSCALE;
-
-    while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
-	{
-	if ( pm_keymatch( argv[argn], "-fs", 2 ) ||
-	     pm_keymatch( argv[argn], "-floyd", 2 ) )
-	    halftone = QT_FS;
-	else if ( pm_keymatch( argv[argn], "-threshold", 2 ) )
-	    halftone = QT_THRESH;
-	else if ( pm_keymatch( argv[argn], "-value", 2 ) )
-	    {
-	    float f;
-
-	    ++argn;
-	    if ( argn == argc || sscanf( argv[argn], "%f", &f ) != 1 ||
-		 f < 0.0 || f > 1.0 )
-		pm_usage( usage );
-	    threshval = f * SCALE;
-	    }
-	else
-	    pm_usage( usage );
-	++argn;
-	}
-
-    if ( argn == argc )
-	pm_usage( usage );
-    if ( sscanf( argv[argn], "%d", &n ) != 1 )
-	pm_usage( usage );
-    if ( n < 2 )
-	pm_error( "N must be greater than 1" );
-    if (n > INT_MAX / n)
-        pm_error("Scale argument too large.  You specified %d", n);
-    ++argn;
-
-    if ( argn == argc )
-	ifp = stdin;
-    else
-	{
-	ifp = pm_openr( argv[argn] );
-	++argn;
-	}
-
-    if ( argn != argc )
-	pm_usage( usage );
-
-    pbm_readpbminit( ifp, &cols, &rows, &format );
-    bitslice = pbm_allocarray( cols, n );
-
-    newrows = rows / n;
-    newcols = cols / n;
+
+
+enum Halftone {QT_FS, QT_THRESH};
+
+struct CmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char *  inputFilespec;
+    enum Halftone halftone;
+    int           value;
+    unsigned int  randomseed;
+    unsigned int  randomseedSpec;
+    int           scale;
+};
+
+
+
+static void
+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 pm_optParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+    unsigned int floydOpt, thresholdOpt;
+    unsigned int valueSpec;
+    float        value;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0, "floyd",       OPT_FLAG,  NULL,
+            &floydOpt,                      0);
+    OPTENT3(0, "fs",          OPT_FLAG,  NULL,
+            &floydOpt,                      0);
+    OPTENT3(0, "threshold",   OPT_FLAG,  NULL,
+            &thresholdOpt,                  0);
+    OPTENT3(0, "value",       OPT_FLOAT, &value,
+            &valueSpec,                     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 */
+
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (floydOpt + thresholdOpt == 0)
+        cmdlineP->halftone = QT_FS;
+    else if (!!floydOpt + !!thresholdOpt > 1)
+        pm_error("Cannot specify both floyd and threshold");
+    else {
+        if (floydOpt)
+            cmdlineP->halftone = QT_FS;
+        else {
+            cmdlineP->halftone = QT_THRESH;
+            if (cmdlineP->randomseedSpec)
+                pm_message("-randomseed value has no effect with -threshold");
+        }
+    }
+
+    if (!valueSpec)
+        cmdlineP->value = HALFSCALE;
+    else {
+        if (value < 0.0)
+            pm_error("-value cannot be negative.  You specified %f", value);
+        if (value > 1.0)
+            pm_error("-value cannot be greater than one.  You specified %f",
+                     value);
+        else
+            cmdlineP->value = value * SCALE;
+    }
+
+    if (argc-1 > 0) {
+        char * endptr;   /* ptr to 1st invalid character in scale arg */
+        unsigned int scale;
+
+        scale = strtol(argv[1], &endptr, 10);
+        if (*argv[1] == '\0') 
+            pm_error("Scale argument is a null string.  Must be a number.");
+        else if (*endptr != '\0')
+            pm_error("Scale argument contains non-numeric character '%c'.",
+                     *endptr);
+        else if (scale < 2)
+            pm_error("Scale argument must be at least 2.  "
+                     "You specified %d", scale);
+        else if (scale > INT_MAX / scale)
+            pm_error("Scale argument too large.  You specified %d", scale);
+        else 
+            cmdlineP->scale = scale;
+
+        if (argc-1 > 1) {
+            cmdlineP->inputFilespec = argv[2];
+
+            if (argc-1 > 2)
+                pm_error("Too many arguments (%d).  There are at most two "
+                         "non-option arguments: "
+                         "scale factor and the file name",
+                         argc-1);
+        } else
+            cmdlineP->inputFilespec = "-";
+    } else
+        pm_error("You must specify the scale factor as an argument");
+
+    free(option_def);
+}
+
+
+
+struct FS {
+  int * thiserr;
+  int * nexterr;
+};
+
+
+static void
+initializeFloydSteinberg(struct FS  * const fsP,
+                         int          const newcols,
+                         unsigned int const seed,
+                         bool         const seedSpec) {
+
+    unsigned int col;
+
+    MALLOCARRAY(fsP->thiserr, newcols + 2);
+    MALLOCARRAY(fsP->nexterr, newcols + 2);
+
+    if (fsP->thiserr == NULL || fsP->nexterr == NULL)
+        pm_error("out of memory");
+
+    srand(seedSpec ? seed : pm_randseed());
+
+    for (col = 0; col < newcols + 2; ++col)
+        fsP->thiserr[col] = (rand() % SCALE - HALFSCALE) / 4;
+        /* (random errors in [-SCALE/8 .. SCALE/8]) */
+}
+
+
+
+/*
+    Scanning method
+    
+    In Floyd-Steinberg dithering mode horizontal direction of scan alternates
+    between rows; this is called "serpentine scanning".
+    
+    Example input (14 x 7), N=3:
+    
+    111222333444xx    Fractional pixels on the right edge and bottom edge (x)
+    111222333444xx    are ignored; their values do not influence output. 
+    111222333444xx
+    888777666555xx
+    888777666555xx
+    888777666555xx
+    xxxxxxxxxxxxxx
+    
+    Output (4 x 2):
+    
+    1234
+    8765
+
+*/
+
+
+
+enum Direction { RIGHT_TO_LEFT, LEFT_TO_RIGHT };
+
+
+static enum Direction
+oppositeDir(enum Direction const arg) {
+
+    switch (arg) {
+    case LEFT_TO_RIGHT: return RIGHT_TO_LEFT;
+    case RIGHT_TO_LEFT: return LEFT_TO_RIGHT;
+    }
+    assert(false);  /* All cases handled above */
+}
+
+
+
+int
+main(int argc, const char * argv[]) {
+
+    FILE * ifP;
+    struct CmdlineInfo cmdline;
+    bit ** bitslice;
+    bit * newbitrow;
+    int rows, cols;
+    int format;
+    unsigned int newrows, newcols;
+    unsigned int row;
+    enum Direction direction;
+    struct FS fs;
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFilespec);
+
+    pbm_readpbminit(ifP, &cols, &rows, &format);
+
+    bitslice = pbm_allocarray(cols, cmdline.scale);
+
+    if (rows < cmdline.scale || cols < cmdline.scale)
+        pm_error("Scale argument (%u) too large for image", cmdline.scale);
+    else {
+        newrows = rows / cmdline.scale;
+        newcols = cols / cmdline.scale;
+    }
     pbm_writepbminit( stdout, newcols, newrows, 0 );
-    newbitrow = pbm_allocrow( newcols );
+    newbitrow = pbm_allocrow_packed( newcols );
 
-    if (halftone == QT_FS) {
-        unsigned int col;
-        /* Initialize Floyd-Steinberg. */
-        MALLOCARRAY(thiserr, newcols + 2);
-        MALLOCARRAY(nexterr, newcols + 2);
-        if (thiserr == NULL || nexterr == NULL)
-            pm_error("out of memory");
-
-        srand(pm_randseed());
-        for (col = 0; col < newcols + 2; ++col)
-            thiserr[col] = (rand() % SCALE - HALFSCALE) / 4;
-	    /* (random errors in [-SCALE/8 .. SCALE/8]) */
-	} else {
+    if (cmdline.halftone == QT_FS)
+        initializeFloydSteinberg(&fs, newcols,
+                                 cmdline.randomseed, cmdline.randomseedSpec);
+    else {
         /* These variables are meaningless in this case, and the values
            should never be used.
         */
-        thiserr = NULL;
-        nexterr = NULL;
+        fs.thiserr = NULL;
+        fs.nexterr = NULL;
     }
-    direction = 1;
-
-    for ( row = 0; row < newrows; ++row )
-	{
-	for ( subrow = 0; subrow < n; ++subrow )
-	    pbm_readpbmrow( ifp, bitslice[subrow], cols, format );
-
-	if ( halftone == QT_FS )
-	    for ( col = 0; col < newcols + 2; ++col )
-		nexterr[col] = 0;
-	if ( direction )
-	    {
-	    col = 0;
-	    limitcol = newcols;
-	    nbP = newbitrow;
-	    }
-	else
-	    {
-	    col = newcols - 1;
-	    limitcol = -1;
-	    nbP = &(newbitrow[col]);
-	    }
-
-	do
-	    {
-	    sum = 0;
-	    count = 0;
-	    for ( subrow = 0; subrow < n; ++subrow )
-		for ( subcol = 0; subcol < n; ++subcol )
-		    if ( row * n + subrow < rows && col * n + subcol < cols )
-			{
-			count += 1;
-			if ( bitslice[subrow][col * n + subcol] == PBM_WHITE )
-			    sum += 1;
-			}
-	    sum = ( sum * SCALE ) / count;
-
-	    if ( halftone == QT_FS )
-		sum += thiserr[col + 1];
-
-	    if ( sum >= threshval )
-		{
-		*nbP = PBM_WHITE;
-		if ( halftone == QT_FS )
-		    sum = sum - threshval - HALFSCALE;
-		}
-	    else
-		*nbP = PBM_BLACK;
-
-	    if ( halftone == QT_FS )
-		{
-		if ( direction )
-		    {
-		    thiserr[col + 2] += ( sum * 7 ) / 16;
-		    nexterr[col    ] += ( sum * 3 ) / 16;
-		    nexterr[col + 1] += ( sum * 5 ) / 16;
-		    nexterr[col + 2] += ( sum     ) / 16;
-		    }
-		else
-		    {
-		    thiserr[col    ] += ( sum * 7 ) / 16;
-		    nexterr[col + 2] += ( sum * 3 ) / 16;
-		    nexterr[col + 1] += ( sum * 5 ) / 16;
-		    nexterr[col    ] += ( sum     ) / 16;
-		    }
-		}
-	    if ( direction )
-		{
-		++col;
-		++nbP;
-		}
-	    else
-		{
-		--col;
-		--nbP;
-		}
-	    }
-	while ( col != limitcol );
-
-	pbm_writepbmrow( stdout, newbitrow, newcols, 0 );
-
-	if ( halftone == QT_FS )
-	    {
-	    temperr = thiserr;
-	    thiserr = nexterr;
-	    nexterr = temperr;
-	    direction = ! direction;
-	    }
-	}
-
-    pm_close( ifp );
-    pm_close( stdout );
-
-    exit( 0 );
+
+    for (row = 0, direction = LEFT_TO_RIGHT; row < newrows; ++row) {
+        unsigned int const colChars = pbm_packed_bytes(newcols);
+
+        unsigned int colChar;
+        unsigned int subrow;
+        unsigned int col;
+        int limitCol;
+        int startCol;
+        int step;
+   
+        for (colChar = 0; colChar < colChars; ++colChar)
+            newbitrow[colChar] = 0x00;  /* Clear to white */
+ 
+        for (subrow = 0; subrow < cmdline.scale; ++subrow)
+            pbm_readpbmrow(ifP, bitslice[subrow], cols, format);
+
+        if (cmdline.halftone == QT_FS) {
+            unsigned int col;
+            for (col = 0; col < newcols + 2; ++col)
+                fs.nexterr[col] = 0;
+        }
+        switch (direction) {
+        case LEFT_TO_RIGHT: {
+            startCol = 0;
+            limitCol = newcols;
+            step = +1;  
+        } break;
+        case RIGHT_TO_LEFT: {
+            startCol = newcols - 1;
+            limitCol = -1;
+            step = -1;
+        } break;
+        }
+
+        for (col = startCol; col != limitCol; col += step) {
+            int const n = cmdline.scale;
+            unsigned int sum;
+            int sumScaled;
+            unsigned int subrow;
+
+            for (subrow = 0, sum = 0; subrow < n; ++subrow) {
+                unsigned int subcol;
+                for (subcol = 0; subcol < n; ++subcol) {
+                    assert(row * n + subrow < rows);
+                    assert(col * n + subcol < cols);
+                    if (bitslice[subrow][col * n + subcol] == PBM_WHITE)
+                        ++sum;
+                }
+            }
+
+            sumScaled = (sum * SCALE) / (SQR(n));
+
+            if (cmdline.halftone == QT_FS)
+                sumScaled += fs.thiserr[col + 1];
+
+            if (sumScaled >= cmdline.value) {
+                if (cmdline.halftone == QT_FS)
+                    sumScaled = sumScaled - cmdline.value - HALFSCALE;
+            } else
+                newbitrow[col/8] |= (PBM_BLACK << (7 - col%8));
+
+            if (cmdline.halftone == QT_FS) {
+                switch (direction) {
+                case LEFT_TO_RIGHT: {
+                    fs.thiserr[col + 2] += ( sumScaled * 7 ) / 16;
+                    fs.nexterr[col    ] += ( sumScaled * 3 ) / 16;
+                    fs.nexterr[col + 1] += ( sumScaled * 5 ) / 16;
+                    fs.nexterr[col + 2] += ( sumScaled     ) / 16;
+                    break;
+                }
+                case RIGHT_TO_LEFT: {
+                    fs.thiserr[col    ] += ( sumScaled * 7 ) / 16;
+                    fs.nexterr[col + 2] += ( sumScaled * 3 ) / 16;
+                    fs.nexterr[col + 1] += ( sumScaled * 5 ) / 16;
+                    fs.nexterr[col    ] += ( sumScaled     ) / 16;
+                    break;
+                }
+                }
+            }
+        }
+
+        pbm_writepbmrow_packed(stdout, newbitrow, newcols, 0);
+
+        if (cmdline.halftone == QT_FS) {
+            int * const temperr = fs.thiserr;
+            fs.thiserr = fs.nexterr;
+            fs.nexterr = temperr;
+            direction  = oppositeDir(direction);
+        }
     }
 
+    free(fs.thiserr);
+    free(fs.nexterr);
+
+    pbm_freerow(newbitrow);
+    pbm_freearray(bitslice, cmdline.scale);
+    pm_close(ifP);
+    pm_close(stdout);
+
+    return 0;
+}
+
 
diff --git a/editor/pgmmedian.c b/editor/pgmmedian.c
index 9d90b6b3..4648af68 100644
--- a/editor/pgmmedian.c
+++ b/editor/pgmmedian.c
@@ -132,13 +132,12 @@ select489(gray * const a,
 
     gray t;
     int i, j, l, r;
-    int ptmp, ttmp;
+    int ptmp;
 
     l = 0;
     r = n - 1;
     while ( r > l ) {
         t = a[parray[k]];
-        ttmp = parray[k];
         i = l;
         j = r;
         ptmp = parray[l];
diff --git a/editor/pnmconvol.c b/editor/pnmconvol.c
index 8d9bb83a..b1d8e025 100644
--- a/editor/pnmconvol.c
+++ b/editor/pnmconvol.c
@@ -169,7 +169,7 @@ getMatrixOptDimensions(const char *   const matrixOptString,
                 if (colCt != *widthP)
                 pm_error("-matrix option value contains rows of different "
                          "widths: %u and %u", *widthP, colCt);
-            }            
+            }
             pm_strfree(rowString);
             cursor = next;
 
@@ -211,7 +211,7 @@ parseMatrixRow(const char * const matrixOptRowString,
                 char * trailingJunk;
                 weight[col] = strtod(colString, &trailingJunk);
 
-                if (*trailingJunk != '\0') 
+                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);
@@ -235,7 +235,7 @@ parseMatrixOptWithDimensions(const char * const matrixOptString,
                              unsigned int const width,
                              unsigned int const height,
                              float **     const weight) {
-    
+
     unsigned int row;
     const char * cursor;
 
@@ -262,7 +262,7 @@ parseMatrixOptWithDimensions(const char * const matrixOptString,
             }
         }
     }
-}    
+}
 
 
 
@@ -311,7 +311,7 @@ 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.  
+   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.
@@ -336,9 +336,9 @@ parseCommandLine(int argc, char ** argv,
             &cmdlineP->matrixSpec,     0)
     OPTENT3(0, "matrixfile",   OPT_STRINGLIST, &cmdlineP->matrixfile,
             &matrixfileSpec,           0)
-    OPTENT3(0, "nooffset",     OPT_FLAG,   NULL,                  
+    OPTENT3(0, "nooffset",     OPT_FLAG,   NULL,
             &cmdlineP->nooffset,       0);
-    OPTENT3(0, "normalize",    OPT_FLAG,   NULL,                  
+    OPTENT3(0, "normalize",    OPT_FLAG,   NULL,
             &cmdlineP->normalize,      0);
     OPTENT3(0, "bias",         OPT_UINT,   &cmdlineP->bias,
             &biasSpec,                 0);
@@ -382,7 +382,7 @@ parseCommandLine(int argc, char ** argv,
                      "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 "
@@ -394,7 +394,7 @@ parseCommandLine(int argc, char ** argv,
                 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");
@@ -416,11 +416,11 @@ struct ConvKernel {
     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.  
+           positive or negative.
         */
     unsigned int bias;
         /* The amount to be added to the linear combination of sample values.
@@ -441,7 +441,7 @@ warnBadKernel(struct ConvKernel * const convKernelP) {
 
     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) {
@@ -462,9 +462,9 @@ warnBadKernel(struct ConvKernel * const convKernelP) {
             if (sum[plane] < 0.0)
                 negative = true;
         }
-    
+
         if (biased) {
-            pm_message("WARNING - this convolution matrix is 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],
@@ -488,15 +488,14 @@ warnBadKernel(struct ConvKernel * const convKernelP) {
 
 static void
 convKernelCreatePnm(struct pam *         const cpamP,
-                    tuple * const *      const ctuples, 
+                    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
-   '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.
+   Compute the convolution matrix 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
@@ -527,7 +526,7 @@ convKernelCreatePnm(struct pam *         const cpamP,
         unsigned int row;
 
         MALLOCARRAY_NOFAIL(convKernelP->weight[plane], cpamP->height);
-    
+
         for (row = 0; row < cpamP->height; ++row) {
             unsigned int col;
 
@@ -570,15 +569,18 @@ convKernelDestroy(struct ConvKernel * const convKernelP) {
 static void
 normalizeKernelPlane(struct ConvKernel * const convKernelP,
                      unsigned int        const plane) {
-
+/*----------------------------------------------------------------------------
+   Modify *convKernelP by scaling every weight in plane 'plane' by the same
+   factor such that the weights in the plane all add up to 1.
+-----------------------------------------------------------------------------*/
     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];
         }
     }
@@ -590,7 +592,7 @@ normalizeKernelPlane(struct ConvKernel * const convKernelP,
 
         for (row = 0; row < convKernelP->rows; ++row) {
             unsigned int col;
-                
+
             for (col = 0; col < convKernelP->cols; ++col)
                 convKernelP->weight[plane][row][col] *= scaler;
         }
@@ -602,8 +604,9 @@ normalizeKernelPlane(struct ConvKernel * const convKernelP,
 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.
+   Modify *convKernelP by scaling each plane as follows: Scale every weight in
+   the plane by the same factor such that the weights in the plane all add up
+   to 1.
 -----------------------------------------------------------------------------*/
     unsigned int plane;
 
@@ -638,7 +641,7 @@ getKernelPnm(const char *         const 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);
@@ -648,20 +651,14 @@ getKernelPnm(const char *         const fileName,
 
 static void
 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.
+   The option value is 'matrixOpt'.
 -----------------------------------------------------------------------------*/
     struct ConvKernel * convKernelP;
     unsigned int plane;
@@ -681,17 +678,14 @@ convKernelCreateMatrixOpt(struct matrixOpt     const matrixOpt,
 
             MALLOCARRAY_NOFAIL(convKernelP->weight[plane][row],
                                matrixOpt.width);
-    
+
             for (col = 0; col < matrixOpt.width; ++col)
                 convKernelP->weight[plane][row][col] =
                     matrixOpt.weight[row][col];
         }
     }
-    if (normalize)
-        normalizeKernel(convKernelP);
 
     convKernelP->bias = bias;
-
     *convKernelPP = convKernelP;
 }
 
@@ -734,12 +728,12 @@ parsePlaneFileLine(const char *   const line,
         else {
             char * trailingJunk;
             weight[colCt] = strtod(token, &trailingJunk);
-            if (*trailingJunk != '\0') 
+            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;
+            ++colCt;
         }
         pm_strfree(token);
     }
@@ -750,7 +744,7 @@ parsePlaneFileLine(const char *   const line,
 
 
 static void
-readPlaneFile(FILE *         const ifP, 
+readPlaneFile(FILE *         const ifP,
               float ***      const weightP,
               unsigned int * const widthP,
               unsigned int * const heightP) {
@@ -789,7 +783,7 @@ readPlaneFile(FILE *         const ifP,
                 eof = true;
             else {
                 REALLOCARRAY(weight, rowCt + 1);
-            
+
                 if (weight == NULL)
                     pm_error("Unable to allocate memory for "
                              "convolution matrix");
@@ -805,7 +799,7 @@ readPlaneFile(FILE *         const ifP,
                             pm_error("Multiple row widths in the convolution "
                                      "matrix file: %u columns and %u columns.",
                                      width, thisWidth);
-                    }                    
+                    }
                     ++rowCt;
                 }
                 pm_strfree(line);
@@ -824,7 +818,7 @@ readPlaneFile(FILE *         const ifP,
 static void
 copyWeight(float **       const srcWeight,
            unsigned int   const width,
-           unsigned int   const height, 
+           unsigned int   const height,
            float ***      const dstWeightP) {
 /*----------------------------------------------------------------------------
    Make a copy, in dynamically allocated memory, of the weight matrix
@@ -838,7 +832,7 @@ copyWeight(float **       const srcWeight,
 
     if (dstWeight == NULL)
         pm_error("Could not allocate memory for convolution matrix");
-   
+
     for (row = 0; row < height; ++row) {
         unsigned int col;
 
@@ -859,7 +853,6 @@ copyWeight(float **       const srcWeight,
 
 static void
 convKernelCreateSimpleFile(const char **        const fileNameList,
-                           bool                 const normalize,
                            unsigned int         const depth,
                            unsigned int         const bias,
                            struct ConvKernel ** const convKernelPP) {
@@ -869,11 +862,6 @@ convKernelCreateSimpleFile(const char **        const fileNameList,
    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;
@@ -923,9 +911,6 @@ convKernelCreateSimpleFile(const char **        const fileNameList,
         }
     }
 
-    if (normalize)
-        normalizeKernel(convKernelP);
-
     convKernelP->cols = width;
     convKernelP->rows = height;
     convKernelP->bias = bias;
@@ -953,11 +938,14 @@ getKernel(struct cmdlineInfo   const cmdline,
         getKernelPnm(cmdline.pnmMatrixFileName, depth, !cmdline.nooffset,
                      &convKernelP);
     else if (cmdline.matrixfile)
-        convKernelCreateSimpleFile(cmdline.matrixfile, cmdline.normalize,
-                                   depth, cmdline.bias, &convKernelP);
+        convKernelCreateSimpleFile(cmdline.matrixfile, depth, cmdline.bias,
+            &convKernelP);
     else if (cmdline.matrixSpec)
-        convKernelCreateMatrixOpt(cmdline.matrix, cmdline.normalize,
-                                  depth, cmdline.bias, &convKernelP);
+        convKernelCreateMatrixOpt(cmdline.matrix, depth, cmdline.bias,
+            &convKernelP);
+
+    if (cmdline.normalize)
+        normalizeKernel(convKernelP);
 
     warnBadKernel(convKernelP);
 
@@ -985,7 +973,7 @@ validateEnoughImageToConvolve(const struct pam *        const inpamP,
         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.",
@@ -1006,7 +994,7 @@ allocRowbuf(struct pam * const pamP,
         pm_error("Failed to allocate %u-row buffer", height);
     else {
         unsigned int row;
-    
+
         for (row = 0; row < height; ++row)
             rowbuf[row] = pnm_allocpamrow(pamP);
     }
@@ -1055,7 +1043,7 @@ readAndScaleRows(struct pam *              const inpamP,
                  unsigned int              const outputDepth) {
 /*----------------------------------------------------------------------------
   Read in 'count' rows into rowbuf[].
-  
+
   Scale the contents to maxval 'outputMaxval' and expand to depth
   'outputDepth'.
 -----------------------------------------------------------------------------*/
@@ -1185,7 +1173,7 @@ convolveGeneralRowPlane(struct pam *              const pamP,
     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 */
@@ -1295,7 +1283,7 @@ allocSum(unsigned int const depth,
 
         for (plane = 0; plane < depth; ++plane) {
             MALLOCARRAY(sum[plane], size);
-            
+
             if (!sum[plane])
                 pm_error("Could not allocate memory for %u sums", size);
         }
@@ -1342,7 +1330,7 @@ computeInitialColumnSums(struct pam *              const pamP,
                  row < convKernelP->rows;
                  ++row)
                 convColumnSum[plane][col] += window[row][col][plane];
-        }            
+        }
     }
 }
 
@@ -1370,7 +1358,7 @@ convolveRowWithColumnSumsMean(const struct ConvKernel * const convKernelP,
   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;
@@ -1397,7 +1385,7 @@ convolveRowWithColumnSumsMean(const struct ConvKernel * const convKernelP,
                 } else {
                     /* Column numbers to subtract or add to isum */
                     unsigned int const subcol = col - ccolso2 - 1;
-                    unsigned int const addcol = col + ccolso2;  
+                    unsigned int const addcol = col + ccolso2;
 
                     gisum -= convColumnSum[plane][subcol];
                     gisum += convColumnSum[plane][addcol];
@@ -1441,7 +1429,7 @@ convolveRowWithColumnSumsVertical(
 
     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 */
@@ -1525,12 +1513,12 @@ convolveMeanRowPlane(struct pam *              const pamP,
             } else {
                 /* Column numbers to subtract or add to isum */
                 unsigned int const subcol = col - ccolso2 - 1;
-                unsigned int const addcol = col + ccolso2;  
-                
+                unsigned int const addcol = col + ccolso2;
+
                 convColumnSum[addcol] = convColumnSum[addcol]
                     - window[subrow][addcol][plane]
                     + window[addrow][addcol][plane];
-                
+
                 gisum = gisum - convColumnSum[subcol] + convColumnSum[addcol];
             }
             outputrow[col][plane] =
@@ -1686,7 +1674,7 @@ allocRowSum(unsigned int const depth,
 
         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);
@@ -1695,7 +1683,7 @@ allocRowSum(unsigned int const depth,
 
                 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");
@@ -1759,7 +1747,7 @@ convolveHorizontalRowPlane0(struct pam *              const outpamP,
                    starts at the left edge of the image.
                 */
                 unsigned int const leftcol = 0;
-            
+
                 unsigned int crow;
 
                 for (crow = 0, matrixSum = 0.0;
@@ -1768,7 +1756,7 @@ convolveHorizontalRowPlane0(struct pam *              const outpamP,
                     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];
@@ -1781,7 +1769,7 @@ convolveHorizontalRowPlane0(struct pam *              const outpamP,
                 unsigned int const addcol  = col + ccolso2;
 
                 unsigned int crow;
-                
+
                 for (crow = 0, matrixSum = 0.0;
                      crow < convKernelP->rows;
                      ++crow) {
@@ -1810,7 +1798,7 @@ setupCircMap2(tuple **     const rowbuf,
               unsigned int const windowHeight) {
 
     unsigned int const toprow = windowTopRow % windowHeight;
-    
+
     unsigned int crow;
     unsigned int i;
 
@@ -1886,7 +1874,7 @@ convolveHorizontalRowPlane(struct pam *              const pamP,
             }
         } else {
             unsigned int const subcol  = col - ccolso2 - 1;
-            unsigned int const addcol  = col + ccolso2;  
+            unsigned int const addcol  = col + ccolso2;
 
             unsigned int crow;
 
@@ -1959,7 +1947,7 @@ convolveHorizontal(struct pam *              const inpamP,
 
         for (crow = 0; crow < convKernelP->rows; ++crow)
             sumCircMap[crow] = convRowSum[plane][crow];
- 
+
         convolveHorizontalRowPlane0(outpamP, circMap, convKernelP, plane,
                                     outputrow, sumCircMap);
     }
@@ -1981,13 +1969,13 @@ convolveHorizontal(struct pam *              const inpamP,
 
             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);
         }
     }
@@ -2017,7 +2005,7 @@ convolveVerticalRowPlane(struct pam *              const pamP,
         */
     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) {
@@ -2316,7 +2304,7 @@ main(int argc, char * argv[]) {
  original) in January 1995.
 
  Reduce run time by general optimizations and handling special cases of
- convolution matrices.  Program automatically determines if convolution 
+ convolution matrices.  Program automatically determines if convolution
  matrix is one of the types it can make use of so no extra command line
  arguments are necessary.
 
@@ -2334,7 +2322,7 @@ main(int argc, char * argv[]) {
  -------------------------------------------
  Created separate functions as code was getting too large to put keep both
  PGM and PPM cases in same function and also because SWITCH statement in
- inner loop can take progressively more time the larger the size of the 
+ inner loop can take progressively more time the larger the size of the
  convolution matrix.  GCC is affected this way.
 
  Removed use of MOD (%) operator from innermost loop by modifying manner in
@@ -2343,50 +2331,50 @@ main(int argc, char * argv[]) {
  This is from the file pnmconvol.README, dated August 1995, extracted in
  April 2000, which was in the March 1994 Netpbm release:
 
- ----------------------------------------------------------------------------- 
+ -----------------------------------------------------------------------------
  This is a faster version of the pnmconvol.c program that comes with netpbm.
  There are no changes to the command line arguments, so this program can be
  dropped in without affecting the way you currently run it.  An updated man
  page is also included.
- 
+
  My original intention was to improve the running time of applying a
  neighborhood averaging convolution matrix to an image by using a different
  algorithm, but I also improved the run time of performing the general
  convolution by optimizing that code.  The general convolution runs in 1/4 to
  1/2 of the original time and neighborhood averaging runs in near constant
  time for the convolution masks I tested (3x3, 5x5, 7x7, 9x9).
- 
+
  Sample times for two computers are below.  Times are in seconds as reported
  by /bin/time for a 512x512 pgm image.
- 
+
  Matrix                  IBM RS6000      SUN IPC
  Size & Type                220
- 
+
  3x3
  original pnmconvol         6.3            18.4
  new general case           3.1             6.0
  new average case           1.8             2.6
- 
+
  5x5
  original pnmconvol        11.9            44.4
  new general case           5.6            11.9
  new average case           1.8             2.6
- 
+
  7x7
  original pnmconvol        20.3            82.9
  new general case           9.4            20.7
  new average case           1.8             2.6
- 
+
  9x9
  original pnmconvol        30.9           132.4
  new general case          14.4            31.8
  new average case           1.8             2.6
- 
- 
+
+
  Send all questions/comments/bugs to me at burns@chem.psu.edu.
- 
+
  - Mike
- 
+
  ----------------------------------------------------------------------------
  Mike Burns                                              System Administrator
  burns@chem.psu.edu                                   Department of Chemistry
diff --git a/editor/pnmcrop.c b/editor/pnmcrop.c
index c6aabff1..d6bae1d3 100644
--- a/editor/pnmcrop.c
+++ b/editor/pnmcrop.c
@@ -1,4 +1,4 @@
-/* pnmcrop.c - crop a portable anymap
+/* pnmcrop.c - crop a Netpbm image
 **
 ** Copyright (C) 1988 by Jef Poskanzer.
 **
@@ -29,10 +29,24 @@
 #include "pnm.h"
 #include "shhopt.h"
 #include "mallocvar.h"
+#include "nstring.h"
 
-enum bg_choice {BG_BLACK, BG_WHITE, BG_DEFAULT, BG_SIDES};
+static double const sqrt3 = 1.73205080756887729352;
+    /* The square root of 3 */
+static double const EPSILON = 1.0e-5;
 
-typedef enum { LEFT = 0, RIGHT = 1, TOP = 2, BOTTOM = 3} edgeLocation;
+enum BgChoice {BG_BLACK, BG_WHITE, BG_DEFAULT, BG_SIDES, BG_CORNER, BG_COLOR};
+
+enum BaseOp {OP_CROP, OP_REPORT_FULL, OP_REPORT_SIZE};
+
+enum BlankMode {BLANK_ABORT, BLANK_PASS, BLANK_MINIMIZE, BLANK_MAXCROP};
+
+typedef enum {LEFT = 0, RIGHT = 1, TOP = 2, BOTTOM = 3} EdgeLocation;
+
+typedef struct {
+    EdgeLocation v;
+    EdgeLocation h;
+} CornerLocation;
 
 static const char * const edgeName[] = {
     "left",
@@ -53,24 +67,32 @@ typedef enum {
         /* Immediately after the raster */
 } imageFilePos;
 
-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;
-    enum bg_choice background;
+    enum BaseOp baseOperation;
+    enum BgChoice background;
     bool wantCrop[4];
         /* User wants crop of left, right, top, bottom, resp. */
-    unsigned int verbose;
     unsigned int margin;
     const char * borderfile;  /* NULL if none */
+    float closeness;
+    CornerLocation bgCorner;     /* valid if background == BG_CORNER */
+    const char * bgColor;  /* valid if background == BG_COLOR */
+        /* Note that we can have only the name of the color, not the color
+           itself, because we don't know the maxval at option parsing time.
+        */
+    enum BlankMode blankMode;
+    unsigned int verbose;
 };
 
 
 
 static void
 parseCommandLine(int argc, const char ** argv,
-                 struct cmdlineInfo *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.
@@ -81,26 +103,42 @@ parseCommandLine(int argc, const char ** argv,
     optStruct3 opt;
 
     unsigned int blackOpt, whiteOpt, sidesOpt;
-    unsigned int marginSpec, borderfileSpec;
+    unsigned int marginSpec, borderfileSpec, closenessSpec;
     unsigned int leftOpt, rightOpt, topOpt, bottomOpt;
-    
+    unsigned int bgCornerSpec, bgColorSpec;
+    unsigned int blankModeSpec;
+    unsigned int reportFullOpt, reportSizeOpt;
+
     unsigned int option_def_index;
 
+    char * bgCornerOpt;
+    char * blankModeOpOpt;
+
     MALLOCARRAY_NOFAIL(option_def, 100);
 
     option_def_index = 0;   /* incremented by OPTENT3 */
-    OPTENT3(0, "black",      OPT_FLAG, NULL, &blackOpt,            0);
-    OPTENT3(0, "white",      OPT_FLAG, NULL, &whiteOpt,            0);
-    OPTENT3(0, "sides",      OPT_FLAG, NULL, &sidesOpt,            0);
-    OPTENT3(0, "left",       OPT_FLAG, NULL, &leftOpt,             0);
-    OPTENT3(0, "right",      OPT_FLAG, NULL, &rightOpt,            0);
-    OPTENT3(0, "top",        OPT_FLAG, NULL, &topOpt,              0);
-    OPTENT3(0, "bottom",     OPT_FLAG, NULL, &bottomOpt,           0);
-    OPTENT3(0, "verbose",    OPT_FLAG, NULL, &cmdlineP->verbose,   0);
-    OPTENT3(0, "margin",     OPT_UINT,   &cmdlineP->margin,    
+    OPTENT3(0, "black",       OPT_FLAG,   NULL, &blackOpt,            0);
+    OPTENT3(0, "white",       OPT_FLAG,   NULL, &whiteOpt,            0);
+    OPTENT3(0, "sides",       OPT_FLAG,   NULL, &sidesOpt,            0);
+    OPTENT3(0, "bg-color",    OPT_STRING, &cmdlineP->bgColor,
+            &bgColorSpec,   0);
+    OPTENT3(0, "bg-corner",   OPT_STRING, &bgCornerOpt,
+            &bgCornerSpec,  0);
+    OPTENT3(0, "left",        OPT_FLAG,   NULL, &leftOpt,             0);
+    OPTENT3(0, "right",       OPT_FLAG,   NULL, &rightOpt,            0);
+    OPTENT3(0, "top",         OPT_FLAG,   NULL, &topOpt,              0);
+    OPTENT3(0, "bottom",      OPT_FLAG,   NULL, &bottomOpt,           0);
+    OPTENT3(0, "margin",      OPT_UINT,   &cmdlineP->margin,
             &marginSpec,     0);
-    OPTENT3(0, "borderfile", OPT_STRING, &cmdlineP->borderfile,
+    OPTENT3(0, "borderfile",  OPT_STRING, &cmdlineP->borderfile,
             &borderfileSpec, 0);
+    OPTENT3(0, "closeness",   OPT_FLOAT,  &cmdlineP->closeness,
+            &closenessSpec,  0);
+    OPTENT3(0, "blank-image", OPT_STRING, &blankModeOpOpt,
+            &blankModeSpec,  0);
+    OPTENT3(0, "reportfull",  OPT_FLAG,   NULL, &reportFullOpt,       0);
+    OPTENT3(0, "reportsize",  OPT_FLAG,   NULL, &reportSizeOpt,       0);
+    OPTENT3(0, "verbose",     OPT_FLAG,   NULL, &cmdlineP->verbose,   0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
@@ -108,30 +146,73 @@ parseCommandLine(int argc, const char ** argv,
 
     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)
         cmdlineP->inputFilespec = "-";  /* stdin */
     else if (argc-1 == 1)
         cmdlineP->inputFilespec = argv[1];
-    else 
+    else
         pm_error("Too many arguments (%d).  "
                  "Only need one: the input filespec", argc-1);
 
-    if (blackOpt && whiteOpt)
-        pm_error("You cannot specify both -black and -white");
-    else if (sidesOpt &&( blackOpt || whiteOpt ))
-        pm_error("You cannot specify both -sides and either -black or -white");
+    /* Base operation */
+
+    if (reportFullOpt && reportSizeOpt)
+        pm_error("You cannot specify both -reportfull and -reportsize");
+
+    if ((reportFullOpt || reportSizeOpt) && borderfileSpec)
+        pm_error("You cannot specify -reportfull or -reportsize "
+                 "with -borderfile");
+
+    if (reportFullOpt)
+        cmdlineP->baseOperation = OP_REPORT_FULL;
+    else if (reportSizeOpt)
+        cmdlineP->baseOperation = OP_REPORT_SIZE;
+    else
+        cmdlineP->baseOperation = OP_CROP;
+
+    /* Background color */
+
+    if (blackOpt + whiteOpt + sidesOpt + bgColorSpec + bgCornerSpec > 1)
+        pm_error("You cannot specify more than one of "
+                 "-black, -white, -sides, -bg-color, -bg-corner");
     else if (blackOpt)
         cmdlineP->background = BG_BLACK;
     else if (whiteOpt)
         cmdlineP->background = BG_WHITE;
     else if (sidesOpt)
         cmdlineP->background = BG_SIDES;
+    else if (bgColorSpec)
+        cmdlineP->background = BG_COLOR;
+    else if (bgCornerSpec)
+        cmdlineP->background = BG_CORNER;
     else
         cmdlineP->background = BG_DEFAULT;
 
+    if (bgCornerSpec) {
+        if (false) {
+        } else if (streq(bgCornerOpt, "topleft")) {
+            cmdlineP->bgCorner.v = TOP;
+            cmdlineP->bgCorner.h = LEFT;
+        } else if (streq(bgCornerOpt, "topright")) {
+            cmdlineP->bgCorner.v = TOP;
+            cmdlineP->bgCorner.h = RIGHT;
+        } else if (streq(bgCornerOpt, "bottomleft")) {
+            cmdlineP->bgCorner.v = BOTTOM;
+            cmdlineP->bgCorner.h = LEFT;
+        } else if (streq(bgCornerOpt, "bottomright")) {
+            cmdlineP->bgCorner.v = BOTTOM;
+            cmdlineP->bgCorner.h = RIGHT;
+        } else
+            pm_error("Invalid value for -bg-corner."
+                     "Must be one of "
+                     "'topleft', 'topright', 'bottomleft', 'bottomright'");
+    }
+
+    /* Border specification */
+
     if (!leftOpt && !rightOpt && !topOpt && !bottomOpt) {
         unsigned int i;
         for (i = 0; i < 4; ++i)
@@ -142,11 +223,45 @@ parseCommandLine(int argc, const char ** argv,
         cmdlineP->wantCrop[TOP]    = !!topOpt;
         cmdlineP->wantCrop[BOTTOM] = !!bottomOpt;
     }
+
+    /* Blank image handling */
+
+    if (blankModeSpec) {
+        if (false) {
+        } else if (streq(blankModeOpOpt, "abort"))
+            cmdlineP->blankMode = BLANK_ABORT;
+        else if (streq(blankModeOpOpt,   "pass"))
+            cmdlineP->blankMode = BLANK_PASS;
+        else if (streq(blankModeOpOpt,   "minimize"))
+            cmdlineP->blankMode = BLANK_MINIMIZE;
+        else if (streq(blankModeOpOpt,   "maxcrop")) {
+            if (cmdlineP->baseOperation == OP_CROP)
+                pm_error("Option -blank-image=maxcrop requires "
+                         "-reportfull or -reportsize");
+            else
+                cmdlineP->blankMode = BLANK_MAXCROP;
+        } else
+            pm_error ("Invalid value for -blank-image");
+    } else
+        cmdlineP->blankMode = BLANK_ABORT; /* the default */
+
+    /* Other options */
+
     if (!marginSpec)
         cmdlineP->margin = 0;
 
     if (!borderfileSpec)
         cmdlineP->borderfile = NULL;
+
+    if (!closenessSpec)
+        cmdlineP->closeness = 0.0;
+
+    if (cmdlineP->closeness < 0.0)
+        pm_error("-closeness value %f is negative", cmdlineP->closeness);
+
+    if (cmdlineP->closeness > 100.0)
+        pm_error("-closeness value %f is more than 100%%",
+                 cmdlineP->closeness);
 }
 
 
@@ -163,21 +278,21 @@ typedef struct {
         /* Size in pixels of the border to remove */
     unsigned int padSize;
         /* Size in pixels of the border to add */
-} cropOp;
+} CropOp;
 
 
 typedef struct {
-    cropOp op[4];
-} cropSet;
+    CropOp op[4];
+} CropSet;
 
 
 
 static xel
-background3Corners(FILE * const ifP,
-                   int    const rows,
-                   int    const cols,
-                   pixval const maxval,
-                   int    const format) {
+background3Corners(FILE *       const ifP,
+                   unsigned int const rows,
+                   unsigned int const cols,
+                   pixval       const maxval,
+                   int          const format) {
 /*----------------------------------------------------------------------------
   Read in the whole image, and check all the corners to determine the
   background color.  This is a quite reliable way to determine the
@@ -186,14 +301,14 @@ background3Corners(FILE * const ifP,
   Expect the file to be positioned to the start of the raster, and leave
   it positioned arbitrarily.
 ----------------------------------------------------------------------------*/
-    int row;
+    unsigned int row;
     xel ** xels;
     xel background;   /* our return value */
 
     xels = pnm_allocarray(cols, rows);
 
     for (row = 0; row < rows; ++row)
-        pnm_readpnmrow( ifP, xels[row], cols, maxval, format );
+        pnm_readpnmrow(ifP, xels[row], cols, maxval, format);
 
     background = pnm_backgroundxel(xels, cols, rows, maxval, format);
 
@@ -205,10 +320,10 @@ background3Corners(FILE * const ifP,
 
 
 static xel
-background2Corners(FILE * const ifP,
-                   int    const cols,
-                   pixval const maxval,
-                   int    const format) {
+background2Corners(FILE *       const ifP,
+                   unsigned int const cols,
+                   pixval       const maxval,
+                   int          const format) {
 /*----------------------------------------------------------------------------
   Look at just the top row of pixels and determine the background
   color from the top corners; often this is enough to accurately
@@ -217,9 +332,9 @@ background2Corners(FILE * const ifP,
   Expect the file to be positioned to the start of the raster, and leave
   it positioned arbitrarily.
 ----------------------------------------------------------------------------*/
-    xel *xelrow;
+    xel * xelrow;
     xel background;   /* our return value */
-    
+
     xelrow = pnm_allocrow(cols);
 
     pnm_readpnmrow(ifP, xelrow, cols, maxval, format);
@@ -234,24 +349,118 @@ background2Corners(FILE * const ifP,
 
 
 static xel
-computeBackground(FILE *         const ifP,
-                  int            const cols,
-                  int            const rows,
-                  xelval         const maxval,
+background1Corner(FILE *         const ifP,
+                  unsigned int   const rows,
+                  unsigned int   const cols,
+                  pixval         const maxval,
                   int            const format,
-                  enum bg_choice const backgroundChoice) {
+                  CornerLocation const corner) {
+/*----------------------------------------------------------------------------
+  Let the pixel in corner 'corner' be the background.
+
+  Expect the file to be positioned to the start of the raster, and leave
+  it positioned arbitrarily.
+----------------------------------------------------------------------------*/
+    xel * xelrow;
+    xel background;   /* our return value */
+
+    xelrow = pnm_allocrow(cols);
+
+    if (corner.v == BOTTOM) {
+        /* read and discard all but bottom row */
+        unsigned int row;
+
+        for (row = 0; row < rows - 1; ++row)
+            pnm_readpnmrow(ifP, xelrow, cols, maxval, format);
+    }
+    pnm_readpnmrow(ifP, xelrow, cols, maxval, format);
+
+    background = corner.h == LEFT ? xelrow[0] : xelrow[cols - 1];
+
+    pnm_freerow(xelrow);
+
+    return background;
+}
+
+
+
+static xel
+backgroundColorFmName(const char * const colorName,
+                      xelval       const maxval,
+                      int          const format) {
+/*----------------------------------------------------------------------------
+   The color indicated by 'colorName'.
+
+   The return value is based on maxval 'maxval' and format 'format'.
+
+   Abort the program if 'colorName' names a color that cannot be represented
+   in format 'format'.
+
+   Development note: It would be logical to relax the above restriction when
+   -closeness is specified.  Implementation is harder than it seems because of
+   the -margin option.  It is unlikely that there is demand for this feature.
+   If really necessary, the user can convert the input image to PPM.
+-----------------------------------------------------------------------------*/
+    pixel const backgroundColor    =
+        ppm_parsecolor(colorName, maxval);
+
+    pixel const backgroundColorMax =
+        ppm_parsecolor(colorName, PNM_MAXMAXVAL);
+
+    bool const hasColor =
+        !(backgroundColorMax.r == backgroundColorMax.g &&
+          backgroundColorMax.r == backgroundColorMax.b);
+
+    bool const hasGray  =
+        !hasColor &&
+        (backgroundColorMax.r != PNM_MAXMAXVAL &&
+         backgroundColorMax.r !=  0 );
+
+    xel backgroundXel;
+
+    if (PPM_FORMAT_TYPE(format)) {
+        backgroundXel = pnm_pixeltoxel(backgroundColor);
+    } else {
+        /* Derive PBM or PGM xel from pixel 'backgroundColor' */
+        if (hasColor)
+            pm_error("Invalid color specified: '%s'. "
+                     "Image does not have color.", colorName);
+        else {
+            if (PBM_FORMAT_TYPE(format) == PBM_TYPE && hasGray)
+                pm_error("Invalid color specified: '%s'. "
+                         "Image has no intermediate levels of gray.",
+                         colorName);
+            else
+                PNM_ASSIGN1(backgroundXel, backgroundColor.r);
+        }
+    }
+
+    return backgroundXel;
+}
+
+
+
+static xel
+backgroundColor(FILE *         const ifP,
+                int            const cols,
+                int            const rows,
+                xelval         const maxval,
+                int            const format,
+                enum BgChoice  const backgroundChoice,
+                CornerLocation const corner,
+                const char   * const colorName) {
 /*----------------------------------------------------------------------------
    Determine what color is the background color of the image in file
    *ifP, which is described by 'cols', 'rows', 'maxval', and 'format'.
 
    'backgroundChoice' is the method we are to use in determining the
    background color.
-   
+
    Expect the file to be positioned to the start of the raster, and leave
    it positioned arbitrarily.
 -----------------------------------------------------------------------------*/
     xel background;  /* Our return value */
-    
+
     switch (backgroundChoice) {
     case BG_WHITE:
         background = pnm_whitexel(maxval, format);
@@ -259,17 +468,45 @@ computeBackground(FILE *         const ifP,
     case BG_BLACK:
         background = pnm_blackxel(maxval, format);
         break;
-    case BG_SIDES: 
-        background = 
+    case BG_COLOR:
+        background =
+            backgroundColorFmName(colorName, maxval, format);
+        break;
+    case BG_SIDES:
+        background =
             background3Corners(ifP, rows, cols, maxval, format);
         break;
-    case BG_DEFAULT: 
-        background = 
+    case BG_DEFAULT:
+        background =
             background2Corners(ifP, cols, maxval, format);
         break;
+    case BG_CORNER:
+        background =
+            background1Corner(ifP, rows, cols, maxval, format, corner);
+        break;
+
+    default:
+        pm_error("internal error");
     }
 
-    return(background);
+    return background;
+}
+
+
+
+static bool
+colorMatches(pixel        const comparand,
+             pixel        const comparator,
+             unsigned int const allowableDiff) {
+/*----------------------------------------------------------------------------
+   The colors 'comparand' and 'comparator' are within 'allowableDiff'
+   color levels of each other, in cartesian distance.
+-----------------------------------------------------------------------------*/
+    /* Fast path for usual case */
+    if (allowableDiff < EPSILON)
+        return PPM_EQUAL(comparand, comparator);
+
+    return PPM_DISTANCE(comparand, comparator) <= SQR(allowableDiff);
 }
 
 
@@ -281,62 +518,66 @@ findBordersInImage(FILE *         const ifP,
                    xelval         const maxval,
                    int            const format,
                    xel            const backgroundColor,
+                   double         const closeness,
                    bool *         const hasBordersP,
                    borderSet *    const borderSizeP) {
 /*----------------------------------------------------------------------------
    Find the left, right, top, and bottom borders in the image 'ifP'.
    Return their sizes in pixels as borderSize[n].
-   
+
    Iff the image is all background, *hasBordersP == FALSE.
 
    Expect the input file to be positioned to the beginning of the
    image raster and leave it positioned arbitrarily.
 -----------------------------------------------------------------------------*/
+    unsigned int const allowableDiff = ROUNDU(sqrt3 * maxval * closeness/100);
+
     xel * xelrow;        /* A row of the input image */
     int row;
-    bool gottop;
+    bool gotTop;
     int left, right, bottom, top;
         /* leftmost, etc. nonbackground pixel found so far; -1 for none */
 
     xelrow = pnm_allocrow(cols);
-    
+
     left   = cols;  /* initial value */
     right  = -1;    /* initial value */
     top    = rows;  /* initial value */
     bottom = -1;    /* initial value */
 
-    gottop = FALSE;
-    for (row = 0; row < rows; ++row) {
+    for (row = 0, gotTop = false; row < rows; ++row) {
         int col;
         int thisRowLeft;
         int thisRowRight;
 
         pnm_readpnmrow(ifP, xelrow, cols, maxval, format);
-        
+
         col = 0;
-        while (col < cols && PNM_EQUAL(xelrow[col], backgroundColor))
+        while (col < cols &&
+               colorMatches(xelrow[col], backgroundColor, allowableDiff))
             ++col;
         thisRowLeft = col;
 
         col = cols-1;
-        while (col >= thisRowLeft && PNM_EQUAL(xelrow[col], backgroundColor))
+        while (col >= thisRowLeft &&
+               colorMatches(xelrow[col], backgroundColor, allowableDiff))
             --col;
         thisRowRight = col + 1;
 
         if (thisRowLeft < cols) {
             /* This row is not entirely background */
-            
+
             left  = MIN(thisRowLeft,  left);
             right = MAX(thisRowRight, right);
 
-            if (!gottop) {
-                gottop = TRUE;
+            if (!gotTop) {
                 top = row;
+                gotTop = true;
             }
             bottom = row + 1;   /* New candidate */
         }
     }
-    
+
     free(xelrow);
 
     if (right == -1)
@@ -359,7 +600,10 @@ analyzeImage(FILE *         const ifP,
              unsigned int   const rows,
              xelval         const maxval,
              int            const format,
-             enum bg_choice const backgroundReq,
+             enum BgChoice  const backgroundReq,
+             double         const closeness,
+             CornerLocation const corner,
+             const char   * const colorName,
              imageFilePos   const newFilePos,
              xel *          const backgroundColorP,
              bool *         const hasBordersP,
@@ -383,13 +627,13 @@ analyzeImage(FILE *         const ifP,
 
     pm_tell2(ifP, &rasterpos, sizeof(rasterpos));
 
-    background = computeBackground(ifP, cols, rows, maxval, format,
-                                   backgroundReq);
+    background = backgroundColor(ifP, cols, rows, maxval, format,
+                                 backgroundReq, corner, colorName);
 
     pm_seek2(ifP, &rasterpos, sizeof(rasterpos));
 
-    findBordersInImage(ifP, cols, rows, maxval, format, 
-                       background, hasBordersP, borderSizeP);
+    findBordersInImage(ifP, cols, rows, maxval, format,
+                       background, closeness, hasBordersP, borderSizeP);
 
     if (newFilePos == FILEPOS_BEG)
         pm_seek2(ifP, &rasterpos, sizeof(rasterpos));
@@ -408,7 +652,7 @@ ending(unsigned int const n) {
 
 
 static void
-reportCroppingParameters(cropSet const crop) {
+reportCroppingParameters(CropSet const crop) {
 
     unsigned int i;
 
@@ -431,6 +675,85 @@ reportCroppingParameters(cropSet const crop) {
 
 
 
+static void
+reportDimensions(CropSet      const crop,
+                 unsigned int const cols,
+                 unsigned int const rows) {
+
+    unsigned int i;
+
+    for (i = 0; i < ARRAY_SIZE(crop.op); ++i) {
+        if (crop.op[i].removeSize > 0 && crop.op[i].padSize > 0)
+            pm_error("Attempt to add %u and crop %u on %s edge.  "
+                     "Simultaneous pad and crop is not allowed",
+                     crop.op[i].padSize, crop.op[i].removeSize, edgeName[i]);
+        else if (crop.op[i].removeSize > 0)   /* crop */
+            printf ("-%u ", crop.op[i].removeSize);
+        else if (crop.op[i].removeSize == 0) {
+            if (crop.op[i].padSize == 0)      /* no operation */
+                printf ("0 ");
+            else                              /* pad */
+                printf ("+%u ", crop.op[i].padSize);
+        }
+    }
+
+    {
+        unsigned int outputCols, outputRows;
+
+        if (crop.op[LEFT ].removeSize == cols ||
+            crop.op[RIGHT].removeSize == cols)
+            outputCols = cols;
+        else {
+            outputCols =
+                cols - crop.op[LEFT].removeSize - crop.op[RIGHT].removeSize +
+                crop.op[LEFT].padSize + crop.op[RIGHT].padSize;
+        }
+
+        if (crop.op[TOP   ].removeSize == rows ||
+            crop.op[BOTTOM].removeSize == rows)
+            outputRows = rows;
+        else
+            outputRows =
+                rows - crop.op[TOP].removeSize - crop.op[BOTTOM].removeSize +
+                crop.op[TOP].padSize + crop.op[BOTTOM].padSize;
+
+        printf("%u %u", outputCols, outputRows);
+    }
+}
+
+
+static void
+reportSize(CropSet      const crop,
+           unsigned int const cols,
+           unsigned int const rows) {
+
+    reportDimensions(crop, cols, rows);
+
+    putchar('\n');
+
+}
+
+
+
+static void
+reportFull(CropSet      const crop,
+           unsigned int const cols,
+           unsigned int const rows,
+           int          const format,
+           xelval       const maxval,
+           xel          const bgColor,
+           float        const closeness) {
+
+    pixel const backgroundPixel = pnm_xeltopixel(bgColor, format);
+
+    reportDimensions(crop, cols, rows);
+
+    printf(" rgb-%u:%u/%u/%u %f\n", maxval,
+           backgroundPixel.r, backgroundPixel.g, backgroundPixel.b,
+           closeness);
+}
+
+
 
 static void
 fillRow(xel *        const xelrow,
@@ -485,7 +808,7 @@ outputNewBorderNonPbm(unsigned int const height,
 
     for (i = 0; i < height; ++i)
         pnm_writepnmrow(ofP, xelrow, width, maxval, format, 0);
-    
+
     pnm_freerow(xelrow);
 }
 
@@ -497,7 +820,7 @@ writeCroppedNonPbm(FILE *       const ifP,
                    unsigned int const rows,
                    xelval       const maxval,
                    int          const format,
-                   cropSet      const crop,
+                   CropSet      const crop,
                    xel          const backgroundColor,
                    FILE *       const ofP) {
 
@@ -529,7 +852,7 @@ writeCroppedNonPbm(FILE *       const ifP,
        the buffer that lines up the first foreground pixel at
        'foregroundLeft'.
 
-       When we output the row, we pick a starting location in the 
+       When we output the row, we pick a starting location in the
        buffer that includes the proper number of left border pixels
        before 'foregroundLeft'.
 
@@ -542,7 +865,7 @@ writeCroppedNonPbm(FILE *       const ifP,
 
     unsigned int const foregroundCols =
         cols - crop.op[LEFT].removeSize - crop.op[RIGHT].removeSize;
-    unsigned int const outputCols     = 
+    unsigned int const outputCols     =
         foregroundCols + crop.op[LEFT].padSize + crop.op[RIGHT].padSize;
     unsigned int const foregroundRows =
         rows - crop.op[TOP].removeSize - crop.op[BOTTOM].removeSize;
@@ -581,19 +904,19 @@ writeCroppedNonPbm(FILE *       const ifP,
 
     /* Read and output foreground rows */
     for (i = 0; i < foregroundRows; ++i) {
- 
+
         /* Read foreground pixels */
         pnm_readpnmrow(ifP,
                        &(xelrow[foregroundLeft - crop.op[LEFT].removeSize]),
                        cols, maxval, format);
-        
+
         pnm_writepnmrow(ofP,
                         &(xelrow[foregroundLeft - crop.op[LEFT].padSize]),
                         outputCols, maxval, format, 0);
     }
 
     readOffBorderNonPbm(crop.op[BOTTOM].removeSize, ifP, cols, maxval, format);
-    
+
     outputNewBorderNonPbm(crop.op[BOTTOM].padSize, outputCols,
                           backgroundColor,
                           ofP, maxval, format);
@@ -616,10 +939,10 @@ fillRowPBM(unsigned char * const bitrow,
     unsigned int i;
 
     assert(blackWhite == 0 || blackWhite == 1);
-    
+
     for (i = 0; i < colChars; ++i)
         bitrow[i] = blackWhite * 0xff;
-        
+
     if (cols % 8 > 0)
         bitrow[colChars-1] <<= 8 - cols % 8;
 }
@@ -664,7 +987,7 @@ outputNewBorderPbm(unsigned int const height,
 
     for (i = 0; i < height; ++i)
         pbm_writepbmrow_packed(ofP, bitrow, width, 0);
-    
+
     pbm_freerow_packed(bitrow);
 }
 
@@ -675,17 +998,17 @@ writeCroppedPBM(FILE *       const ifP,
                 unsigned int const cols,
                 unsigned int const rows,
                 int          const format,
-                cropSet      const crop,
+                CropSet      const crop,
                 xel          const backgroundColor,
                 FILE *       const ofP) {
-    
-    /* See comments for writeCroppedNonPBM(), which uses identical logic flow. 
+
+    /* See comments for writeCroppedNonPBM(), which uses identical logic flow.
        Uses pbm functions instead of general pnm functions.
     */
 
     unsigned int const foregroundCols =
         cols - crop.op[LEFT].removeSize - crop.op[RIGHT].removeSize;
-    unsigned int const outputCols     = 
+    unsigned int const outputCols     =
         foregroundCols + crop.op[LEFT].padSize + crop.op[RIGHT].padSize;
     unsigned int const foregroundRows =
         rows - crop.op[TOP].removeSize - crop.op[BOTTOM].removeSize;
@@ -697,7 +1020,7 @@ writeCroppedPBM(FILE *       const ifP,
     unsigned int const foregroundRight = foregroundLeft + foregroundCols;
 
     unsigned int const allocCols =
-        foregroundRight + 
+        foregroundRight +
         MAX(crop.op[RIGHT].removeSize, crop.op[RIGHT].padSize);
 
     unsigned int const backgroundBlackWhite =
@@ -709,7 +1032,7 @@ writeCroppedPBM(FILE *       const ifP,
     unsigned int const lastWriteChar = writeOffset/8 + (outputCols-1)/8;
     unsigned char * bitrow;
     unsigned int i;
-    
+
     pbm_writepbminit(ofP, outputCols, outputRows, 0);
 
     bitrow = pbm_allocrow_packed(allocCols);
@@ -726,15 +1049,15 @@ writeCroppedPBM(FILE *       const ifP,
     for (i = 0; i < foregroundRows; ++i) {
         /* Read foreground pixels */
         pbm_readpbmrow_bitoffset(ifP, bitrow, cols, format, readOffset);
-  
+
         pbm_writepbmrow_bitoffset(ofP,
                                   bitrow, outputCols, format, writeOffset);
-                              
+
         /* If there is right-side padding, repair the write buffer
-           distorted by pbm_writepbmrow_bitoffset() 
+           distorted by pbm_writepbmrow_bitoffset()
            (No need to mend any left-side padding)
         */
-        if (crop.op[RIGHT].padSize > 0)    
+        if (crop.op[RIGHT].padSize > 0)
             bitrow[lastWriteChar] = backgroundBlackWhite * 0xff;
     }
 
@@ -749,29 +1072,168 @@ writeCroppedPBM(FILE *       const ifP,
 
 
 
-static void
-determineCrops(struct cmdlineInfo const cmdline,
-               borderSet *        const oldBorderSizeP,
-               cropSet *          const cropP) {
+static CropSet
+crops(struct CmdlineInfo const cmdline,
+      borderSet          const oldBorderSize) {
 
-    edgeLocation i;
+    CropSet retval;
 
-    for (i = 0; i < 4; ++i) {
+    EdgeLocation i;
+
+    for (i = 0; i < ARRAY_SIZE(retval.op); ++i) {
         if (cmdline.wantCrop[i]) {
-            if (oldBorderSizeP->size[i] > cmdline.margin) {
-                cropP->op[i].removeSize =
-                    oldBorderSizeP->size[i] - cmdline.margin;
-                cropP->op[i].padSize    = 0;
+            if (oldBorderSize.size[i] > cmdline.margin) {
+                retval.op[i].removeSize =
+                    oldBorderSize.size[i] - cmdline.margin;
+                retval.op[i].padSize    = 0;
             } else {
-                cropP->op[i].removeSize = 0;
-                cropP->op[i].padSize    =
-                    cmdline.margin - oldBorderSizeP->size[i];
+                retval.op[i].removeSize = 0;
+                retval.op[i].padSize    =
+                    cmdline.margin - oldBorderSize.size[i];
             }
         } else {
-            cropP->op[i].removeSize = 0;
-            cropP->op[i].padSize    = 0;
+            retval.op[i].removeSize = 0;
+            retval.op[i].padSize    = 0;
         }
     }
+    return retval;
+}
+
+
+
+static CropSet
+noCrops(struct CmdlineInfo const cmdline) {
+
+    CropSet retval;
+
+    EdgeLocation i;
+
+    if (cmdline.verbose)
+        pm_message("The image is entirely background; "
+                   "there is nothing to crop.  Copying to output.");
+
+    if (cmdline.margin > 0)
+        pm_message ("-margin value %u ignored", cmdline.margin);
+
+    for (i = 0; i < 4; ++i) {
+        retval.op[i].removeSize = 0;
+        retval.op[i].padSize    = 0;
+    }
+    return retval;
+}
+
+
+
+static CropSet
+extremeCrops(struct CmdlineInfo const cmdline,
+             unsigned int       const cols,
+             unsigned int       const rows) {
+/*----------------------------------------------------------------------------
+   Crops that crop as much as possible, reducing output to a single pixel.
+-----------------------------------------------------------------------------*/
+    CropSet retval;
+
+    if (cmdline.verbose)
+        pm_message("Input image has no distinction between "
+                   "border and content");
+
+    /* We can't just pick a representive pixel, say top-left corner.
+       If -top and/or -bottom was specified but not -left and -right,
+       the output should be one row, not a single pixel.
+
+       The "entirely background" image may have several colors: this
+       happens when -closeness was specified.
+    */
+
+    if (cmdline.wantCrop[LEFT] && cmdline.wantCrop[RIGHT]) {
+        retval.op[LEFT ].removeSize = cols / 2;
+        retval.op[RIGHT].removeSize = cols - retval.op[LEFT].removeSize -1;
+    } else if (cmdline.wantCrop[LEFT]) {
+        retval.op[LEFT ].removeSize = cols - 1;
+        retval.op[RIGHT].removeSize = 0;
+    } else if (cmdline.wantCrop[RIGHT]) {
+        retval.op[LEFT ].removeSize = 0;
+        retval.op[RIGHT].removeSize = cols - 1;
+    } else {
+        retval.op[LEFT ].removeSize = 0;
+        retval.op[RIGHT].removeSize = 0;
+    }
+
+    if (cmdline.wantCrop[TOP] && cmdline.wantCrop[BOTTOM]) {
+        retval.op[ TOP  ].removeSize = rows / 2;
+        retval.op[BOTTOM].removeSize = rows - retval.op[TOP].removeSize -1;
+    } else if (cmdline.wantCrop[TOP]) {
+        retval.op[ TOP  ].removeSize = rows - 1;
+        retval.op[BOTTOM].removeSize = 0;
+    } else if (cmdline.wantCrop[BOTTOM]) {
+        retval.op[ TOP  ].removeSize = 0;
+        retval.op[BOTTOM].removeSize = rows - 1;
+    } else {
+        retval.op[ TOP  ].removeSize = 0;
+        retval.op[BOTTOM].removeSize = 0;
+    }
+
+    if (cmdline.margin > 0)
+        pm_message ("-margin value %u ignored", cmdline.margin);
+
+    {
+        EdgeLocation i;
+        for (i = 0; i < ARRAY_SIZE(retval.op); ++i)
+            retval.op[i].padSize = 0;
+    }
+    return retval;
+}
+
+
+
+static CropSet
+maxcropReport(struct CmdlineInfo const cmdline,
+              unsigned int       const cols,
+              unsigned int       const rows) {
+/*----------------------------------------------------------------------------
+   Report maximum possible crop extents.
+-----------------------------------------------------------------------------*/
+    CropSet retval;
+
+    if (cmdline.wantCrop[LEFT] && cmdline.wantCrop[RIGHT]) {
+        retval.op[LEFT ].removeSize = cols;
+        retval.op[RIGHT].removeSize = cols;
+    } else if (cmdline.wantCrop[LEFT]) {
+        retval.op[LEFT ].removeSize = cols;
+        retval.op[RIGHT].removeSize = 0;
+    } else if (cmdline.wantCrop[RIGHT]) {
+        retval.op[LEFT ].removeSize = 0;
+        retval.op[RIGHT].removeSize = cols;
+    } else {
+        retval.op[LEFT ].removeSize = 0;
+        retval.op[RIGHT].removeSize = 0;
+    }
+
+    if (cmdline.wantCrop[TOP] && cmdline.wantCrop[BOTTOM]) {
+        retval.op[ TOP  ].removeSize = rows;
+        retval.op[BOTTOM].removeSize = rows;
+    } else if (cmdline.wantCrop[TOP]) {
+        retval.op[ TOP  ].removeSize = rows;
+        retval.op[BOTTOM].removeSize = 0;
+    } else if (cmdline.wantCrop[BOTTOM]) {
+        retval.op[ TOP  ].removeSize = 0;
+        retval.op[BOTTOM].removeSize = rows;
+    } else {
+        retval.op[ TOP  ].removeSize = 0;
+        retval.op[BOTTOM].removeSize = 0;
+    }
+
+
+    if (cmdline.margin > 0)
+        pm_message("-margin value %u ignored", cmdline.margin);
+
+    {
+        EdgeLocation i;
+
+        for (i = 0; i < ARRAY_SIZE(retval.op); ++i)
+            retval.op[i].padSize = 0;
+    }
+    return retval;
 }
 
 
@@ -779,7 +1241,7 @@ determineCrops(struct cmdlineInfo const cmdline,
 static void
 validateComputableSize(unsigned int const cols,
                        unsigned int const rows,
-                       cropSet      const crop) {
+                       CropSet      const crop) {
 
     double const newcols =
         (double)cols +
@@ -798,7 +1260,7 @@ validateComputableSize(unsigned int const cols,
 
 
 static void
-cropOneImage(struct cmdlineInfo const cmdline,
+cropOneImage(struct CmdlineInfo const cmdline,
              FILE *             const ifP,
              FILE *             const bdfP,
              FILE *             const ofP) {
@@ -811,51 +1273,95 @@ cropOneImage(struct cmdlineInfo const cmdline,
 
    Both files are seekable.
 -----------------------------------------------------------------------------*/
-    xelval maxval, bmaxval;
-    int format, bformat;
-    int rows, cols, brows, bcols;
+    int rows, cols, format;
+    xelval maxval;      /* The input file image */
+
+    int brows, bcols, bformat;
+    xelval bmaxval;     /* The separate border file, if specified */
+
+    FILE * afP;
+    int arows, acols, aformat;
+    xelval amaxval;
+    /* The file we use for analysis, either the input file or border file */
+
     bool hasBorders;
     borderSet oldBorder;
         /* The sizes of the borders in the input image */
-    cropSet crop;
+    CropSet crop;
         /* The crops we have to do on each side */
     xel background;
 
     pnm_readpnminit(ifP, &cols, &rows, &maxval, &format);
 
-    if (bdfP)
+    if (bdfP) {
         pnm_readpnminit(bdfP, &bcols, &brows, &bmaxval, &bformat);
 
-    if (bdfP)
-        analyzeImage(bdfP, bcols, brows, bmaxval, bformat, cmdline.background,
-                     FILEPOS_END,
-                     &background, &hasBorders, &oldBorder);
-    else
-        analyzeImage(ifP, cols, rows, maxval, format, cmdline.background,
-                     FILEPOS_BEG,
-                     &background, &hasBorders, &oldBorder);
+        if (cols != bcols || rows != brows)
+            pm_error("Input file image [%u x %u] and border file image "
+                     "[%u x %u] differ in size", cols, rows, bcols, brows);
+        else {
+            afP = bdfP;
+            acols = bcols; arows = brows;
+            amaxval = maxval;
+            aformat = bformat;
+        }
+    } else {
+        afP = ifP;
+        acols = cols; arows = rows;
+        amaxval = maxval;
+        aformat = format;
+    }
+
+    analyzeImage(afP, acols, arows, amaxval, aformat,
+                 cmdline.background, cmdline.closeness,
+                 cmdline.bgCorner, cmdline.bgColor,
+                 (bdfP || cmdline.baseOperation != OP_CROP) ?
+                     FILEPOS_END : FILEPOS_BEG,
+                 &background, &hasBorders, &oldBorder);
 
     if (cmdline.verbose) {
         pixel const backgroundPixel = pnm_xeltopixel(background, format);
-        pm_message("Background color is %s", 
+        pm_message("Background color is %s",
                    ppm_colorname(&backgroundPixel, maxval, TRUE /*hexok*/));
     }
-    if (!hasBorders)
-        pm_error("The image is entirely background; "
-                 "there is nothing to crop.");
-
-    determineCrops(cmdline, &oldBorder, &crop);
+    if (!hasBorders) {
+        switch (cmdline.blankMode) {
+        case BLANK_ABORT:
+            pm_error("The image is entirely background; "
+                     "there is nothing to crop.");
+            break;
+        case BLANK_PASS:
+            crop = noCrops(cmdline);                   break;
+        case BLANK_MINIMIZE:
+            crop = extremeCrops(cmdline, cols, rows);  break;
+        case BLANK_MAXCROP:
+            crop = maxcropReport(cmdline, cols, rows); break;
+        }
+    } else {
+        crop = crops(cmdline, oldBorder);
 
-    validateComputableSize(cols, rows, crop);
+        validateComputableSize(cols, rows, crop);
 
-    if (cmdline.verbose) 
-        reportCroppingParameters(crop);
+        if (cmdline.verbose)
+            reportCroppingParameters(crop);
+    }
 
-    if (PNM_FORMAT_TYPE(format) == PBM_TYPE)
-        writeCroppedPBM(ifP, cols, rows, format, crop, background, ofP);
-    else
-        writeCroppedNonPbm(ifP, cols, rows, maxval, format, crop,
-                           background, ofP);
+    switch (cmdline.baseOperation) {
+    case OP_CROP:
+        if (PNM_FORMAT_TYPE(format) == PBM_TYPE)
+            writeCroppedPBM(ifP, cols, rows, format, crop, background, ofP);
+        else
+            writeCroppedNonPbm(ifP, cols, rows, maxval, format, crop,
+                               background, ofP);
+        break;
+    case OP_REPORT_FULL:
+        reportFull(crop, cols, rows,
+                   aformat, amaxval, background, cmdline.closeness);
+        break;
+    case OP_REPORT_SIZE:
+        reportSize(crop, cols, rows);
+        break;
+    }
 }
 
 
@@ -863,8 +1369,8 @@ cropOneImage(struct cmdlineInfo const cmdline,
 int
 main(int argc, const char *argv[]) {
 
-    struct cmdlineInfo cmdline;
-    FILE * ifP;   
+    struct CmdlineInfo cmdline;
+    FILE * ifP;
         /* The program's regular input file.  Could be a seekable copy of
            it in a temporary file.
         */
@@ -884,18 +1390,17 @@ main(int argc, const char *argv[]) {
     else
         bdfP = NULL;
 
-    eof = beof = FALSE;
-    while (!eof) {
+    for (eof = beof = FALSE; !eof; ) {
         cropOneImage(cmdline, ifP, bdfP, stdout);
 
         pnm_nextimage(ifP, &eof);
 
         if (bdfP) {
             pnm_nextimage(bdfP, &beof);
-            
+
             if (eof != beof) {
                 if (!eof)
-                    pm_error("Input file has more images than border file."); 
+                    pm_error("Input file has more images than border file.");
                 else
                     pm_error("Border file has more images than image file.");
             }
diff --git a/editor/pnmhisteq.c b/editor/pnmhisteq.c
index 8af42019..a339f73f 100644
--- a/editor/pnmhisteq.c
+++ b/editor/pnmhisteq.c
@@ -51,9 +51,9 @@ parseCommandLine(int argc, char ** argv,
     MALLOCARRAY_NOFAIL(option_def, 100);
 
     option_def_index = 0;   /* incremented by OPTENT3 */
-    OPTENT3(0, "rmap",     OPT_STRING, &cmdlineP->rmap, 
+    OPTENT3(0, "rmap",     OPT_STRING, &cmdlineP->rmap,
             &rmapSpec,          0);
-    OPTENT3(0, "wmap",     OPT_STRING, &cmdlineP->wmap, 
+    OPTENT3(0, "wmap",     OPT_STRING, &cmdlineP->wmap,
             &wmapSpec,          0);
     OPTENT3(0, "gray",     OPT_FLAG,   NULL,
             &cmdlineP->gray,    0);
@@ -175,27 +175,27 @@ readMapFile(const char * const rmapFileName,
             xelval       const maxval,
             gray *       const lumamap) {
 
-    int rmcols, rmrows; 
+    int rmcols, rmrows;
     gray rmmaxv;
     int rmformat;
     FILE * rmapfP;
-        
+
     rmapfP = pm_openr(rmapFileName);
     pgm_readpgminit(rmapfP, &rmcols, &rmrows, &rmmaxv, &rmformat);
-    
+
     if (rmmaxv != maxval)
         pm_error("maxval in map file (%u) different from input (%u)",
                  rmmaxv, maxval);
-    
+
     if (rmrows != 1)
         pm_error("Map must have 1 row.  Yours has %u", rmrows);
-    
+
     if (rmcols != maxval + 1)
         pm_error("Map must have maxval + 1 (%u) columns.  Yours has %u",
                  maxval + 1, rmcols);
-    
+
     pgm_readpgmrow(rmapfP, lumamap, maxval+1, rmmaxv, rmformat);
-    
+
     pm_close(rmapfP);
 }
 
@@ -245,7 +245,7 @@ equalize(const unsigned int * const lumahist,
         maxLumaPresent(lumahist, darkestRemap, brightestRemap);
 
     unsigned int const range = brightestRemap - darkestRemap;
-    
+
     {
         xelval origLum;
         unsigned int pixsum;
@@ -253,7 +253,7 @@ equalize(const unsigned int * const lumahist,
         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.
             */
@@ -261,7 +261,7 @@ equalize(const unsigned int * const lumahist,
             lumamap[origLum] =
                 darkestRemap +
                 ROUNDU((((double) pixsum * range)) / remapPixelCount);
-            
+
             pixsum += lumahist[origLum];
         }
 
@@ -277,7 +277,7 @@ equalize(const unsigned int * const lumahist,
 
         for (origLum = darkestRemap; origLum <= brightestRemap; ++origLum)
             lumamap[origLum] =
-                MIN(brightestRemap, 
+                MIN(brightestRemap,
                     darkestRemap + ROUNDU(lumamap[origLum] * lscale));
     }
 }
@@ -301,7 +301,7 @@ computeMap(const unsigned int * const lumahist,
 
   '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.
 
@@ -407,7 +407,7 @@ scaleXel(xel    const thisXel,
                ((xelval)(PNM_GETR(thisXel) * scaler + 0.5)),
                ((xelval)(PNM_GETG(thisXel) * scaler + 0.5)),
                ((xelval)(PNM_GETB(thisXel) * scaler + 0.5)));
-    
+
     return retval;
 }
 
@@ -426,10 +426,10 @@ remapRgbValue(xel          const thisXel,
     struct hsv const hsv =
         ppm_hsv_from_color(thisXel, maxval);
     xelval const oldValue =
-        MIN(maxval, ROUNDU(hsv.v * maxval));
+        MIN(maxval, pnm_unnormalize(hsv.v, maxval));
     xelval const newValue =
         lumamap[oldValue];
-    
+
     return scaleXel(thisXel, (double)newValue/oldValue);
 }
 
diff --git a/editor/pnminvert.c b/editor/pnminvert.c
index 4bee8837..d4aad503 100644
--- a/editor/pnminvert.c
+++ b/editor/pnminvert.c
@@ -16,8 +16,8 @@
    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
+   could change the code to use uint32_t or uint64_t and it will do four or
+   eight bytes at a time.  (But we don't think it's worth complicating the
    code for that).
 */
 
diff --git a/editor/pnmmargin b/editor/pnmmargin
index 0f57d1d4..9dbe24b7 100755
--- a/editor/pnmmargin
+++ b/editor/pnmmargin
@@ -11,9 +11,8 @@
 # documentation.  This software is provided "as is" without express or
 # implied warranty.
 
-tempdir="${TMPDIR-/tmp}/pnmmargin.$$"
-mkdir -m 0700 $tempdir || \
-  { echo "Could not create temporary file. Exiting." 1>&2; exit 1;}
+tempdir=$(mktemp -d "${TMPDIR:-/tmp}/netpbm.XXXXXXXX") ||
+    ( echo "Could not create temporary file. Exiting." 1>&2; exit 1; ) 
 trap 'rm -rf $tempdir' 0 1 3 15
 
 tmp1=$tempdir/pnmm1
@@ -27,6 +26,9 @@ plainopt=""
 # Parse args.
 while true ; do
     case "$1" in
+        -version|--version )
+        pnmpad --version; exit $?;
+        ;;
         -p|-pl|-pla|-plai|-plain )
         plainopt="-plain"
         shift
diff --git a/editor/pnmmontage.c b/editor/pnmmontage.c
index e54afc45..7bd00dbe 100644
--- a/editor/pnmmontage.c
+++ b/editor/pnmmontage.c
@@ -10,6 +10,7 @@
  * implied warranty.
  */
 
+#define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE  /* Make sure strdup() is in <string.h> */
 #include <assert.h>
diff --git a/editor/pnmnorm.c b/editor/pnmnorm.c
index 131b39d0..2f9a6b20 100644
--- a/editor/pnmnorm.c
+++ b/editor/pnmnorm.c
@@ -9,20 +9,20 @@
 
   Ppmnorm is by Wilson H. Bent, Jr. (whb@usc.edu)
   Extensively hacked from pgmnorm.c, which carries the following note:
-  
+
   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.
-  
+
   (End of note from pgmnorm.c)
 
   Pgmnorm's man page also said:
-  
+
   Partially based on the fbnorm filter in Michael Mauldin's "Fuzzy Pixmap"
   package.
 *****************************************************************************/
@@ -73,7 +73,7 @@ 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.  
+   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.
@@ -89,41 +89,41 @@ parseCommandLine(int argc, const char ** argv,
     unsigned int luminosity, colorvalue, saturation;
     unsigned int middleSpec, maxexpandSpec;
     float maxexpand;
-    
+
     unsigned int option_def_index;
 
     MALLOCARRAY_NOFAIL(option_def, 100);
 
     option_def_index = 0;   /* incremented by OPTENT3 */
-    OPTENT3(0,   "bpercent",      OPT_FLOAT,   
+    OPTENT3(0,   "bpercent",      OPT_FLOAT,
             &cmdlineP->bpercent,   &cmdlineP->bpercentSpec, 0);
-    OPTENT3(0,   "wpercent",      OPT_FLOAT,   
+    OPTENT3(0,   "wpercent",      OPT_FLOAT,
             &cmdlineP->wpercent,   &cmdlineP->wpercentSpec, 0);
-    OPTENT3(0,   "bvalue",        OPT_UINT,   
+    OPTENT3(0,   "bvalue",        OPT_UINT,
             &cmdlineP->bvalue,     &cmdlineP->bvalueSpec, 0);
-    OPTENT3(0,   "wvalue",        OPT_UINT,   
+    OPTENT3(0,   "wvalue",        OPT_UINT,
             &cmdlineP->wvalue,     &cmdlineP->wvalueSpec, 0);
-    OPTENT3(0,   "bsingle",       OPT_FLAG,   
+    OPTENT3(0,   "bsingle",       OPT_FLAG,
             NULL,                 &cmdlineP->bsingle, 0);
-    OPTENT3(0,   "wsingle",       OPT_FLAG,   
+    OPTENT3(0,   "wsingle",       OPT_FLAG,
             NULL,                 &cmdlineP->wsingle, 0);
-    OPTENT3(0,   "middle",        OPT_FLOAT,   
+    OPTENT3(0,   "middle",        OPT_FLOAT,
             &cmdlineP->middle,     &middleSpec, 0);
-    OPTENT3(0,   "midvalue",      OPT_UINT,   
+    OPTENT3(0,   "midvalue",      OPT_UINT,
             &cmdlineP->midvalue,   &cmdlineP->midvalueSpec, 0);
-    OPTENT3(0,   "maxexpand",     OPT_FLOAT,   
+    OPTENT3(0,   "maxexpand",     OPT_FLOAT,
             &maxexpand,            &maxexpandSpec, 0);
-    OPTENT3(0,   "keephues",      OPT_FLAG,   
+    OPTENT3(0,   "keephues",      OPT_FLAG,
             NULL,                  &cmdlineP->keephues, 0);
-    OPTENT3(0,   "luminosity",    OPT_FLAG,   
+    OPTENT3(0,   "luminosity",    OPT_FLAG,
             NULL,                  &luminosity, 0);
-    OPTENT3(0,   "colorvalue",    OPT_FLAG,   
+    OPTENT3(0,   "colorvalue",    OPT_FLAG,
             NULL,                  &colorvalue, 0);
-    OPTENT3(0,   "saturation",    OPT_FLAG,   
+    OPTENT3(0,   "saturation",    OPT_FLAG,
             NULL,                  &saturation, 0);
-    OPTENT3(0,   "brightmax",     OPT_FLAG,   
+    OPTENT3(0,   "brightmax",     OPT_FLAG,
             NULL,                  &colorvalue, 0);
-    OPTENT3(0,   "verbose",       OPT_FLAG,   
+    OPTENT3(0,   "verbose",       OPT_FLAG,
             NULL,                  &cmdlineP->verbose, 0);
 
     /* Note: -brightmax was documented and accepted long before it was
@@ -203,7 +203,7 @@ parseCommandLine(int argc, const char ** argv,
 
 
 static void
-buildHistogram(FILE *   const ifp, 
+buildHistogram(FILE *   const ifp,
                int      const cols,
                int      const rows,
                xelval   const maxval,
@@ -226,7 +226,7 @@ buildHistogram(FILE *   const ifp,
 -----------------------------------------------------------------------------*/
     int row;
     xel * xelrow;
-    
+
     xelrow = pnm_allocrow(cols);
 
     {
@@ -307,10 +307,10 @@ maximumValue(const unsigned int * const hist,
 
 
 static void
-computeBottomPercentile(unsigned int         hist[], 
+computeBottomPercentile(unsigned int         hist[],
                         unsigned int   const highest,
                         unsigned int   const total,
-                        float          const percent, 
+                        float          const percent,
                         unsigned int * const percentileP) {
 /*----------------------------------------------------------------------------
    Compute the lowest index of hist[] such that the sum of the hist[]
@@ -332,17 +332,17 @@ computeBottomPercentile(unsigned int         hist[],
                      "values");
         ++percentile;
         count += hist[percentile];
-    }        
+    }
     *percentileP = percentile;
 }
 
 
 
 static void
-computeTopPercentile(unsigned int         hist[], 
-                     unsigned int   const highest, 
+computeTopPercentile(unsigned int         hist[],
+                     unsigned int   const highest,
                      unsigned int   const total,
-                     float          const percent, 
+                     float          const percent,
                      unsigned int * const percentileP) {
 /*----------------------------------------------------------------------------
    Compute the highest index of hist[] such that the sum of the hist[]
@@ -400,7 +400,7 @@ computeAdjustmentForExpansionLimit(xelval   const maxval,
            to 0 .. maxval, if we used the unlimited bvalue and wvalue
         */
     float const unlExpansion = (float)newRange/oldRange;
-    
+
     if (unlExpansion <= maxExpansion) {
         /* No capping is necessary.  Unlimited values are already within
            range.
@@ -527,7 +527,7 @@ resolvePercentParams(FILE *             const ifP,
             *bvalueP = cmdline.bvalue;
         } else {
             xelval percentBvalue;
-            computeBottomPercentile(hist, maxval, cols*rows, cmdline.bpercent, 
+            computeBottomPercentile(hist, maxval, cols*rows, cmdline.bpercent,
                                     &percentBvalue);
             if (cmdline.bvalueSpec)
                 *bvalueP = MIN(percentBvalue, cmdline.bvalue);
@@ -541,7 +541,7 @@ resolvePercentParams(FILE *             const ifP,
             *wvalueP = cmdline.wvalue;
         } else {
             xelval percentWvalue;
-            computeTopPercentile(hist, maxval, cols*rows, cmdline.wpercent, 
+            computeTopPercentile(hist, maxval, cols*rows, cmdline.wpercent,
                                  &percentWvalue);
             if (cmdline.wvalueSpec)
                 *wvalueP = MAX(percentWvalue, cmdline.wvalue);
@@ -630,8 +630,8 @@ computeLinearTransfer(xelval   const bvalue,
        newBrightness[i] = (i-bvalue)*maxval/range);
        (with proper rounding)
     */
-    for (i = bvalue, val = range/2; 
-         i <= wvalue; 
+    for (i = bvalue, val = range/2;
+         i <= wvalue;
          ++i, val += maxval)
         newBrightness[i] = MIN(val / range, maxval);
 
@@ -674,11 +674,11 @@ computeQuadraticFunction(xelval   const bvalue,
     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) {
@@ -714,11 +714,11 @@ computeQuadraticTransfer(xelval   const bvalue,
 
    Set this mapping in newBrightness[].
 -----------------------------------------------------------------------------*/
-    xelval const middle = ROUNDU(middleNorm * maxval);
+    xelval const middle = pnm_unnormalize(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)
@@ -772,8 +772,8 @@ computeQuadraticTransfer(xelval   const bvalue,
 
 static void
 computeTransferFunction(bool      const quadratic,
-                        xelval    const bvalue, 
-                        xelval    const midvalue, 
+                        xelval    const bvalue,
+                        xelval    const midvalue,
                         xelval    const wvalue,
                         float     const middle,
                         xelval    const maxval,
@@ -809,12 +809,12 @@ computeTransferFunction(bool      const quadratic,
         pm_error("Unable to allocate memory for transfer function.");
 
     /* Clip the lowest brightnesses to zero */
-    if (bvalue > 0) 
+    if (bvalue > 0)
         for (i = 0; i < bvalue; ++i)
             newBrightness[i] = 0;
 
     /* Map the middle brightnesses onto 0..maxval */
-    
+
     if (quadratic)
         computeQuadraticTransfer(bvalue, midvalue, wvalue, middle, maxval,
                                  verbose, newBrightness);
@@ -827,7 +827,7 @@ computeTransferFunction(bool      const quadratic,
 
     *newBrightnessP = newBrightness;
 }
-            
+
 
 
 static float
@@ -846,7 +846,7 @@ brightScaler(xel               const p,
 -----------------------------------------------------------------------------*/
     xelval oldBrightness;
     float scaler;
-             
+
     switch (brightMethod) {
     case BRIGHT_LUMINOSITY:
         oldBrightness = ppm_luminosity(p);
@@ -867,7 +867,7 @@ brightScaler(xel               const p,
 
     return scaler;
 }
-            
+
 
 
 static void
@@ -880,13 +880,13 @@ writeRowNormalized(xel *             const xelrow,
                    xelval            const newBrightness[],
                    xel *             const rowbuf) {
 /*----------------------------------------------------------------------------
-   Write to Standard Output a normalized version of the xel row 
+   Write to Standard Output a normalized version of the xel row
    'xelrow'.  Normalize it via the transfer function newBrightness[].
 
    Use 'rowbuf' as a work buffer.  It is at least 'cols' columns wide.
 -----------------------------------------------------------------------------*/
     xel * const outrow = rowbuf;
-                
+
     unsigned int col;
     for (col = 0; col < cols; ++col) {
         xel const p = xelrow[col];
@@ -900,12 +900,12 @@ writeRowNormalized(xel *             const xelrow,
                 xelval const g = MIN(ROUNDU(PPM_GETG(p)*scaler), maxval);
                 xelval const b = MIN(ROUNDU(PPM_GETB(p)*scaler), maxval);
                 PNM_ASSIGN(outrow[col], r, g, b);
-            } else 
-                PNM_ASSIGN(outrow[col], 
-                           newBrightness[PPM_GETR(p)], 
-                           newBrightness[PPM_GETG(p)], 
+            } else
+                PNM_ASSIGN(outrow[col],
+                           newBrightness[PPM_GETR(p)],
+                           newBrightness[PPM_GETG(p)],
                            newBrightness[PPM_GETB(p)]);
-        } else 
+        } else
             PNM_ASSIGN1(outrow[col], newBrightness[PNM_GET1(p)]);
     }
     pnm_writepnmrow(stdout, outrow, cols, maxval, format, 0);
@@ -924,7 +924,7 @@ reportTransferParm(bool   const quadratic,
     if (quadratic)
         pm_message("remapping %u..%u..%u to %u..%u..%u",
                    bvalue, midvalue, wvalue,
-                   0, ROUNDU(maxval*middle), maxval);
+                   0, pnm_unnormalize(middle, maxval), maxval);
     else
         pm_message("remapping %u..%u to %u..%u",
                    bvalue, wvalue, 0, maxval);
@@ -942,7 +942,7 @@ main(int argc, const char *argv[]) {
     int rows, cols, format;
     bool quadratic;
     xelval bvalue, midvalue, wvalue;
-    
+
     pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
@@ -953,14 +953,14 @@ main(int argc, const char *argv[]) {
     pnm_readpnminit(ifP, &cols, &rows, &maxval, &format);
     pm_tell2(ifP, &imagePos, sizeof(imagePos));
 
-    computeEndValues(ifP, cols, rows, maxval, format, cmdline, 
+    computeEndValues(ifP, cols, rows, maxval, format, cmdline,
                      &bvalue, &wvalue, &quadratic, &midvalue);
     {
         xelval * newBrightness;
         int row;
         xel * xelrow;
         xel * rowbuf;
-        
+
         assert(wvalue > bvalue);
 
         xelrow = pnm_allocrow(cols);
@@ -968,7 +968,7 @@ main(int argc, const char *argv[]) {
         reportTransferParm(quadratic, bvalue, midvalue, wvalue, maxval,
                            cmdline.middle);
 
-        
+
         computeTransferFunction(quadratic, bvalue, midvalue, wvalue,
                                 cmdline.middle, maxval, cmdline.verbose,
                                 &newBrightness);
@@ -987,7 +987,7 @@ main(int argc, const char *argv[]) {
         free(newBrightness);
         pnm_freerow(rowbuf);
         pnm_freerow(xelrow);
-    } 
+    }
     pm_close(ifP);
     return 0;
 }
diff --git a/editor/pnmpad.c b/editor/pnmpad.c
index 051f3895..9c7a77e5 100644
--- a/editor/pnmpad.c
+++ b/editor/pnmpad.c
@@ -2,6 +2,7 @@
    ** AJCD 4/9/90
  */
 
+#include <assert.h>
 #include <string.h>
 #include <stdio.h>
 
@@ -315,8 +316,10 @@ computePadSizeBeforeMult(unsigned int   const unpaddedSize,
                 (unpaddedSize + endPadReq);
         } else {
             if (sizeReq > unpaddedSize) {
+                assert(align <= 1.0 && align >= 0.0);
                 *begPadP = ROUNDU((sizeReq - unpaddedSize) * align);
                 *endPadP = sizeReq - unpaddedSize - *begPadP;
+                assert(*begPadP + unpaddedSize + *endPadP == sizeReq);
             } else {
                 *begPadP = 0;
                 *endPadP = 0;
diff --git a/editor/pnmpaste.c b/editor/pnmpaste.c
index c27e288c..3baaec7d 100644
--- a/editor/pnmpaste.c
+++ b/editor/pnmpaste.c
@@ -19,7 +19,7 @@
 #include "pnm.h"
 
 
-enum boolOp {REPLACE, AND, OR, XOR /*, NAND, NOR, NXOR */ };
+enum boolOp {REPLACE, AND, OR, XOR, NAND, NOR, NXOR};
 
 struct CmdlineInfo {
     /* All the information the user supplied in the command line,
@@ -47,7 +47,7 @@ parseCommandLine(int argc, const char ** argv,
     optStruct3 opt;
 
     unsigned int option_def_index;
-    unsigned int replaceOpt, andOpt, orOpt, xorOpt;
+    unsigned int replaceOpt, andOpt, orOpt, xorOpt, nandOpt, norOpt, nxorOpt;
 
     MALLOCARRAY_NOFAIL(option_def, 100);
 
@@ -60,6 +60,12 @@ parseCommandLine(int argc, const char ** argv,
             &orOpt,                0);
     OPTENT3(0,   "xor",         OPT_FLAG,    NULL,
             &xorOpt,               0);
+    OPTENT3(0,   "nand",        OPT_FLAG,    NULL,
+            &nandOpt,              0);
+    OPTENT3(0,   "nor",         OPT_FLAG,    NULL,
+            &norOpt,               0);
+    OPTENT3(0,   "nxor",        OPT_FLAG,    NULL,
+            &nxorOpt,              0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
@@ -68,16 +74,20 @@ parseCommandLine(int argc, const char ** argv,
     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)
-        pm_error("You may specify only one of -replace, -and, -or, and -xor");
+    if (replaceOpt + andOpt + orOpt + xorOpt + nandOpt + norOpt + nxorOpt > 1)
+        pm_error("You may specify only one of -replace, -and, -or, "
+                 "-xor, -nand, -nor and -nxor");
 
     cmdlineP->operation =
         replaceOpt ? REPLACE :
         andOpt     ? AND     :
         orOpt      ? OR      :
         xorOpt     ? XOR     :
+        nandOpt    ? NAND    :
+        norOpt     ? NOR     :
+        nxorOpt    ? NXOR    :
         replaceOpt;
-        
+
 
     if (argc-1 >= 3) {
         cmdlineP->insetFilename = argv[1];
@@ -168,11 +178,9 @@ insertDirect(FILE *          const ifP,
             case AND: destrow[i] |= buffer[i]; break;
             case OR : destrow[i] &= buffer[i]; break;
             case XOR: destrow[i]  = ~( destrow[i] ^ buffer[i] ) ; break;
-            /*
             case NAND: destrow[i] = ~( destrow[i] | buffer[i] ) ; break;
             case NOR : destrow[i] = ~( destrow[i] & buffer[i] ) ; break;
             case NXOR: destrow[i] ^= buffer[i]  ; break;
-            */
             case REPLACE: assert(false); break;
             }
         }
@@ -229,11 +237,9 @@ insertShift(FILE *          const ifP,
         case AND:     destrow[i] |= t; break;
         case OR :     destrow[i] &= t; break;
         case XOR:     destrow[i] = ~ (destrow[i] ^ t); break;
-        /*
         case NAND:    destrow[i] = ~ (destrow[i] | t); break;
         case NOR :    destrow[i] = ~ (destrow[i] & t); break;
         case NXOR:    destrow[i] ^= t; break;
-        */
         }
     }
 
@@ -244,7 +250,7 @@ insertShift(FILE *          const ifP,
 
     destrow[0] = leftBits(origLeft, offset) |
         rightBits(destrow[0], 8-offset);
-   
+
     if (padOffset % 8 > 0)
         destrow[last] = leftBits(destrow[last], padOffset) |
             rightBits(origRight , 8-padOffset);
@@ -279,7 +285,7 @@ pastePbm(FILE *       const fpInset,
 
     for (row = 0; row < baseRows; ++row) {
         pbm_readpbmrow_packed(fpBase, baserow, baseCols, baseFormat);
-        
+
         if (row >= insertRow && row < insertRow + insetRows) {
             if (shiftOffset == 0)
                 insertDirect(fpInset, &baserow[shiftByteCt], insetCols,
@@ -316,7 +322,7 @@ pasteNonPbm(FILE *       const fpInset,
             unsigned int const insertCol,
             unsigned int const insertRow) {
 
-    /* Logic works for PBM, but cannot do bitwise operations */             
+    /* Logic works for PBM, but cannot do bitwise operations */
 
     xelval const newmaxval = MAX(maxvalInset, maxvalBase);
 
@@ -344,7 +350,7 @@ pasteNonPbm(FILE *       const fpInset,
         }
         pnm_writepnmrow(stdout, xelrowBase, colsBase, newmaxval, newformat, 0);
     }
-    
+
     pnm_freerow(xelrowBase);
     pnm_freerow(xelrowInset);
 }
diff --git a/editor/pnmquant b/editor/pnmquant
index 93d452cd..0bb328d2 100755
--- a/editor/pnmquant
+++ b/editor/pnmquant
@@ -42,6 +42,21 @@ my ($TRUE, $FALSE) = (1,0);
 
 my ($SEEK_SET, $SEEK_CUR, $SEEK_END) = (0, 1, 2);
 
+
+
+sub doVersionHack($) {
+    my ($argvR) = @_;
+
+    my $arg1 = $argvR->[0];
+
+    if (defined($arg1) && (($arg1 eq "--version") || ($arg1 eq "-version"))) {
+        my $termStatus = system('pnmcolormap', '--version');
+        exit($termStatus == 0 ? 0 : 1);
+    }
+}
+
+
+
 sub tempFile($) {
 
     # We trust Perl's File::Temp to do a better job of creating the temp
@@ -81,6 +96,8 @@ sub parseCommandLine(@) {
                                   "spreadbrightness",
                                   "spreadluminosity",
                                   "floyd|fs!",
+                                  "norandom",
+                                  "randomseed=i",
                                   "quiet",
                                   "plain");
 
@@ -93,7 +110,7 @@ sub parseCommandLine(@) {
               scalar(@ARGV), "\n");
         exit(1);
     } 
-    if (@ARGV < 1) {
+    elsif (@ARGV < 1) {
         print(STDERR 
               "You must specify the number of colors as an argument.\n");
         exit(1);
@@ -233,9 +250,10 @@ sub makeColormap($$$$$) {
 
 
 
-sub remap($$$$) {
+sub remap($$$$$$) {
 
-    my ($mapfileSpec, $opt_floyd, $opt_plain, $opt_quiet) = @_;
+    my ($mapfileSpec, $opt_floyd, $opt_norandom, $opt_randomseed,
+        $opt_plain, $opt_quiet) = @_;
 
     # Remap the image on Standard Input to Standard Output, using the colors
     # from the colormap file named $mapfileSpec.
@@ -246,6 +264,17 @@ sub remap($$$$) {
     if ($opt_floyd) {
         push(@options, "-floyd");
     }
+    if ($opt_norandom) {
+        push(@options, "-norandom");
+    }
+    if (defined($opt_randomseed)) {
+        if ($opt_randomseed < 0) {
+             print(STDERR "-randomseed value must not be negative.  " .
+                   "You specified $opt_randomseed\n");
+             exit(10);
+        }
+        push(@options, "-randomseed=$opt_randomseed");
+    }
     if ($opt_plain) {
         push(@options, "-plain");
     }
@@ -267,6 +296,8 @@ sub remap($$$$) {
 #                              MAIN PROGRAM
 ##############################################################################
 
+doVersionHack(\@ARGV);
+
 my $cmdlineR = parseCommandLine(@ARGV);
 
 openSeekableAsStdin($cmdlineR->{infile}); 
@@ -294,6 +325,8 @@ open(STDOUT, ">&OLDOUT");
 
 remap($mapfileSpec, 
       $cmdlineR->{floyd}, 
+      $cmdlineR->{norandom}, 
+      $cmdlineR->{randomseed}, 
       $cmdlineR->{plain},
       $cmdlineR->{quiet});
 
diff --git a/editor/pnmquantall b/editor/pnmquantall
index 2f1a3adf..d268227d 100755
--- a/editor/pnmquantall
+++ b/editor/pnmquantall
@@ -62,6 +62,19 @@ my $TRUE=1; my $FALSE = 0;
 
 
 
+sub doVersionHack($) {
+    my ($argvR) = @_;
+
+    my $arg1 = $argvR->[0];
+
+    if (defined($arg1) && (($arg1 eq "--version") || ($arg1 eq "-version"))) {
+        my $termStatus = system('pnmcolormap', '--version');
+        exit($termStatus == 0 ? 0 : 1);
+    }
+}
+
+
+
 sub parseArgs($$$$) {
     my ($argvR, $extR, $newColorCtR, $fileNamesR) = @_;
 
@@ -157,7 +170,7 @@ sub remapFiles($$$$) {
             my $pnmremapTermStatus = system($pnmremapCmd);
 
             if ($pnmremapTermStatus != 0) {
-                $errorR =
+                $$errorR =
                     "Shell command to quantize '$inFileName'  failed:  " .
                     "'$pnmremapCmd'";
             } else {
@@ -165,7 +178,7 @@ sub remapFiles($$$$) {
 
                 unlink($newFileName);
                 File::Copy::move($outputFileName, $newFileName)
-                    or $errorR = "Rename to '$newFileName' failed.";
+                    or $$errorR = "Rename to '$newFileName' failed.";
             }
         }
         unlink($outputFileName);  # In case something failed
@@ -180,6 +193,8 @@ sub remapFiles($$$$) {
 
 my $progError;
 
+doVersionHack(\@ARGV);
+
 parseArgs(\@ARGV, \my $ext, \my $newColorCt, \my @fileNames);
 
 my ($colorMapFh, $colorMapFileName) = tempFile("pnm");
diff --git a/editor/pnmremap.c b/editor/pnmremap.c
index ed758aa3..0038f4d7 100644
--- a/editor/pnmremap.c
+++ b/editor/pnmremap.c
@@ -41,6 +41,17 @@ enum MissingMethod {
     MISSING_CLOSE
 };
 
+enum InitRandom {
+    RANDOM_NONE,
+    RANDOM_WITHSEED,
+    RANDOM_NOSEED
+};
+
+struct Random {
+    enum InitRandom init;
+    unsigned int seed;
+};
+
 struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
@@ -48,9 +59,9 @@ struct CmdlineInfo {
     const char * inputFilespec;  /* Filespec of input file */
     const char * mapFilespec;    /* Filespec of colormap file */
     unsigned int floyd;   /* Boolean: -floyd/-fs option */
-    unsigned int norandom;
+    struct Random random;
     enum MissingMethod missingMethod;
-    char * missingcolor;      
+    char * missingcolor;
         /* -missingcolor value.  Null if not specified */
     unsigned int verbose;
 };
@@ -62,7 +73,7 @@ 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.  
+   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.
@@ -78,28 +89,30 @@ parseCommandLine (int argc, const char ** argv,
     unsigned int option_def_index;
 
     unsigned int nofloyd, firstisdefault;
-    unsigned int missingSpec, mapfileSpec;
+    unsigned int missingSpec, mapfileSpec, norandomSpec, randomseedSpec;
 
     MALLOCARRAY_NOFAIL(option_def, 100);
-    
+
     option_def_index = 0;   /* incremented by OPTENT3 */
-    OPTENT3(0,   "floyd",          OPT_FLAG,   
+    OPTENT3(0,   "floyd",          OPT_FLAG,
             NULL,                       &cmdlineP->floyd,    0);
-    OPTENT3(0,   "fs",             OPT_FLAG,   
+    OPTENT3(0,   "fs",             OPT_FLAG,
             NULL,                       &cmdlineP->floyd,    0);
-    OPTENT3(0,   "nofloyd",        OPT_FLAG,   
+    OPTENT3(0,   "nofloyd",        OPT_FLAG,
             NULL,                       &nofloyd,            0);
-    OPTENT3(0,   "nofs",           OPT_FLAG,   
+    OPTENT3(0,   "nofs",           OPT_FLAG,
             NULL,                       &nofloyd,            0);
-    OPTENT3(0,   "norandom",       OPT_FLAG,   
-            NULL,                       &cmdlineP->norandom, 0);
-    OPTENT3(0,   "firstisdefault", OPT_FLAG,   
+    OPTENT3(0,   "norandom",       OPT_FLAG,
+            NULL,                       &norandomSpec,       0);
+    OPTENT3(0,   "randomseed",     OPT_UINT,
+            &cmdlineP->random.seed,     &randomseedSpec,     0);
+    OPTENT3(0,   "firstisdefault", OPT_FLAG,
             NULL,                       &firstisdefault,     0);
-    OPTENT3(0,   "mapfile",        OPT_STRING, 
+    OPTENT3(0,   "mapfile",        OPT_STRING,
             &cmdlineP->mapFilespec,    &mapfileSpec,         0);
-    OPTENT3(0,   "missingcolor",   OPT_STRING, 
+    OPTENT3(0,   "missingcolor",   OPT_STRING,
             &cmdlineP->missingcolor,   &missingSpec,         0);
-    OPTENT3(0, "verbose",          OPT_FLAG,   NULL,                  
+    OPTENT3(0, "verbose",          OPT_FLAG,   NULL,
             &cmdlineP->verbose,                              0);
 
     opt.opt_table = option_def;
@@ -107,13 +120,32 @@ parseCommandLine (int argc, const char ** argv,
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
     cmdlineP->missingcolor = NULL;  /* default value */
-    
+
     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)
         pm_error("You cannot specify both -floyd and -nofloyd options.");
 
+    if (cmdlineP->floyd) {
+        if (norandomSpec) {
+            if (randomseedSpec)
+                pm_error("You cannot specify both -norandom and -randomseed.");
+            else
+                cmdlineP->random.init = RANDOM_NONE;
+        } else {
+            if (randomseedSpec)
+                cmdlineP->random.init = RANDOM_WITHSEED;
+            else
+                cmdlineP->random.init = RANDOM_NOSEED;
+        }
+    } else {
+        if (norandomSpec)
+            pm_message("-floyd not specified.  -norandom has no effect.");
+        if (randomseedSpec)
+            pm_message("-floyd not specified.  Ignoring -randomseed value.");
+    }
+
     if (firstisdefault && missingSpec)
         pm_error("You cannot specify both -missing and -firstisdefault.");
 
@@ -176,7 +208,7 @@ grayscaleToDepth3(tuple const tuple) {
 static void
 adjustDepthTuple(tuple           const tuple,
                  depthAdjustment const adjustment) {
-    
+
     switch (adjustment) {
     case ADJUST_NONE:
         break;
@@ -194,7 +226,7 @@ adjustDepthTuple(tuple           const tuple,
 static void
 inverseAdjustDepthTuple(tuple           const tuple,
                         depthAdjustment const adjustment) {
-    
+
     switch (adjustment) {
     case ADJUST_NONE:
         break;
@@ -249,7 +281,7 @@ selectDepthAdjustment(const struct pam * const pamP,
 
    The only depth changes we know how to do are:
 
-     - from tuple type RGB, depth 3 to depth 1 
+     - from tuple type RGB, depth 3 to depth 1
 
        We change it to grayscale or black and white.
 
@@ -296,7 +328,7 @@ selectDepthAdjustment(const struct pam * const pamP,
                      "that I know how to convert to the map depth.  "
                      "I can convert RGB, GRAYSCALE, and BLACKANDWHITE.  "
                      "The input image is '%.*s'.",
-                     newDepth, pamP->depth, 
+                     newDepth, pamP->depth,
                      (int)sizeof(pamP->tuple_type), pamP->tuple_type);
         }
     }
@@ -305,8 +337,8 @@ selectDepthAdjustment(const struct pam * const pamP,
 
 
 static void
-computeColorMapFromMap(struct pam *   const mappamP, 
-                       tuple **       const maptuples, 
+computeColorMapFromMap(struct pam *   const mappamP,
+                       tuple **       const maptuples,
                        tupletable *   const colormapP,
                        unsigned int * const newcolorsP) {
 /*----------------------------------------------------------------------------
@@ -317,12 +349,12 @@ computeColorMapFromMap(struct pam *   const mappamP,
 
    Return the number of colors in the returned colormap as *newcolorsP.
 -----------------------------------------------------------------------------*/
-    unsigned int colors; 
+    unsigned int colors;
 
     if (mappamP->width == 0 || mappamP->height == 0)
         pm_error("colormap file contains no pixels");
 
-    *colormapP = 
+    *colormapP =
         pnm_computetuplefreqtable(mappamP, maptuples, MAXCOLORS, &colors);
     if (*colormapP == NULL)
         pm_error("too many colors in colormap!");
@@ -334,7 +366,7 @@ computeColorMapFromMap(struct pam *   const mappamP,
 
 #define FS_SCALE 1024
 
-struct fserr {
+struct Fserr {
     unsigned int width;
         /* Width of the image being dithered */
     long ** thiserr;
@@ -359,20 +391,26 @@ struct fserr {
 
 
 static void
-randomizeError(long **      const err,
-               unsigned int const width,
-               unsigned int const depth) {
+randomizeError(long **       const err,
+               unsigned int  const width,
+               unsigned int  const depth,
+               struct Random const random) {
 /*----------------------------------------------------------------------------
    Set a random error in the range [-1 .. 1] (normalized via FS_SCALE)
    in the error array err[][].
 -----------------------------------------------------------------------------*/
+    unsigned int const seed = (random.init == RANDOM_WITHSEED) ?
+        random.seed : pm_randseed();
+
     unsigned int col;
 
-    srand(pm_randseed());
+    assert(random.init != RANDOM_NONE);
+
+    srand(seed);
 
     for (col = 0; col < width; ++col) {
         unsigned int plane;
-        for (plane = 0; plane < depth; ++plane) 
+        for (plane = 0; plane < depth; ++plane)
             err[plane][col] = rand() % (FS_SCALE * 2) - FS_SCALE;
     }
 }
@@ -390,7 +428,7 @@ zeroError(long **      const err,
 
     for (col = 0; col < width; ++col) {
         unsigned int plane;
-        for (plane = 0; plane < depth; ++plane) 
+        for (plane = 0; plane < depth; ++plane)
             err[plane][col] = 0;
     }
 }
@@ -398,7 +436,7 @@ zeroError(long **      const err,
 
 
 static void
-fserrSetForward(struct fserr * const fserrP) {
+fserr_setForward(struct Fserr * const fserrP) {
 
     fserrP->fsForward = TRUE;
     fserrP->begCol = 0;
@@ -409,7 +447,7 @@ fserrSetForward(struct fserr * const fserrP) {
 
 
 static void
-fserrSetBackward(struct fserr * const fserrP) {
+fserr_setBackward(struct Fserr * const fserrP) {
 
     fserrP->fsForward = FALSE;
     fserrP->begCol = fserrP->width - 1;
@@ -420,9 +458,9 @@ fserrSetBackward(struct fserr * const fserrP) {
 
 
 static void
-initFserr(struct pam *   const pamP,
-          struct fserr * const fserrP,
-          bool           const initRandom) {
+fserr_init(struct pam *   const pamP,
+           struct Fserr * const fserrP,
+           struct Random  const random) {
 /*----------------------------------------------------------------------------
    Initialize the Floyd-Steinberg error vectors
 -----------------------------------------------------------------------------*/
@@ -440,7 +478,7 @@ initFserr(struct pam *   const pamP,
     if (fserrP->nexterr == NULL)
         pm_error("Out of memory allocating Floyd-Steinberg structures "
                  "for depth %u", pamP->depth);
-    
+
     for (plane = 0; plane < pamP->depth; ++plane) {
         MALLOCARRAY(fserrP->thiserr[plane], fserrSize);
         if (fserrP->thiserr[plane] == NULL)
@@ -452,24 +490,25 @@ initFserr(struct pam *   const pamP,
                      "for Plane %u, size %u", plane, fserrSize);
     }
 
-    if (initRandom)
-        randomizeError(fserrP->thiserr, fserrSize, pamP->depth);
+    if (random.init != RANDOM_NONE)
+        randomizeError(fserrP->thiserr, fserrSize, pamP->depth, random);
     else
         zeroError(fserrP->thiserr, fserrSize, pamP->depth);
 
-    fserrSetForward(fserrP);
+    fserr_setForward(fserrP);
 }
 
 
 
 static void
-floydInitRow(struct pam * const pamP, struct fserr * const fserrP) {
+floydInitRow(struct pam *   const pamP,
+             struct Fserr * const fserrP) {
+
+    unsigned int col;
 
-    int col;
-    
     for (col = 0; col < pamP->width + 2; ++col) {
         unsigned int plane;
-        for (plane = 0; plane < pamP->depth; ++plane) 
+        for (plane = 0; plane < pamP->depth; ++plane)
             fserrP->nexterr[plane][col] = 0;
     }
 }
@@ -477,10 +516,10 @@ floydInitRow(struct pam * const pamP, struct fserr * const fserrP) {
 
 
 static void
-floydAdjustColor(struct pam *   const pamP, 
-                 tuple          const intuple, 
-                 tuple          const outtuple, 
-                 struct fserr * const fserrP, 
+floydAdjustColor(struct pam *   const pamP,
+                 tuple          const intuple,
+                 tuple          const outtuple,
+                 struct Fserr * const fserrP,
                  int            const col) {
 /*----------------------------------------------------------------------------
   Use Floyd-Steinberg errors to adjust actual color.
@@ -497,10 +536,10 @@ floydAdjustColor(struct pam *   const pamP,
 
 
 static void
-floydPropagateErr(struct pam *   const pamP, 
-                  struct fserr * const fserrP, 
-                  int            const col, 
-                  tuple          const oldtuple, 
+floydPropagateErr(struct pam *   const pamP,
+                  struct Fserr * const fserrP,
+                  int            const col,
+                  tuple          const oldtuple,
                   tuple          const newtuple) {
 /*----------------------------------------------------------------------------
   Propagate Floyd-Steinberg error terms.
@@ -515,7 +554,7 @@ floydPropagateErr(struct pam *   const pamP,
         long const newSample = newtuple[plane];
         long const oldSample = oldtuple[plane];
         long const err = (oldSample - newSample) * FS_SCALE;
-            
+
         if (fserrP->fsForward) {
             fserrP->thiserr[plane][col + 2] += ( err * 7 ) / 16;
             fserrP->nexterr[plane][col    ] += ( err * 3 ) / 16;
@@ -533,7 +572,8 @@ floydPropagateErr(struct pam *   const pamP,
 
 
 static void
-floydSwitchDir(struct pam * const pamP, struct fserr * const fserrP) {
+floydSwitchDir(struct pam *   const pamP,
+               struct Fserr * const fserrP) {
 
     unsigned int plane;
 
@@ -544,9 +584,9 @@ floydSwitchDir(struct pam * const pamP, struct fserr * const fserrP) {
     }
 
     if (fserrP->fsForward)
-        fserrSetBackward(fserrP);
+        fserr_setBackward(fserrP);
     else
-        fserrSetForward(fserrP);
+        fserr_setForward(fserrP);
 }
 
 
@@ -567,7 +607,7 @@ struct colormapFinder {
         /* The value by which our intermediate distance calculations
            have to be divided to make sure we don't overflow our
            unsigned int data structure.
-           
+
            To the extent 'distanceDivider' is greater than 1, closest
            color results will be approximate -- there could
            conceivably be a closer one that we miss.
@@ -590,13 +630,13 @@ createColormapFinder(struct pam *             const pamP,
     colormapFinderP->colors = colors;
 
     {
-        unsigned int const maxHandleableSqrDiff = 
+        unsigned int const maxHandleableSqrDiff =
             (unsigned int)UINT_MAX / pamP->depth;
-        
+
         if (SQR(pamP->maxval) > maxHandleableSqrDiff)
             colormapFinderP->distanceDivider = (unsigned int)
                 (SQR(pamP->maxval) / maxHandleableSqrDiff + 0.1 + 1.0);
-                /* The 0.1 is a fudge factor to keep us out of rounding 
+                /* The 0.1 is a fudge factor to keep us out of rounding
                    trouble.  The 1.0 effects a round-up.
                 */
         else
@@ -664,8 +704,8 @@ searchColormapClose(struct pam *            const pamP,
         newdist = 0;
 
         for (plane=0; plane < pamP->depth; ++plane) {
-            newdist += 
-                SQR(tuple[plane] - colorFinderP->colormap[i]->tuple[plane]) 
+            newdist +=
+                SQR(tuple[plane] - colorFinderP->colormap[i]->tuple[plane])
                 / colorFinderP->distanceDivider;
         }
         if (newdist < dist) {
@@ -695,15 +735,15 @@ searchColormapExact(struct pam *            const pamP,
 -----------------------------------------------------------------------------*/
     unsigned int i;
     bool found;
-    
+
     found = FALSE;  /* initial value */
     for (i = 0; i < colorFinderP->colors && !found; ++i) {
         unsigned int plane;
         found = TRUE;  /* initial assumption */
-        for (plane=0; plane < pamP->depth; ++plane) 
-            if (tuple[plane] != colorFinderP->colormap[i]->tuple[plane]) 
+        for (plane=0; plane < pamP->depth; ++plane)
+            if (tuple[plane] != colorFinderP->colormap[i]->tuple[plane])
                 found = FALSE;
-        if (found) 
+        if (found)
             *colormapIndexP = i;
     }
     *foundP = found;
@@ -712,11 +752,11 @@ searchColormapExact(struct pam *            const pamP,
 
 
 static void
-lookupThroughHash(struct pam *            const pamP, 
-                  tuple                   const tuple, 
+lookupThroughHash(struct pam *            const pamP,
+                  tuple                   const tuple,
                   bool                    const needExactMatch,
                   struct colormapFinder * const colorFinderP,
-                  tuplehash               const colorhash,       
+                  tuplehash               const colorhash,
                   int *                   const colormapIndexP,
                   bool *                  const usehashP) {
 /*----------------------------------------------------------------------------
@@ -748,11 +788,11 @@ lookupThroughHash(struct pam *            const pamP,
                                 colormapIndexP, &found);
             if (!found)
                 *colormapIndexP = -1;
-        } else 
+        } else
             searchColormapClose(pamP, tuple, colorFinderP, colormapIndexP);
         if (*usehashP) {
             int fits;
-            pnm_addtotuplehash(pamP, colorhash, tuple, *colormapIndexP, 
+            pnm_addtotuplehash(pamP, colorhash, tuple, *colormapIndexP,
                                &fits);
             if (!fits) {
                 pm_message("out of memory adding to hash table; "
@@ -771,7 +811,7 @@ mapTuple(struct pam *            const pamP,
          tuple                   const defaultColor,
          tupletable              const colormap,
          struct colormapFinder * const colorFinderP,
-         tuplehash               const colorhash, 
+         tuplehash               const colorhash,
          bool *                  const usehashP,
          tuple                   const outTuple,
          bool *                  const missingP) {
@@ -781,7 +821,7 @@ mapTuple(struct pam *            const pamP,
            there is no usable color in the color map.
         */
 
-    lookupThroughHash(pamP, inTuple, !!defaultColor, colorFinderP, 
+    lookupThroughHash(pamP, inTuple, !!defaultColor, colorFinderP,
                       colorhash, &colormapIndex, usehashP);
 
     if (colormapIndex == -1) {
@@ -800,12 +840,12 @@ mapTuple(struct pam *            const pamP,
 
 static void
 convertRowStraight(struct pam *            const inpamP,
-                   struct pam *            const outpamP, 
+                   struct pam *            const outpamP,
                    tuple                         inrow[],
                    depthAdjustment         const depthAdjustment,
                    tupletable              const colormap,
                    struct colormapFinder * const colorFinderP,
-                   tuplehash               const colorhash, 
+                   tuplehash               const colorhash,
                    bool *                  const usehashP,
                    tuple                   const defaultColor,
                    tuple                         outrow[],
@@ -822,7 +862,7 @@ convertRowStraight(struct pam *            const inpamP,
 -----------------------------------------------------------------------------*/
     unsigned int col;
     unsigned int missingCount;
-    
+
     /* The following modify tuplerow, to make it consistent with
      *outpamP instead of *inpamP.
      */
@@ -833,7 +873,7 @@ convertRowStraight(struct pam *            const inpamP,
     adjustDepthRow(outrow, outpamP->width, depthAdjustment);
 
     missingCount = 0;  /* initial value */
-    
+
     for (col = 0; col < outpamP->width; ++col) {
         bool missing;
         mapTuple(outpamP, outrow[col], defaultColor,
@@ -856,10 +896,10 @@ convertRowDither(struct pam *            const inpamP,
                  depthAdjustment         const depthAdjustment,
                  tupletable              const colormap,
                  struct colormapFinder * const colorFinderP,
-                 tuplehash               const colorhash, 
+                 tuplehash               const colorhash,
                  bool *                  const usehashP,
                  tuple                   const defaultColor,
-                 struct fserr *          const fserrP,
+                 struct Fserr *          const fserrP,
                  tuple                         outrow[],
                  unsigned int *          const missingCountP) {
 /*----------------------------------------------------------------------------
@@ -885,7 +925,7 @@ convertRowDither(struct pam *            const inpamP,
     floydInitRow(inpamP, fserrP);
 
     missingCount = 0;  /* initial value */
-    
+
     for (col = fserrP->begCol; col != fserrP->endCol; col += fserrP->step) {
         bool missing;
 
@@ -929,11 +969,11 @@ convertRow(struct pam *            const inpamP,
            depthAdjustment               depthAdjustment,
            tupletable              const colormap,
            struct colormapFinder * const colorFinderP,
-           tuplehash               const colorhash, 
+           tuplehash               const colorhash,
            bool *                  const usehashP,
-           bool                    const floyd, 
+           bool                    const floyd,
            tuple                   const defaultColor,
-           struct fserr *          const fserrP,
+           struct Fserr *          const fserrP,
            tuple                         outrow[],
            unsigned int *          const missingCountP) {
 /*----------------------------------------------------------------------------
@@ -959,7 +999,7 @@ convertRow(struct pam *            const inpamP,
                          depthAdjustment, colormap, colorFinderP, colorhash,
                          usehashP, defaultColor,
                          fserrP, outrow, missingCountP);
-    else 
+    else
         convertRowStraight(inpamP, outpamP, inrow,
                            depthAdjustment, colormap, colorFinderP, colorhash,
                            usehashP, defaultColor,
@@ -969,14 +1009,14 @@ convertRow(struct pam *            const inpamP,
 
 
 static void
-copyRaster(struct pam *       const inpamP, 
-           struct pam *       const outpamP,
-           tupletable         const colormap, 
-           unsigned int       const colormapSize,
-           bool               const floyd, 
-           bool               const randomize,
-           tuple              const defaultColor, 
-           unsigned int *     const missingCountP) {
+copyRaster(struct pam *   const inpamP,
+           struct pam *   const outpamP,
+           tupletable     const colormap,
+           unsigned int   const colormapSize,
+           bool           const floyd,
+           struct Random  const random,
+           tuple          const defaultColor,
+           unsigned int * const missingCountP) {
 
     tuplehash const colorhash = pnm_createtuplehash();
 
@@ -992,7 +1032,7 @@ copyRaster(struct pam *       const inpamP,
     depthAdjustment depthAdjustment;
     struct colormapFinder * colorFinderP;
     bool usehash;
-    struct fserr fserr;
+    struct Fserr fserr;
     int row;
 
     workpam = *outpamP;
@@ -1017,7 +1057,7 @@ copyRaster(struct pam *       const inpamP,
     createColormapFinder(outpamP, colormap, colormapSize, &colorFinderP);
 
     if (floyd)
-        initFserr(inpamP, &fserr, randomize);
+        fserr_init(inpamP, &fserr, random);
 
     *missingCountP = 0;  /* initial value */
 
@@ -1030,9 +1070,9 @@ copyRaster(struct pam *       const inpamP,
                    depthAdjustment, colormap, colorFinderP, colorhash,
                    &usehash, floyd, defaultColor,
                    &fserr,  outrow, &missingCount);
-        
+
         *missingCountP += missingCount;
-        
+
         pnm_writepamrow(outpamP, outrow);
     }
     destroyColormapFinder(colorFinderP);
@@ -1046,10 +1086,10 @@ copyRaster(struct pam *       const inpamP,
 static void
 remap(FILE *             const ifP,
       const struct pam * const outpamCommonP,
-      tupletable         const colormap, 
+      tupletable         const colormap,
       unsigned int       const colormapSize,
       bool               const floyd,
-      bool               const randomize,
+      struct Random      const random,
       tuple              const defaultColor,
       bool               const verbose) {
 /*----------------------------------------------------------------------------
@@ -1075,7 +1115,7 @@ remap(FILE *             const ifP,
             */
 
         pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(allocation_depth));
-    
+
         outpam = *outpamCommonP;
         outpam.width  = inpam.width;
         outpam.height = inpam.height;
@@ -1086,13 +1126,13 @@ remap(FILE *             const ifP,
            convert the input to the output depth.
         */
         pnm_setminallocationdepth(&inpam, outpam.depth);
-    
+
         copyRaster(&inpam, &outpam, colormap, colormapSize, floyd,
-                   randomize, defaultColor, &missingCount);
-        
+                   random, defaultColor, &missingCount);
+
         if (verbose)
             pm_message("%u pixels not matched in color map", missingCount);
-        
+
         pnm_nextimage(ifP, &eof);
     }
 }
@@ -1130,7 +1170,7 @@ processMapFile(const char *   const mapFileName,
 
     pnm_freepamarray(maptuples, &mappam);
 
-    *outpamCommonP = mappam; 
+    *outpamCommonP = mappam;
     outpamCommonP->file = stdout;
 }
 
@@ -1142,7 +1182,7 @@ getSpecifiedMissingColor(struct pam * const pamP,
                          tuple *      const specColorP) {
 
     tuple specColor;
-                             
+
     specColor = pnm_allocpamtuple(pamP);
 
     if (colorName) {
@@ -1213,8 +1253,8 @@ main(int argc, const char * argv[] ) {
         break;
     }
 
-    remap(ifP, &outpamCommon, colormap, colormapSize, 
-          cmdline.floyd, !cmdline.norandom, defaultColor,
+    remap(ifP, &outpamCommon, colormap, colormapSize,
+          cmdline.floyd, cmdline.random, defaultColor,
           cmdline.verbose);
 
     pnm_freepamtuple(firstColor);
diff --git a/editor/pnmrotate.c b/editor/pnmrotate.c
index da5de3a8..44952a59 100644
--- a/editor/pnmrotate.c
+++ b/editor/pnmrotate.c
@@ -10,7 +10,7 @@
 ** implied warranty.
 */
 
-#define _XOPEN_SOURCE   /* get M_PI in math.h */
+#define _XOPEN_SOURCE 500  /* get M_PI in math.h */
 
 #include <math.h>
 #include <assert.h>
diff --git a/editor/pnmshear.c b/editor/pnmshear.c
index 99fa3026..1c330bb7 100644
--- a/editor/pnmshear.c
+++ b/editor/pnmshear.c
@@ -10,7 +10,7 @@
 ** implied warranty.
 */
 
-#define _XOPEN_SOURCE   /* get M_PI in math.h */
+#define _XOPEN_SOURCE 500  /* get M_PI in math.h */
 
 #include <assert.h>
 #include <math.h>
diff --git a/editor/pnmstitch.c b/editor/pnmstitch.c
index 849445fb..eae5e1b9 100644
--- a/editor/pnmstitch.c
+++ b/editor/pnmstitch.c
@@ -82,6 +82,7 @@
  *      - user selectable blending algorithms?
  */
 
+#define _DEFAULT_SOURCE 1 /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE 1   /* Make sure strdup() is in string.h */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
diff --git a/editor/ppmbrighten.c b/editor/ppmbrighten.c
index a3e9a270..0446bb75 100644
--- a/editor/ppmbrighten.c
+++ b/editor/ppmbrighten.c
@@ -1,28 +1,19 @@
-/* ppmbrighten.c - allow user control over Value and Saturation of PPM file
-**
-** Copyright (C) 1989 by Jef Poskanzer.
-** Copyright (C) 1990 by Brian Moffet.
-**
-** 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.
-*/
+/*=============================================================================
+                              ppmbrighten
+===============================================================================
+  Change Value and Saturation of PPM image.
+=============================================================================*/
 
 #include "pm_c_util.h"
 #include "ppm.h"
 #include "shhopt.h"
 #include "mallocvar.h"
 
-#define MULTI   1000
-
-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 */
     float saturation;
     float value;
     unsigned int normalize;
@@ -31,11 +22,11 @@ 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.  
+   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.
@@ -63,14 +54,13 @@ parseCommandLine(int argc, char ** argv,
     OPTENT3(0, "normalize",   OPT_FLAG,   NULL,
             &cmdlineP->normalize, 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, 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 (saturationSpec) {
         if (saturationOpt < -100)
             pm_error("Saturation reduction cannot be more than 100%%.  "
@@ -90,172 +80,41 @@ parseCommandLine(int argc, char ** argv,
         cmdlineP->value = 1.0;
 
     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:  file specification");
 }
 
 
 
-static __inline__ unsigned int
-mod(int const dividend, unsigned int const divisor) {
-
-    int remainder = dividend % (int)divisor;
-
-    if (remainder < 0)
-        return divisor + remainder;
-    else 
-        return (unsigned int) remainder;
-}
-
-
-
-static void 
-RGBtoHSV(pixel          const color,
-         pixval         const maxval,
-         unsigned int * const hP, 
-         unsigned int * const sP, 
-         unsigned int * const vP) {
-
-    unsigned int const R = (MULTI * PPM_GETR(color) + maxval - 1) / maxval;
-    unsigned int const G = (MULTI * PPM_GETG(color) + maxval - 1) / maxval;
-    unsigned int const B = (MULTI * PPM_GETB(color) + maxval - 1) / maxval;
-
-    unsigned int s, v;
-    unsigned int t;
-    unsigned int sector;
-
-    v = MAX(R, MAX(G, B));
-
-    t = MIN(R, MIN(G, B));
-
-    if (v == 0)
-        s = 0;
-    else
-        s = ((v - t)*MULTI)/v;
-
-    if (s == 0)
-        sector = 0;
-    else {
-        unsigned int const cr = (MULTI * (v - R))/(v - t);
-        unsigned int const cg = (MULTI * (v - G))/(v - t);
-        unsigned int const cb = (MULTI * (v - B))/(v - t);
-
-        if (R == v)
-            sector = mod((int)(cb - cg), 6*MULTI);
-        else if (G == v)
-            sector = mod((int)((2*MULTI) + cr - cb), 6*MULTI);
-        else if (B == v)
-            sector = mod((int)((4*MULTI) + cg - cr), 6*MULTI);
-        else
-            pm_error("Internal error: neither r, g, nor b is maximum");
-    }
-
-    *hP = sector * 60;
-    *sP = s;
-    *vP = v;
-}
-
-
-
-static void
-HSVtoRGB(unsigned int   const h, 
-         unsigned int   const s, 
-         unsigned int   const v, 
-         pixval         const maxval,
-         pixel *        const colorP) {
-    
-    unsigned int R, G, B;
-
-    if (s == 0) {
-        R = v;
-        G = v;
-        B = v;
-    } else {
-        unsigned int const sectorSize = 60 * MULTI;
-            /* Color wheel is divided into six 60 degree sectors. */
-        unsigned int const sector = (h/sectorSize);
-            /* The sector in which our color resides.  Value is in 0..5 */
-        unsigned int const f = (h - sector*sectorSize)/60;
-            /* The fraction of the way the color is from one side of
-               our sector to the other side, going clockwise.  Value is
-               in [0, MULTI).
-            */
-        unsigned int const m = (v * (MULTI - s)) / MULTI;
-        unsigned int const n = (v * (MULTI - (s * f)/MULTI)) / MULTI;
-        unsigned int const k = (v * (MULTI - (s * (MULTI - f))/MULTI)) / MULTI;
-
-        switch (sector) {
-        case 0:
-            R = v;
-            G = k;
-            B = m;
-            break;
-        case 1:
-            R = n;
-            G = v;
-            B = m;
-            break;
-        case 2:
-            R = m;
-            G = v;
-            B = k;
-            break;
-        case 3:
-            R = m;
-            G = n;
-            B = v;
-            break;
-        case 4:
-            R = k;
-            G = m;
-            B = v;
-            break;
-        case 5:
-            R = v;
-            G = m;
-            B = n;
-            break;
-        default:
-            pm_error("Invalid H value passed to HSVtoRGB: %u/%u", h, MULTI);
-        }
-    }
-    PPM_ASSIGN(*colorP, 
-               (R * maxval) / MULTI,
-               (G * maxval) / MULTI,
-               (B * maxval) / MULTI);
-}
-
-
-
 static void
-getMinMax(FILE *         const ifP,
-          int            const cols,
-          int            const rows,
-          pixval         const maxval,
-          int            const format,
-          unsigned int * const minValueP,
-          unsigned int * const maxValueP) {
+getMinMax(FILE *       const ifP,
+          unsigned int const cols,
+          unsigned int const rows,
+          pixval       const maxval,
+          int          const format,
+          double *     const minValueP,
+          double *     const maxValueP) {
 
     pixel * pixelrow;
-    unsigned int minValue, maxValue;
-    int row;
+    double minValue, maxValue;
+    unsigned int row;
 
     pixelrow = ppm_allocrow(cols);
 
-    maxValue = 0;
-    minValue = MULTI;
-    for (row = 0; row < rows; ++row) {
+    for (row = 0, minValue = 65536.0, maxValue = 0.0; row < rows; ++row) {
         unsigned int col;
+
         ppm_readppmrow(ifP, pixelrow, cols, maxval, format);
+
         for (col = 0; col < cols; ++col) {
-            unsigned int H, S, V;
+            struct hsv const pixhsv =
+                ppm_hsv_from_color(pixelrow[col], maxval);
 
-            RGBtoHSV(pixelrow[col], maxval, &H, &S, &V);
-            maxValue = MAX(maxValue, V);
-            minValue = MIN(minValue, V);
+            maxValue = MAX(maxValue, pixhsv.v);
+            minValue = MIN(minValue, pixhsv.v);
         }
     }
     ppm_freerow(pixelrow);
@@ -267,23 +126,24 @@ getMinMax(FILE *         const ifP,
 
 
 int
-main(int argc, char * argv[]) {
+main(int argc, const char ** argv) {
 
-    struct cmdlineInfo cmdline;
+    double const EPSILON = 1.0e-5;
+    struct CmdlineInfo cmdline;
     FILE * ifP;
-    pixval minValue, maxValue;
     pixel * pixelrow;
     pixval maxval;
     int rows, cols, format, row;
+    double minValue, maxValue;
 
-    ppm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
     if (cmdline.normalize)
-        ifP = pm_openr_seekable(cmdline.inputFilespec);
+        ifP = pm_openr_seekable(cmdline.inputFileName);
     else
-        ifP = pm_openr(cmdline.inputFilespec);
+        ifP = pm_openr(cmdline.inputFileName);
 
     ppm_readppminit(ifP, &cols, &rows, &maxval, &format);
 
@@ -292,17 +152,17 @@ main(int argc, char * argv[]) {
         pm_tell2(ifP, &rasterPos, sizeof(rasterPos));
         getMinMax(ifP, cols, rows, maxval, format, &minValue, &maxValue);
         pm_seek2(ifP, &rasterPos, sizeof(rasterPos));
-        if (maxValue > minValue) {
-            pm_message("Minimum value %u%% of full intensity "
+        if (maxValue - minValue > EPSILON) {
+            pm_message("Minimum value %.0f%% of full intensity "
                        "being remapped to zero.",
-                       (minValue*100+MULTI/2)/MULTI);
-            pm_message("Maximum value %u%% of full intensity "
+                       (minValue * 100.0));
+            pm_message("Maximum value %.0f%% of full intensity "
                        "being remapped to full.",
-                       (maxValue*100+MULTI/2)/MULTI);
+                       (maxValue * 100.0));
         } else
-            pm_message("Sole intensity value %u%% of full intensity "
+            pm_message("Sole value of %.0f%% of full intensity "
                        "not being remapped",
-                       (minValue*100+MULTI/2)/MULTI);
+                       (maxValue * 100.0));
     }
 
     pixelrow = ppm_allocrow(cols);
@@ -311,34 +171,48 @@ main(int argc, char * argv[]) {
 
     for (row = 0; row < rows; ++row) {
         unsigned int col;
+
         ppm_readppmrow(ifP, pixelrow, cols, maxval, format);
+
         for (col = 0; col < cols; ++col) {
-            unsigned int H, S, V;
+            struct hsv pixhsv;
+
+            pixhsv = ppm_hsv_from_color(pixelrow[col], maxval);
+                /* initial value */
 
-            RGBtoHSV(pixelrow[col], maxval, &H, &S, &V);
-            
             if (cmdline.normalize) {
-                if (maxValue > minValue) {
-                    V -= minValue;
-                    V = (V * MULTI) /
-                        (MULTI - (minValue+MULTI-maxValue));
-                }
+                if (maxValue - minValue > EPSILON)
+                    pixhsv.v = (pixhsv.v - minValue) / (maxValue - minValue);
             }
-
-            S = MIN(MULTI, (unsigned int) (S * cmdline.saturation + 0.5));
-            V = MIN(MULTI, (unsigned int) (V * cmdline.value + 0.5));
-
-            HSVtoRGB(H, S, V, maxval, &pixelrow[col]);
+            pixhsv.s = pixhsv.s * cmdline.saturation;
+            pixhsv.s = MAX(0.0, MIN(1.0, pixhsv.s));
+            pixhsv.v = pixhsv.v * cmdline.value;
+            pixhsv.v = MAX(0.0, MIN(1.0, pixhsv.v));
+            pixelrow[col] = ppm_color_from_hsv(pixhsv, maxval);
         }
-
         ppm_writeppmrow(stdout, pixelrow, cols, maxval, 0);
     }
     ppm_freerow(pixelrow);
 
     pm_close(ifP);
 
-    /* If the program failed, it previously aborted with nonzero completion
-       code, via various function calls.
+    /* If the program failed, it previously aborted with nonzero exit status
+       via various function calls.
     */
     return 0;
 }
+
+
+
+/**
+** Copyright (C) 1989 by Jef Poskanzer.
+** Copyright (C) 1990 by Brian Moffet.
+**
+** 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.
+*/
+
diff --git a/editor/ppmchange.c b/editor/ppmchange.c
index dea85a77..cfc34769 100644
--- a/editor/ppmchange.c
+++ b/editor/ppmchange.c
@@ -19,20 +19,22 @@
 #include "mallocvar.h"
 
 #define TCOLS 256
-#define SQRT3 1.73205080756887729352
+static double const sqrt3 = 1.73205080756887729352;
     /* The square root of 3 */
+static double const EPSILON = 1.0e-5;
 
-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 */
     int ncolors;      /* Number of valid entries in color0[], color1[] */
-    char * oldcolorname[TCOLS];  /* colors user wants replaced */
-    char * newcolorname[TCOLS];  /* colors with which he wants them replaced */
-    int closeness;    
-       /* -closeness option value.  Zero if no -closeness option */
-    char * remainder_colorname;  
+    const char * oldcolorname[TCOLS];
+        /* colors user wants replaced */
+    const char * newcolorname[TCOLS];
+        /* colors with which he wants them replaced */
+    float closeness;    
+    const char * remainder_colorname;  
       /* Color user specified for -remainder.  Null pointer if he didn't
          specify -remainder.
       */
@@ -42,8 +44,8 @@ struct cmdlineInfo {
 
 
 static void
-parseCommandLine(int argc, char ** 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.
@@ -59,7 +61,7 @@ parseCommandLine(int argc, char ** argv,
     MALLOCARRAY_NOFAIL(option_def, 100);
 
     option_def_index = 0;   /* incremented by OPTENTRY */
-    OPTENT3(0, "closeness",     OPT_UINT,
+    OPTENT3(0, "closeness",     OPT_FLOAT,
             &cmdlineP->closeness,           &closenessSpec,     0);
     OPTENT3(0, "remainder",     OPT_STRING,
             &cmdlineP->remainder_colorname, &remainderSpec,     0);
@@ -70,15 +72,22 @@ 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 */
 
-    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (!remainderSpec)
         cmdlineP->remainder_colorname = NULL;
 
     if (!closenessSpec)
-        cmdlineP->closeness = 0;
+        cmdlineP->closeness = 0.0;
 
+    if (cmdlineP->closeness < 0.0)
+        pm_error("-closeness value %f is negative", cmdlineP->closeness);
+
+    if (cmdlineP->closeness > 100)
+        pm_error("-closeness value %f is more than 100%%",
+                 cmdlineP->closeness);
+    
     if ((argc-1) % 2 == 0) 
         cmdlineP->input_filespec = "-";
     else
@@ -99,26 +108,19 @@ parseCommandLine(int argc, char ** argv,
 
 
 
-static double
-sqrf(float const F) {
-    return F*F;
-}
-
-
-
-static int 
-colormatch(pixel const comparand, 
-           pixel const comparator, 
-           float const closeness) {
+static bool
+colorMatches(pixel        const comparand, 
+             pixel        const comparator, 
+             unsigned int const allowableDiff) {
 /*----------------------------------------------------------------------------
-   Return true iff 'comparand' matches 'comparator' in color within the
-   fuzz factor 'closeness'.
+   The colors 'comparand' and 'comparator' are within 'allowableDiff'
+   color levels of each other, in cartesian distance.
 -----------------------------------------------------------------------------*/
     /* Fast path for usual case */
-    if (closeness == 0)
+    if (allowableDiff < EPSILON)
         return PPM_EQUAL(comparand, comparator);
 
-    return PPM_DISTANCE(comparand, comparator) <= sqrf(closeness);
+    return PPM_DISTANCE(comparand, comparator) <= SQR(allowableDiff);
 }
 
 
@@ -132,15 +134,18 @@ changeRow(const pixel * const inrow,
           const pixel         colorto[],
           bool          const remainder_specified, 
           pixel         const remainder_color, 
-          float         const closeness) {
+          unsigned int  const allowableDiff) {
 /*----------------------------------------------------------------------------
    Replace the colors in a single row.  There are 'ncolors' colors to 
    replace.  The to-replace colors are in the array colorfrom[], and the
    replace-with colors are in corresponding elements of colorto[].
    Iff 'remainder_specified' is true, replace all colors not mentioned
-   in colorfrom[] with 'remainder_color'.  Use the closeness factor
-   'closeness' in determining if something in the input row matches
-   a color in colorfrom[].
+   in colorfrom[] with 'remainder_color'.  
+
+   Consider the color in inrow[] to match a color in colorfrom[] if it is
+   within 'allowableDiff' color levels of it, in cartesian distance (e.g.
+   color (1,1,1) is sqrt(12) = 3.5 color levels distant from (3,3,3),
+   so if 'allowableDiff' is 4, they match).
 
    The input row is 'inrow'.  The output is returned as 'outrow', in
    storage which must be already allocated.  Both are 'cols' columns wide.
@@ -157,7 +162,7 @@ changeRow(const pixel * const inrow,
 
         haveMatch = FALSE;  /* haven't found a match yet */
         for (i = 0; i < ncolors && !haveMatch; ++i) {
-            haveMatch = colormatch(inrow[col], colorfrom[i], closeness);
+            haveMatch = colorMatches(inrow[col], colorfrom[i], allowableDiff);
             newcolor = colorto[i];
         }
         if (haveMatch)
@@ -172,16 +177,23 @@ changeRow(const pixel * const inrow,
 
 
 int
-main(int argc, char *argv[]) {
-    struct cmdlineInfo cmdline;
+main(int argc, const char ** const argv) {
+
+    struct CmdlineInfo cmdline;
     FILE * ifP;
     int format;
     int rows, cols;
     pixval maxval;
-    float closeness;
+    unsigned int allowableDiff;
+        /* The amount of difference between two colors we allow and still
+           consider those colors to be the same, for the purposes of
+           determining which pixels in the image to change.  This is a
+           cartesian distance between the color triples, on a maxval scale
+           (which means it can be as high as sqrt(3) * maxval)
+        */
     int row;
-    pixel* inrow;
-    pixel* outrow;
+    pixel * inrow;
+    pixel * outrow;
 
     pixel oldcolor[TCOLS];  /* colors user wants replaced */
     pixel newcolor[TCOLS];  /* colors with which he wants them replaced */
@@ -190,7 +202,7 @@ main(int argc, char *argv[]) {
          specify -remainder.
       */
 
-    ppm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
     
@@ -210,8 +222,8 @@ main(int argc, char *argv[]) {
                                           cmdline.closeok);
         }
     }
-    closeness = SQRT3 * maxval * cmdline.closeness/100;
-
+    allowableDiff = ROUNDU(sqrt3 * maxval * cmdline.closeness/100);
+    
     ppm_writeppminit( stdout, cols, rows, maxval, 0 );
     inrow = ppm_allocrow(cols);
     outrow = ppm_allocrow(cols);
@@ -222,7 +234,7 @@ main(int argc, char *argv[]) {
 
         changeRow(inrow, outrow, cols, cmdline.ncolors, oldcolor, newcolor,
                   cmdline.remainder_colorname != NULL,
-                  remainder_color, closeness);
+                  remainder_color, allowableDiff);
 
         ppm_writeppmrow(stdout, outrow, cols, maxval, 0);
     }
diff --git a/editor/ppmcolormask.c b/editor/ppmcolormask.c
index 5ef8d1c1..3812ac86 100644
--- a/editor/ppmcolormask.c
+++ b/editor/ppmcolormask.c
@@ -9,6 +9,7 @@
   Contributed to the public domain by its author.
 =========================================================================*/
 
+#define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE  /* Make sure strdup() is in <string.h> */
 #include <assert.h>
@@ -19,23 +20,23 @@
 #include "mallocvar.h"
 #include "nstring.h"
 #include "ppm.h"
-#include "pbm.h"
+#include "pam.h"
 
-enum matchType {
+typedef enum {
     MATCH_EXACT,
     MATCH_BK
-};
+} MatchType;
 
-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 * inputFilename;
-    unsigned int colorCount;
+    unsigned int colorCt;
     struct {
-        enum matchType matchType;
+        MatchType matchType;
         union {
-            pixel    color;   /* matchType == MATCH_EXACT */
+            tuplen   color;   /* matchType == MATCH_EXACT */
             bk_color bkColor; /* matchType == MATCH_BK */
         } u;
     } maskColor[16];
@@ -45,46 +46,59 @@ struct cmdlineInfo {
 
 
 static void
+freeCmdline(struct CmdlineInfo * const cmdlineP) {
+
+    unsigned int i;
+
+    for (i = 0; i < cmdlineP->colorCt; ++ i) {
+        if (cmdlineP->maskColor[i].matchType == MATCH_EXACT)
+            free(cmdlineP->maskColor[i].u.color);
+    }
+}
+
+
+
+static void
 parseColorOpt(const char *         const colorOpt,
-              struct cmdlineInfo * const cmdlineP) {
+              struct CmdlineInfo * const cmdlineP) {
 
-    unsigned int colorCount;
+    unsigned int colorCt;
     char * colorOptWork;
     char * cursor;
     bool eol;
-    
+
     colorOptWork = strdup(colorOpt);
     cursor = &colorOptWork[0];
-    
+
     eol = FALSE;    /* initial value */
-    colorCount = 0; /* initial value */
-    while (!eol && colorCount < ARRAY_SIZE(cmdlineP->maskColor)) {
+    colorCt = 0;    /* initial value */
+    while (!eol && colorCt < ARRAY_SIZE(cmdlineP->maskColor)) {
         const char * token;
         token = pm_strsep(&cursor, ",");
         if (token) {
             if (strneq(token, "bk:", 3)) {
-                cmdlineP->maskColor[colorCount].matchType = MATCH_BK;
-                cmdlineP->maskColor[colorCount].u.bkColor =
+                cmdlineP->maskColor[colorCt].matchType = MATCH_BK;
+                cmdlineP->maskColor[colorCt].u.bkColor =
                     ppm_bk_color_from_name(&token[3]);
             } else {
-                cmdlineP->maskColor[colorCount].matchType = MATCH_EXACT;
-                cmdlineP->maskColor[colorCount].u.color =
-                    ppm_parsecolor(token, PPM_MAXMAXVAL);
+                cmdlineP->maskColor[colorCt].matchType = MATCH_EXACT;
+                cmdlineP->maskColor[colorCt].u.color =
+                    pnm_parsecolorn(token);
             }
-            ++colorCount;
+            ++colorCt;
         } else
             eol = TRUE;
     }
     free(colorOptWork);
 
-    cmdlineP->colorCount = colorCount;
+    cmdlineP->colorCt = colorCt;
 }
 
 
 
 static void
-parseCommandLine(int argc, char ** argv,
-                 struct cmdlineInfo *cmdlineP) {
+parseCommandLine(int argc, const char ** argv,
+                 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
@@ -109,7 +123,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 */
 
-    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and all of *cmdlineP. */
 
     if (colorSpec)
@@ -128,16 +142,15 @@ parseCommandLine(int argc, char ** argv,
         if (argc-1 < 1)
             pm_error("You must specify the -color option.");
         else {
-            cmdlineP->colorCount = 1;
+            cmdlineP->colorCt = 1;
             cmdlineP->maskColor[0].matchType = MATCH_EXACT;
-            cmdlineP->maskColor[0].u.color =
-                ppm_parsecolor(argv[1], PPM_MAXMAXVAL);
+            cmdlineP->maskColor[0].u.color = pnm_parsecolorn(argv[1]);
 
             if (argc - 1 < 2)
                 cmdlineP->inputFilename = "-";  /* he wants stdin */
             else if (argc-1 == 2)
                 cmdlineP->inputFilename = argv[2];
-            else 
+            else
                 pm_error("Too many arguments.  The only arguments accepted "
                          "are the mask color and optional input file name");
         }
@@ -146,16 +159,47 @@ parseCommandLine(int argc, char ** argv,
 
 
 
+static void
+setupOutput(FILE *       const fileP,
+            unsigned int const width,
+            unsigned int const height,
+            struct pam * const outPamP) {
+
+    outPamP->size             = sizeof(*outPamP);
+    outPamP->len              = PAM_STRUCT_SIZE(tuple_type);
+    outPamP->file             = fileP;
+    outPamP->format           = RPBM_FORMAT;
+    outPamP->plainformat      = 0;
+    outPamP->height           = height;
+    outPamP->width            = width;
+    outPamP->depth            = 1;
+    outPamP->maxval           = 1;
+    outPamP->bytes_per_sample = 1;
+    strcpy(outPamP->tuple_type, PAM_PBM_TUPLETYPE);
+}
+
+
+
 static bool
-isBkColor(pixel    const comparator,
-          pixval   const maxval,
-          bk_color const comparand) {
+isBkColor(tuple        const comparator,
+          struct pam * const pamP,
+          bk_color     const comparand) {
+
+    pixel comparatorPixel;
+    bk_color comparatorBk;
 
     /* TODO: keep a cache of the bk color for each color in
        a colorhash_table.
     */
-    
-    bk_color const comparatorBk = ppm_bk_color_from_color(comparator, maxval);
+
+    assert(pamP->depth >= 3);
+
+    PPM_ASSIGN(comparatorPixel,
+               comparator[PAM_RED_PLANE],
+               comparator[PAM_GRN_PLANE],
+               comparator[PAM_BLU_PLANE]);
+
+    comparatorBk = ppm_bk_color_from_color(comparatorPixel, pamP->maxval);
 
     return comparatorBk == comparand;
 }
@@ -163,86 +207,94 @@ isBkColor(pixel    const comparator,
 
 
 static bool
-colorIsInSet(pixel              const color,
-             pixval             const maxval,
-             struct cmdlineInfo const cmdline) {
+colorIsInSet(tuple              const color,
+             struct pam *       const pamP,
+             struct CmdlineInfo const cmdline) {
 
     bool isInSet;
     unsigned int i;
+    tuple maskColorUnnorm;
+
+    maskColorUnnorm = pnm_allocpamtuple(pamP);
 
-    for (i = 0, isInSet = FALSE;
-         i < cmdline.colorCount && !isInSet; ++i) {
+    for (i = 0, isInSet = FALSE; i < cmdline.colorCt && !isInSet; ++i) {
 
         assert(i < ARRAY_SIZE(cmdline.maskColor));
 
         switch(cmdline.maskColor[i].matchType) {
         case MATCH_EXACT:
-            if (PPM_EQUAL(color, cmdline.maskColor[i].u.color))
+            pnm_unnormalizetuple(pamP,
+                                 cmdline.maskColor[i].u.color,
+                                 maskColorUnnorm);
+            if (pnm_tupleequal(pamP, color, maskColorUnnorm))
                 isInSet = TRUE;
             break;
         case MATCH_BK:
-            if (isBkColor(color, maxval, cmdline.maskColor[i].u.bkColor))
+            if (isBkColor(color, pamP, cmdline.maskColor[i].u.bkColor))
                 isInSet = TRUE;
             break;
         }
     }
+
+    free(maskColorUnnorm);
+
     return isInSet;
 }
 
 
 
 int
-main(int argc, char *argv[]) {
+main(int argc, const char *argv[]) {
 
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
 
     FILE * ifP;
+    struct pam inPam;
+    struct pam outPam;
 
-    /* Parameters of input image: */
-    int rows, cols;
-    pixval maxval;
-    int format;
-
-    ppm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
     ifP = pm_openr(cmdline.inputFilename);
 
-    ppm_readppminit(ifP, &cols, &rows, &maxval, &format);
-    pbm_writepbminit(stdout, cols, rows, 0);
+    pnm_readpaminit(ifP, &inPam, PAM_STRUCT_SIZE(allocation_depth));
+
+    pnm_setminallocationdepth(&inPam, 3);
+
+    setupOutput(stdout, inPam.width, inPam.height, &outPam);
+
+    pnm_writepaminit(&outPam);
     {
-        pixel * const inputRow = ppm_allocrow(cols);
-        bit *   const maskRow  = pbm_allocrow(cols);
+        tuple * const inputRow = pnm_allocpamrow(&inPam);
+        tuple * const maskRow  = pnm_allocpamrow(&outPam);
 
         unsigned int numPixelsMasked;
 
         unsigned int row;
-        for (row = 0, numPixelsMasked = 0; row < rows; ++row) {
-            int col;
-            ppm_readppmrow(ifP, inputRow, cols, maxval, format);
-            for (col = 0; col < cols; ++col) {
-                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;
+
+        for (row = 0, numPixelsMasked = 0; row < inPam.height; ++row) {
+            unsigned int col;
+            pnm_readpamrow(&inPam, inputRow);
+            pnm_makerowrgb(&inPam, inputRow);
+            for (col = 0; col < inPam.width; ++col) {
+                if (colorIsInSet(inputRow[col], &inPam, cmdline)) {
+                    maskRow[col][0] = PAM_BLACK;
                     ++numPixelsMasked;
-                } else 
-                    maskRow[col] = PBM_WHITE;
+                } else
+                    maskRow[col][0] = PAM_BW_WHITE;
             }
-            pbm_writepbmrow(stdout, maskRow, cols, 0);
+            pnm_writepamrow(&outPam, maskRow);
         }
 
         if (cmdline.verbose)
             pm_message("%u pixels found matching %u requested colors",
-                       numPixelsMasked, cmdline.colorCount);
+                       numPixelsMasked, cmdline.colorCt);
 
-        pbm_freerow(maskRow);
-        ppm_freerow(inputRow);
+        pnm_freepamrow(maskRow);
+        pnm_freepamrow(inputRow);
     }
+    freeCmdline(&cmdline);
     pm_close(ifP);
 
     return 0;
diff --git a/editor/ppmdraw.c b/editor/ppmdraw.c
index bd569e03..3453a7e1 100644
--- a/editor/ppmdraw.c
+++ b/editor/ppmdraw.c
@@ -1,4 +1,5 @@
-#define _XOPEN_SOURCE 500 
+#define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
+#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) */
 
@@ -49,7 +50,7 @@ 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.  
+   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.
@@ -83,7 +84,7 @@ parseCommandLine (int argc, const char ** argv,
 
     pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
-    
+
     if (!scriptSpec && !scriptfileSpec)
         pm_error("You must specify either -script or -scriptfile");
 
@@ -281,7 +282,7 @@ struct drawCommand {
 
 static void
 freeDrawCommand(const struct drawCommand * const commandP) {
-    
+
     switch (commandP->verb) {
     case VERB_SETPOS:
         break;
@@ -312,7 +313,7 @@ freeDrawCommand(const struct drawCommand * const commandP) {
         pm_strfree(commandP->u.textArg.text);
         break;
     }
-    
+
 
     free((void *) commandP);
 }
@@ -360,21 +361,21 @@ doFilledCircle(pixel **                   const pixels,
     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);
-} 
+}
 
 
 
@@ -385,7 +386,7 @@ doTextHere(pixel **                   const pixels,
            pixval                     const maxval,
            const struct drawCommand * const commandP,
            struct drawState *         const drawStateP) {
-    
+
     ppmd_text(pixels, cols, rows, maxval,
               drawStateP->currentPos.x,
               drawStateP->currentPos.y,
@@ -394,14 +395,14 @@ doTextHere(pixel **                   const pixels,
               commandP->u.textArg.text,
               PPMD_NULLDRAWPROC,
               &drawStateP->color);
-    
+
     {
         int left, top, right, bottom;
-        
+
         ppmd_text_box(commandP->u.textArg.height, 0,
                       commandP->u.textArg.text,
                       &left, &top, &right, &bottom);
-        
+
 
         drawStateP->currentPos.x +=
             ROUND((right-left) * cosdeg(commandP->u.textArg.angle));
@@ -528,10 +529,10 @@ executeScript(struct script * const scriptP,
 
 
 struct tokenSet {
-    
+
     const char * token[10];
     unsigned int count;
-    
+
 };
 
 
@@ -570,7 +571,7 @@ parseDrawCommand(struct tokenSet             const commandTokens,
                 if (streq(typeArg, "normal"))
                     drawCommandP->u.setlinetypeArg.type = PPMD_LINETYPE_NORMAL;
                 else if (streq(typeArg, "nodiag"))
-                    drawCommandP->u.setlinetypeArg.type = 
+                    drawCommandP->u.setlinetypeArg.type =
                         PPMD_LINETYPE_NODIAGS;
                 else
                     pm_error("Invalid type");
@@ -609,7 +610,7 @@ parseDrawCommand(struct tokenSet             const commandTokens,
                 drawCommandP->u.lineArg.y0 = atoi(commandTokens.token[2]);
                 drawCommandP->u.lineArg.x1 = atoi(commandTokens.token[3]);
                 drawCommandP->u.lineArg.y1 = atoi(commandTokens.token[4]);
-            } 
+            }
         } else if (streq(verb, "line_here")) {
             drawCommandP->verb = VERB_LINE_HERE;
             if (commandTokens.count < 3)
@@ -620,7 +621,7 @@ parseDrawCommand(struct tokenSet             const commandTokens,
                     &drawCommandP->u.lineHereArg;
                 argP->right = atoi(commandTokens.token[1]);
                 argP->down = atoi(commandTokens.token[2]);
-            } 
+            }
        } else if (streq(verb, "spline3")) {
             drawCommandP->verb = VERB_SPLINE3;
             if (commandTokens.count < 7)
@@ -635,7 +636,7 @@ parseDrawCommand(struct tokenSet             const commandTokens,
                 argP->y1 = atoi(commandTokens.token[4]);
                 argP->x2 = atoi(commandTokens.token[5]);
                 argP->y2 = atoi(commandTokens.token[6]);
-            } 
+            }
         } else if (streq(verb, "circle")) {
             drawCommandP->verb = VERB_CIRCLE;
             if (commandTokens.count < 4)
@@ -646,7 +647,7 @@ parseDrawCommand(struct tokenSet             const commandTokens,
                 argP->cx     = atoi(commandTokens.token[1]);
                 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)
@@ -657,7 +658,7 @@ parseDrawCommand(struct tokenSet             const commandTokens,
                 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)
@@ -670,7 +671,7 @@ parseDrawCommand(struct tokenSet             const commandTokens,
                 argP->y      = atoi(commandTokens.token[2]);
                 argP->width  = atoi(commandTokens.token[3]);
                 argP->height = atoi(commandTokens.token[4]);
-            } 
+            }
         } else if (streq(verb, "text")) {
             drawCommandP->verb = VERB_TEXT;
             if (commandTokens.count < 6)
@@ -712,9 +713,9 @@ disposeOfCommandTokens(struct tokenSet * const tokenSetP,
     /* We've got a whole command in 'tokenSet'.  Parse it into *scriptP
        and reset tokenSet to empty.
     */
-    
+
     struct commandListElt * commandListEltP;
-    
+
     MALLOCVAR(commandListEltP);
     if (commandListEltP == NULL)
         pm_error("Out of memory allocating command list element frame");
@@ -746,12 +747,14 @@ processToken(const char *      const scriptText,
              struct script *   const scriptP,
              struct tokenSet * const tokenSetP) {
 
-    char * token;
     unsigned int const tokenLength = cursor - tokenStart;
+
+    char * token;
+
     MALLOCARRAY_NOFAIL(token, tokenLength + 1);
     memcpy(token, &scriptText[tokenStart], tokenLength);
     token[tokenLength] = '\0';
-    
+
     if (streq(token, ";")) {
         disposeOfCommandTokens(tokenSetP, scriptP);
         free(token);
@@ -778,7 +781,7 @@ parseScript(const char *     const scriptText,
         */
     bool quotedToken;
         /* Current token is a quoted string.  Meaningless if 'intoken'
-           is false 
+           is false
         */
     struct tokenSet tokenSet;
 
@@ -802,7 +805,7 @@ parseScript(const char *     const scriptText,
 
     while (scriptText[cursor] != '\0') {
         char const scriptChar = scriptText[cursor];
-        
+
         if (intoken) {
             if ((quotedToken && scriptChar == '"') ||
                 (!quotedToken && (isspace(scriptChar) || scriptChar == ';'))) {
@@ -834,7 +837,7 @@ parseScript(const char *     const scriptText,
                 }
             }
             ++cursor;
-        }            
+        }
     }
 
     if (intoken) {
@@ -878,7 +881,7 @@ getScript(struct cmdlineInfo const cmdline,
     pm_strfree(scriptText);
 }
 
-          
+
 
 static void
 doOneImage(FILE *          const ifP,
@@ -887,13 +890,13 @@ doOneImage(FILE *          const ifP,
     pixel ** pixels;
     pixval maxval;
     int rows, cols;
-    
+
     pixels = ppm_readppm(ifP, &cols, &rows, &maxval);
-    
+
     executeScript(scriptP, pixels, cols, rows, maxval);
-    
+
     ppm_writeppm(stdout, pixels, cols, rows, maxval, 0);
-    
+
     ppm_freearray(pixels, rows);
 }
 
diff --git a/editor/ppmfade b/editor/ppmfade
index 027fc793..dcd7bf26 100755
--- a/editor/ppmfade
+++ b/editor/ppmfade
@@ -41,6 +41,19 @@ exec perl -w -x -S -- "$0" "$@"
 ##############################################################################
 use strict;
 
+sub doVersionHack($) {
+    my ($argvR) = @_;
+
+    my $arg1 = $argvR->[0];
+
+    if (defined($arg1) && (($arg1 eq "--version") || ($arg1 eq "-version"))) {
+        my $termStatus = system('ppmmix', '--version');
+        exit($termStatus == 0 ? 0 : 1);
+    }
+}
+
+
+
 my $SPREAD =  1;
 my $SHIFT =   2;
 my $RELIEF =  3;
@@ -59,6 +72,7 @@ my $base_name = "fade";		# default base name of output files
 my $image = "ppm";		# default output storage format
 my $mode = $SPREAD;		# default fading mode
 
+doVersionHack(\@ARGV);
 
 my $n;  # argument number
 
@@ -98,8 +112,6 @@ for ($n = 0; $n < @ARGV; $n++) {
         $mode = $BLOCK;
     } elsif ("$ARGV[$n]" eq "-mix") {
         $mode = $MIX;
-    } elsif ($ARGV[$n] eq "-help" || $ARGV[$n] eq "-h") {
-        usage();
     } else {
         print "Unknown argument: $ARGV[$n]\n";
         exit 100;
diff --git a/editor/ppmlabel.c b/editor/ppmlabel.c
index 885d7d36..389ab59e 100644
--- a/editor/ppmlabel.c
+++ b/editor/ppmlabel.c
@@ -7,7 +7,7 @@
                   June 1995
 */
 
-#define _XOPEN_SOURCE   /* get M_PI in math.h */
+#define _XOPEN_SOURCE 500  /* get M_PI in math.h */
 
 #include <math.h>
 #include <string.h>
diff --git a/editor/ppmquant b/editor/ppmquant
index 57963982..fe8ca046 100755
--- a/editor/ppmquant
+++ b/editor/ppmquant
@@ -35,6 +35,19 @@ exec perl -w -x -S -- "$0" "$@"
 
 use strict;
 
+sub doVersionHack($) {
+    my ($argvR) = @_;
+
+    my $arg1 = $argvR->[0];
+
+    if (defined($arg1) && (($arg1 eq "--version") || ($arg1 eq "-version"))) {
+        my $termStatus = system('pnmquant', '--version');
+        exit($termStatus == 0 ? 0 : 1);
+    }
+}
+
+doVersionHack(\@ARGV);
+
 use Getopt::Long;
 
 my @ppmquantArgv = @ARGV;
diff --git a/editor/ppmshadow b/editor/ppmshadow
index 62cdf8b8..ae6b1b0f 100755
--- a/editor/ppmshadow
+++ b/editor/ppmshadow
@@ -48,6 +48,7 @@ exec perl -w -x -S -- "$0" "$@"
 ##############################################################################
 
 use strict;
+use File::Temp;
 require 5.0;
 #  The good open() syntax, with the mode separate from the file name,
 #  came after 5.0.  So did mkdir() with default mode.
@@ -55,22 +56,60 @@ require 5.0;
 my $true=1; my $false=0;
 
 
-sub getDimensions($) {
+
+sub doVersionHack($) {
+    my ($argvR) = @_;
+
+    my $arg1 = $argvR->[0];
+
+    if (defined($arg1) && (($arg1 eq "--version") || ($arg1 eq "-version"))) {
+        my $termStatus = system('pamarith', '--version');
+        exit($termStatus == 0 ? 0 : 1);
+    }
+}
+
+
+
+sub imageDimensions($) {
     my ($fileName) = @_;
 #-----------------------------------------------------------------------------
-#  Return the dimensions of the Netpbm image in the named file
+#  Return the dimensions of the Netpbm image in the file named $fileName.
 #-----------------------------------------------------------------------------
-    my ($width, $height);
+    my ($width, $height, $depth);
     my $pamfileOutput = `pamfile $fileName`;
-    if ($pamfileOutput =~ m/.*\sP[BGP]M\s.*,\s*(\d*)\sby\s(\d*)/) {
-        ($width, $height) = ($1, $2);
+    if ($pamfileOutput =~
+            m/.*\sP[BGP]M\s.*,\s*(\d*)\sby\s(\d*)\s*maxval\s(\d*)/) {
+        ($width, $height, $depth) = ($1, $2, $3);
     } else {
         die("Unrecognized output from 'pamfile' shell command");
     }
-    return ($width, $height);
+    return ($width, $height, $depth);
+}    
+
+sub backgroundColor($) {
+    my ($fileName) = @_;
+#-----------------------------------------------------------------------------
+#  Return the color of the backround of the image in the file named $fileName.
+#-----------------------------------------------------------------------------
+    # We call the color of the top left pixel the background color.
+
+    my $ppmhistOut = qx{pamcut 0 0 1 1 $fileName | ppmhist -noheader -float};
+
+    my ($ired, $igrn, $iblu, $lum, $count);
+
+    if ($ppmhistOut =~
+        m{\s*([01].\d+)\s*([01].\d+)\s*([01].\d+)\s*([01].\d+)\s*(\d+)}) {
+        ($ired, $igrn, $iblu, $lum, $count) = ($1, $2, $3, $4, $5);
+    } else {
+        die("Unrecognized format of output from 'ppmhist' shell command");
+    }
+    my $irgb = sprintf("rgbi:%f/%f/%f", $ired, $igrn, $iblu);
+
+    return $irgb;
 }    
 
 
+
 sub makeConvolutionKernel($$) {
     my ($convkernelfile, $ckern) = @_;
 
@@ -95,15 +134,10 @@ sub makeConvolutionKernel($$) {
 #                           MAINLINE
 ##############################################################################
 
-
-my $tmpdir = $ENV{TMPDIR} || "/tmp";
-my $ourtmp = "$tmpdir/ppmshadow$$";
-mkdir($ourtmp, 0777) or
-    die("Unable to create directory for temporary files '$ourtmp");
+doVersionHack(\@ARGV);
 
 #   Process command line options
 
-
 my $ifile; # Input file name
 my ($xoffset, $yoffset);
 
@@ -113,6 +147,7 @@ my $translucent = $false;            # Default not translucent
 
 while (@ARGV) {
     my $arg = shift;
+
     if ((substr($arg, 0, 1) eq '-') && (length($arg) > 1)) {
         my $opt;
         $opt = substr($arg, 1, 1);
@@ -142,6 +177,8 @@ while (@ARGV) {
             if ($yoffset < 0) {
                 $yoffset = -$xoffset;
             }
+        } else {
+            die("Unknown option '$opt'\n");
         }
     } else {
         if (defined $ifile) {
@@ -151,6 +188,19 @@ while (@ARGV) {
     }
 }
 
+# Create temporary directory
+
+my $tmpdir = $ENV{TMPDIR} || "/tmp";
+my $ourtmp;
+
+if ($keeptemp) {
+    $ourtmp = "$tmpdir/ppmshadow$$";
+    mkdir($ourtmp, 0777) or
+        die("Unable to create directory for temporary files '$ourtmp");
+} else {
+    $ourtmp = File::Temp::tempdir("$tmpdir/ppmshadowXXXX", UNLINK=>1);
+}
+
 #   Apply defaults for arguments not specified
 
 if (!(defined $xoffset)) {
@@ -181,21 +231,25 @@ system("ppmtoppm");
 # seem to be able to open stdin and stdout pipes properly if stdin and 
 # stdout didn't already exist.  2002.09.07 BJH
 
-my ($sourceImageWidth, $sourceImageHeight) = getDimensions($infile);
+my ($sourceImageWidth, $sourceImageHeight, $sourceImageDepth) =
+    imageDimensions($infile);
+
+my $bgColorIrgb = backgroundColor($infile);
 
-#   Create an all-background-color image (same size as original image)
+# Create an all-background-color image (same size as original image),
+# named $backgroundfile. 
 
 my $backgroundfile = "$ourtmp/background.ppm";
-system("pamcut -left=0 -top=0 -width=1 -height=1 $infile | " .
-       "pamscale -xsize=$sourceImageWidth " .
-       "-ysize=$sourceImageHeight >$backgroundfile");
+system("ppmmake $bgColorIrgb $sourceImageWidth $sourceImageHeight " .
+    "-maxval $sourceImageDepth " .
+    ">$backgroundfile");
 
-#   Create mask file for background.  It is white wherever there is background
-#   image in the input.
+# Create mask file for background, named $bgmaskfile.  It is a PBM, white
+# wherever there is background image in the input.
 
 my $bgmaskfile = "$ourtmp/bgmask.pbm";
-system("pamarith -difference $infile $backgroundfile | pnminvert | ppmtopgm " .
-       "| pgmtopbm -thresh -value 1.0 >$bgmaskfile");
+system("ppmchange -remainder=black $bgColorIrgb white $infile | " .
+       "ppmtopgm | pgmtopbm -threshold -value=0.5 >$bgmaskfile"); 
 
 my $ckern = $convolve <= 11 ? $convolve : 11;
 
@@ -208,7 +262,7 @@ if ($translucent) {
     #   Convolve the input color image with the kernel
     #   to create a translucent shadow image.
 
-    system("pnmconvol $convkernelfile $infile >$ourtmp/blurred.ppm");
+    system("pnmconvol -quiet $convkernelfile $infile >$ourtmp/blurred.ppm");
     unlink("$convkernelfile") unless $keeptemp;
     while ($ckern < $convolve) {
         system("pnmsmooth $ourtmp/blurred.ppm >$ourtmp/convolvedx.ppm");
@@ -220,8 +274,8 @@ if ($translucent) {
     #   Convolve the positive mask with the kernel to create shadow
  
     my $blurredblackshadfile = "$ourtmp/blurredblackshad.pgm";
-    system("pamdepth -quiet 255 $bgmaskfile | " .
-           "pnmconvol $convkernelfile >$blurredblackshadfile");
+    system("pamdepth -quiet $sourceImageDepth $bgmaskfile | " .
+           "pnmconvol -quiet $convkernelfile >$blurredblackshadfile");
     unlink($convkernelfile) unless $keeptemp;
 
     while ($ckern < $convolve) {
@@ -251,21 +305,6 @@ my $shadowfile = "$ourtmp/shadow.ppm";
 }
 unlink("$ourtmp/blurred.ppm") unless $keeptemp;
 
-#   Make mask for foreground
-
-my $fgmaskfile = "$ourtmp/fgmask.pbm";
-open(STDIN, "<$bgmaskfile") or die();
-open(STDOUT, ">$fgmaskfile") or die();
-system("pnminvert");
-
-#   Make image which is just foreground; rest is black.
-
-my $justfgfile = "$ourtmp/justfg.ppm";
-open(STDOUT, ">$justfgfile") or die();
-system("pamarith", "-multiply", $infile, $fgmaskfile);
-
-unlink($fgmaskfile) unless $keeptemp;
-unlink($infile) unless $keeptemp;
 
 #   Paste shadow onto background.
 
@@ -276,22 +315,15 @@ system("pnmpaste", "-replace", $shadowfile, $xoffset, $yoffset,
 unlink($shadowfile) unless $keeptemp;
 unlink($backgroundfile) unless $keeptemp;
 
-#   Knock out (make black) foreground area
-
-my $allbutfgfile = "$ourtmp/allbutfg.ppm";
-open(STDOUT, ">$allbutfgfile") or die();
-system("pamarith", "-multiply", $shadbackfile, $bgmaskfile);
-
-unlink($shadbackfile) unless $keeptemp;
-unlink($bgmaskfile) unless $keeptemp;
 
-#   Place foreground in blacked out area, send to original Standard Output.
+#   Create composite file, send to original Standard Output.
 
 open(STDOUT, ">&OLDOUT");
 
-system("pamarith", "-add", $justfgfile, $allbutfgfile);
-unlink($justfgfile) unless $keeptemp;
-unlink($allbutfgfile) unless $keeptemp;
+system("pamcomp -invert -alpha $bgmaskfile $infile $shadbackfile");
+unlink($bgmaskfile) unless $keeptemp;
+unlink($infile) unless $keeptemp;
+unlink($shadbackfile) unless $keeptemp;
 
 if (!$keeptemp) {
     rmdir($ourtmp) or die ("Unable to remove temporary directory '$ourtmp'");
diff --git a/editor/ppmshadow.doc b/editor/ppmshadow.doc
index 1539c708..879faa66 100644
--- a/editor/ppmshadow.doc
+++ b/editor/ppmshadow.doc
@@ -1,3 +1,7 @@
+This file is a concatenation of two HTML files that John Walker wrote for the
+original 'pnmshadow' program.  It is not all relevant to the current
+'ppmshadow' program.
+
 <html>
 <head>
 <title>pnmshadow: How it Works</title>
diff --git a/editor/specialty/Makefile b/editor/specialty/Makefile
index 427c2c8f..8d9ca044 100644
--- a/editor/specialty/Makefile
+++ b/editor/specialty/Makefile
@@ -41,12 +41,14 @@ OBJECTS = $(BINARIES:%=%.o)
 
 MERGE_OBJECTS = $(MERGEBINARIES:%=%.o2)
 
+HAVE_MERGE_COMPAT=YES
+
 .PHONY: all
 all: $(BINARIES)
 
 include $(SRCDIR)/common.mk
 
-install.bin: install.bin.local
+install.bin install.merge: install.bin.local
 
 .PHONY: install.bin.local
 install.bin.local: $(PKGDIR)/bin
@@ -55,3 +57,7 @@ install.bin.local: $(PKGDIR)/bin
 	cd $(PKGDIR)/bin ; \
 	rm -f pgmoil$(EXE) ; \
 	$(SYMLINK) pamoil$(EXE) pgmoil$(EXE)
+
+mergecomptrylist:
+	cat /dev/null >$@
+	echo "TRY(\"pgmoil\", main_pamoil);" >>$@
diff --git a/editor/specialty/pammixinterlace.c b/editor/specialty/pammixinterlace.c
index 7410a8f1..28dace25 100644
--- a/editor/specialty/pammixinterlace.c
+++ b/editor/specialty/pammixinterlace.c
@@ -2,7 +2,7 @@
                              pammixinterlace
 *******************************************************************************
   De-interlace an image by merging adjacent rows.
-   
+
   Copyright (C) 2007 Bruce Guenter, FutureQuest, Inc.
 
   Permission to use, copy, modify, and distribute this software and its
@@ -14,6 +14,7 @@
 
 ******************************************************************************/
 
+#define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE    /* Make sure strcaseeq() is in nstring.h */
 
 #include <string.h>
@@ -36,9 +37,9 @@ clamp(sample const val,
 
 
 static bool
-distant(long const above,
-        long const mid,
-        long const below) {
+distant(int const above,
+        int const mid,
+        int const below) {
 
     return abs(mid - (above + below) / 2) > abs(above - below);
 }
@@ -59,9 +60,9 @@ filterLinearBlend(tuple *      const outputrow,
         unsigned int plane;
 
         for (plane = 0; plane < depth; ++plane) {
-            long const above = tuplerowWindow[0][col][plane];
-            long const mid   = tuplerowWindow[1][col][plane];
-            long const below = tuplerowWindow[2][col][plane];
+            int const above = tuplerowWindow[0][col][plane];
+            int const mid   = tuplerowWindow[1][col][plane];
+            int const below = tuplerowWindow[2][col][plane];
 
             sample out;
 
@@ -69,7 +70,7 @@ filterLinearBlend(tuple *      const outputrow,
                 out = (above + mid * 2 + below) / 4;
             else
                 out = mid;
-            
+
             outputrow[col][plane] = out;
         }
     }
@@ -86,23 +87,23 @@ filterFfmpeg(tuple *      const outputrow,
              sample       const maxval) {
 
     unsigned int col;
-    
+
     for (col = 0; col < width; ++col) {
         unsigned int plane;
-        
+
         for (plane = 0; plane < depth; ++plane) {
-            long const above = tuplerowWindow[1][col][plane];
-            long const mid   = tuplerowWindow[2][col][plane];
-            long const below = tuplerowWindow[3][col][plane];
+            int const above = tuplerowWindow[1][col][plane];
+            int const mid   = tuplerowWindow[2][col][plane];
+            int const below = tuplerowWindow[3][col][plane];
 
             sample out;
-            
+
             if (!adaptive || distant(above, mid, below)) {
-                long const a = (- (long)tuplerowWindow[0][col][plane]
+                int const a = (- (int)tuplerowWindow[0][col][plane]
                                 + above * 4
                                 + mid * 2
                                 + below * 4
-                                - (long)tuplerowWindow[4][col][plane]) / 8;
+                                - (int)tuplerowWindow[4][col][plane]) / 8;
                 out = clamp(a, maxval);
             } else
                 out = mid;
@@ -129,22 +130,22 @@ filterFIR(tuple *      const outputrow,
 
         for (plane = 0; plane < depth; ++plane) {
 
-            long const above = tuplerowWindow[1][col][plane];
-            long const mid   = tuplerowWindow[2][col][plane];
-            long const below = tuplerowWindow[3][col][plane];
+            int const above = tuplerowWindow[1][col][plane];
+            int const mid   = tuplerowWindow[2][col][plane];
+            int const below = tuplerowWindow[3][col][plane];
 
             sample out;
 
             if (!adaptive || distant(above, mid, below)) {
-                long const a = (- (long)tuplerowWindow[0][col][plane]
+                int const a = (- (int)tuplerowWindow[0][col][plane]
                                 + above * 2
                                 + mid * 6
                                 + below * 2
-                                - (long)tuplerowWindow[4][col][plane]) / 8;
+                                - (int)tuplerowWindow[4][col][plane]) / 8;
                 out = clamp(a, maxval);
             } else
                 out = mid;
-            
+
             outputrow[col][plane] = out;
         }
     }
@@ -217,7 +218,7 @@ parseCommandLine(int argc, char ** argv,
         if (!cmdlineP->filterP)
             pm_error("The filter name '%s' is not known.", filterName);
     }
-    
+
     if (argc-1 < 1)
         cmdlineP->inputFileName = "-";
     else if (argc-1 == 1)
@@ -279,12 +280,12 @@ main(int argc, char *argv[]) {
 
     FILE * ifP;
     struct cmdlineInfo cmdline;
-    struct pam inpam;  
+    struct pam inpam;
     struct pam outpam;
     tuple * tuplerowWindow[5];
     tuple * outputrow;
     unsigned int rows;
-    
+
     pnm_init(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
@@ -292,7 +293,7 @@ main(int argc, char *argv[]) {
     rows = cmdline.filterP->rows;
 
     ifP = pm_openr(cmdline.inputFileName);
-    
+
     pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
 
     outpam = inpam;    /* Initial value -- most fields should be same */
@@ -327,10 +328,10 @@ main(int argc, char *argv[]) {
                                 inpam.width, inpam.depth,
                                 cmdline.adaptive, inpam.maxval);
         pnm_writepamrow(&outpam, outputrow);
-        
+
         slideWindowDown(tuplerowWindow, rows);
     }
-    
+
     /* Pass through last rows */
     for (row = rows/2; row < rows-1; ++row)
         pnm_writepamrow(&outpam, tuplerowWindow[row]);
@@ -340,6 +341,9 @@ main(int argc, char *argv[]) {
     pnm_freepamrow(outputrow);
     pm_close(inpam.file);
     pm_close(outpam.file);
-    
+
     return 0;
 }
+
+
+
diff --git a/editor/specialty/pampaintspill.c b/editor/specialty/pampaintspill.c
index 745c9b68..eb1888f7 100644
--- a/editor/specialty/pampaintspill.c
+++ b/editor/specialty/pampaintspill.c
@@ -253,7 +253,7 @@ locatePaintSources(struct pam *            const pamP,
     if (downsample > 0 && downsample < paintSources.size) {
         unsigned int i;
 
-        srand(time(NULL));
+        srand(pm_randseed());
 
         for (i = 0; i < downsample; ++i) {
             unsigned int const swapIdx =
diff --git a/editor/specialty/pgmabel.c b/editor/specialty/pgmabel.c
index 1764c5d7..1a6e481f 100644
--- a/editor/specialty/pgmabel.c
+++ b/editor/specialty/pgmabel.c
@@ -171,7 +171,6 @@ int main( argc, argv )
     float pixsize=0.1;
     /* no verbose, calculating both sides                                */
     int verb = FALSE, left = TRUE, right = TRUE;
-    int nologo = FALSE;
     const char* const usage = "[-help] [-axis N] [-factor N] [-pixsize N] [-left|-right] [-verbose] [pgmfile]";
 
     pgm_init( &argc, argv );
@@ -213,10 +212,6 @@ int main( argc, argv )
                 if ( right ) left = FALSE;
                 else pm_usage( usage );
             }
-        else if ( pm_keymatch( argv[argn], "-nologo", 4 ) )
-            {
-                nologo = TRUE;
-            }
         else
             pm_usage( usage );
         ++ argn;
diff --git a/editor/specialty/pnmindex.c b/editor/specialty/pnmindex.c
index 4ec9edaa..0a8e35cc 100644
--- a/editor/specialty/pnmindex.c
+++ b/editor/specialty/pnmindex.c
@@ -14,6 +14,7 @@
 
 ============================================================================*/
 
+#define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE   /* Make sure strdup is in string.h */
 
diff --git a/editor/specialty/pnmmercator.c b/editor/specialty/pnmmercator.c
index cd9ff19b..81f7f148 100644
--- a/editor/specialty/pnmmercator.c
+++ b/editor/specialty/pnmmercator.c
@@ -16,7 +16,7 @@
 **
 */
 
-#define _XOPEN_SOURCE  /* Make sure M_PI is in <math.h> */
+#define _XOPEN_SOURCE 500  /* get M_PI in math.h */
 #include <math.h>
 #include <string.h>
 
diff --git a/editor/specialty/ppmglobe.c b/editor/specialty/ppmglobe.c
index 92e22746..bb043cc6 100644
--- a/editor/specialty/ppmglobe.c
+++ b/editor/specialty/ppmglobe.c
@@ -9,7 +9,7 @@
  */
 
 
-#define _XOPEN_SOURCE  /* get M_PI in math.h */
+#define _XOPEN_SOURCE 500  /* get M_PI in math.h */
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
diff --git a/editor/specialty/ppmntsc.c b/editor/specialty/ppmntsc.c
index a721b891..08fbc835 100644
--- a/editor/specialty/ppmntsc.c
+++ b/editor/specialty/ppmntsc.c
@@ -39,6 +39,7 @@
 
  */
 
+#define _DEFAULT_SOURCE 1  /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
@@ -392,14 +393,11 @@ convertOneImage(FILE *             const ifP,
         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 */
 
         PPM_ASSIGN(black, 0, 0, 0);
 
-        PPM_ASSIGN(lastIllegalPixel, 0, 0, 0);  /* initial value */
         {
             unsigned int row;
 
diff --git a/generator/Makefile b/generator/Makefile
index d0ea6b60..d54a6cc5 100644
--- a/generator/Makefile
+++ b/generator/Makefile
@@ -7,6 +7,8 @@ VPATH=.:$(SRCDIR)/$(SUBDIR)
 
 include $(BUILDDIR)/config.mk
 
+SUBDIRS = pamtris
+
 # 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.
 # That way, if there is a problem with a dependency, we can still
@@ -16,10 +18,13 @@ include $(BUILDDIR)/config.mk
 
 PORTBINARIES = pamcrater pamgauss pamgradient \
 	       pamseq pamshadedrelief pamstereogram \
-	       pbmpage pbmmake pbmtext pbmtextps pbmupc \
+	       pbmpage pbmmake pbmtext pbmupc \
 	       pgmkernel pgmmake pgmnoise pgmramp \
 	       ppmcie ppmcolors ppmforge ppmmake ppmpat ppmrough ppmwheel \
 
+ifneq ($(DONT_HAVE_PROCESS_MGMT),Y)
+PORTBINARIES += pbmtextps 
+endif
 # We don't include programs that have special library dependencies in the
 # merge scheme, because we don't want those dependencies to prevent us
 # from building all the other programs.
@@ -36,6 +41,6 @@ OBJECTS = $(BINARIES:%=%.o)
 MERGE_OBJECTS = $(MERGEBINARIES:%=%.o2)
 
 .PHONY: all
-all: $(BINARIES)
+all: $(BINARIES) $(SUBDIRS:%=%/all)
 
 include $(SRCDIR)/common.mk
diff --git a/generator/pamcrater.c b/generator/pamcrater.c
index 8c1fda40..43c27dbc 100644
--- a/generator/pamcrater.c
+++ b/generator/pamcrater.c
@@ -41,7 +41,7 @@
    right edge. Make craters wrap around the image (enables tiling of image).
  */
 
-#define _XOPEN_SOURCE   /* get M_PI in math.h */
+#define _XOPEN_SOURCE 500  /* get M_PI in math.h */
 
 #include <assert.h>
 #include <math.h>
diff --git a/generator/pamgauss.c b/generator/pamgauss.c
index 2dd6a726..9656708b 100644
--- a/generator/pamgauss.c
+++ b/generator/pamgauss.c
@@ -1,3 +1,4 @@
+#include <assert.h>
 #include <string.h>
 #include <unistd.h>
 #include <stdlib.h>
@@ -18,7 +19,9 @@ struct CmdlineInfo {
     unsigned int width;
     unsigned int height;
     unsigned int maxval;
-    float sigma;
+    float        sigma;
+    unsigned int oversample;
+    unsigned int maximize;
     const char * tupletype;
 };
 
@@ -35,23 +38,27 @@ parseCommandLine(int argc, const char ** argv,
   Note that some string information we return as *cmdlineP is in the storage 
   argv[] points to.
 -----------------------------------------------------------------------------*/
-    optEntry *option_def;
+    optEntry * option_def;
         /* Instructions to OptParseOptions2 on how to parse our options.
          */
     optStruct3 opt;
 
-    unsigned int tupletypeSpec, maxvalSpec, sigmaSpec;
+    unsigned int tupletypeSpec, maxvalSpec, sigmaSpec, oversampleSpec;
     unsigned int option_def_index;
 
     MALLOCARRAY_NOFAIL(option_def, 100);
 
     option_def_index = 0;   /* incremented by OPTENTRY */
     OPTENT3(0,   "tupletype",  OPT_STRING, &cmdlineP->tupletype, 
-            &tupletypeSpec,     0);
+            &tupletypeSpec,      0);
     OPTENT3(0,   "maxval",     OPT_UINT,   &cmdlineP->maxval, 
-            &maxvalSpec,        0);
+            &maxvalSpec,         0);
     OPTENT3(0,   "sigma",      OPT_FLOAT,  &cmdlineP->sigma, 
-            &sigmaSpec,        0);
+            &sigmaSpec,          0);
+    OPTENT3(0,   "maximize",   OPT_FLAG,   NULL,
+            &cmdlineP->maximize, 0);
+    OPTENT3(0,   "oversample", OPT_UINT,   &cmdlineP->oversample,
+            &oversampleSpec,     0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
@@ -86,6 +93,13 @@ parseCommandLine(int argc, const char ** argv,
             pm_error("-maxval must be at least 1");
     }    
 
+    if (oversampleSpec) {
+        if (cmdlineP->oversample < 1)
+            pm_error("The oversample factor (-oversample) "
+                     "must be at least 1.");
+    } else
+        cmdlineP->oversample = ceil(5.0 / cmdlineP->sigma);
+
     if (argc-1 < 2)
         pm_error("Need two arguments: width and height.");
     else if (argc-1 > 2)
@@ -107,12 +121,13 @@ parseCommandLine(int argc, const char ** argv,
 
 
 static double
-distFromCenter(struct pam * const pamP,
-               int          const col,
-               int          const row) {
-
-    return sqrt(SQR(0.5 + col - (double)pamP->width/2) +
-                SQR(0.5 + row - (double)pamP->height/2));
+distFromCenter(unsigned int const width,
+               unsigned int const height,
+               double       const x,
+               double       const y)
+{
+    return sqrt(SQR(x - (double)width  / 2) +
+                SQR(y - (double)height / 2));
 }
 
 
@@ -121,87 +136,214 @@ static double
 gauss(double const arg,
       double const sigma) {
 /*----------------------------------------------------------------------------
-   Compute the value of the gaussian function with sigma parameter 'sigma'
-   and mu parameter zero of argument 'arg'.
+   Compute the value of the gaussian function centered at zero with
+   standard deviation 'sigma' and amplitude 1, at 'arg'.
 -----------------------------------------------------------------------------*/
-    double const pi = 3.14159;
-    double const coefficient = 1 / (sigma * sqrt(2*pi));
-    double const exponent = - SQR(arg-0) / (2 * SQR(sigma));
+    double const exponent = - SQR(arg) / (2 * SQR(sigma));
 
-    return coefficient * exp(exponent);
+    return exp(exponent);
 }
 
 
 
 static double
-imageNormalizer(struct pam * const pamP,
-                double       const sigma) {
+pixelValue(unsigned int const width,
+           unsigned int const height,
+           unsigned int const row,
+           unsigned int const col,
+           unsigned int const subpixDivision,
+           double       const sigma) {
 /*----------------------------------------------------------------------------
-   Compute the value that has to be multiplied by the value of the 
-   one-dimensional gaussian function of the distance from center in
-   order to get the value for a normalized two-dimensional gaussian
-   function.  Normalized here means that the volume under the whole
-   curve is 1, just as the area under a whole one-dimensional gaussian
-   function is 1.
+  The gaussian value for the pixel at row 'row', column 'col' in an image
+  described by *pamP.
+
+  This is the mean of the values of the gaussian function computed at
+  all the subpixel locations within the pixel when it is divided into
+  subpixels 'subpixDivision' times horizontally and vertically.
+
+  The gaussian function has standard deviation 'sigma' and amplitude 1.
 -----------------------------------------------------------------------------*/
-    double volume;
+    double const offset = 1.0 / (subpixDivision * 2);
+    double const y0     = (double)row + offset; 
+    double const x0     = (double)col + offset;
+
+    double const subpixSize = 1.0 / subpixDivision;
 
+    unsigned int i;
+    double total;
+        /* Running total of the gaussian values at all subpixel locations */
+
+    for (i = 0, total = 0.0; i < subpixDivision; ++i) {
+        /* Sum up one column of subpixels */
+
+        unsigned int j;
+
+        for (j = 0; j < subpixDivision; ++j) {
+            double const dist =
+                distFromCenter(width, height,
+                               x0 + i * subpixSize,
+                               y0 + j * subpixSize);
+
+            total += gauss(dist, sigma);
+        }
+    }
+
+    return total / SQR(subpixDivision);
+}
+
+
+
+static double **
+gaussianKernel(unsigned int const width,
+               unsigned int const height,
+               unsigned int const subpixDivision,
+               double       const sigma) {
+/*----------------------------------------------------------------------------
+   A Gaussian matrix 'width' by 'height', with each value being the mean
+   of a Gaussian function evaluated at 'subpixDivision' x 'subpixDivision'
+   locations.
+
+   Return value is newly malloc'ed storage that Caller must free.
+-----------------------------------------------------------------------------*/
+    double ** kernel;
     unsigned int row;
 
-    volume = 0.0;   /* initial value */
+    MALLOCARRAY2(kernel, height, width);
 
-    for (row = 0; row < pamP->height; ++row) {
+    if (!kernel)
+        pm_error("Unable to allocate %u x %u array in which to build kernel",
+                 height, width);
+
+    for (row = 0; row < height; ++row) {
         unsigned int col;
-        for (col = 0; col < pamP->width; ++col)
-            volume += gauss(distFromCenter(pamP, col, row), sigma);
+        for (col = 0; col < width; ++col) {
+            double const gaussval =
+                pixelValue(width, height, row, col, subpixDivision, sigma);
+            kernel[row][col] = gaussval;
+        }
     }
-    return 1.0 / volume;
+    return kernel;
 }
 
 
 
-int
-main(int argc, const char **argv) {
+static double
+maximumKernelValue(double **    const kernel,
+                   unsigned int const width,
+                   unsigned int const height) {
 
-    struct CmdlineInfo cmdline;
+    /* As this is Gaussian in both directions, centered at the center,
+       we know the maximum value is at the center.
+    */
+    return kernel[height/2][width/2];
+}        
+
+
+
+static double
+totalKernelValue(double **    const kernel,
+                 unsigned int const width,
+                 unsigned int const height) {
+
+    double total;
+    unsigned int row;
+
+    for (row = 0, total = 0.0; row < height; ++row) {
+        unsigned int col;
+        
+        for (col = 0; col < width; ++col)
+            total += kernel[row][col];
+    }
+
+    return total;
+}        
+
+
+
+static void
+initpam(struct pam * const pamP,
+        unsigned int const width,
+        unsigned int const height,
+        sample       const maxval,
+        const char * const tupleType,
+        FILE *       const ofP) {
+
+    pamP->size        = sizeof(*pamP);
+    pamP->len         = PAM_STRUCT_SIZE(tuple_type);
+    pamP->file        = ofP;
+    pamP->format      = PAM_FORMAT;
+    pamP->plainformat = 0;
+    pamP->width       = width;
+    pamP->height      = height;
+    pamP->depth       = 1;
+    pamP->maxval      = maxval;
+    strcpy(pamP->tuple_type, tupleType);
+}
+
+
+
+static void
+writePam(double **    const kernel,
+         unsigned int const width,
+         unsigned int const height,
+         sample       const maxval,
+         const char * const tupleType,
+         double       const normalizer,
+         FILE *       const ofP) {
+/*----------------------------------------------------------------------------
+   Write the kernel 'kernel', which is 'width' by 'height', as a PAM image
+   with maxval 'maxval' and tuple type 'tupleType' to file *ofP.
+
+   Divide the kernel values by 'normalizer' to get the normalized PAM sample
+   value.  Assume that no value in 'kernel' is greater that 'normalizer'.
+-----------------------------------------------------------------------------*/
     struct pam pam;
-    int row;
-    double normalizer;
+    unsigned int row;
     tuplen * tuplerown;
-    
-    pm_proginit(&argc, argv);
-   
-    parseCommandLine(argc, argv, &cmdline);
 
-    pam.size        = sizeof(pam);
-    pam.len         = PAM_STRUCT_SIZE(tuple_type);
-    pam.file        = stdout;
-    pam.format      = PAM_FORMAT;
-    pam.plainformat = 0;
-    pam.width       = cmdline.width;
-    pam.height      = cmdline.height;
-    pam.depth       = 1;
-    pam.maxval      = cmdline.maxval;
-    strcpy(pam.tuple_type, cmdline.tupletype);
-
-    normalizer = imageNormalizer(&pam, cmdline.sigma);
-    
+    initpam(&pam, width, height, maxval, tupleType, ofP);
+
     pnm_writepaminit(&pam);
-   
+
     tuplerown = pnm_allocpamrown(&pam);
 
     for (row = 0; row < pam.height; ++row) {
-        int col;
+        unsigned int col;
         for (col = 0; col < pam.width; ++col) {
-            double const gauss1 = gauss(distFromCenter(&pam, col, row),
-                                        cmdline.sigma);
-
-            tuplerown[col][0] = gauss1 * normalizer;
+            tuplerown[col][0] = kernel[row][col] / normalizer;
+        
+            assert(tuplerown[col][0] <= 1.0);
         }
         pnm_writepamrown(&pam, tuplerown);
     }
-    
+
     pnm_freepamrown(tuplerown);
+}
+    
+
+
+int
+main(int argc, const char **argv) {
+    struct CmdlineInfo cmdline;
+    double ** kernel;
+    double    normalizer;
+    
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
 
+    kernel = gaussianKernel(cmdline.width, cmdline.height, cmdline.oversample,
+                            cmdline.sigma);
+
+    normalizer = cmdline.maximize ?
+        maximumKernelValue(kernel, cmdline.width, cmdline.height) :
+        totalKernelValue(kernel, cmdline.width, cmdline.height);
+
+    writePam(kernel,
+             cmdline.width, cmdline.height, cmdline.maxval, cmdline.tupletype,
+             normalizer, stdout);
+
+    pm_freearray2((void **)kernel);
+    
     return 0;
 }
diff --git a/generator/pamgradient.c b/generator/pamgradient.c
index 57e78288..526efdae 100644
--- a/generator/pamgradient.c
+++ b/generator/pamgradient.c
@@ -7,7 +7,7 @@
 
 
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     tuple colorTopLeft;
     tuple colorTopRight;
     tuple colorBottomLeft;
@@ -19,13 +19,13 @@ struct cmdlineInfo {
 
 static void
 parseCommandLine(int argc, const char **argv,
-                 struct cmdlineInfo * const cmdlineP) {
+                 struct CmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
-  Convert program invocation arguments (argc,argv) into a format the 
-  program can use easily, struct cmdlineInfo.  Validate arguments along
+  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 
+  Note that some string information we return as *cmdlineP is in the storage
   argv[] points to.
 -----------------------------------------------------------------------------*/
     optEntry * option_def;
@@ -55,14 +55,14 @@ parseCommandLine(int argc, const char **argv,
             pm_error("The value you specified for -maxval (%u) is too big.  "
                      "Max allowed is %u", cmdlineP->maxval,
                      PAM_OVERALL_MAXVAL);
-        
+
         if (cmdlineP->maxval < 1)
             pm_error("You cannot specify 0 for -maxval");
-    }    
+    }
 
     if (argc-1 != 6) {
         pm_error("Need 6 arguments: colorTopLeft, colorTopRight, "
-                 "colorBottomLeft, colorBottomRight, width, height"); 
+                 "colorBottomLeft, colorBottomRight, width, height");
     } else {
         cmdlineP->colorTopLeft     = pnm_parsecolor(argv[1], cmdlineP->maxval);
         cmdlineP->colorTopRight    = pnm_parsecolor(argv[2], cmdlineP->maxval);
@@ -77,12 +77,13 @@ parseCommandLine(int argc, const char **argv,
             pm_error("height argument must be a positive number.  You "
                      "specified '%s'", argv[6]);
     }
+    free(option_def);
 }
 
 
 
 static void
-freeCmdline(struct cmdlineInfo const cmdline) {
+freeCmdline(struct CmdlineInfo const cmdline) {
 
     pnm_freepamtuple(cmdline.colorTopLeft);
     pnm_freepamtuple(cmdline.colorTopRight);
@@ -99,7 +100,7 @@ interpolate(struct pam * const pamP,
             tuple        const last) {
 
     unsigned int plane;
-    
+
     for (plane = 0; plane < pamP->depth; ++plane) {
         int const spread = last[plane] - first[plane];
 
@@ -155,13 +156,13 @@ createEdge(const struct pam * const pamP,
 int
 main(int argc, const char *argv[]) {
 
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
     struct pam pam;
     tuple * tupleRow;
     tuple * leftEdge;
     tuple * rightEdge;
     unsigned int row;
-    
+
     pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
@@ -187,7 +188,7 @@ main(int argc, const char *argv[]) {
     }
 
     pnm_writepaminit(&pam);
-    
+
     tupleRow = pnm_allocpamrow(&pam);
 
     leftEdge  = createEdge(&pam,
@@ -198,7 +199,7 @@ main(int argc, const char *argv[]) {
     /* interpolate each row between the left edge and the right edge */
     for (row = 0; row < pam.height; ++row) {
         interpolate(&pam, tupleRow, leftEdge[row], rightEdge[row]);
-        pnm_writepamrow(&pam, tupleRow); 
+        pnm_writepamrow(&pam, tupleRow);
     }
 
     pm_close(stdout);
diff --git a/generator/pamshadedrelief.c b/generator/pamshadedrelief.c
index 89996c83..35d1e3e0 100644
--- a/generator/pamshadedrelief.c
+++ b/generator/pamshadedrelief.c
@@ -38,7 +38,7 @@
    edge.
 */
 
-#define _XOPEN_SOURCE   /* get M_PI in math.h */
+#define _XOPEN_SOURCE 500  /* get M_PI in math.h */
 
 #include <assert.h>
 #include <math.h>
diff --git a/generator/pamtris/Makefile b/generator/pamtris/Makefile
new file mode 100644
index 00000000..d27606e3
--- /dev/null
+++ b/generator/pamtris/Makefile
@@ -0,0 +1,27 @@
+ifeq ($(SRCDIR)x,x)
+  SRCDIR = $(CURDIR)/../..
+  BUILDDIR = $(SRCDIR)
+endif
+SUBDIR = generator/pamtris
+VPATH=.:$(SRCDIR)/$(SUBDIR)
+
+include $(BUILDDIR)/config.mk
+
+PORTBINARIES = pamtris
+
+MERGEBINARIES = $(PORTBINARIES)
+
+BINARIES = $(MERGEBINARIES) $(NOMERGEBINARIES)
+
+ADDL_OBJECTS = boundaries.o framebuffer.o input.o triangle.o utils.o
+
+OBJECTS = pamtris.o $(ADDL_OBJECTS)
+
+MERGE_OBJECTS = pamtris.o2 $(ADDL_OBJECTS)
+
+.PHONY: all
+all: $(BINARIES)
+
+pamtris:%:%.o $(ADDL_OBJECTS)
+
+include $(SRCDIR)/common.mk
diff --git a/generator/pamtris/boundaries.c b/generator/pamtris/boundaries.c
new file mode 100644
index 00000000..7045cbc7
--- /dev/null
+++ b/generator/pamtris/boundaries.c
@@ -0,0 +1,262 @@
+/*=============================================================================
+                                 boundaries.c
+===============================================================================
+   Boundary buffer functions
+
+   New triangles are drawn one row at a time, and for every such row we have
+   left and right boundary columns within the frame buffer such that the
+   fraction of the triangle's area within that scanline is enclosed between
+   those two points (inclusive). Those coordinates may correspond to columns
+   outside the frame buffer's actual limits, in which case proper
+   post-processing should be made wherever such coordinates are used to
+   actually plot anything into the frame buffer.
+=============================================================================*/
+
+#include <stdlib.h>
+
+#include <netpbm/mallocvar.h>
+#include <netpbm/pm.h>
+
+#include "varying.h"
+#include "utils.h"
+
+
+#include "boundaries.h"
+
+
+
+void
+init_boundary_buffer(boundary_info * const bi,
+                     int16_t         const height) {
+
+    MALLOCARRAY(bi->buffer, height * 2);
+
+    if (!bi->buffer) {
+        pm_error("unable to get memory for %u-row high boundary buffer.",
+                 height);
+    }
+}
+
+
+
+void
+free_boundary_buffer(boundary_info * bi) {
+    free(bi->buffer);
+}
+
+
+
+bool
+gen_triangle_boundaries(Xy              const xy,
+                        boundary_info * const bi,
+                        int16_t         const width,
+                        int16_t         const height) {
+/*----------------------------------------------------------------------------
+  Generate an entry in the boundary buffer for the boundaries of every
+  VISIBLE row of a particular triangle. In case there is no such row,
+  start_scanline is accordingly set to -1. "xy" is a 3-element array
+  of pairs of integers representing the coordinates of the vertices of
+  a triangle. Those vertices MUST be already sorted in order from the
+  uppermost to the lowermost vertex (which is what draw_triangle, the
+  only function which uses this one, does with the help of sort3).
+
+  The return value indicates whether the middle vertex is to the left of
+  the line connecting the top vertex to the bottom vertex or not.
+-----------------------------------------------------------------------------*/
+    int16_t leftmost_x;
+    int16_t rightmost_x;
+    int mid_is_to_the_left;
+    varying top_x;
+    varying mid_x;
+    varying bot_x;
+    varying top2mid;
+    varying top2bot;
+    varying mid2bot;
+    varying* upper_left;
+    varying* lower_left;
+    varying* upper_right;
+    varying* lower_right;
+    varying* left[2];
+    varying* right[2];
+    int16_t* num_rows_ptr[2];
+    int32_t y;
+    int32_t i;
+    uint8_t k;
+
+    leftmost_x = xy._[0][0];   /* initial value */
+    rightmost_x = xy._[0][0];  /* initial value */
+
+    bi->start_scanline = -1;
+    bi->num_upper_rows = 0;
+    bi->num_lower_rows = 0;
+
+    if (xy._[2][1] < 0 || xy._[0][1] >= height) {
+        /* Triangle is either completely above the uppermost scanline or
+           completely below the lowermost scanline.
+        */
+
+        return false; /* Actual value doesn't matter. */
+    }
+
+    {
+        unsigned int i;
+
+        for (i = 1; i < 3; i++) {
+            if (xy._[i][0] < leftmost_x) {
+                leftmost_x = xy._[i][0];
+            }
+
+            if (xy._[i][0] > rightmost_x) {
+                rightmost_x = xy._[i][0];
+            }
+        }
+    }
+    if (rightmost_x < 0 || leftmost_x >= width) {
+        /* Triangle is either completely to the left of the leftmost
+           framebuffer column or completely to the right of the rightmost
+           framebuffer column.
+        */
+        return false; /* Actual value doesn't matter. */
+    }
+
+    if (xy._[0][1] == xy._[1][1] && xy._[1][1] == xy._[2][1]) {
+        /* Triangle is degenarate: its visual representation consists only of
+           a horizontal straight line.
+        */
+
+        bi->start_scanline = xy._[0][1];
+
+        return false; /* Actual value doesn't matter. */
+    }
+
+    mid_is_to_the_left = 2;
+
+    int32_to_varying_array(&xy._[0][0], &top_x, 1);
+    int32_to_varying_array(&xy._[1][0], &mid_x, 1);
+    int32_to_varying_array(&xy._[2][0], &bot_x, 1);
+
+    if (xy._[0][1] == xy._[1][1]) {
+        /* Triangle has only a lower part. */
+        k = 1;
+
+        mid_is_to_the_left = 0;
+    } else {
+        k = 0;
+
+        if (xy._[1][1] == xy._[2][1]) {
+            /* Triangle has only an upper part (plus the row of the middle
+               vertex).
+            */
+            mid_is_to_the_left = 1;
+        }
+    }
+
+    prepare_for_interpolation(&top_x, &mid_x, &top2mid, xy._[1][1] - xy._[0][1], 1);
+    prepare_for_interpolation(&top_x, &bot_x, &top2bot, xy._[2][1] - xy._[0][1], 1);
+    prepare_for_interpolation(&mid_x, &bot_x, &mid2bot, xy._[2][1] - xy._[1][1], 1);
+
+    if (mid_is_to_the_left == 2) {
+        mid_is_to_the_left = top2mid.s < top2bot.s;
+    }
+
+    if (mid_is_to_the_left) {
+        upper_left     = &top2mid;
+        lower_left     = &mid2bot;
+        upper_right    = &top2bot;
+        lower_right    = upper_right;
+    } else {
+        upper_right    = &top2mid;
+        lower_right    = &mid2bot;
+        upper_left     = &top2bot;
+        lower_left     = upper_left;
+    }
+
+    left[0] = upper_left;
+    left[1] = lower_left;
+    right[0] = upper_right;
+    right[1] = lower_right;
+
+    num_rows_ptr[0] = &bi->num_upper_rows;
+    num_rows_ptr[1] = &bi->num_lower_rows;
+
+    y = xy._[0][1];
+
+    i = 0;
+
+    while (k < 2) {
+        int32_t end;
+
+        end = xy._[k + 1][1] + k;  /* initial value */
+
+        if (y < 0) {
+            int32_t delta;
+
+            if (end > 0) {
+                delta = -y;
+            } else {
+                delta = xy._[k + 1][1] - y;
+            }
+
+            y += delta;
+
+            multi_step_up(left[k], delta, 1);
+            multi_step_up(right[k], delta, 1);
+
+            if (y < 0) {
+                k++;
+                continue;
+            }
+        } else if(y >= height) {
+            return mid_is_to_the_left;
+        }
+
+        if (end > height) {
+            end = height;
+        }
+
+        while (y < end) {
+            if (round_varying(*left[k]) >= width || round_varying(*right[k]) < 0) {
+                if (bi->start_scanline > -1) {
+                    return mid_is_to_the_left;
+                }
+            } else {
+                if (bi->start_scanline == -1) {
+                    bi->start_scanline = y;
+                }
+
+                bi->buffer[i++] = round_varying(*left[k]);
+                bi->buffer[i++] = round_varying(*right[k]);
+
+                (*(num_rows_ptr[k]))++;
+            }
+
+            step_up(left[k], 1);
+            step_up(right[k], 1);
+
+            y++;
+        }
+        k++;
+    }
+    return mid_is_to_the_left;
+}
+
+
+
+void
+get_triangle_boundaries(uint16_t              const row_index,
+                        int32_t *             const left,
+                        int32_t *             const right,
+                        const boundary_info * const bi) {
+/*----------------------------------------------------------------------------
+  Return the left and right boundaries for a given VISIBLE triangle row (the
+  row index is relative to the first visible row). These values may be out of
+  the horizontal limits of the frame buffer, which is necessary in order to
+  compute correct attribute interpolations.
+-----------------------------------------------------------------------------*/
+    uint32_t const i  = row_index << 1;
+
+    *left       = bi->buffer[i];
+    *right      = bi->buffer[i + 1];
+}
+
+
diff --git a/generator/pamtris/boundaries.h b/generator/pamtris/boundaries.h
new file mode 100644
index 00000000..70f7f90d
--- /dev/null
+++ b/generator/pamtris/boundaries.h
@@ -0,0 +1,72 @@
+#ifndef BOUNDARIES_H_INCLUDED
+#define BOUNDARIES_H_INCLUDED
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "triangle.h"
+
+typedef struct boundary_info {
+/*----------------------------------------------------------------------------
+  Information about visible triangle rows' boundaries. Also see the
+  "boundary buffer functions" below.
+
+  A "visible" triangle row is one which:
+
+    1. Corresponds to a frame buffer row whose index (from top to bottom) is
+       equal to or greater than 0 and smaller than the image height; and
+
+    2. Has at least some of its pixels between the frame buffer columns whose
+       index (from left to right) is equal to or greater than 0 and smaller
+       than the image width.
+-----------------------------------------------------------------------------*/
+    int16_t start_scanline;
+        /* Index of the frame buffer scanline which contains the first visible
+           row of the current triangle, if there is any such row. If not, it
+           contains the value -1.
+        */
+
+    int16_t num_upper_rows;
+        /* The number of visible rows in the upper part of the triangle. The
+           upper part of a triangle is composed of all the rows starting from
+           the top vertex down to the middle vertex, but not including this
+           last one.
+        */
+
+    int16_t num_lower_rows;
+        /* The number of visible rows in the lower part of the triangle. The
+           lower part of a triangle is composed of all the rows from the
+           middle vertex to the bottom vertex -- all inclusive.
+        */
+
+    int16_t * buffer;
+        /* This is the "boundary buffer": a pointer to an array of int16_t's
+           where each consecutive pair of values indicates, in this order, the
+           columns of the left and right boundary pixels for a particular
+           visible triangle row. Those boundaries are inclusive on both sides
+           and may be outside the limits of the frame buffer. This field is
+           initialized and freed by the functions "init_boundary_buffer" and
+           "free_boundary_buffer", respectively.
+        */
+} boundary_info;
+
+void
+init_boundary_buffer(boundary_info * const bdi,
+                     int16_t         const height);
+
+void
+free_boundary_buffer(boundary_info *);
+
+bool
+gen_triangle_boundaries(Xy              const xy,
+                        boundary_info * const bdi,
+                        int16_t         const width,
+                        int16_t         const height);
+
+void
+get_triangle_boundaries(uint16_t              const row_index,
+                        int32_t *             const left,
+                        int32_t *             const right,
+                        const boundary_info * const bdi);
+
+#endif
diff --git a/generator/pamtris/framebuffer.c b/generator/pamtris/framebuffer.c
new file mode 100644
index 00000000..93263c91
--- /dev/null
+++ b/generator/pamtris/framebuffer.c
@@ -0,0 +1,339 @@
+/*=============================================================================
+                              framebuffer.c
+===============================================================================
+  Frame buffer functions
+
+  Every drawing operation is applied on an internal "frame buffer", which is
+  simply an "image buffer" which represents the picture currently being drawn,
+  along with a "Z-Buffer" which contains the depth values for every pixel in
+  the image buffer. Once all desired drawing operations for a particular
+  picture are effected, a function is provided to print the current contents
+  of the image buffer as a PAM image on standard output.  Another function is
+  provided to clear the contents of the frame buffer (i. e. set all image
+  samples and Z-Buffer entries to 0), with the option of only clearing either
+  the image buffer or the Z-Buffer individually.
+
+  The Z-Buffer works as follows: Every pixel in the image buffer has a
+  corresponding entry in the Z-Buffer. Initially, every entry in the Z-Buffer
+  is set to 0. Every time we desire to plot a pixel at some particular
+  position in the frame buffer, the current value of the corresponding entry
+  in the Z-Buffer is compared against the the Z component of the incoming
+  pixel. If MAX_Z minus the value of the Z component of the incoming pixel is
+  equal to or greater than the current value of the corresponding entry in the
+  Z-Buffer, the frame buffer is changed as follows:
+
+    1. All the samples but the last of the corresponding position in the
+       image buffer are set to equal those of the incoming pixel.
+
+    2. The last sample, that is, the A-component of the corresponding position
+       in the image buffer is set to equal the maxval.
+
+    3. The corresponding entry in the Z-Buffer is set to equal MAX_Z minus the
+       value of the Z component of the incoming pixel.
+
+    Otherwise, no changes are made on the frame buffer.
+=============================================================================*/
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "utils.h"
+#include "varying.h"
+#include "limits_pamtris.h"
+
+#include "framebuffer.h"
+
+
+
+int
+set_tupletype(const char * const str,
+              char *       const tupletype) {
+/*----------------------------------------------------------------------------
+  Set the tuple type for the output PAM images given a string ("str") of 255
+  characters or less. If the string has more than 255 characters, the function
+  returns 0. Otherwise, it returns 1. If NULL is given for the "str" argument,
+  the tuple type is set to a null string. This function is called during
+  program initialization and whenever a "r" command is executed. The second
+  argument must point to the tuple_type member of the "outpam" field in the
+  framebuffer_info struct.
+-----------------------------------------------------------------------------*/
+    if (str == NULL) {
+        memset(tupletype, 0, 256);
+    } else {
+        size_t len;
+
+        len = strlen(str);   /* initial value */
+
+        if (len > 255) {
+            return 0;
+        }
+
+        if (len > 0) {
+            memcpy(tupletype, str, len);
+        }
+
+        tupletype[len--] = '\0';
+
+        while(len > 0 && isspace(tupletype[len])) {
+            tupletype[len--] = '\0';
+        }
+    }
+
+    return 1;
+}
+
+
+
+int
+init_framebuffer(framebuffer_info * const fbi) {
+
+    uint8_t const num_planes = fbi->num_attribs + 1;
+
+    uint32_t const elements = fbi->width * fbi->height;
+
+    fbi->img.bytes = elements * (num_planes * sizeof(uint16_t));
+    fbi->z.bytes = elements * sizeof(uint32_t);
+
+    fbi->img.buffer =
+        calloc(fbi->img.bytes / sizeof(uint16_t), sizeof(uint16_t));
+    fbi->z.buffer =
+        calloc(fbi->z.bytes / sizeof(uint32_t), sizeof(uint32_t));
+
+    if(fbi->img.buffer == NULL || fbi->z.buffer == NULL) {
+        free(fbi->img.buffer);
+        free(fbi->z.buffer);
+
+        return 0;
+    }
+
+    fbi->outpam.size = sizeof(struct pam);
+    fbi->outpam.len = sizeof(struct pam);
+    fbi->outpam.file = stdout;
+    fbi->outpam.format = PAM_FORMAT;
+    fbi->outpam.plainformat = 0;
+    fbi->outpam.height = fbi->height;
+    fbi->outpam.width = fbi->width;
+    fbi->outpam.depth = num_planes;
+    fbi->outpam.maxval = fbi->maxval;
+    fbi->outpam.allocation_depth = 0;
+    fbi->outpam.comment_p = NULL;
+
+    fbi->pamrow = NULL;
+    fbi->pamrow = pnm_allocpamrow(&fbi->outpam);
+
+    if (fbi->pamrow == NULL) {
+        free(fbi->img.buffer);
+        free(fbi->z.buffer);
+
+        return 0;
+    }
+
+    return 1;
+}
+
+
+
+void
+free_framebuffer(framebuffer_info * const fbi) {
+
+    free(fbi->img.buffer);
+    free(fbi->z.buffer);
+
+    pnm_freepamrow(fbi->pamrow);
+}
+
+
+
+int
+realloc_image_buffer(int32_t            const new_maxval,
+                     int32_t            const new_num_attribs,
+                     framebuffer_info * const fbi) {
+/*----------------------------------------------------------------------------
+  Reallocate the image buffer with a new maxval and depth, given the struct
+  with information about the framebuffer. The fields variables "maxval" and
+  "num_attribs".
+
+  From the point this function is called onwards, new PAM images printed on
+  standard output will have the new maxval for the maxval and num_attribs + 1
+  for the depth.
+
+  This function does *not* check whether the new maxval and num_attribs are
+  within the proper allowed limits. That is done inside the input processing
+  function "process_next_command", which is the only function that calls this
+  one.
+
+  If the function suceeds, the image buffer is left in cleared state. The
+  Z-Buffer, however, is not touched at all.
+
+  If the new depth is equal to the previous one, no actual reallocation is
+  performed: only the global variable "maxval" is changed. But the image
+  buffer is nonetheless left in cleared state regardless.
+-----------------------------------------------------------------------------*/
+    uint8_t num_planes;
+
+    pnm_freepamrow(fbi->pamrow);
+    fbi->pamrow = NULL;
+
+    num_planes = fbi->num_attribs + 1;  /* initial value */
+
+    if (new_num_attribs != fbi->num_attribs) {
+        fbi->num_attribs = new_num_attribs;
+        num_planes = fbi->num_attribs + 1;
+
+        fbi->img.bytes =
+            fbi->width * fbi->height * (num_planes * sizeof(uint16_t));
+
+        {
+            uint16_t * const new_ptr =
+                realloc(fbi->img.buffer, fbi->img.bytes);
+
+            if (new_ptr == NULL) {
+                free(fbi->img.buffer);
+                fbi->img.buffer = NULL;
+
+                return 0;
+            }
+            fbi->img.buffer = new_ptr;
+        }
+    }
+
+    fbi->maxval = new_maxval;
+
+    fbi->outpam.size             = sizeof(struct pam);
+    fbi->outpam.len              = sizeof(struct pam);
+    fbi->outpam.file             = stdout;
+    fbi->outpam.format           = PAM_FORMAT;
+    fbi->outpam.plainformat      = 0;
+    fbi->outpam.height           = fbi->height;
+    fbi->outpam.width            = fbi->width;
+    fbi->outpam.depth            = num_planes;
+    fbi->outpam.maxval           = fbi->maxval;
+    fbi->outpam.allocation_depth = 0;
+    fbi->outpam.comment_p        = NULL;
+
+    fbi->pamrow = pnm_allocpamrow(&fbi->outpam);
+
+    if (fbi->pamrow == NULL) {
+        free(fbi->img.buffer);
+        fbi->img.buffer = NULL;
+
+        return 0;
+    }
+
+    memset(fbi->img.buffer, 0, fbi->img.bytes);
+
+    return 1;
+}
+
+
+
+void
+print_framebuffer(framebuffer_info * const fbi) {
+
+    uint8_t  const num_planes = fbi->num_attribs + 1;
+    uint32_t const end        = fbi->width * fbi->height;
+
+    uint32_t i;
+
+    pnm_writepaminit(&fbi->outpam);
+
+    for (i = 0; i != end; ) {
+        int j;
+        for (j = 0; j < fbi->width; j++) {
+            uint32_t const k = (i + j) * num_planes;
+
+            unsigned int l;
+
+            for (l = 0; l < num_planes; l++) {
+                fbi->pamrow[j][l] = fbi->img.buffer[k + l];
+            }
+        }
+
+        pnm_writepamrow(&fbi->outpam, fbi->pamrow);
+
+        i += fbi->width;
+    }
+}
+
+
+
+void
+clear_framebuffer(bool               const clear_image_buffer,
+                  bool               const clear_z_buffer,
+                  framebuffer_info * const fbi) {
+
+    if (clear_image_buffer) {
+        memset(fbi->img.buffer, 0, fbi->img.bytes);
+    }
+
+    if (clear_z_buffer) {
+        memset(fbi->z.buffer, 0, fbi->z.bytes);
+    }
+}
+
+
+
+void
+draw_span(uint32_t           const base,
+          uint16_t           const length,
+          varying *          const attribs,
+          framebuffer_info * const fbi) {
+/*----------------------------------------------------------------------------
+  Draw a horizontal span of "length" pixels into the frame buffer, performing
+  the appropriate depth tests. "base" must equal the row of the frame buffer
+  where one desires to draw the span *times* the image width, plus the column
+  of the first pixel in the span.
+
+  This function does not perform any kind of bounds checking.
+-----------------------------------------------------------------------------*/
+    static double const depth_range = MAX_Z;
+
+    uint16_t const maxval = fbi->maxval;
+    uint8_t  const z      = fbi->num_attribs;
+    uint8_t  const w      = z + 1;
+    uint8_t  const n      = w + 1;
+
+    uint8_t  const num_planes = w;
+
+    unsigned int i;
+
+    /* Process each pixel in the span: */
+
+    for (i = 0; i < length; i++) {
+        int32_t  const d      = round(depth_range * attribs[z].v);
+        uint32_t const d_mask = geq_mask64(d, fbi->z.buffer[base + i]);
+
+        uint32_t const j = base + i;
+        uint32_t const k = j * num_planes;
+
+        varying const inverse_w = inverse_varying(attribs[w]);
+
+        unsigned int l;
+
+        /* The following statements will only have any effect if the depth
+           test, performed above, has suceeded. I. e. if the depth test fails,
+           no changes will be made on the frame buffer; otherwise, the
+           frame buffer will be updated with the new values.
+        */
+        fbi->z.buffer[j] = (fbi->z.buffer[j] & ~d_mask) | (d & d_mask);
+
+        for (l = 0; l < z; l++) {
+	    varying const newval = multiply_varyings(attribs[l], inverse_w);
+
+            fbi->img.buffer[k + l] =
+                (fbi->img.buffer[k + l] & ~d_mask) |
+                (round_varying(newval) &  d_mask);
+        }
+
+        fbi->img.buffer[k + z] |= (maxval & d_mask);
+
+        /* Compute the attribute values for the next pixel: */
+
+        step_up(attribs, n);
+    }
+}
+
+
+
diff --git a/generator/pamtris/framebuffer.h b/generator/pamtris/framebuffer.h
new file mode 100644
index 00000000..b3d4f7a3
--- /dev/null
+++ b/generator/pamtris/framebuffer.h
@@ -0,0 +1,75 @@
+#ifndef FRAMEBUFFER_H_INCLUDED
+#define FRAMEBUFFER_H_INCLUDED
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "varying.h"
+#include "netpbm/pam.h"
+
+typedef struct framebuffer_info {
+/*----------------------------------------------------------------------------
+   Information about the frame buffer and PAM output
+-----------------------------------------------------------------------------*/
+    /* These fields are initialized once by reading the command line
+       arguments. "maxval" and "num_attribs" may be modified later
+       through "realloc_image_buffer"; "correct" may also be modified
+       if the eponymous command is given.
+    */
+    int32_t width;
+    int32_t height;
+    int32_t maxval;
+    int32_t num_attribs;
+
+    /* The fields below must be initialized by "init_framebuffer" and
+       freed by "free_framebuffer", except for the tuple_type field in
+       "outpam" which is initialized once by reading the command line
+       arguments and may be modified later through "set_tupletype".
+    */
+    struct {
+        uint16_t * buffer;
+        uint32_t   bytes;
+    } img; /* Image buffer */
+
+    struct {
+        uint32_t * buffer;
+        uint32_t   bytes;
+    } z;  /* Z-buffer */
+
+    struct pam outpam;
+
+    tuple * pamrow;
+} framebuffer_info;
+
+
+
+int
+set_tupletype(const char * const str,
+              char *       const tupletype);
+
+int
+init_framebuffer(framebuffer_info * const fbi);
+
+void
+free_framebuffer(framebuffer_info * const fbi);
+
+void
+print_framebuffer(framebuffer_info * const fbi);
+
+void
+clear_framebuffer(bool               const clear_image_buffer,
+                  bool               const clear_z_buffer,
+                  framebuffer_info * const fbi);
+
+int
+realloc_image_buffer(int32_t            const new_maxval,
+                     int32_t            const new_num_attribs,
+                     framebuffer_info * const fbi);
+
+void
+draw_span(uint32_t           const base,
+          uint16_t           const length,
+          varying *          const attribs,
+          framebuffer_info * const fbi);
+
+#endif
diff --git a/generator/pamtris/input.c b/generator/pamtris/input.c
new file mode 100644
index 00000000..ffb2a859
--- /dev/null
+++ b/generator/pamtris/input.c
@@ -0,0 +1,695 @@
+/*=============================================================================
+                              input.c
+===============================================================================
+   Input handling functions
+=============================================================================*/
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "netpbm/mallocvar.h"
+#include "netpbm/pm.h"
+#include "netpbm/nstring.h"
+
+#include "limits_pamtris.h"
+#include "framebuffer.h"
+#include "triangle.h"
+
+#include "input.h"
+
+#define DRAW_MODE_TRIANGLES 1
+#define DRAW_MODE_STRIP     2
+#define DRAW_MODE_FAN       3
+
+#define CMD_SET_MODE        "mode"
+#define CMD_SET_ATTRIBS     "attribs"
+#define CMD_VERTEX          "vertex"
+#define CMD_PRINT           "print"
+#define CMD_CLEAR           "clear"
+#define CMD_RESET           "reset"
+#define CMD_QUIT            "quit"
+
+#define ARG_TRIANGLES       "triangles"
+#define ARG_STRIP           "strip"
+#define ARG_FAN             "fan"
+#define ARG_IMAGE           "image"
+#define ARG_DEPTH           "depth"
+
+
+typedef struct {
+    Xy v_xy;
+        /* X- and Y-coordinates of the vertices for the current triangle.
+        */
+    Attribs v_attribs;
+        /* Vertex attributes for the current triangle. Includes the
+           Z-coordinates.
+        */
+    int32_t curr_attribs[MAX_NUM_ATTRIBS];
+        /* Attributes that will be assigned to the next vertex. Does not
+           include the Z-coordinate.
+        */
+    uint8_t next;
+        /* Index of the next vertex to be read. */
+    bool draw;
+        /* If true, draws a new triangle upon reading a new vertex. */
+
+    uint8_t mode;
+        /* Drawing mode. */
+
+    bool initialized;
+} state_info;
+
+
+
+static void
+clearAttribs(state_info * const si,
+             int32_t      const maxval,
+             int16_t      const num_attribs) {
+
+    unsigned int i;
+
+    for (i = 0; i < num_attribs; ++i) {
+        si->curr_attribs[i] = maxval;
+    }
+}
+
+
+
+void
+input_init(Input * const inputP) {
+
+    inputP->buffer = NULL;
+    inputP->length = 0;
+    inputP->number = 1;
+}
+
+
+
+void
+input_term(Input * const inputP) {
+
+    if (inputP->buffer)
+        free(inputP->buffer);
+}
+
+
+
+typedef struct {
+/*----------------------------------------------------------------------------
+  Indicates a whitespace-delimited input symbol. "begin" points to its first
+  character, and "end" points to one position past its last character.
+-----------------------------------------------------------------------------*/
+    char * begin;
+    char * end;
+} Token;
+
+
+
+static Token
+nextToken(char * const startPos) {
+
+    Token retval;
+    char * p;
+
+    for (p = startPos; *p && isspace(*p); ++p);
+
+    retval.begin = p;
+
+    for (; *p && !isspace(*p); ++p);
+
+    retval.end = p;
+
+    return retval;
+}
+
+
+
+static bool
+stringIsValid(const char * const target,
+              const char * const srcBegin,
+              const char * const srcEnd) {
+
+    unsigned int charsMatched;
+    const char * p;
+
+    for (p = srcBegin, charsMatched = 0;
+         p != srcEnd && target[charsMatched] != '\0'; ++p) {
+
+        if (*p == target[charsMatched])
+            ++charsMatched;
+        else
+            break;
+    }
+
+    return (*p == '\0' || isspace(*p));
+}
+
+
+
+static void
+initState(state_info * const siP) {
+
+    siP->next = 0;
+    siP->draw = false;
+    siP->mode = DRAW_MODE_TRIANGLES;
+}
+
+
+
+static void
+makeLowercase(Token const t) {
+
+    char * p;
+
+    for (p = t.begin; p != t.end; ++p)
+        *p = tolower(*p);
+}
+
+
+
+static void
+removeComments(char * const str) {
+
+    char * p;
+
+    for (p = &str[0]; *p; ++p) {
+        if (*p == '#') {
+            *p = '\0';
+
+            break;
+        }
+    }
+}
+
+
+
+static void
+processM(Token *       const ntP,
+         state_info *  const stateP,
+         bool *        const unrecognizedCmdP,
+         const char ** const errorP) {
+
+    if (!stringIsValid(CMD_SET_MODE, ntP->begin, ntP->end)) {
+        *unrecognizedCmdP = true;
+    } else {
+        *ntP = nextToken(ntP->end);
+
+        *unrecognizedCmdP = false;
+
+        if (*ntP->begin == '\0')
+            pm_asprintf(errorP, "syntax error");
+        else {
+            makeLowercase(*ntP);
+
+            switch (*ntP->begin) {
+            case 't':
+                if (!stringIsValid(ARG_TRIANGLES, ntP->begin, ntP->end))
+                    pm_asprintf(errorP, "unrecognized drawing mode");
+                else {
+                    stateP->mode = DRAW_MODE_TRIANGLES;
+                    stateP->draw = false;
+                    stateP->next = 0;
+
+                    *errorP = NULL;
+                }
+                break;
+            case 's':
+                if (!stringIsValid(ARG_STRIP, ntP->begin, ntP->end))
+                    pm_asprintf(errorP, "unrecognized drawing mode");
+                else {
+                    stateP->mode = DRAW_MODE_STRIP;
+                    stateP->draw = false;
+                    stateP->next = 0;
+
+                    *errorP = NULL;
+                }
+                break;
+            case 'f':
+                if (!stringIsValid(ARG_FAN, ntP->begin, ntP->end))
+                    pm_asprintf(errorP, "unrecognized drawing mode");
+                else {
+                    stateP->mode = DRAW_MODE_FAN;
+                    stateP->draw = false;
+                    stateP->next = 0;
+
+                    *errorP = NULL;
+                }
+                break;
+            default:
+                pm_asprintf(errorP, "unrecognized drawing mode");
+            }
+        }
+    }
+}
+
+
+
+static void
+processA(Token *            const ntP,
+         state_info *       const stateP,
+         framebuffer_info * const fbiP,
+         bool *             const unrecognizedCmdP,
+         long int *         const iArgs,
+         const char **      const errorP) {
+
+    if (!stringIsValid(CMD_SET_ATTRIBS, ntP->begin, ntP->end)) {
+        *unrecognizedCmdP = true;
+    } else {
+        unsigned int i;
+
+        *unrecognizedCmdP = false;
+
+        for (i = 0, *errorP = NULL; i < fbiP->num_attribs && !*errorP; ++i) {
+            char * strtolEnd;
+
+            *ntP = nextToken(ntP->end);
+
+            iArgs[i] = strtol(ntP->begin, &strtolEnd, 10);
+
+            if (*ntP->begin == '\0' || strtolEnd != ntP->end)
+                pm_asprintf(errorP, "syntax error");
+            else {
+                if (iArgs[i] < 0 || iArgs[i] > fbiP->maxval)
+                    pm_asprintf(errorP, "argument(s) out of bounds");
+            }
+        }
+
+        if (!*errorP) {
+            unsigned int i;
+
+            for (i = 0; i < fbiP->num_attribs; ++i)
+                stateP->curr_attribs[i] = iArgs[i];
+        }
+    }
+}
+
+
+
+static void
+processV(Token *                const ntP,
+         state_info *           const stateP,
+         struct boundary_info * const biP,
+         framebuffer_info *     const fbiP,
+         bool *                 const unrecognizedCmdP,
+         long int *             const iArgs,
+         const char **          const errorP) {
+
+    if (!stringIsValid(CMD_VERTEX, ntP->begin, ntP->end))
+        *unrecognizedCmdP = true;
+    else {
+        unsigned int i;
+
+        *unrecognizedCmdP = false;
+
+        for (i = 0, *errorP = NULL; i < 4 && !*errorP; ++i) {
+            char * strtolEnd;
+
+            *ntP = nextToken(ntP->end);
+
+            iArgs[i] = strtol(ntP->begin, &strtolEnd, 10);
+
+            if (*ntP->begin == '\0') {
+                if (i != 3)
+                    pm_asprintf(errorP, "syntax error");
+                else
+                    iArgs[i] = 1;
+            } else {
+                if (strtolEnd != ntP->end)
+                    pm_asprintf(errorP, "syntax error");
+            }
+
+            if (!*errorP) {
+                if (i < 3) {
+                    if (iArgs[i] < MIN_COORD || iArgs[i] > MAX_COORD)
+                        pm_asprintf(errorP, "coordinates out of bounds");
+                } else {
+                    if (iArgs[i] < MIN_INPUT_W || iArgs[i] > MAX_INPUT_W)
+                        pm_asprintf(errorP,
+                                    "perspective correction factor (w) "
+                                    "out of bounds");
+                }
+            }
+        }
+
+        if (!*errorP) {
+            unsigned int i;
+
+            for (i = 0; i < fbiP->num_attribs; ++i) {
+                stateP->v_attribs._[stateP->next][i] = stateP->curr_attribs[i];
+            }
+
+            stateP->v_attribs._[stateP->next][fbiP->num_attribs + 0] =
+                iArgs[2];
+            stateP->v_attribs._[stateP->next][fbiP->num_attribs + 1] =
+                iArgs[3];
+
+            stateP->v_xy._[stateP->next][0] = iArgs[0];
+            stateP->v_xy._[stateP->next][1] = iArgs[1];
+
+            ++stateP->next;
+
+            if (!stateP->draw) {
+                if (stateP->next == 3)
+                    stateP->draw = true;
+            }
+
+            if (stateP->draw)
+                draw_triangle(stateP->v_xy, stateP->v_attribs, biP, fbiP);
+
+            if (stateP->next == 3) {
+                switch(stateP->mode) {
+                case DRAW_MODE_FAN:
+                    stateP->next = 1;
+                    break;
+                case DRAW_MODE_TRIANGLES:
+                    stateP->draw = false;
+                    stateP->next = 0;
+                    break;
+                case DRAW_MODE_STRIP:
+                    stateP->next = 0;
+                    break;
+                default:
+                    stateP->next = 0;
+                }
+            }
+        }
+    }
+}
+
+
+
+static void
+processP(Token *            const ntP,
+         framebuffer_info * const fbiP,
+         bool *             const unrecognizedCmdP,
+         const char **      const errorP) {
+
+    if (!stringIsValid(CMD_PRINT, ntP->begin, ntP->end))
+        *unrecognizedCmdP = true;
+    else {
+        *unrecognizedCmdP = false;
+
+        print_framebuffer(fbiP);
+
+        *errorP = NULL;
+    }
+}
+
+
+
+
+static void
+processExcl(Token *            const ntP,
+            framebuffer_info * const fbiP,
+            bool *             const unrecognizedCmdP,
+            const char **      const errorP) {
+
+    if (ntP->end - ntP->begin > 1)
+        *unrecognizedCmdP = true;
+    else {
+        *unrecognizedCmdP = false;
+
+        print_framebuffer(fbiP);
+
+        *errorP = NULL;
+    }
+}
+
+
+
+static void
+clear(Token *            const ntP,
+      framebuffer_info * const fbiP,
+      const char **      const errorP) {
+
+    *ntP = nextToken(ntP->end);
+
+    if (*ntP->begin != '\0') {
+        makeLowercase(*ntP);
+
+        switch(*ntP->begin) {
+        case 'i':
+            if (!stringIsValid("image", ntP->begin, ntP->end))
+                pm_asprintf(errorP, "unrecognized argument");
+            else {
+                clear_framebuffer(true, false, fbiP);
+                *errorP = NULL;
+            }
+            break;
+        case 'd':
+            if (!stringIsValid("depth", ntP->begin, ntP->end))
+                pm_asprintf(errorP, "unrecognized argument");
+            else {
+                clear_framebuffer(false, true, fbiP);
+                *errorP = NULL;
+            }
+            break;
+        case 'z':
+            if (ntP->end - ntP->begin > 1)
+                pm_asprintf(errorP, "unrecognized argument");
+            else {
+                clear_framebuffer(false, true, fbiP);
+                *errorP = NULL;
+            }
+            break;
+        default:
+            pm_asprintf(errorP, "unrecognized argument");
+        }
+    } else {
+        clear_framebuffer(true, true, fbiP);
+        *errorP = NULL;
+    }
+}
+
+
+
+static void
+processC(Token *            const ntP,
+         framebuffer_info * const fbiP,
+         bool *             const unrecognizedCmdP,
+         const char **      const errorP) {
+
+    if (!stringIsValid(CMD_CLEAR, ntP->begin, ntP->end))
+        *unrecognizedCmdP = true;
+    else {
+        *unrecognizedCmdP = false;
+
+        clear(ntP, fbiP, errorP);
+    }
+}
+
+
+
+static void
+processAsterisk(Token *            const ntP,
+                framebuffer_info * const fbiP,
+                bool *             const unrecognizedCmdP,
+                const char **      const errorP) {
+
+    if (ntP->end - ntP->begin > 1)
+        *unrecognizedCmdP = true;
+    else {
+        *unrecognizedCmdP = false;
+
+        clear(ntP, fbiP, errorP);
+    }
+}
+
+
+
+static void
+processR(Token *                const ntP,
+         state_info *           const stateP,
+         framebuffer_info *     const fbiP,
+         bool *                 const unrecognizedCmdP,
+         long int *             const iArgs,
+         const char **          const errorP) {
+
+    if (!stringIsValid(CMD_RESET, ntP->begin, ntP->end))
+        *unrecognizedCmdP = true;
+    else {
+        unsigned int i;
+
+        *unrecognizedCmdP = false;
+
+        for (i = 0, *errorP = NULL; i < 2 && !*errorP; ++i) {
+            char * strtolEnd;
+
+            *ntP = nextToken(ntP->end);
+
+            iArgs[i] = strtol(ntP->begin, &strtolEnd, 10);
+
+            if (*ntP->begin == '\0' || ntP->end != strtolEnd)
+                pm_asprintf(errorP, "syntax error");
+        }
+
+        if (!*errorP) {
+            if (iArgs[0] < 1 || iArgs[0] > PAM_OVERALL_MAXVAL)
+                pm_asprintf(errorP, "invalid new maxval");
+            else {
+                if (iArgs[1] < 1 || iArgs[1] > MAX_NUM_ATTRIBS)
+                    pm_asprintf(errorP, "invalid new number of generic vertex "
+                                "attributes");
+                else {
+                    *ntP = nextToken(ntP->end);
+
+                    if (*ntP->begin != '\0') {
+                        if (!set_tupletype(ntP->begin,
+                                           fbiP->outpam.tuple_type)) {
+                            pm_message(
+                                "warning: could not set new tuple type; "
+                                "using a null string");
+                            set_tupletype(NULL, fbiP->outpam.tuple_type);
+                        }
+                    } else
+                        set_tupletype(NULL, fbiP->outpam.tuple_type);
+
+                    if (!realloc_image_buffer(iArgs[0], iArgs[1], fbiP)) {
+                        pm_error("Unable to allocate memory for "
+                                 "image buffer");
+                    }
+
+                    stateP->next = 0;
+                    stateP->draw = false;
+
+                    clearAttribs(stateP, fbiP->maxval, fbiP->num_attribs);
+                }
+            }
+        }
+    }
+}
+
+
+
+static void
+processQ(Token *                const ntP,
+         bool *                 const unrecognizedCmdP,
+         bool *                 const noMoreCommandsP,
+         const char **          const errorP) {
+
+    if (!stringIsValid(CMD_QUIT, ntP->begin, ntP->end))
+        *unrecognizedCmdP = true;
+    else {
+        *unrecognizedCmdP = false;
+
+        *noMoreCommandsP = true;
+
+        *errorP = NULL;
+    }
+}
+
+
+
+void
+input_process_next_command(Input *                const inputP,
+                           struct boundary_info * const biP,
+                           framebuffer_info *     const fbiP,
+                           bool *                 const noMoreCommandsP) {
+/*----------------------------------------------------------------------------
+  Doesn't necessarily process a command, just the next line of input, which
+  may be empty.
+
+  Return *noMoreCommandsP true iff the next command is a quit command of
+  there is no next command.
+-----------------------------------------------------------------------------*/
+    static state_info state;
+
+    Token nt;
+
+    long int iArgs[MAX_NUM_ATTRIBS];
+        /* For storing potential integer arguments. */
+    bool unrecognizedCmd;
+        /* Unrecognized command detected */
+    bool noMoreCommands;
+    const char * error;
+        /* Description of problem with the command; NULL if no problem.
+           Meaningful only when 'unrecognizedCmd' is false.
+        */
+
+    if (!state.initialized) {
+        initState(&state);
+        clearAttribs(&state, fbiP->maxval, fbiP->num_attribs);
+
+        state.initialized = true;
+    }
+
+    {
+        int eof;
+        size_t lineLen;
+
+        pm_getline(stdin, &inputP->buffer, &inputP->length, &eof, &lineLen);
+
+        if (eof) {
+            *noMoreCommandsP = true;
+            return;
+        }
+    }
+
+    removeComments(inputP->buffer);
+
+    nt = nextToken(inputP->buffer);
+
+    makeLowercase(nt);
+
+    noMoreCommands = false;  /* initial assumption */
+
+    switch (nt.begin[0]) {
+    case 'm':
+        processM(&nt, &state, &unrecognizedCmd, &error);
+        break;
+    case 'a':
+        processA(&nt, &state, fbiP, &unrecognizedCmd, iArgs, &error);
+        break;
+    case 'v':
+        processV(&nt, &state, biP, fbiP, &unrecognizedCmd, iArgs, &error);
+        break;
+    case 'p':
+        processP(&nt, fbiP, &unrecognizedCmd, &error);
+        break;
+    case '!':
+        processExcl(&nt, fbiP, &unrecognizedCmd, &error);
+        break;
+    case 'c':
+        processC(&nt, fbiP, &unrecognizedCmd, &error);
+        break;
+    case '*':
+        processAsterisk(&nt, fbiP, &unrecognizedCmd, &error);
+        break;
+    case 'r':
+        processR(&nt, &state, fbiP, &unrecognizedCmd, iArgs, &error);
+        break;
+    case 'q':
+        processQ(&nt, &unrecognizedCmd, &noMoreCommands, &error);
+        break;
+    case '\0':
+        break;
+    default:
+        unrecognizedCmd = true;
+    }
+
+    if (!noMoreCommands) {
+        char const next = *nextToken(nt.end).begin;
+
+        if (unrecognizedCmd) {
+            pm_errormsg("error: unrecognized command: line %u.",
+                        (unsigned)inputP->number);
+        } else {
+            if (error) {
+                pm_errormsg("Error in line %u: %s",
+                            (unsigned)inputP->number, error);
+                pm_strfree(error);
+            } else {
+                if (next != '\0')
+                    pm_message("warning: ignoring excess arguments: line %u",
+                               (unsigned)inputP->number);
+            }
+        }
+    }
+    ++inputP->number;
+
+    *noMoreCommandsP = noMoreCommands;
+}
+
+
diff --git a/generator/pamtris/input.h b/generator/pamtris/input.h
new file mode 100644
index 00000000..d34de3a1
--- /dev/null
+++ b/generator/pamtris/input.h
@@ -0,0 +1,27 @@
+#ifndef INPUT_H_INCLUDED
+#define INPUT_H_INCLUDED
+
+#include <stdint.h>
+
+struct boundary_info;
+struct framebuffer_info;
+
+typedef struct {
+    char *   buffer;
+    size_t   length;
+    uint64_t number;
+} Input;
+
+void
+input_init(Input * const inputP);
+
+void
+input_term(Input * const inputP);
+
+void
+input_process_next_command(Input *                   const inputP,
+                           struct boundary_info *    const bdiP,
+                           struct framebuffer_info * const fbiP,
+                           bool *                    const noMoreCommandsP);
+
+#endif
diff --git a/generator/pamtris/limits_pamtris.h b/generator/pamtris/limits_pamtris.h
new file mode 100644
index 00000000..a7ed503f
--- /dev/null
+++ b/generator/pamtris/limits_pamtris.h
@@ -0,0 +1,11 @@
+#ifndef LIMITS_H_INCLUDED
+#define LIMITS_H_INCLUDED
+
+#define MAX_NUM_ATTRIBS 20
+#define MAX_COORD       32767
+#define MIN_COORD       (-MAX_COORD)
+#define MAX_INPUT_W     1048575
+#define MIN_INPUT_W     1
+#define MAX_Z           0x3FFFFFFF
+
+#endif
diff --git a/generator/pamtris/pamtris.c b/generator/pamtris/pamtris.c
new file mode 100644
index 00000000..e0becf7a
--- /dev/null
+++ b/generator/pamtris/pamtris.c
@@ -0,0 +1,171 @@
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "netpbm/mallocvar.h"
+#include "netpbm/shhopt.h"
+#include "netpbm/pam.h"
+
+#include "limits_pamtris.h"
+#include "framebuffer.h"
+#include "boundaries.h"
+#include "input.h"
+
+#define MAX_METRICS 8192
+
+
+
+static int
+parse_command_line(int *         const argc_ptr,
+                   const char ** const argv,
+                   int32_t *     const width,
+                   int32_t *     const height,
+                   int32_t *     const maxval,
+                   int32_t *     const num_attribs,
+                   char *        const tupletype) {
+
+    optEntry * option_def;
+    optStruct3 opt;
+        /* Instructions to pm_optParseOptions3 on how to parse our options */
+    unsigned int option_def_index;
+
+    char * tupletype_tmp;
+
+    unsigned int width_spec, height_spec, attribs_spec, tupletype_spec;
+    unsigned int rgb_spec, grayscale_spec, maxval_spec;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;  /* incremented by OPTENT3 */
+    OPTENT3(0, "width",       OPT_INT,    width,          &width_spec,      0);
+    OPTENT3(0, "height",      OPT_INT,    height,         &height_spec,     0);
+    OPTENT3(0, "num_attribs", OPT_INT,    num_attribs,    &attribs_spec,    0);
+    OPTENT3(0, "tupletype",   OPT_STRING, &tupletype_tmp, &tupletype_spec,  0);
+    OPTENT3(0, "rgb",         OPT_FLAG,   NULL,           &rgb_spec,        0);
+    OPTENT3(0, "grayscale",   OPT_FLAG,   NULL,           &grayscale_spec,  0);
+    OPTENT3(0, "maxval",      OPT_INT,    maxval,         &maxval_spec,     0);
+
+    opt.opt_table     = option_def;
+    opt.short_allowed = false;
+    opt.allowNegNum   = false;
+
+    pm_optParseOptions3(argc_ptr, (char **)argv, opt, sizeof(opt), 0);
+
+    if (!width_spec || !height_spec || (!attribs_spec && !(rgb_spec || grayscale_spec))) {
+        pm_errormsg(
+            "you must at least specify -width, -height and "
+	    "either -num_attribs, -rgb or -grayscale.");
+
+        return 0;
+    }
+
+    if (rgb_spec + grayscale_spec + attribs_spec != 1) {
+        pm_errormsg("you must provide either only -num_attribs, "
+                    "-rgb or -grayscale; not a combination of those.");
+
+        return 0;
+    }
+
+    if (*width < 1 || *width > MAX_METRICS) {
+        pm_errormsg("invalid width.");
+
+        return 0;
+    }
+
+    if (*height < 1 || *height > MAX_METRICS) {
+        pm_errormsg("invalid height.");
+
+        return 0;
+    }
+
+    if (maxval_spec) {
+        if (*maxval < 1 || *maxval > PAM_OVERALL_MAXVAL) {
+            pm_errormsg("invalid maxval.");
+
+            return 0;
+        }
+    } else {
+        *maxval = 255;
+    }
+
+    if (rgb_spec) {
+        *num_attribs = 3;
+        set_tupletype("RGB_ALPHA", tupletype);
+    }
+
+    if (grayscale_spec) {
+        *num_attribs = 1;
+        set_tupletype("GRAYSCALE_ALPHA", tupletype);
+    }
+
+    if (*num_attribs < 1 || *num_attribs > MAX_NUM_ATTRIBS) {
+        pm_errormsg("invalid number of generic attributes per vertex.");
+
+        return 0;
+    }
+
+    if (tupletype_spec) {
+        if(rgb_spec || grayscale_spec) {
+            pm_errormsg("you may not provide -tupletype together with "
+                        "-rgb or -grayscale.");
+
+            return 0;
+        }
+
+        if (!set_tupletype(tupletype_tmp, tupletype)) {
+            pm_errormsg("warning: invalid tuple type; using empty string.");
+
+            set_tupletype(NULL, tupletype);
+        }
+    }
+
+    free(option_def);
+
+    return 1;
+}
+
+
+
+int
+main(int argc, const char ** argv) {
+
+    framebuffer_info fbi;
+    boundary_info bi;
+    Input input;
+    bool no_more_commands;
+
+    pm_proginit(&argc, (const char**)argv);
+
+    set_tupletype(NULL, fbi.outpam.tuple_type);
+
+    if (!parse_command_line(&argc,
+                            argv,
+                            &fbi.width,
+                            &fbi.height,
+                            &fbi.maxval,
+                            &fbi.num_attribs,
+                            fbi.outpam.tuple_type)) {
+        return 1;
+    }
+
+    if (!init_framebuffer(&fbi)) {
+        pm_errormsg("out of memory.");
+
+        return 3;
+    }
+
+    init_boundary_buffer(&bi, fbi.height);
+
+    input_init(&input);
+
+    for (no_more_commands = false; !no_more_commands; )
+        input_process_next_command(&input, &bi, &fbi, &no_more_commands);
+
+    input_term(&input);
+    free_boundary_buffer(&bi);
+    free_framebuffer(&fbi);
+
+    return 0;
+}
+
+
diff --git a/generator/pamtris/triangle.c b/generator/pamtris/triangle.c
new file mode 100644
index 00000000..5143f9ee
--- /dev/null
+++ b/generator/pamtris/triangle.c
@@ -0,0 +1,327 @@
+/*=============================================================================
+                                  triangle.c
+===============================================================================
+   Triangle functions
+=============================================================================*/
+#include <stdlib.h>
+#include <string.h>
+
+#include "netpbm/mallocvar.h"
+
+#include "utils.h"
+#include "varying.h"
+#include "boundaries.h"
+#include "framebuffer.h"
+
+#include "triangle.h"
+
+static void
+draw_partial_triangle(
+    const varying *       const left_attribs_input,
+    const varying *       const rght_attribs_input,
+    bool                  const upper_part,
+    const boundary_info * const bi,
+    framebuffer_info *    const fbi) {
+
+    uint8_t const z = fbi->num_attribs;
+    uint8_t const w = z + 1;
+    uint8_t const n = w + 1;
+
+    varying * left_attribs;
+    varying * rght_attribs;
+
+    varying * attribs;
+
+    int32_t first_row_index;
+    int32_t last_row_index;
+
+    MALLOCARRAY_NOFAIL(left_attribs, n);
+    MALLOCARRAY_NOFAIL(rght_attribs, n);
+    MALLOCARRAY_NOFAIL(attribs, n);
+
+    memcpy(left_attribs, left_attribs_input, n * sizeof(varying));
+    memcpy(rght_attribs, rght_attribs_input, n * sizeof(varying));
+
+    if (upper_part) {
+        first_row_index = 0;
+        last_row_index = bi->num_upper_rows - 1;
+    } else {
+        first_row_index = bi->num_upper_rows;
+        last_row_index = bi->num_upper_rows + bi->num_lower_rows - 1;
+    }
+
+    {
+        int32_t const row_delta = last_row_index - first_row_index;
+
+        int32_t row;
+
+        int32_t left_boundary;
+        int32_t rght_boundary;
+
+        for (row = first_row_index; row <= last_row_index; row++) {
+            get_triangle_boundaries(row, &left_boundary, &rght_boundary, bi);
+            {
+                int32_t const column_delta = rght_boundary - left_boundary;
+                int32_t start_column;
+                int32_t span_length;
+
+                start_column = left_boundary;  /* initial value */
+                span_length = column_delta;    /* initial value */
+
+                prepare_for_interpolation(left_attribs, rght_attribs,
+                                          attribs, column_delta,
+                                          n);
+
+                if (left_boundary < 0) {
+                    start_column = 0;
+
+                    span_length += left_boundary;
+
+                    multi_step_up(attribs, -left_boundary, n);
+                }
+
+                if (rght_boundary >= fbi->width) {
+                    span_length -= rght_boundary - fbi->width;
+                } else {
+                    span_length++;
+                }
+
+                draw_span(
+                    (bi->start_scanline + row) * fbi->width + start_column,
+                    span_length, attribs, fbi);
+
+                if (row_delta > 0) {
+                    step_up(left_attribs, n);
+                    step_up(rght_attribs, n);
+                }
+            }
+        }
+    }
+    free(attribs);
+    free(rght_attribs);
+    free(left_attribs);
+}
+
+
+
+static void
+draw_degenerate_horizontal(Xy                 const xy,
+                           varying *          const top2mid,
+                           varying *          const top2bot,
+                           varying *          const mid2bot,
+                           framebuffer_info * const fbi) {
+
+    uint8_t const n = fbi->num_attribs + 2;
+
+    {
+        int16_t const y = xy._[0][1];
+
+        int16_t x[3];
+        int16_t x_start[3];
+        varying * attribs[3];
+        int32_t span_length[3];
+        unsigned int i;
+
+        x[0] = xy._[0][0];
+        x[1] = xy._[1][0];
+        x[2] = xy._[2][0];
+
+        x_start[0] = x[0];
+        x_start[1] = x[0];
+        x_start[2] = x[1];
+
+        attribs[0] = top2bot;
+        attribs[1] = top2mid;
+        attribs[2] = mid2bot;
+
+        span_length[0] = x[2] - x[0];
+        span_length[1] = x[1] - x[0];
+        span_length[2] = x[2] - x[1];
+
+        for (i = 0; i < 3; i++) {
+            if (x_start[i] >= fbi->width || x_start[i] + span_length[i] < 0) {
+                continue;
+            }
+
+            if (x_start[i] < 0) {
+                multi_step_up(attribs[i], -x_start[i], n);
+
+                span_length[i] += x_start[i];
+
+                x_start[i] = 0;
+            }
+
+            if (x_start[i] + span_length[i] >= fbi->width) {
+                span_length[i] -= x_start[i] + span_length[i] - fbi->width;
+            } else {
+                span_length[i]++;
+            }
+
+            draw_span(y * fbi->width + x_start[i], span_length[i],
+                      attribs[i], fbi);
+        }
+    }
+}
+
+
+
+void
+draw_triangle(Xy                 const xy_input,
+              Attribs            const attribs_input,
+              boundary_info *    const bi,
+              framebuffer_info * const fbi) {
+
+    uint8_t const z = fbi->num_attribs;
+    uint8_t const w = z + 1;
+    uint8_t const n = w + 1;
+
+    Xy xy;
+    varying * attribs[3];
+    unsigned int i;
+    uint8_t index_array[3];
+    int32_t y_array[3];
+    int32_t x_array[3];
+
+    MALLOCARRAY_NOFAIL(attribs[0], n);
+    MALLOCARRAY_NOFAIL(attribs[1], n);
+    MALLOCARRAY_NOFAIL(attribs[2], n);
+
+    xy = xy_input;
+
+    for (i = 0; i < 3; i++) {
+        int32_to_varying_array(attribs_input._[i], attribs[i], n);
+	attribs[i][z] = compute_varying_z(attribs_input._[i][z]);
+	attribs[i][w] = inverse_varying(attribs[i][w]);
+        multiply_varying_array_by_varying(attribs[i], attribs[i][w], z);
+    }
+
+    /* Argument preparations for sort3: */
+
+    index_array[0] = 0; index_array[1] = 1; index_array[2] = 2;
+    y_array[0] = xy._[0][1]; y_array[1] = xy._[1][1]; y_array[2] = xy._[2][1];
+    x_array[0] = xy._[0][0]; x_array[1] = xy._[1][0]; x_array[2] = xy._[2][0];
+
+    sort3(index_array, y_array, x_array);
+
+    {
+        uint8_t const top = index_array[0];
+        uint8_t const mid = index_array[1];
+        uint8_t const bot = index_array[2];
+
+        bool mid_is_to_the_left;
+
+        Xy xy_sorted;
+
+        xy_sorted._[0][0] = xy._[top][0];
+        xy_sorted._[0][1] = xy._[top][1];
+        xy_sorted._[1][0] = xy._[mid][0];
+        xy_sorted._[1][1] = xy._[mid][1];
+        xy_sorted._[2][0] = xy._[bot][0];
+        xy_sorted._[2][1] = xy._[bot][1];
+
+        mid_is_to_the_left =
+            gen_triangle_boundaries(xy_sorted, bi, fbi->width, fbi->height);
+
+        if (bi->start_scanline == -1) {
+            /* Triangle is completely out of the bounds of the frame buffer. */
+        } else {
+            bool const no_upper_part =
+                (xy_sorted._[1][1] == xy_sorted._[0][1]);
+
+            bool const horizontal =
+                (xy._[0][1] == xy._[1][1] && xy._[1][1] == xy._[2][1]);
+                /* Tells whether we are dealing with a degenerate
+                 * horizontal triangle */
+
+            uint8_t const t = horizontal ^ 1;
+
+            int32_t top2mid_delta = xy._[mid][t] - xy._[top][t];
+            int32_t top2bot_delta = xy._[bot][t] - xy._[top][t];
+            int32_t mid2bot_delta = xy._[bot][t] - xy._[mid][t];
+
+            varying * top2mid;
+            varying * top2bot;
+            varying * mid2bot;
+
+            varying * upper_left_attribs;
+            varying * lower_left_attribs;
+            varying * upper_rght_attribs;
+            varying * lower_rght_attribs;
+
+            MALLOCARRAY_NOFAIL(top2mid, n);
+            MALLOCARRAY_NOFAIL(top2bot, n);
+            MALLOCARRAY_NOFAIL(mid2bot, n);
+
+            prepare_for_interpolation(attribs[top], attribs[mid], top2mid, top2mid_delta, n);
+            prepare_for_interpolation(attribs[top], attribs[bot], top2bot, top2bot_delta, n);
+            prepare_for_interpolation(attribs[mid], attribs[bot], mid2bot, mid2bot_delta, n);
+
+            if (mid_is_to_the_left) {
+                upper_left_attribs = top2mid;
+                lower_left_attribs = mid2bot;
+                upper_rght_attribs = top2bot;
+                lower_rght_attribs = upper_rght_attribs;
+            } else {
+                upper_rght_attribs = top2mid;
+                lower_rght_attribs = mid2bot;
+                upper_left_attribs = top2bot;
+                lower_left_attribs = upper_left_attribs;
+            }
+
+            if (!(horizontal || no_upper_part)) {
+                int32_t delta;
+
+                if (bi->num_upper_rows > 0) {
+                    if (bi->start_scanline > xy._[top][1]) {
+                        delta = bi->start_scanline - xy._[top][1];
+
+                        multi_step_up(upper_left_attribs, delta, n);
+                        multi_step_up(upper_rght_attribs, delta, n);
+                    }
+
+                    draw_partial_triangle(
+                        upper_left_attribs,
+                        upper_rght_attribs,
+                        true,
+                        bi,
+                        fbi
+                        );
+
+                    delta = xy._[mid][1] - bi->start_scanline;
+                } else {
+                    delta = top2mid_delta;
+                }
+
+                multi_step_up(upper_left_attribs, delta, n);
+                multi_step_up(upper_rght_attribs, delta, n);
+            }
+
+            if (horizontal) {
+                draw_degenerate_horizontal(
+                    xy_sorted,
+                    top2mid, top2bot, mid2bot,
+                    fbi
+                    );
+            } else {
+                if (bi->start_scanline > xy._[mid][1]) {
+                    int32_t const delta = bi->start_scanline - xy._[mid][1];
+
+                    multi_step_up(lower_left_attribs, delta, n);
+                    multi_step_up(lower_rght_attribs, delta, n);
+                }
+
+                draw_partial_triangle(
+                    lower_left_attribs,
+                    lower_rght_attribs,
+                    false,
+                    bi,
+                    fbi
+                    );
+            }
+            free(mid2bot); free(top2bot); free(top2mid);
+        }
+    }
+    free(attribs[2]); free(attribs[1]); free(attribs[0]);
+}
+
+
diff --git a/generator/pamtris/triangle.h b/generator/pamtris/triangle.h
new file mode 100644
index 00000000..e043e95c
--- /dev/null
+++ b/generator/pamtris/triangle.h
@@ -0,0 +1,25 @@
+#ifndef TRIANGLE_H_INCLUDED
+#define TRIANGLE_H_INCLUDED
+
+#include <stdint.h>
+
+#include "limits_pamtris.h"
+
+struct boundary_info;
+struct framebuffer_info;
+
+typedef struct {
+    int32_t _[3][2];
+} Xy;
+
+typedef struct {
+    int32_t _[3][MAX_NUM_ATTRIBS + 2];
+} Attribs;
+
+void
+draw_triangle(Xy                        const xy,
+              Attribs                   const attribs,
+              struct boundary_info *    const bdi,
+              struct framebuffer_info * const fbi);
+
+#endif
diff --git a/generator/pamtris/utils.c b/generator/pamtris/utils.c
new file mode 100644
index 00000000..a6b6e7d4
--- /dev/null
+++ b/generator/pamtris/utils.c
@@ -0,0 +1,266 @@
+/*=============================================================================
+                              utils.c
+===============================================================================
+   Utility functions
+=============================================================================*/
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <math.h>
+
+#include "limits_pamtris.h"
+#include "varying.h"
+
+#include "utils.h"
+
+
+
+void
+prepare_for_interpolation(const varying * const begin,
+                          const varying * const end,
+                          varying *       const out,
+                          int32_t               num_steps,
+                          uint8_t         const elements) {
+
+    double inverse_num_steps;
+    unsigned int i;
+
+    if (num_steps < 1) {
+        num_steps = 1;
+    }
+
+    inverse_num_steps = 1.0 / num_steps;
+
+    for (i = 0; i < elements; i++) {
+        out[i].v = begin[i].v;
+        out[i].s = (end[i].v - begin[i].v) * inverse_num_steps;
+    }
+}
+
+
+
+varying
+compute_varying_z(int32_t const input_z) {
+
+    varying retval;
+
+    retval.v = 1.0 / (1 + input_z - MIN_COORD);
+    retval.s = 0.0;
+
+    return retval;
+}
+
+
+
+void
+multiply_varying_array_by_varying(varying * const vars,
+                                  varying   const multiplier,
+                                  uint8_t   const elements) {
+
+    unsigned int i;
+
+    for (i = 0; i < elements; i++) {
+        vars[i].v *= multiplier.v;
+	vars[i].s  = 0.0;
+    }
+}
+
+
+void
+divide_varying_array_by_varying(varying * const vars,
+                                varying   const divisor,
+                                uint8_t   const elements) {
+
+    double const inverse_divisor = 1.0 / divisor.v;
+
+    unsigned int i;
+
+    for (i = 0; i < elements; i++) {
+        vars[i].v *= inverse_divisor;
+	vars[i].s  = 0.0;
+    }
+}
+
+
+
+varying
+inverse_varying(varying const var) {
+
+    varying retval;
+
+    retval.v = 1.0 / var.v;
+    retval.s = 0.0;
+
+    return retval;
+}
+
+
+
+varying
+multiply_varyings(varying const a,
+                  varying const b) {
+
+    varying retval;
+
+    retval.v = a.v * b.v;
+    retval.s = 0.0;
+
+    return retval;
+}
+
+
+
+void
+step_up(varying * const vars,
+        uint8_t   const elements) {
+
+    unsigned int i;
+
+    for (i = 0; i < elements; i++) {
+        vars[i].v += vars[i].s;
+    }
+}
+
+
+
+void
+multi_step_up(varying * const vars,
+              int32_t   const times,
+              uint8_t   const elements) {
+
+    unsigned int i;
+
+    for (i = 0; i < elements; i++) {
+        vars[i].v += times * vars[i].s;
+    }
+}
+
+
+
+void
+int32_to_varying_array(const int32_t * const in,
+                       varying *       const out,
+                       uint8_t         const elements) {
+
+    unsigned int i;
+
+    for (i = 0; i < elements; i++) {
+        out[i].v = in[i];
+        out[i].s = 0.0;
+    }
+}
+
+
+
+/* static int64_t
+abs64(int64_t x)
+{
+
+    int64_t const nm = ~geq_mask64(x, 0);
+
+    return (-x & nm) | (x & ~nm);
+} */
+
+
+
+int32_t
+round_varying(varying const var) {
+
+    return round(var.v);
+}
+
+
+
+int64_t
+geq_mask64(int64_t a, int64_t b) {
+
+    uint64_t const diff = a - b;
+
+    return -((~diff) >> 63);
+}
+
+
+
+static void
+swap(uint8_t * const a,
+     uint8_t * const b) {
+/*----------------------------------------------------------------------------
+  Swap the contents pointed to by a and b.
+-----------------------------------------------------------------------------*/
+    uint8_t const temp = *a;
+
+    *a = *b;
+    *b = temp;
+}
+
+
+
+void
+sort3(uint8_t *       const index_array,
+      const int32_t * const y_array,
+      const int32_t * const x_array) {
+/*----------------------------------------------------------------------------
+  Sort an index array of 3 elements. This function is used to sort vertices
+  with regard to relative row from top to bottom, but instead of sorting
+  an array of vertices with all their coordinates, we simply sort their
+  indices. Each element in the array pointed to by "index_array" should
+  contain one of the numbers 0, 1 or 2, and each one of them should be
+  different. "y_array" should point to an array containing the corresponding
+  Y coordinates (row) of each vertex and "x_array" should point to an array
+  containing the corresponding X coordinates (column) of each vertex.
+
+  If the Y coordinates are all equal, the indices are sorted with regard to
+  relative X coordinate from left to right. If only the top two vertex have
+  the same Y coordinate, the array is sorted normally with regard to relative
+  Y coordinate, but the first two indices are then sorted with regard to
+  relative X coordinate. Finally, If only the bottom two vertex have the same
+  Y coordinate, the array is sorted normally with regard to relative Y
+  coordinate, but the last two indices are then sorted with regard to relative
+  X coordinate.
+-----------------------------------------------------------------------------*/
+    uint8_t * const ia = index_array;
+
+    const int32_t * ya;
+    const int32_t * xa;
+
+    ya = y_array;  /* initial value */
+    xa = x_array;  /* initial value */
+
+    if (ya[0] == ya[1] && ya[1] == ya[2]) {
+        /* In case the vertices represent a degenerate horizontal triangle, we
+           sort according to relative X coordinate, as opposed to Y.
+        */
+        ya = xa;
+    }
+
+    if (ya[ia[2]] < ya[ia[1]]) {
+        swap(ia, ia + 2);
+        if (ya[ia[2]] < ya[ia[1]]) {
+            swap(ia + 1, ia + 2);
+            if (ya[ia[1]] < ya[ia[0]]) {
+                swap(ia, ia + 1);
+            }
+        }
+    } else if (ya[ia[1]] < ya[ia[0]]) {
+        swap(ia, ia + 1);
+        if (ya[ia[2]] < ya[ia[1]]) {
+            swap(ia + 1, ia + 2);
+        }
+    }
+
+    if (ya == xa) {
+        return;
+    }
+
+    if (ya[ia[0]] == ya[ia[1]]) {
+        if (xa[ia[1]] < xa[ia[0]]) {
+            swap(ia, ia + 1);
+        }
+    } else if (ya[ia[1]] == ya[ia[2]]) {
+        if (xa[ia[2]] < xa[ia[1]]) {
+            swap(ia + 1, ia + 2);
+        }
+    }
+}
+
+
diff --git a/generator/pamtris/utils.h b/generator/pamtris/utils.h
new file mode 100644
index 00000000..bd9dcdbe
--- /dev/null
+++ b/generator/pamtris/utils.h
@@ -0,0 +1,58 @@
+#ifndef UTIL_H_INCLUDED
+#define UTIL_H_INCLUDED
+
+#include "varying.h"
+
+void
+prepare_for_interpolation(const varying * const begin,
+                          const varying * const end,
+                          varying *       const out,
+                          int32_t               num_steps,
+                          uint8_t         const elements);
+
+varying
+compute_varying_z(int32_t const input_z);
+
+void
+multiply_varying_array_by_varying(varying * const vars,
+                                  varying   const divisor,
+                                  uint8_t   const elements);
+
+void
+divide_varying_array_by_varying(varying * const vars,
+                                varying   const divisor,
+                                uint8_t   const elements);
+
+varying
+inverse_varying(varying const var);
+
+varying
+multiply_varyings(varying const a,
+                  varying const b);
+
+void
+step_up(varying * const vars,
+       uint8_t    const elements);
+
+void
+multi_step_up(varying * const vars,
+             int32_t    const times,
+             uint8_t    const elements);
+
+void
+int32_to_varying_array(const int32_t * const in,
+                       varying *       const out,
+                       uint8_t         const elements);
+
+int32_t
+round_varying(varying const var);
+
+int64_t
+geq_mask64(int64_t a, int64_t b);
+
+void
+sort3(uint8_t *       const index_array,
+      const int32_t * const y_array,
+      const int32_t * const x_array);
+
+#endif
diff --git a/generator/pamtris/varying.h b/generator/pamtris/varying.h
new file mode 100644
index 00000000..6605f02d
--- /dev/null
+++ b/generator/pamtris/varying.h
@@ -0,0 +1,12 @@
+#ifndef VARYING_H_INCLUDED
+#define VARYING_H_INCLUDED
+
+#include <stdint.h>
+
+
+typedef struct {
+    double v; /* Value */
+    double s; /* Step */
+} varying;
+
+#endif
diff --git a/generator/pbmmake.c b/generator/pbmmake.c
index 600440f0..0352d007 100644
--- a/generator/pbmmake.c
+++ b/generator/pbmmake.c
@@ -61,7 +61,7 @@ parseCommandLine(int argc, char ** argv,
 
     if (blackOpt + whiteOpt + grayOpt > 1)
         pm_error("You can specify only one of -black, -white, and -gray");
-    
+
     if (blackOpt)
         cmdlineP->color = COLOR_BLACK;
     else if (whiteOpt)
@@ -83,8 +83,8 @@ parseCommandLine(int argc, char ** argv,
 
 
 
-static void 
-writeGrayRaster(unsigned int const cols, 
+static void
+writeGrayRaster(unsigned int const cols,
                 unsigned int const rows,
                 FILE *       const ofP) {
 
@@ -96,7 +96,7 @@ writeGrayRaster(unsigned int const cols,
     bitrow0 = pbm_allocrow_packed(cols);
     bitrow1 = pbm_allocrow_packed(cols);
 
-    for (i=0; i <= lastCol; ++i) { 
+    for (i=0; i <= lastCol; ++i) {
         bitrow0[i] = (PBM_WHITE*0xaa) | (PBM_BLACK*0x55);
         bitrow1[i] = (PBM_WHITE*0x55) | (PBM_BLACK*0xaa);
         /* 0xaa = 10101010 ; 0x55 = 01010101 */
@@ -119,7 +119,7 @@ writeGrayRaster(unsigned int const cols,
     pbm_freerow(bitrow1);
 }
 
-    
+
 
 static void
 writeSingleColorRaster(unsigned int const cols,
@@ -134,7 +134,7 @@ writeSingleColorRaster(unsigned int const cols,
 
     bitrow0 = pbm_allocrow_packed(cols);
 
-    for (i = 0; i <= lastCol; ++i) 
+    for (i = 0; i <= lastCol; ++i)
         bitrow0[i] = color*0xff;
 
     if (color != 0)
@@ -161,7 +161,7 @@ main(int argc, char * argv[]) {
     parseCommandLine(argc, argv, &cmdline);
 
     pbm_writepbminit(stdout, cmdline.width, cmdline.height, 0);
-    
+
     if (cmdline.color == COLOR_GRAY)
         writeGrayRaster(cmdline.width, cmdline.height, stdout);
     else {
@@ -169,6 +169,6 @@ main(int argc, char * argv[]) {
         writeSingleColorRaster(cmdline.width, cmdline.height, color, stdout);
     }
     pm_close(stdout);
-    
+
     return 0;
 }
diff --git a/generator/pbmtext.c b/generator/pbmtext.c
index 9f4366d4..e6f27865 100644
--- a/generator/pbmtext.c
+++ b/generator/pbmtext.c
@@ -10,41 +10,93 @@
 ** implied warranty.
 */
 
+#define _DEFAULT_SOURCE 1  /* New name for SVID & BSD source defines */
 #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 <math.h>
 #include <limits.h>
 #include <assert.h>
+#include <setjmp.h>
+#include <locale.h>
+#include <wchar.h>
 
 #include "pm_c_util.h"
 #include "mallocvar.h"
+#include "nstring.h"
 #include "shhopt.h"
+#include "pm.h"
 #include "pbm.h"
 #include "pbmfont.h"
 
-struct cmdlineInfo {
+#define  MAXLINECHARS 5000
+
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    const char * text;    /* text from command line or NULL if none */
+    const PM_WCHAR * text; /* text from command line or NULL if none */
     const char * font;    /* -font option value or NULL if none */
     const char * builtin; /* -builtin option value or NULL if none */
-    unsigned int dump;   
-        /* undocumented dump option for installing a new built-in font */
-    float space;   /* -space option value or default */
-    unsigned int width;     /* -width option value or zero */
-    int lspace;    /* lspace option value or default */
-    unsigned int nomargins;     /* -nomargins */
-    unsigned int verbose;
+    float space;          /* -space option value or default */
+    int lspace;           /* -lspace option value or default */
+    unsigned int width;   /* -width option value or zero */
+    unsigned int wchar;   /* -wchar option specified  */
+    unsigned int nomargins;  /* -nomargins option specified  */
+    unsigned int dryrun;     /* -dry-run option specified */
+    unsigned int textdump;   /* -text-dump option specified */
+    unsigned int verbose;    /* -verbose option specified */
+        /* undocumented option */
+    unsigned int dumpsheet; /* font data sheet in PBM format for -font */
 };
 
 
 
+static const PM_WCHAR *
+textFmCmdLine(int argc, const char ** argv) {
+
+    char * text;
+    PM_WCHAR * wtext;
+    unsigned int i;
+    unsigned int totaltextsize;
+
+    MALLOCARRAY(text, MAXLINECHARS+1);
+
+    if (!text)
+        pm_error("Unable to allocate memory for a buffer of up to %u "
+                 "characters of text", MAXLINECHARS);
+
+    text[0] = '\0';
+
+    for (i = 1, totaltextsize = 1; i < argc; ++i) {
+        if (i > 1) {
+            strcat(text, " ");
+        }
+        totaltextsize += strlen(argv[i]) + 1;
+        if (totaltextsize > MAXLINECHARS)
+            pm_error("input text too long");
+        strcat(text, argv[i]);
+    }
+    MALLOCARRAY(wtext, totaltextsize * sizeof(PM_WCHAR));
+
+    if (!wtext)
+        pm_error("Unable to allocate memory for a buffer of up to %u "
+                 "wide characters of text", totaltextsize);
+
+    for (i = 0; i < totaltextsize; ++i)
+        wtext[i] = (PM_WCHAR) text[i];
+
+    free(text);
+
+    return wtext;
+}
+
+
 
 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.
@@ -59,110 +111,151 @@ parseCommandLine(int argc, const char ** argv,
     MALLOCARRAY_NOFAIL(option_def, 100);
 
     option_def_index = 0;   /* incremented by OPTENTRY */
-    OPTENT3(0, "font",      OPT_STRING, &cmdlineP->font, NULL,        0);
-    OPTENT3(0, "builtin",   OPT_STRING, &cmdlineP->builtin, NULL,     0);
-    OPTENT3(0, "dump",      OPT_FLAG,   NULL, &cmdlineP->dump,        0);
-    OPTENT3(0, "space",     OPT_FLOAT,  &cmdlineP->space, NULL,       0);
-    OPTENT3(0, "width",     OPT_UINT,   &cmdlineP->width, NULL,       0);
-    OPTENT3(0, "lspace",    OPT_INT,    &cmdlineP->lspace, NULL,      0);
-    OPTENT3(0, "nomargins", OPT_FLAG,   NULL, &cmdlineP->nomargins,   0);
-    OPTENT3(0, "verbose",   OPT_FLAG,   NULL, &cmdlineP->verbose,     0);
+    OPTENT3(0, "font",       OPT_STRING, &cmdlineP->font,    NULL,   0);
+    OPTENT3(0, "builtin",    OPT_STRING, &cmdlineP->builtin, NULL,   0);
+    OPTENT3(0, "space",      OPT_FLOAT,  &cmdlineP->space,   NULL,   0);
+    OPTENT3(0, "lspace",     OPT_INT,    &cmdlineP->lspace,  NULL,   0);
+    OPTENT3(0, "width",      OPT_UINT,   &cmdlineP->width,   NULL,   0);
+    OPTENT3(0, "nomargins",  OPT_FLAG,   NULL, &cmdlineP->nomargins, 0);
+    OPTENT3(0, "wchar",      OPT_FLAG,   NULL, &cmdlineP->wchar,     0);
+    OPTENT3(0, "verbose",    OPT_FLAG,   NULL, &cmdlineP->verbose,   0);
+    OPTENT3(0, "dry-run",    OPT_FLAG,   NULL, &cmdlineP->dryrun,    0);
+    OPTENT3(0, "text-dump",  OPT_FLAG,   NULL, &cmdlineP->textdump,  0);
+    OPTENT3(0, "dump-sheet", OPT_FLAG,   NULL, &cmdlineP->dumpsheet, 0);
 
     /* Set the defaults */
-    cmdlineP->font = NULL;
+    cmdlineP->font    = NULL;
     cmdlineP->builtin = NULL;
-    cmdlineP->space = 0.0;
-    cmdlineP->width = 0;
-    cmdlineP->lspace = 0;
+    cmdlineP->space   = 0.0;
+    cmdlineP->width   = 0;
+    cmdlineP->lspace  = 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. */
+    /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (cmdlineP->width > 0 && cmdlineP->nomargins) {
+        pm_message("-nomargins has no effect when -width is specified");
+        cmdlineP->nomargins = FALSE;
+    } else if (cmdlineP->width > INT_MAX-10)
+        pm_error("-width value too large");
+
+    if (cmdlineP->space > pbm_maxfontwidth())
+        pm_error("-space value too large");
+    else if (cmdlineP->space < -pbm_maxfontwidth())
+        pm_error("negative -space value too large");
+
+    if (cmdlineP->lspace > pbm_maxfontheight())
+        pm_error("-lspace value too large");
+    else if (cmdlineP->lspace < -pbm_maxfontheight())
+        pm_error("negative -lspace value too large");
+
+    if (cmdlineP->textdump) {
+        if (cmdlineP->dryrun)
+            pm_error("You cannot specify both -dry-run and -text-dump");
+        else if (cmdlineP->dumpsheet)
+            pm_error("You cannot specify both -dump-sheet and -text-dump");
+    }
+
+    if (cmdlineP->dryrun && cmdlineP->dumpsheet)
+        pm_error("You cannot specify both -dry-run and -dump-sheet");
 
     if (argc-1 == 0)
         cmdlineP->text = NULL;
-    else {
-        char *text;
-        int i;
-        int totaltextsize;
-
-        totaltextsize = 1;  /* initial value */
-        
-        text = malloc(totaltextsize);  /* initial allocation */
-        text[0] = '\0';
-        
-        for (i = 1; i < argc; ++i) {
-            if (i > 1) {
-                totaltextsize += 1;
-                text = realloc(text, totaltextsize);
-                if (text == NULL)
-                    pm_error("out of memory allocating space for input text");
-                strcat(text, " ");
-            } 
-            totaltextsize += strlen(argv[i]);
-            text = realloc(text, totaltextsize);
-            if (text == NULL)
-                pm_error("out of memory allocating space for input text");
-            strcat(text, argv[i]);
-        }
-        cmdlineP->text = text;
+    else {  /* Text to render is part of command line */
+        if (cmdlineP->wchar)
+            pm_error("-wchar is not valid when text is from command line");
+
+        cmdlineP->text = textFmCmdLine(argc, argv);
+
+
     }
+    free(option_def);
 }
 
 
 
 static void
-reportFont(struct font * const fontP) {
-
-    unsigned int n;
-    unsigned int c;
+reportFont(const struct font2 * const fontP) {
 
     pm_message("FONT:");
-    pm_message("  character dimensions: %uw x %uh",
+    pm_message("  Name: %s", fontP->name);
+    pm_message("  Encoding: %s", fontP->charset_string);
+    pm_message("  Origin: %s", pbmFontOrigin[fontP->load_fn]);
+    pm_message("  Character dimensions: %uw x %uh",
                fontP->maxwidth, fontP->maxheight);
     pm_message("  Additional vert white space: %d pixels", fontP->y);
+    pm_message("  # characters loaded: %u", fontP->chars);
+}
+
+
+
+
+
+
+static struct font2 *
+font2FromFile(const char * const fileName,
+              PM_WCHAR     const maxmaxglyph) {
+
+    struct font2 * font2P;
+
+    jmp_buf jmpbuf;
+    int rc;
 
-    for (c = 0, n = 0; c < ARRAY_SIZE(fontP->glyph); ++c)
-        if (fontP->glyph[c])
-            ++n;
+    rc = setjmp(jmpbuf);
 
-    pm_message("  # characters: %u", n);
+    if (rc == 0) {
+        /* This is the normal program flow */
+        pm_setjmpbuf(&jmpbuf);
+
+        font2P = pbm_loadfont2(fileName, maxmaxglyph);
+
+        pm_setjmpbuf(NULL);
+    } else {
+        /* This is the second pass, after pbm_loadbdffont2 does a longjmp
+           because it fails.
+        */
+        pm_setjmpbuf(NULL);
+
+        pm_error("Failed to load font from file '%s'", fileName);
+    }
+
+    return font2P;
 }
 
 
 
 static void
-computeFont(struct cmdlineInfo const cmdline,
-            struct font **     const fontPP) {
+computeFont(struct CmdlineInfo const cmdline,
+            struct font2 **    const fontPP) {
 
-    struct font * fontP;
+    struct font2 * font2P;
 
     if (cmdline.font)
-        fontP = pbm_loadfont(cmdline.font);
-    else {
-        if (cmdline.builtin)
-            fontP = pbm_defaultfont(cmdline.builtin);
-        else
-            fontP = pbm_defaultfont("bdf");
-    }
+        font2P = font2FromFile(cmdline.font,
+                               cmdline.wchar ? PM_FONT2_MAXGLYPH :
+                                               PM_FONT_MAXGLYPH);
+    else if (cmdline.builtin)
+        font2P = pbm_defaultfont2(cmdline.builtin);
+    else
+        font2P = pbm_defaultfont2(cmdline.wchar ? "bdf" : "bdf");
 
     if (cmdline.verbose)
-        reportFont(fontP);
+        reportFont(font2P);
 
-    if (cmdline.dump) {
-        pbm_dumpfont(fontP);
-        exit(0);
-    }
-    *fontPP = fontP;
+    *fontPP = font2P;
 }
 
 
 
-struct text {
-    char **      textArray;  /* malloc'ed */
+struct Text {
+    PM_WCHAR **  textArray;  /* malloc'ed */
+        /* This is strictly characters that are in user's font - no control
+           characters, no undefined code points.
+        */
     unsigned int allocatedLineCount;
     unsigned int lineCount;
 };
@@ -170,41 +263,49 @@ struct text {
 
 
 static void
-allocTextArray(struct text * const textP,
+allocTextArray(struct Text * const textP,
                unsigned int  const maxLineCount,
                unsigned int  const maxColumnCount) {
 
     unsigned int line;
 
-    textP->allocatedLineCount = maxLineCount;
-
+    textP->allocatedLineCount = maxColumnCount > 0 ? maxLineCount : 0;
     MALLOCARRAY_NOFAIL(textP->textArray, maxLineCount);
-    
-    for (line = 0; line < maxLineCount; ++line)
-        MALLOCARRAY_NOFAIL(textP->textArray[line], maxColumnCount+1);
 
+    for (line = 0; line < maxLineCount; ++line) {
+        if (maxColumnCount > 0)
+            MALLOCARRAY_NOFAIL(textP->textArray[line], maxColumnCount+1);
+    else
+        textP->textArray[line] = NULL;
+    }
     textP->lineCount = 0;
 }
 
 
 
 static void
-freeTextArray(struct text const text) {
+freeTextArray(struct Text const text) {
 
     unsigned int line;
 
     for (line = 0; line < text.allocatedLineCount; ++line)
-        free((char **)text.textArray[line]);
+        free((PM_WCHAR **)text.textArray[line]);
 
     free(text.textArray);
 }
 
 
 
+enum FixMode {SILENT, /* convert silently */
+              WARN,   /* output message to stderr */
+              QUIT    /* abort */ };
+
+
 static void
-fixControlChars(const char *  const input,
-                struct font * const fontP,
-                const char ** const outputP) {
+fixControlChars(const PM_WCHAR  * const input,
+                struct font2    * const fontP,
+                const PM_WCHAR ** const outputP,
+                enum FixMode      const fixMode) {
 /*----------------------------------------------------------------------------
    Return a translation of input[] that can be rendered as glyphs in
    the font 'fontP'.  Return it as newly malloced *outputP.
@@ -214,8 +315,9 @@ fixControlChars(const char *  const input,
    Remove any trailing newline.  (But leave intermediate ones as line
    delimiters).
 
-   Turn anything that isn't a code point in the font to a single space
-   (which isn't guaranteed to be in the font either, of course).
+   Depending on value of fixMode, turn anything that isn't a code point
+   in the font to a single space (which isn't guaranteed to be in the
+   font either, of course).
 -----------------------------------------------------------------------------*/
     /* We don't know in advance how big the output will be because of the
        tab expansions.  So we make sure before processing each input
@@ -228,10 +330,10 @@ fixControlChars(const char *  const input,
     unsigned int const tabSize = 8;
 
     unsigned int inCursor, outCursor;
-    char * output;      /* Output buffer.  Malloced */
+    PM_WCHAR * output;      /* Output buffer.  Malloced */
     size_t outputSize;  /* Currently allocated size of 'output' */
 
-    outputSize = strlen(input) + 1 + tabSize;
+    outputSize = wcslen(input) + 1 + tabSize;
         /* Leave room for one worst case tab expansion and NUL terminator */
     MALLOCARRAY(output, outputSize);
 
@@ -239,7 +341,8 @@ fixControlChars(const char *  const input,
         pm_error("Couldn't allocate %u bytes for a line of text.",
                  (unsigned)outputSize);
 
-    for (inCursor = 0, outCursor = 0; input[inCursor] != '\0'; ++inCursor) {
+    for (inCursor = 0, outCursor = 0; input[inCursor] != L'\0'; ++inCursor) {
+        PM_WCHAR const currentChar = input[inCursor];
         if (outCursor + 1 + tabSize > outputSize) {
             outputSize = outCursor + 1 + 4 * tabSize;
             REALLOCARRAY(output, outputSize);
@@ -247,24 +350,43 @@ fixControlChars(const char *  const input,
                 pm_error("Couldn't allocate %u bytes for a line of text.",
                          (unsigned)outputSize);
         }
-        if (input[inCursor] == '\n' && input[inCursor+1] == '\0') {
+        if (currentChar == L'\n' && input[inCursor+1] == L'\0') {
             /* This is a terminating newline.  We don't do those. */
-        } else if (input[inCursor] == '\t') { 
+        } else if (currentChar == L'\t') {
             /* Expand this tab into the right number of spaces. */
             unsigned int const nextTabStop =
                 (outCursor + tabSize) / tabSize * tabSize;
 
+            if (fontP->glyph[L' '] == NULL)
+                pm_error("space character not defined in font");
+
             while (outCursor < nextTabStop)
-                output[outCursor++] = ' ';
-        } else if (!fontP->glyph[(unsigned char)input[inCursor]]) {
+                output[outCursor++] = L' ';
+        } else if (currentChar > fontP->maxglyph ||
+                   !fontP->glyph[currentChar]) {
+            if (currentChar > PM_FONT2_MAXGLYPH)
+                pm_message("code point %X is beyond what this program "
+                           "can handle.  Max=%X",
+                           (unsigned int)currentChar, PM_FONT2_MAXGLYPH);
+
             /* Turn this unknown char into a single space. */
-            output[outCursor++] = ' ';
+            if (fontP->glyph[L' '] == NULL)
+                pm_error("space character not defined in font");
+            else if (fixMode == QUIT)
+                pm_error("code point %X not defined in font",
+                         (unsigned int) currentChar );
+            else {
+                if (fixMode == WARN)
+                    pm_message("converting code point %X to space",
+                               (unsigned int) currentChar );
+                output[outCursor++] = ' ';
+            }
         } else
             output[outCursor++] = input[inCursor];
 
         assert(outCursor <= outputSize);
     }
-    output[outCursor++] = '\0';
+    output[outCursor++] = L'\0';
 
     assert(outCursor <= outputSize);
 
@@ -274,478 +396,702 @@ fixControlChars(const char *  const input,
 
 
 static void
-fill_rect(bit** const bits, 
-          int   const row0, 
-          int   const col0, 
-          int   const height, 
-          int   const width, 
-          bit   const color) {
-
-    int row;
-
-    for (row = row0; row < row0 + height; ++row) {
-        int col;
-        for (col = col0; col < col0 + width; ++col)
-            bits[row][col] = color;
+clearBackground(bit ** const bits,
+                int    const cols,
+                int    const rows) {
+
+    unsigned int row;
+
+    for (row = 0; row < rows; ++row) {
+        unsigned int colChar;
+        for (colChar = 0; colChar < pbm_packed_bytes(cols); ++colChar)
+            bits[row][colChar] = 0x00;
     }
 }
 
 
 
 static void
-get_line_dimensions(const char line[], const struct font * const font_p, 
-                    const float intercharacter_space,
-                    double * const bwidP, int * const backup_space_needed_p) {
+getEdges(double               const currentPosition,
+         PM_WCHAR             const currentChar,
+         const struct glyph * const glyphP,
+         int                  const currLeftEdge,
+         double               const currRightEdge,
+         int                * const newLeftEdgeP,
+         double             * const newRightEdgeP) {
+
+    int leftEdge;
+    double rightEdge;
+
+    if (glyphP == NULL)
+        pm_error("Unrenderable char: %04X", (unsigned int) currentChar);
+    else {
+        leftEdge  =  (int) MIN(currentPosition + glyphP->x, currLeftEdge);
+        rightEdge =  MAX(currentPosition + glyphP->x + glyphP->width,
+                         currRightEdge);
+    }
+    *newLeftEdgeP  = leftEdge;
+    *newRightEdgeP = rightEdge;
+}
+
+
+
+static void
+advancePosition(double               const currentPosition,
+                PM_WCHAR             const currentChar,
+                const struct glyph * const glyphP,
+                float                const space,
+                double               const accumulatedSpace,
+                double             * const newPositionP,
+                double             * const newAccumulatedSpaceP) {
+/*----------------------------------------------------------------------------
+  Advance position according to value for glyph.
+  Add extra intercharacter space if -space option was used.
+
+  The advance value must be zero or positive.
+----------------------------------------------------------------------------*/
+
+    /* Start position of next character */
+    /* Must not move left from current position */
+    int const fullPixels = (int) (accumulatedSpace + space);
+        /* round toward 0 */
+    int const advance    = (int) glyphP->xadd + fullPixels;
+
+    if (advance < 0) {
+        if (space < 0)
+            pm_error("Negative -space value too large");
+        else
+            pm_error("Abnormal horizontal advance value %d "
+                     "for code point 0x%lx.",
+                     glyphP->xadd, (unsigned long int) currentChar);
+    }
+    else if (currentPosition + advance > INT_MAX)
+        pm_error("Image is too wide");
+    else {
+        *newPositionP = currentPosition + advance;
+        *newAccumulatedSpaceP = accumulatedSpace + space
+            - (double) fullPixels;
+    }
+}
+
+
+
+static void
+getLineDimensions(PM_WCHAR             const line[],
+                  const struct font2 * const fontP,
+                  float                const intercharacterSpace,
+                  double *             const rightEdgeP,
+                  int    *             const leftEdgeP) {
 /*----------------------------------------------------------------------------
-   Determine the width in pixels of the line of text line[] in the font
-   *font_p, and return it as *bwidP.  Also determine how much of this
-   width goes to the left of the nominal starting point of the line because
-   the first character in the line has a "backup" distance.  Return that
-   as *backup_space_needed_p.
+   Determine the left edge and right edge in pixels of the line of text
+   line[] in the font *fontP, and return them as *leftEdgeP and *rightEdgeP.
+   *leftEdgeP will be negative if the leftmost character in the line has a
+   "backup" distance.
+
+   Note that the right (left) edge may not belong to the last (first)
+   character in the text line.  This happens when the font is slanted
+   (xadd is smaller than width) and/or intercharacter space is negative.
+   This is illustrated by the following:
+
+     pbmtext -nomargin "ART." | pnmshear -30 -noantialias
+
+   Also note that there may be no black pixels on what is reported as an edge.
+   This often happens with fixed-width font in which the white areas on the
+   sides are not trimmed.
 -----------------------------------------------------------------------------*/
-    int cursor;  /* cursor into the line of text */
-    double accumulatedIcs;
-        /* accumulated intercharacter space so far in the line we are 
-           stepping through.  Because the intercharacter space might not be
-           an integer, we accumulate it here and realize full pixels whenever
-           we have more than one pixel.  Note that this can be negative
-           (which means were crowding, rather than spreading, text).
+    unsigned int cursor;  /* cursor into the line of text */
+    double currentPosition;
+        /* sum of xadd values and intercharacter space so far in line.  this
+           is never negative.
         */
-    double bwid;
-    bool no_chars_yet; 
-        /* We haven't seen any renderable characters yet in the line. */
-    struct glyph * lastGlyphP;
-        /* Glyph of last character processed so far.  Undefined if
-           'no_chars_yet'.
+    double accumulatedIcs;
+        /* accumulated intercharacter space so far in the line we are stepping
+           through.  Because the intercharacter space might not be an integer,
+           we accumulate it here and realize full pixels whenever we have more
+           than one pixel.  Note that this can be negative (which means were
+           crowding, rather than spreading, text).
         */
+    int leftEdge;
+    double rightEdge;
 
-    no_chars_yet = TRUE;   /* initial value */
-    accumulatedIcs = 0.0;  /* initial value */
-    bwid = 0.0;  /* initial value */
-    
-    for (cursor = 0; line[cursor] != '\0'; cursor++) {
-        struct glyph * const glyphP = 
-            font_p->glyph[(unsigned char)line[cursor]];
-
-        if (glyphP) {
-            if (no_chars_yet) {
-                no_chars_yet = FALSE;
-                if (glyphP->x < 0) 
-                    *backup_space_needed_p = -glyphP->x;
-                else {
-                    *backup_space_needed_p = 0;
-                    bwid += glyphP->x;
-                }
-            } else {
-                /* handle extra intercharacter space (-space option) */
-                accumulatedIcs += intercharacter_space;
-                if (accumulatedIcs >= INT_MAX)
-                    pm_error("Image width too large.");
-                if (accumulatedIcs <= INT_MIN)
-                    pm_error("Absurdly large negative -space value.");
-                {
-                    int const fullPixels = (int) accumulatedIcs;
-                    bwid           += fullPixels;
-                    accumulatedIcs -= fullPixels;
-                }
-            }
-            lastGlyphP = glyphP;
-            bwid += glyphP->xadd;
-        }
+    currentPosition = 0;  /* initial value */
+    accumulatedIcs  = 0.0;  /* initial value */
+
+    leftEdge  = INT_MAX;  /* initial value */
+    rightEdge = INT_MIN;  /* initial value */
+
+    for (cursor = 0; line[cursor] != L'\0'; ++cursor) {
+        PM_WCHAR          const currentChar = line[cursor];
+        unsigned long int const glyphIndex  = (unsigned long int) currentChar;
+        struct glyph *    const glyphP      = fontP->glyph[glyphIndex];
+
+        getEdges(currentPosition, currentChar, glyphP, leftEdge, rightEdge,
+                 &leftEdge, &rightEdge);
+
+        advancePosition(currentPosition, currentChar, glyphP,
+                        intercharacterSpace, accumulatedIcs,
+                        &currentPosition, &accumulatedIcs);
     }
-    if (no_chars_yet)
-        /* Line has no renderable characters */
-        *backup_space_needed_p = 0;
-    else {
-        /* Line has at least one renderable character.
-           Recalculate width of last character in line so it ends
-           right at the right edge of the glyph (no extra space to
-           anticipate another character).
-        */
-        bwid -= lastGlyphP->xadd;
-        bwid += lastGlyphP->width + lastGlyphP->x;
+
+    if (line[0] == L'\0') {     /* Empty line */
+        leftEdge  = 0;
+        rightEdge = 0.0;
     }
-    if (bwid > INT_MAX)
-        pm_error("Image width too large.");
-    else
-        *bwidP = bwid; 
+
+    *leftEdgeP  = leftEdge;
+    *rightEdgeP = rightEdge;
 }
 
 
 
 static void
-insert_character(const struct glyph * const glyph, 
-                 int                  const toprow, 
-                 int                  const leftcol,
-                 bit **               const bits) {
+getCharsWithinWidth(PM_WCHAR             const line[],
+                    const struct font2 * const fontP,
+                    float                const intercharacter_space,
+                    unsigned int         const targetWidth,
+                    unsigned int       * const charCountP,
+                    int                * const leftEdgeP) {
+/*----------------------------------------------------------------------------
+   Determine how many characters of text line[] fit into an image of target
+   width targetWidth.
+
+   *leftEdgeP will be negative if the leftmost character in the line has a
+   "backup" distance and zero if it does not.
+-----------------------------------------------------------------------------*/
+    if (line[0] == L'\0') {
+        /* Empty line */
+        *leftEdgeP = 0;
+        *charCountP = 0;
+    } else {
+        unsigned int cursor;  /* cursor into the line of text */
+        double currentPosition;
+        double accumulatedIcs;
+        int leftEdge;
+        double rightEdge;
+        unsigned int currentWidth;
+
+        currentPosition = 0;    /* initial value */
+        accumulatedIcs  = 0.0;  /* initial value */
+
+        leftEdge     = INT_MAX;  /* initial value */
+        rightEdge    = INT_MIN;  /* initial value */
+
+        for (cursor = 0, currentWidth = 0;
+             currentWidth <= targetWidth && line[cursor] != L'\0';
+             ++cursor) {
+            PM_WCHAR const currentChar = line[cursor];
+            unsigned long int const glyphIndex =
+              (unsigned long int) currentChar;
+            struct glyph * const glyphP = fontP->glyph[glyphIndex];
+
+            getEdges(currentPosition, currentChar, glyphP, leftEdge, rightEdge,
+                     &leftEdge, &rightEdge);
+
+            advancePosition(currentPosition, currentChar, glyphP,
+                            intercharacter_space, accumulatedIcs,
+                            &currentPosition, &accumulatedIcs);
+
+            currentWidth = rightEdge - ((leftEdge > 0 ) ? 0 : leftEdge);
+        }
+
+        if (currentWidth > targetWidth) {
+            if (cursor == 1)
+                pm_error("-width value too small "
+                         "to accomodate single character");
+            else
+                *charCountP = cursor - 1;
+        } else
+            *charCountP = cursor;
+
+        *leftEdgeP  = leftEdge;
+    }
+}
+
+
+
+static void
+insertCharacter(const struct glyph * const glyphP,
+                int                  const toprow,
+                int                  const leftcol,
+                unsigned int         const cols,
+                unsigned int         const rows,
+                bit **               const bits) {
 /*----------------------------------------------------------------------------
    Insert one character (whose glyph is 'glyph') into the image bits[].
    Its top left corner shall be row 'toprow', column 'leftcol'.
 -----------------------------------------------------------------------------*/
+    if (glyphP->width == 0 && glyphP->height == 0) {
+        /* No bitmap data.  Some BDF files code space this way */
+    } else {
+        unsigned int glyph_y;  /* Y position within the glyph */
+
+        if (leftcol + glyphP->x < 0 ||
+            leftcol + glyphP->x + glyphP->width > cols ||
+            toprow < 0 ||
+            toprow + glyphP->height >rows )
+            pm_error("internal error.  Rendering out of bounds");
 
-    int glyph_y, glyph_x;  /* position within the glyph */
+        for (glyph_y = 0; glyph_y < glyphP->height; ++glyph_y) {
+            unsigned int glyph_x;  /* position within the glyph */
 
-    for (glyph_y = 0; glyph_y < glyph->height; glyph_y++) {
-        for (glyph_x = 0; glyph_x < glyph->width; glyph_x++) {
-            if (glyph->bmap[glyph_y * glyph->width + glyph_x])
-                bits[toprow+glyph_y][leftcol+glyph->x+glyph_x] = 
-                    PBM_BLACK;
+            for (glyph_x = 0; glyph_x < glyphP->width; ++glyph_x) {
+                if (glyphP->bmap[glyph_y * glyphP->width + glyph_x]) {
+                    unsigned int const col = leftcol + glyphP->x + glyph_x;
+                    bits[toprow+glyph_y][col/8] |= PBM_BLACK << (7-col%8);
+                }
+            }
         }
     }
-}    
+}
 
 
 
 static void
-insert_characters(bit **        const bits, 
-                  struct text   const lp,
-                  struct font * const fontP, 
-                  int           const topmargin, 
-                  int           const leftmargin,
-                  float         const intercharacter_space,
-                  int           const lspace) {
+insertCharacters(bit **         const bits,
+                 struct Text    const lp,
+                 struct font2 * const fontP,
+                 int            const topmargin,
+                 int            const leftmargin,
+                 float          const intercharacter_space,
+                 unsigned int   const cols,
+                 unsigned int   const rows,
+                 int            const lspace,
+                 bool           const fixedAdvance) {
 /*----------------------------------------------------------------------------
    Render the text 'lp' into the image 'bits' using font *fontP and
    putting 'intercharacter_space' pixels between characters and
    'lspace' pixels between the lines.
 -----------------------------------------------------------------------------*/
-    int line;  /* Line number in input text */
+    unsigned int line;  /* Line number in input text */
 
     for (line = 0; line < lp.lineCount; ++line) {
-        int row;  /* row in image of top of current typeline */
-        int leftcol;  /* Column in image of left edge of current glyph */
-        int cursor;  /* cursor into a line of input text */
-        float accumulated_ics;
+        unsigned int row;  /* row in image of top of current typeline */
+        double leftcol;  /* Column in image of left edge of current glyph */
+        unsigned int cursor;  /* cursor into a line of input text */
+        double accumulatedIcs;
             /* accumulated intercharacter space so far in the line we
                are building.  Because the intercharacter space might
                not be an integer, we accumulate it here and realize
-               full pixels whenever we have more than one pixel. 
+               full pixels whenever we have more than one pixel.
             */
 
         row = topmargin + line * (fontP->maxheight + lspace);
         leftcol = leftmargin;
-        accumulated_ics = 0.0;  /* initial value */
-    
+        accumulatedIcs = 0.0;  /* initial value */
+
         for (cursor = 0; lp.textArray[line][cursor] != '\0'; ++cursor) {
-            unsigned int const glyphIndex = 
-                (unsigned char)lp.textArray[line][cursor];
-            struct glyph* glyph;   /* the glyph for this character */
-
-            glyph = fontP->glyph[glyphIndex];
-            if (glyph != NULL) {
-                const int toprow = row + fontP->maxheight + fontP->y 
-                    - glyph->height - glyph->y;
-                    /* row number in image of top row in glyph */
-                
-                insert_character(glyph, toprow, leftcol, bits);
-
-                leftcol += glyph->xadd;
-                {
-                    /* handle extra intercharacter space (-space option) */
-                    int full_pixels;  /* integer part of accumulated_ics */
-                    accumulated_ics += intercharacter_space;
-                    full_pixels = (int) accumulated_ics;
-                    if (full_pixels > 0) {
-                        leftcol += full_pixels;
-                        accumulated_ics -= full_pixels;
-                    }
-                }
-            }
+            PM_WCHAR const currentChar = lp.textArray[line][cursor];
+            unsigned long int const glyphIndex =
+                (unsigned long int)currentChar;
+            struct glyph * const glyphP = fontP->glyph[glyphIndex];
+            int const toprow =
+                row + fontP->maxheight + fontP->y - glyphP->height - glyphP->y;
+                /* row number in image of top row in glyph */
+
+            assert(glyphP != NULL);
+
+            insertCharacter(glyphP, toprow, leftcol, cols, rows, bits);
+
+        if (fixedAdvance)
+            leftcol += fontP->maxwidth;
+        else
+            advancePosition(leftcol, currentChar, glyphP,
+                            intercharacter_space, accumulatedIcs,
+                            &leftcol, &accumulatedIcs);
         }
     }
 }
 
 
-struct outputTextCursor {
-    struct text text;
-        /* The output text.  The lineCount field of this represents
-           the number of lines we have completed.  The line after that
-           is the one we are currently filling.
-        */
-    unsigned int maxWidth;
-        /* A line of output can't be wider than this many pixels */
-    float intercharacterSpace;
-        /* The amount of extra space, in characters, that should be added
-           between every two characters (Pbmtext -space option)
-        */
-    unsigned int columnNo;
-        /* The column Number (starting at 0) in the current line that we are
-           filling where the next character goes.
-        */
-    bool noCharsYet;
-        /* We haven't put any renderable characters yet in the
-           output line. 
-        */
-    unsigned int widthSoFar;
-        /* The accumulated width, in pixels, of all the characters now
-           in the current output line 
-        */
-    float accumulatedIcs;
-        /* accumulated intercharacter space so far in the line we
-           are stepping through.  Because the intercharacter space
-           might not be an integer, we accumulate it here and
-           realize full pixels whenever we have more than one
-           pixel.  Note that this is negative if we're crowding, rather
-           than spreading, characters.
-        */
-};
 
+static void
+flowText(struct Text    const inputText,
+         int            const targetWidth,
+         struct font2 * const fontP,
+         float          const intercharacterSpace,
+         struct Text  * const outputTextP,
+         unsigned int * const maxleftbP) {
 
+    unsigned int outputLineNum;
+    unsigned int incursor;   /* cursor into the line we are reading */
+    unsigned int const maxLineCount = 50; /* max output lines */
+    int leftEdge;
+    int leftExtreme = 0;
+    unsigned int charCount;
 
-static void
-initializeFlowedOutputLine(struct outputTextCursor * const cursorP) {
+    allocTextArray(outputTextP, maxLineCount, 0);
 
-    cursorP->columnNo = 0;
-    cursorP->noCharsYet = TRUE;
-    cursorP->widthSoFar = 0.0;
-    cursorP->accumulatedIcs = 0.0;
-}
+    for (incursor = 0, outputLineNum = 0;
+         inputText.textArray[0][incursor] != L'\0'; ) {
 
+        unsigned int outcursor;
 
+        getCharsWithinWidth(&inputText.textArray[0][incursor], fontP,
+                            intercharacterSpace, targetWidth,
+                            &charCount, &leftEdge);
 
-static void
-initializeFlowedOutput(struct outputTextCursor * const cursorP,
-                       unsigned int              const maxLines,
-                       unsigned int              const maxWidth,
-                       float                     const intercharacterSpace) {
-    
-    allocTextArray(&cursorP->text, maxLines, maxWidth);
-    cursorP->maxWidth = maxWidth;
-    cursorP->intercharacterSpace = intercharacterSpace;
-    initializeFlowedOutputLine(cursorP);
-}
+        MALLOCARRAY(outputTextP->textArray[outputLineNum], charCount+1);
 
+        if (!outputTextP->textArray[outputLineNum])
+            pm_error("Unable to allocate memory for the text of line %u, "
+                     "%u characters long", outputLineNum, charCount);
 
+        ++outputTextP->allocatedLineCount;
 
-static void
-finishOutputLine(struct outputTextCursor * const cursorP) {
+        for (outcursor = 0; outcursor < charCount; ++outcursor, ++incursor)
+            outputTextP->textArray[outputLineNum][outcursor] =
+                inputText.textArray[0][incursor];
+
+        outputTextP->textArray[outputLineNum][charCount] = L'\0';
+        ++outputLineNum;
+        if (outputLineNum >= maxLineCount)
+            pm_error("-width too small.  too many output lines");
 
-    if (cursorP->text.lineCount < cursorP->text.allocatedLineCount) {
-        char * const currentLine = 
-            cursorP->text.textArray[cursorP->text.lineCount];
-        currentLine[cursorP->columnNo++] = '\0';
-        ++cursorP->text.lineCount;
+        leftExtreme = MIN(leftEdge, leftExtreme);
     }
+    outputTextP->lineCount = outputLineNum;
+    *maxleftbP = (unsigned int) -leftExtreme;
 }
 
 
 
 static void
-placeCharacterInOutput(char                      const lastch,
-                       struct font *             const fontP, 
-                       struct outputTextCursor * const cursorP) {
-/*----------------------------------------------------------------------------
-   Place a character of text in the text array at the position indicated
-   by *cursorP, keeping track of what space this character will occupy
-   when this text array is ultimately rendered using font *fontP.
+truncateText(struct Text    const inputText,
+             unsigned int   const targetWidth,
+             struct font2 * const fontP,
+             float          const intercharacterSpace,
+             unsigned int * const maxleftbP) {
 
-   Note that while we compute how much space the character will take when
-   rendered, we don't render it.
------------------------------------------------------------------------------*/
-    if (cursorP->text.lineCount < cursorP->text.allocatedLineCount) {
-        unsigned int const glyphIndex = (unsigned char)lastch;
-        if (fontP->glyph[glyphIndex]) {
-            if (cursorP->noCharsYet) {
-                cursorP->noCharsYet = FALSE;
-                if (fontP->glyph[glyphIndex]->x > 0) 
-                    cursorP->widthSoFar += fontP->glyph[glyphIndex]->x;
-            } else {
-                /* handle extra intercharacter space (-space option) */
-                cursorP->accumulatedIcs += cursorP->intercharacterSpace;
-                {
-                    int const fullPixels = (int)cursorP->accumulatedIcs;
-                    cursorP->widthSoFar     += fullPixels;
-                    cursorP->accumulatedIcs -= fullPixels;
-                }
-            }
-            cursorP->widthSoFar += fontP->glyph[glyphIndex]->xadd;
-        }
-        if (cursorP->widthSoFar < cursorP->maxWidth) {
-            char * const currentLine = 
-                cursorP->text.textArray[cursorP->text.lineCount];
-            currentLine[cursorP->columnNo++] = lastch;
-        } else {
-            /* Line is full; finish it off, start the next one, and
-               place the character there.
-            */
-            /* TODO: We really should back up to the previous white space
-               character and move the rest of the line to the next line
-            */
-            finishOutputLine(cursorP);
-            initializeFlowedOutputLine(cursorP);
-            placeCharacterInOutput(lastch, fontP, cursorP);
+    unsigned int lineNum;  /* Line number on which we are currently working */
+    int leftEdge;
+    int leftExtreme = 0;
+
+    for (lineNum = 0; lineNum < inputText.lineCount; ++lineNum) {
+        PM_WCHAR * const currentLine = inputText.textArray[lineNum];
+
+        unsigned int charCount;
+
+        getCharsWithinWidth(currentLine, fontP,
+                            intercharacterSpace, targetWidth,
+                            &charCount, &leftEdge);
+
+        if (currentLine[charCount] != L'\0') {
+            pm_message("truncating line %u from %u to %u characters",
+                       lineNum, (unsigned) wcslen(currentLine), charCount);
+            currentLine[charCount] = L'\0';
         }
+
+        leftExtreme = MIN(leftEdge, leftExtreme);
     }
+    *maxleftbP = (unsigned int) - leftExtreme;
 }
 
 
 
 static void
-flowText(struct text    const inputText,
-         int            const width, 
-         struct font *  const fontP, 
-         float          const intercharacterSpace,
-         struct text *  const outputTextP) {
-    
-    unsigned int const maxLineCount = 50;
-
-    unsigned int inputLine;  
-        /* Input line number on which we are currently working */
-    struct outputTextCursor outputCursor;
-
-    for (inputLine = 0; inputLine < inputText.lineCount; ++inputLine) {
-        unsigned int incursor;   /* cursor into the line we are reading */
-
-        initializeFlowedOutput(&outputCursor, maxLineCount,
-                               width, intercharacterSpace);
-        
-        for (incursor = 0; 
-             inputText.textArray[inputLine][incursor] != '\0'; 
-             ++incursor)
-            placeCharacterInOutput(inputText.textArray[inputLine][incursor],
-                                   fontP, &outputCursor);
-        finishOutputLine(&outputCursor);
+fgetWideString(PM_WCHAR *    const widestring,
+               unsigned int  const size,
+               FILE *        const ifP,
+               bool *        const eofP,
+               const char ** const errorP) {
+
+    wchar_t * rc;
+
+    assert(widestring);
+    assert(size > 0);
+
+    rc = fgetws(widestring, size, ifP);
+
+    if (rc == NULL) {
+        if (feof(ifP)) {
+            *eofP   = true;
+            *errorP = NULL;
+        } else if (ferror(ifP) && errno == EILSEQ)
+            pm_asprintf(errorP,
+                        "fgetws(): conversion error: sequence is "
+                        "invalid for locale '%s'",
+                        setlocale(LC_CTYPE, NULL));
+        else
+            pm_asprintf(errorP,
+                        "fgetws() of max %u bytes failed",
+                        size);
+    } else {
+        *eofP   = false;
+        *errorP = NULL;
     }
-    *outputTextP = outputCursor.text;
 }
 
 
 
 static void
-truncateText(struct text   const inputText, 
-             unsigned int  const width, 
-             struct font * const fontP, 
-             float         const intercharacterSpace,
-             struct text * const outputTextP) {
-
-    struct text truncatedText;
-    int line;  /* Line number on which we are currently working */
-
-    allocTextArray(&truncatedText, inputText.lineCount, width);
-
-    for (line = 0; line < inputText.lineCount; ++line){
-        int cursor;  /* cursor into the line of text */
-        unsigned char lastch;  /* line[cursor] */
-        int widthSoFar;
-            /* How long the line we've built, in pixels, is so far */
-        float accumulatedIcs;
-        /* accumulated intercharacter space so far in the line we are 
-           stepping through.  Because the intercharacter space might not be
-           an integer, we accumulate it here and realize full pixels whenever
-           we have more than one pixel.  Note that this is negative if we're
-           crowding, not spreading, characters.
-        */
+fgetNarrowString(PM_WCHAR *    const widestring,
+                 unsigned int  const size,
+                 FILE *        const ifP,
+                 bool *        const eofP,
+                 const char ** const errorP) {
 
-        int noCharsYet; 
-        /* logical: we haven't seen any renderable characters yet in 
-           the line.
-        */
-        noCharsYet = TRUE;   /* initial value */
-        widthSoFar = 0;  /* initial value */
-        accumulatedIcs = 0.0;  /* initial value */
+    char * bufNarrow;
+    char * rc;
 
-        truncatedText.textArray[line][0] = '\0';  /* Start with empty line */
-
-        for (cursor = 0; 
-             inputText.textArray[line][cursor] != '\0' && widthSoFar < width; 
-             cursor++) {
-            lastch = inputText.textArray[line][cursor];
-            if (fontP->glyph[(unsigned char)lastch]) {
-                if (noCharsYet) {
-                    noCharsYet = FALSE;
-                    if (fontP->glyph[lastch]->x > 0) 
-                        widthSoFar += fontP->glyph[lastch]->x;
-                } else {
-                    /* handle extra intercharacter space (-space option) */
-                    accumulatedIcs += intercharacterSpace;
-                    {
-                        int const fullPixels = (int) intercharacterSpace;
-                        widthSoFar     += fullPixels;
-                        accumulatedIcs -= fullPixels;
-                    }
-                }
-                widthSoFar += fontP->glyph[lastch]->xadd;
-            }
-            if (widthSoFar < width) {
-                truncatedText.textArray[line][cursor] = 
-                    inputText.textArray[line][cursor];
-                truncatedText.textArray[line][cursor+1] = '\0';
-            }
-        }
+    assert(widestring);
+    assert(size > 0);
+
+    MALLOCARRAY_NOFAIL(bufNarrow, MAXLINECHARS+1);
+
+    rc = fgets(bufNarrow, size, ifP);
+
+    if (rc == NULL) {
+        if (feof(ifP)) {
+            *eofP   = true;
+            *errorP = NULL;
+        } else
+            pm_asprintf(errorP, "Error reading file");
+    } else {
+        size_t cnt;
+
+        for (cnt = 0; cnt < size && bufNarrow[cnt] != '\0'; ++cnt)
+            widestring[cnt] = (PM_WCHAR)(unsigned char) bufNarrow[cnt];
+
+        widestring[cnt] = L'\0';
+
+        *eofP   = false;
+        *errorP = NULL;
     }
-    truncatedText.lineCount = inputText.lineCount;
-    *outputTextP = truncatedText;
+    free(bufNarrow);
 }
 
 
 
 static void
-getText(const char          cmdline_text[], 
-        struct font * const fontP,
-        struct text * const input_textP) {
-
-    struct text input_text;
-
-    if (cmdline_text) {
-        MALLOCARRAY_NOFAIL(input_text.textArray, 1);
-        input_text.allocatedLineCount = 1;
-        input_text.lineCount = 1;
-        fixControlChars(cmdline_text, fontP,
-                        (const char**)&input_text.textArray[0]);
+fgetNarrowWideString(PM_WCHAR *    const widestring,
+                     unsigned int  const size,
+                     FILE *        const ifP,
+                     bool *        const eofP,
+                     const char ** const errorP) {
+/*----------------------------------------------------------------------------
+  Return the next line from file *ifP, as *widestring.
+
+  Lines are delimited by newline characters and EOF.
+
+  'size' is the size in characters of the buffer at *widestring.  If the line
+  to which the file is positioned is longer than that minus 1, we consider it
+  to be only that long and consider the next character of the actual line to
+  be the first character of the next line.  We leave the file positioned
+  to that character.
+
+  Return *eofP == true iff we encounter end of file (and therefore don't read
+  a line).
+
+  If we can't read the file (or sense EOF), return as *errorP a text
+  explanation of why; otherwise, return *errorP = NULL.
+
+  The line we return is null-terminated.  But it also includes any embedded
+  null characters that are within the line in the file.  It is not strictly
+  possible for Caller to tell whether a null character in *widestring comes
+  from the file or is the one we put there, so Caller should just ignore any
+  null character and anything after it.  It is also not possible for Caller to
+  tell if we trunctaed the actual line because of 'size' if there is a null
+  character in the line.  This means there just isn't any way to get
+  reasonable behavior from this function if the input file contains null
+  characters (but at least the damage is limited to presenting arbitrary text
+  as the contents of the file - the program won't crash).
+
+  Null characters never appear within normal text (including wide-character
+  text).  If there is one in the input file, it is probably because the input
+  is corrupted.
+
+  The line we return may or may not end in a newline character.  It ends in a
+  newline character unless it doesn't fit in 'size' characters or it is the
+  last line in the file and doesn't end in newline.
+-----------------------------------------------------------------------------*/
+    /* The limitations described above with respect to null characters in
+       *ifP are derived from the same limitations in POSIX 'fgets' and
+       'fgetws'.  To avoid them, we would have to read *ifP one character
+       at a time with 'fgetc' and 'fgetwc'.
+    */
+
+    int const wideCode = fwide(ifP, 0);
+        /* Width orientation for *ifP: positive means wide, negative means
+           byte, zero means undecided.
+        */
+
+    assert(widestring);
+    assert(size > 0);
+
+    if (wideCode > 0)
+        /* *ifP is wide-oriented */
+        fgetWideString(widestring, size, ifP, eofP, errorP);
+    else
+        fgetNarrowString(widestring, size, ifP, eofP, errorP);
+}
+
+
+
+
+static void
+getText(PM_WCHAR       const cmdlineText[],
+        struct font2 * const fontP,
+        struct Text  * const inputTextP,
+        enum FixMode   const fixMode) {
+/*----------------------------------------------------------------------------
+   Get as *inputTextP the text to format, given that the text on the
+   command line (one word per command line argument, separated by spaces),
+   is 'cmdlineText'.
+
+   If 'cmdlineText' is null, that means to get the text from Standard Input.
+   Otherwise, 'cmdlineText' is that text.
+
+   But we return text as only renderable characters - characters in *fontP -
+   with control characters interpreted or otherwise fixed, according to
+   'fixMode'.
+
+   If *inputTextP indicates Standard Input and Standard Input contains null
+   characters, we will truncate lines or consider a single line to be multiple
+   lines.
+-----------------------------------------------------------------------------*/
+    struct Text inputText;
+
+    if (cmdlineText) {
+        MALLOCARRAY_NOFAIL(inputText.textArray, 1);
+        inputText.allocatedLineCount = 1;
+        inputText.lineCount = 1;
+        fixControlChars(cmdlineText, fontP,
+                        (const PM_WCHAR**)&inputText.textArray[0], fixMode);
+        free((void *) cmdlineText);
     } else {
         /* Read text from stdin. */
 
-        unsigned int maxlines;  
-            /* Maximum number of lines for which we presently have space
-               in the text array 
+        unsigned int maxlines;
+            /* Maximum number of lines for which we presently have space in
+               the text array
             */
-        char buf[5000];
-        char ** text_array;
+        PM_WCHAR *   buf;
+        PM_WCHAR **  textArray;
         unsigned int lineCount;
+        bool         eof;
+
+        MALLOCARRAY(buf, MAXLINECHARS+1);
+
+        if (!buf)
+            pm_error("Unable to allocate memory for up to %u characters of "
+                     "text", MAXLINECHARS);
 
         maxlines = 50;  /* initial value */
-        MALLOCARRAY_NOFAIL(text_array, maxlines);
-        
-        lineCount = 0;  /* initial value */
-        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.", (unsigned)sizeof(buf)-1);
-            if (lineCount >= maxlines) {
-                maxlines *= 2;
-                REALLOCARRAY(text_array, maxlines);
-                if (text_array == NULL)
-                    pm_error("out of memory");
+        MALLOCARRAY(textArray, maxlines);
+
+        if (!textArray)
+            pm_error("Unable to allocate memory for a buffer for up to %u "
+                     "lines of text", maxlines);
+
+        for (lineCount = 0, eof = false; !eof; ) {
+            const char * error;
+            fgetNarrowWideString(buf, MAXLINECHARS, stdin, &eof, &error);
+            if (error)
+                pm_error("Unable to read line %u from file.  %s",
+                         lineCount, error);
+            else {
+                if (!eof) {
+                    if (wcslen(buf) + 1 >= MAXLINECHARS)
+                        pm_error(
+                            "Line %u (starting at zero) of input text "
+                            "is longer than %u characters."
+                            "Cannot process",
+                            lineCount, (unsigned int) MAXLINECHARS-1);
+                    if (lineCount >= maxlines) {
+                        maxlines *= 2;
+                        REALLOCARRAY(textArray, maxlines);
+                        if (textArray == NULL)
+                            pm_error("out of memory");
+                    }
+                    fixControlChars(buf, fontP,
+                                    (const PM_WCHAR **)&textArray[lineCount],
+                                    fixMode);
+                    if (textArray[lineCount] == NULL)
+                        pm_error("out of memory");
+                    ++lineCount;
+                }
             }
-            fixControlChars(buf, fontP, (const char **)&text_array[lineCount]);
-            if (text_array[lineCount] == NULL)
-                pm_error("out of memory");
-            ++lineCount;
         }
-        input_text.textArray = text_array;
-        input_text.lineCount = lineCount;
-        input_text.allocatedLineCount = lineCount;
+        inputText.textArray = textArray;
+        inputText.lineCount = lineCount;
+        inputText.allocatedLineCount = lineCount;
+        free(buf);
+    }
+    *inputTextP = inputText;
+}
+
+
+
+static void
+computeMargins(struct CmdlineInfo const cmdline,
+               struct Text        const inputText,
+               struct font2 *     const fontP,
+               unsigned int *     const vmarginP,
+               unsigned int *     const hmarginP) {
+
+    if (cmdline.nomargins) {
+        *vmarginP = 0;
+        *hmarginP = 0;
+    } else {
+        if (inputText.lineCount == 1) {
+            *vmarginP = fontP->maxheight / 2;
+            *hmarginP = fontP->maxwidth;
+        } else {
+            *vmarginP = fontP->maxheight;
+            *hmarginP = 2 * fontP->maxwidth;
+        }
     }
-    *input_textP = input_text;
 }
 
 
 
 static void
-computeImageHeight(struct text         const formattedText, 
-                   const struct font * const fontP,
-                   int                 const interlineSpace,
-                   unsigned int        const vmargin,
-                   unsigned int *      const rowsP) {
+formatText(struct CmdlineInfo const cmdline,
+           struct Text        const inputText,
+           struct font2 *     const fontP,
+           unsigned int       const hmargin,
+           struct Text *      const formattedTextP,
+           unsigned int *     const maxleftb0P) {
+/*----------------------------------------------------------------------------
+  Flow or truncate lines to meet user's width request.
+-----------------------------------------------------------------------------*/
+    if (cmdline.width > 0) {
+        unsigned int const fontMargin = fontP->x < 0 ? -fontP->x : 0;
+
+        if (cmdline.width > INT_MAX -10)
+            pm_error("-width value too large: %u", cmdline.width);
+        else if (cmdline.width < 2 * hmargin)
+            pm_error("-width value too small: %u", cmdline.width);
+        else if (inputText.lineCount == 1) {
+            flowText(inputText, cmdline.width - fontMargin,
+                     fontP, cmdline.space, formattedTextP, maxleftb0P);
+            freeTextArray(inputText);
+        } else {
+            truncateText(inputText, cmdline.width - fontMargin,
+                         fontP, cmdline.space, maxleftb0P);
+            *formattedTextP = inputText;
+        }
+    } else
+        *formattedTextP = inputText;
+}
+
+
+
+static void
+computeImageHeight(struct Text          const formattedText,
+                   const struct font2 * const fontP,
+                   int                  const interlineSpace,
+                   unsigned int         const vmargin,
+                   unsigned int       * const rowsP) {
 
     if (interlineSpace < 0 && fontP->maxheight < -interlineSpace)
         pm_error("-lspace value (%d) negative and exceeds font height.",
-                 interlineSpace);     
+                 interlineSpace);
     else {
-        double const rowsD = 2 * (double) vmargin + 
-            (double) formattedText.lineCount * fontP->maxheight + 
+        double const rowsD = 2 * (double) vmargin +
+            (double) formattedText.lineCount * fontP->maxheight +
             (double) (formattedText.lineCount-1) * interlineSpace;
-        
+
         if (rowsD > INT_MAX-10)
             pm_error("Image height too large.");
         else
@@ -756,128 +1102,286 @@ computeImageHeight(struct text         const formattedText,
 
 
 static void
-computeImageWidth(struct text         const formattedText, 
-                  const struct font * const fontP,
-                  float               const intercharacterSpace,
-                  unsigned int        const hmargin,
-                  unsigned int *      const colsP,
-                  int *               const maxleftbP) {
+computeImageWidth(struct Text          const formattedText,
+                  const struct font2 * const fontP,
+                  float                const intercharacterSpace,
+                  unsigned int         const hmargin,
+                  unsigned int *       const colsP,
+                  unsigned int *       const maxleftbP) {
 
     if (intercharacterSpace < 0 && fontP->maxwidth < -intercharacterSpace)
-        pm_error("-space value (%f) negative; exceeds font width.",
-                 intercharacterSpace);     
+        pm_error("negative -space value %.2f exceeds font width",
+                 intercharacterSpace);
     else {
         /* Find the widest line, and the one that backs up the most past
            the nominal start of the line.
         */
-    
-        unsigned int line;
-        double maxwidth;
-        int maxleftb;
+
+        unsigned int lineNum;
+        double rightExtreme;
+        int leftExtreme;
         double colsD;
 
-        for (line = 0, maxwidth = 0.0, maxleftb = 0;
-             line < formattedText.lineCount;
-             ++line) {
-
-            double bwid;
-            int backupSpaceNeeded;
-            
-            get_line_dimensions(formattedText.textArray[line], fontP,
-                                intercharacterSpace,
-                                &bwid, &backupSpaceNeeded);
-            
-            maxwidth = MAX(maxwidth, bwid);
-            maxleftb = MAX(maxleftb, backupSpaceNeeded);
+        rightExtreme = 0.0;  /* initial value */
+        leftExtreme = 0;     /* initial value */
+
+        for (lineNum = 0; lineNum < formattedText.lineCount;  ++lineNum) {
+            double rightEdge;
+            int leftEdge;
+
+            getLineDimensions(formattedText.textArray[lineNum], fontP,
+                              intercharacterSpace,
+                              &rightEdge, &leftEdge);
+            rightExtreme = MAX(rightExtreme, rightEdge);
+            leftExtreme  = MIN(leftExtreme,  leftEdge);
         }
-        colsD = 2 * (double) hmargin + (double) maxwidth;
-    
+        leftExtreme = MIN(leftExtreme, 0);
+
+        colsD = (double) (-leftExtreme) + rightExtreme + 2 * hmargin;
+
         if (colsD > INT_MAX-10)
             pm_error("Image width too large.");
         else
             *colsP = (unsigned int) colsD;
-    
-        *maxleftbP = maxleftb;
+
+        *maxleftbP = (unsigned int) - leftExtreme;
     }
 }
 
 
 
-int
-main(int argc, const char *argv[]) {
+static void
+renderText(unsigned int   const cols,
+           unsigned int   const rows,
+           struct font2 * const fontP,
+           unsigned int   const hmargin,
+           unsigned int   const vmargin,
+           struct Text    const formattedText,
+           unsigned int   const maxleftb,
+           float          const space,
+           int            const lspace,
+           bool           const fixedAdvance,
+           FILE *         const ofP) {
+
+    bit ** const bits = pbm_allocarray(pbm_packed_bytes(cols), rows);
 
-    struct cmdlineInfo cmdline;
-    bit ** bits;
-    unsigned int rows, cols;
-    struct font * fontP;
-    unsigned int vmargin, hmargin;
-    struct text inputText;
-    struct text formattedText;
-    int maxleftb;
+    /* Fill background with white */
+    clearBackground(bits, cols, rows);
 
-    pm_proginit(&argc, argv);
+    /* Put the text in  */
+    insertCharacters(bits, formattedText, fontP, vmargin, hmargin + maxleftb,
+                     space, cols, rows, lspace, fixedAdvance);
 
-    parseCommandLine(argc, argv, &cmdline);
-    
-    computeFont(cmdline, &fontP);
+    /* Free all font data */
+    pbm_destroybdffont2(fontP); 
 
-    getText(cmdline.text, fontP, &inputText);
-       
-    if (cmdline.nomargins) {
-        vmargin = 0;
-        hmargin = 0;
+    {
+        unsigned int row;
+
+        pbm_writepbminit(ofP, cols, rows, 0);
+
+        for (row = 0; row < rows; ++row)
+            pbm_writepbmrow_packed(ofP, bits[row], cols, 0);
+    }
+
+    pbm_freearray(bits, rows);
+}
+
+
+
+static PM_WCHAR const * sheetTextArray[] = {
+L"M \",/^_[`jpqy| M",
+L"                ",
+L"/  !\"#$%&'()*+ /",
+L"< ,-./01234567 <",
+L"> 89:;<=>?@ABC >",
+L"@ DEFGHIJKLMNO @",
+L"_ PQRSTUVWXYZ[ _",
+L"{ \\]^_`abcdefg {",
+L"} hijklmnopqrs }",
+L"~ tuvwxyz{|}~  ~",
+L"                ",
+L"M \",/^_[`jpqy| M" };
+
+
+
+static void
+validateText(const PM_WCHAR ** const textArray,
+             struct font2    * const fontP) {
+/*----------------------------------------------------------------------------
+   Abort the program if there are characters in 'textArray' which cannot be
+   rendered in font *fontP.
+-----------------------------------------------------------------------------*/
+    const PM_WCHAR * output;
+    unsigned int textRow;
+
+    for (textRow = 0; textRow < 12; ++textRow)
+        fixControlChars(textArray[textRow], fontP, &output, QUIT);
+
+    free((PM_WCHAR *)output);
+}
+
+
+
+static void
+renderSheet(struct font2 * const fontP,
+            FILE *         const ofP) {
+
+    int const cols  = fontP->maxwidth  * 16;
+    int const rows  = fontP->maxheight * 12;
+    struct Text const sheetText =
+        { (PM_WCHAR ** const) sheetTextArray, 12, 12};
+
+    validateText(sheetTextArray, fontP);
+
+    renderText(cols, rows, fontP, 0, 0, sheetText, MAX(-(fontP->x),0),
+               0.0, 0, TRUE, ofP);
+}
+
+
+
+static void
+dryrunOutput(unsigned int const cols,
+             unsigned int const rows,
+             FILE *       const ofP) {
+
+    fprintf(ofP, "%u %u\n", cols, rows);
+}
+
+
+
+static void
+textDumpOutput(struct Text   const lp,
+               FILE *        const ofP) {
+/*----------------------------------------------------------------------------
+   Output the text 'lp' as characters.  (Do not render.)
+
+   Note that the output stream is wide-oriented; it cannot be mixed with
+   narrow-oriented output.  The libnetpbm library functions are
+   narrow-oriented.  Thus, when this output is specified, it must not be mixed
+   with any output from the library; it should be the sole output.
+-----------------------------------------------------------------------------*/
+    int rc;
+
+    rc = fwide(ofP, 1);
+    if (rc != 1) {
+        /* This occurs when narrow-oriented output to ofP happens before we
+           get here.
+        */
+        pm_error("Failed to set output stream to wide "
+                 "(fwide() returned %d.  Maybe the output file "
+                 "was written in narrow mode before this program was invoked?",
+                 rc);
     } else {
-        if (inputText.lineCount == 1) {
-            vmargin = fontP->maxheight / 2;
-            hmargin = fontP->maxwidth;
-        } else {
-            vmargin = fontP->maxheight;
-            hmargin = 2 * fontP->maxwidth;
+        unsigned int line;  /* Line number in input text */
+
+        for (line = 0; line < lp.lineCount; ++line) {
+            fputws(lp.textArray[line], ofP);
+            fputwc(L'\n', ofP);
         }
     }
-    
-    if (cmdline.width > 0) {
-        if (cmdline.width > INT_MAX -10)
-            pm_error("-width value too large: %u", cmdline.width);
-            
-        /* Flow or truncate lines to meet user's width request */
-        if (inputText.lineCount == 1) 
-            flowText(inputText, cmdline.width, fontP, cmdline.space,
-                     &formattedText);
-        else
-            truncateText(inputText, cmdline.width, fontP, cmdline.space,
-                         &formattedText);
-        freeTextArray(inputText);
-    } else
-        formattedText = inputText;
-        
+}
+
+
+
+static void
+pbmtext(struct CmdlineInfo const cmdline,
+        struct font2 *     const fontP,
+        FILE *             const ofP) {
+
+    unsigned int rows, cols;
+        /* Dimensions in pixels of the output image */
+    unsigned int cols0;
+    unsigned int vmargin, hmargin;
+        /* Margins in pixels we add to the output image */
+    unsigned int hmargin0;
+    struct Text inputText;
+    struct Text formattedText;
+    unsigned int maxleftb, maxleftb0;
+
+    getText(cmdline.text, fontP, &inputText,
+            cmdline.verbose ? WARN : SILENT);
+
+    computeMargins(cmdline, inputText, fontP, &vmargin, &hmargin0);
+
+    formatText(cmdline, inputText, fontP, hmargin0,
+               &formattedText, &maxleftb0);
+
     if (formattedText.lineCount == 0)
-        pm_error("No input text.");
-    
-    computeImageHeight(formattedText, fontP, cmdline.lspace, vmargin,
-                       &rows);
+        pm_error("No input text");
+
+    computeImageHeight(formattedText, fontP, cmdline.lspace, vmargin, &rows);
 
-    computeImageWidth(formattedText, fontP, cmdline.space, hmargin,
-                      &cols, &maxleftb);
+    computeImageWidth(formattedText, fontP, cmdline.space,
+                      cmdline.width > 0 ? 0 : hmargin0, &cols0, &maxleftb);
 
-    if (cols == 0 || rows == 0)
+    if (cols0 == 0 || rows == 0)
         pm_error("Input is all whitespace and/or non-renderable characters.");
 
-    bits = pbm_allocarray(cols, rows);
+    if (cmdline.width == 0) {
+        cols    = cols0;
+        hmargin = hmargin0;
+    } else {
+        if (cmdline.width < cols0)
+            pm_error("internal error: calculated image width (%u) exceeds "
+                     "specified -width value: %u",
+                     cols0, cmdline.width);
+        else if (maxleftb0 != maxleftb)
+            pm_error("internal error: contradicting backup values");
+        else {
+            hmargin = MIN(hmargin0, (cmdline.width - cols0) / 2);
+            cols = cmdline.width;
+        }
+    }
 
-    /* Fill background with white */
-    fill_rect(bits, 0, 0, rows, cols, PBM_WHITE);
+    if (cmdline.dryrun)
+        dryrunOutput(cols, rows, ofP);
+    else if (cmdline.textdump)
+        textDumpOutput(formattedText, ofP);
+    else
+        renderText(cols, rows, fontP, hmargin, vmargin, formattedText,
+                   maxleftb, cmdline.space, cmdline.lspace, FALSE, ofP);
 
-    /* Put the text in  */
-    insert_characters(bits, formattedText, fontP, vmargin, hmargin + maxleftb, 
-                      cmdline.space, cmdline.lspace);
+    freeTextArray(formattedText);
+}
 
-    pbm_writepbm(stdout, bits, cols, rows, 0);
 
-    pbm_freearray(bits, rows);
 
-    freeTextArray(formattedText);
+int
+main(int argc, const char *argv[]) {
+
+    struct CmdlineInfo cmdline;
+    struct font2 * fontP;
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    if (cmdline.wchar) {
+        char * newLocale;
+        newLocale = setlocale(LC_ALL, "");
+        if (!newLocale)
+            pm_error("Failed to set locale (LC_ALL) from environment");
+
+        /* Orient standard input stream to wide */
+        fwide(stdin,  1);
+    } else
+        fwide(stdin, -1);
+
+    if (cmdline.verbose)
+        pm_message("LC_CTYPE is set to '%s'", setlocale(LC_CTYPE, NULL) );
+
+    computeFont(cmdline, &fontP);
+
+    if (cmdline.dumpsheet)
+        renderSheet(fontP, stdout);
+    else
+        pbmtext(cmdline, fontP, stdout);
+
     pm_close(stdout);
 
     return 0;
 }
+
+
+
diff --git a/generator/pbmtextps.c b/generator/pbmtextps.c
index e6367530..f543618d 100644
--- a/generator/pbmtextps.c
+++ b/generator/pbmtextps.c
@@ -1,4 +1,4 @@
-/*
+ /*
  * pbmtextps.c -  render text into a bitmap using a postscript interpreter
  *
  * Copyright (C) 2002 by James McCann.
@@ -14,68 +14,33 @@
  *
  * Additions by Bryan Henderson contributed to public domain by author.
  *
+ * PostScript(R) Language Reference, Third Edition  (a.k.a. "Red Book")
+ * http://www.adobe.com/products/postscript/pdfs/PLRM.pdf
+ * ISBN 0-201-37922-8
+ *
+ * Postscript Font Naming Issues:
+ * https://partners.adobe.com/public/developer/en/font/5088.FontNames.pdf
+ *
+ * Other resources:
+ * http://partners.adobe.com/public/developer/ps/index_specs.html
  */
-#define _XOPEN_SOURCE   /* Make sure popen() is in stdio.h */
-#define _BSD_SOURCE     /* Make sure stdrup() is in string.h */
+
+#define _XOPEN_SOURCE 500
+  /* Make sure popen() is in stdio.h, strdup() is in string.h */
 #include <unistd.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
+#include <assert.h>
 
 #include "pm_c_util.h"
 #include "mallocvar.h"
 #include "nstring.h"
 #include "shhopt.h"
+#include "pm_system.h"
 #include "pbm.h"
 
-
-#define BUFFER_SIZE 2048
-
-struct cmdlineInfo {
-    /* All the information the user supplied in the command line,
-       in a form easy for the program to use.
-    */
-    unsigned int res;         /* resolution, DPI */
-    unsigned int fontsize;    /* Size of font in points */
-    const char * font;      /* Name of postscript font */
-    float        stroke;
-        /* Width of stroke in points (only for outline font) */
-    unsigned int verbose;
-    const char * text;
-};
-
-
-
-static void
-writeFileToStdout(const char * const fileName){
-    /* simple pbmtopbm */
-
-    FILE * ifP;
-    int format;
-    int cols, rows, row ;
-    unsigned char * bitrow; 
-    
-    ifP = pm_openr(fileName);
-    pbm_readpbminit(ifP, &cols, &rows, &format);
-
-    if (cols==0 || rows==0 || cols>INT_MAX-10 || rows>INT_MAX-10)
-      pm_error("Abnormal output from gs program.  "
-               "width x height = %u x %u", cols, rows);
-               
-    pbm_writepbminit(stdout, cols, rows, 0);           
-               
-    bitrow = pbm_allocrow_packed(cols);
-    
-    for (row = 0; row < rows; ++row) {
-        pbm_readpbmrow_packed(ifP, bitrow, cols, format);
-        pbm_writepbmrow_packed(stdout, bitrow, cols, 0);
-    }
-    pbm_freerow_packed(bitrow);
-}
-
-
-
 static void
 validateFontName(const char * const name) {
 /*-----------------------------------------------------------------------------
@@ -88,11 +53,11 @@ validateFontName(const char * const name) {
     unsigned int idx;
 
     for (idx = 0; name[idx] != '\0'; ++idx) {
-        char const c = name[idx]; 
+        char const c = name[idx];
 
         if (c < 32 || c > 125)
             pm_error("Invalid character in font name");
-        else 
+        else
             switch (c) {
               case '[':   case ']':   case '(':   case ')':
               case '{':   case '}':   case '/':   case '\\':
@@ -119,7 +84,7 @@ asciiHexEncode(char *          const inbuff,
     unsigned int idx;
 
     for (idx = 0; inbuff[idx] != '\0'; ++idx) {
-        unsigned int const item = (unsigned char) inbuff[idx]; 
+        unsigned int const item = (unsigned char) inbuff[idx];
 
         outbuff[idx*2]   = hexits[item >> 4];
         outbuff[idx*2+1] = hexits[item & 0xF];
@@ -132,7 +97,7 @@ asciiHexEncode(char *          const inbuff,
 
 static void
 buildTextFromArgs(int           const argc,
-                  char **       const argv,
+                  const char ** const argv,
                   const char ** const asciiHexTextP) {
 /*----------------------------------------------------------------------------
    Build the array of text to be included in the Postscript program to
@@ -161,7 +126,7 @@ buildTextFromArgs(int           const argc,
             if (text == NULL)
                 pm_error("out of memory");
             strcat(text, " ");
-        } 
+        }
         totalTextSize += strlen(argv[i]);
         text = realloc(text, totalTextSize);
         if (text == NULL)
@@ -169,7 +134,7 @@ buildTextFromArgs(int           const argc,
         strcat(text, argv[i]);
     }
 
-    { 
+    {
         char * asciiHexText;
 
         MALLOCARRAY(asciiHexText, totalTextSize * 2);
@@ -186,9 +151,31 @@ buildTextFromArgs(int           const argc,
 
 
 
+struct CmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    unsigned int res;
+    float        fontsize;
+    const char * font;
+    float        stroke;
+    float        ascent;
+    float        descent;
+    float        leftmargin;
+    float        rightmargin;
+    float        topmargin;
+    float        bottommargin;
+    unsigned int pad;
+    unsigned int verbose;
+    unsigned int dump;
+    const char * text;
+};
+
+
+
 static void
-parseCommandLine(int argc, char ** argv,
-                 struct cmdlineInfo *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.
@@ -199,37 +186,104 @@ parseCommandLine(int argc, char ** argv,
     optStruct3 opt;
 
     unsigned int option_def_index;
+    unsigned int cropSpec, ascentSpec, descentSpec;
+    unsigned int leftmarginSpec, rightmarginSpec;
+    unsigned int topmarginSpec, bottommarginSpec;
 
     MALLOCARRAY(option_def, 100);
 
     option_def_index = 0;   /* incremented by OPTENTRY */
-    OPTENT3(0, "resolution", OPT_UINT,   &cmdlineP->res,            NULL,  0);
-    OPTENT3(0, "font",       OPT_STRING, &cmdlineP->font,           NULL,  0);
-    OPTENT3(0, "fontsize",   OPT_UINT,   &cmdlineP->fontsize,       NULL,  0);
-    OPTENT3(0, "stroke",     OPT_FLOAT,  &cmdlineP->stroke,         NULL,  0);
-    OPTENT3(0, "verbose",    OPT_FLAG,   NULL, &cmdlineP->verbose,         0);
+    OPTENT3(0, "resolution",    OPT_UINT,
+            &cmdlineP->res,          NULL,                      0);
+    OPTENT3(0, "font",          OPT_STRING,
+            &cmdlineP->font,         NULL,                      0);
+    OPTENT3(0, "fontsize",      OPT_FLOAT,
+            &cmdlineP->fontsize,     NULL,                      0);
+    OPTENT3(0, "stroke",        OPT_FLOAT,
+            &cmdlineP->stroke,       NULL,                      0);
+    OPTENT3(0, "ascent",        OPT_FLOAT,
+            &cmdlineP->ascent,       &ascentSpec,               0);
+    OPTENT3(0, "descent",       OPT_FLOAT,
+            &cmdlineP->descent,      &descentSpec,              0);
+    OPTENT3(0, "leftmargin",    OPT_FLOAT,
+            &cmdlineP->leftmargin,   &leftmarginSpec,           0);
+    OPTENT3(0, "rightmargin",   OPT_FLOAT,
+            &cmdlineP->rightmargin,  &rightmarginSpec,          0);
+    OPTENT3(0, "topmargin",     OPT_FLOAT,
+            &cmdlineP->topmargin,    &topmarginSpec,            0);
+    OPTENT3(0, "bottommargin",  OPT_FLOAT,
+            &cmdlineP->bottommargin, &bottommarginSpec,         0);
+    OPTENT3(0, "crop",          OPT_FLAG,
+            NULL,                    &cropSpec,                 0);
+    OPTENT3(0, "pad",           OPT_FLAG,
+            NULL,                    &cmdlineP->pad,            0);
+    OPTENT3(0, "verbose",       OPT_FLAG,
+            NULL,                    &cmdlineP->verbose,        0);
+    OPTENT3(0, "dump-ps",       OPT_FLAG,
+            NULL,                    &cmdlineP->dump,           0);
 
     /* Set the defaults */
     cmdlineP->res = 150;
     cmdlineP->fontsize = 24;
     cmdlineP->font = "Times-Roman";
-    cmdlineP->stroke = -1;
+    cmdlineP->stroke  = -1;
+    cmdlineP->ascent  = 0;
+    cmdlineP->descent = 0;
+    cmdlineP->rightmargin = 0;
+    cmdlineP->leftmargin  = 0;
+    cmdlineP->topmargin   = 0;
+    cmdlineP->bottommargin = 0;
+    cropSpec       = FALSE;
+    cmdlineP->pad  = FALSE;
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;
 
-    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
 
     validateFontName(cmdlineP->font);
 
+    if (cmdlineP->fontsize <= 0)
+        pm_error("-fontsize must be positive");
+    if (cmdlineP->ascent < 0)
+        pm_error("-ascent must not be negative");
+    if (cmdlineP->descent < 0)
+        pm_error("-descent must not be negative");
+    if (cmdlineP->leftmargin <0)
+        pm_error("-leftmargin must not be negative");
+    if (cmdlineP->rightmargin <0)
+        pm_error("-rightmargin must not be negative");
+    if (cmdlineP->topmargin <0)
+        pm_error("-topmargin must not be negative");
+    if (cmdlineP->bottommargin <0)
+        pm_error("-bottommargin must not be negative");
+
+    if (cropSpec == TRUE) {
+        if (ascentSpec || descentSpec ||
+            leftmarginSpec || rightmarginSpec ||
+            topmarginSpec || bottommarginSpec ||
+            cmdlineP->pad)
+              pm_error("-crop cannot be specified with -ascent, -descent, "
+                       "-leftmargin, -rightmargin, "
+                       "-topmargin, -bottommargin or -pad");
+    } else {
+        if (!descentSpec && !bottommarginSpec && !cmdlineP->pad)
+            cmdlineP->descent = cmdlineP->fontsize * 1.5;
+
+        if (!leftmarginSpec)
+             cmdlineP->leftmargin = cmdlineP->fontsize / 2;
+    }
+
     buildTextFromArgs(argc, argv, &cmdlineP->text);
+
+    free(option_def);
 }
 
 
 
 static void
-termCmdline(struct cmdlineInfo const cmdline) {
+termCmdline(struct CmdlineInfo const cmdline) {
 
     pm_strfree(cmdline.text);
 }
@@ -237,305 +291,431 @@ termCmdline(struct cmdlineInfo const cmdline) {
 
 
 static const char *
-construct_postscript(struct cmdlineInfo const cmdline) {
+postscriptProgram(struct CmdlineInfo const cmdline) {
+/*-----------------------------------------------------------------------------
+  In Postscript, the bottom of the page is row zero.  Postscript allows
+  negative values but negative regions are clipped from the output image.
+  We make adjustments to ensure that nothing is lost.
+
+  Postscript also allow fonts to have negative values in the bounding box
+  coordinates.  The bottom edge of "L" is row zero: this row is called the
+  "baseline".  The feet of "g" "p" "y" extend into negative region.  In a
+  similar manner the left edge of the bounding box may be negative.  We add
+  margins on the left and the bottom with "xorigin" and "yorigin" to
+  provide for such characters.
+
+  The sequence "textstring false charpath flattenpath pathbbox" determines
+  the bounding box of the entire text when rendered.
+-----------------------------------------------------------------------------*/
+
+    /* C89 limits the size of a string constant, so we have to build the
+       Postscript command in pieces.
+
+       psVariable, psTemplate: Set variables.
+       psFixed1: Scale font.  Calculate pad metrics.
+       psFixed2: Determine width, height, xorigin, yorigin.
+       psFixed3: Render.
+       psFixed4: Verbose mode: Report font name, metrics.
+
+       We could add code to psFixed2 for handling right-to-left writing
+       (Hebrew, Arabic) and vertical writing (Chinese, Korean, Japanese).
+    */
+
+    const char * const psTemplate =
+        "/FindFont {/%s findfont} def\n"
+        "/fontsize %f def\n"
+        "/pensize %f def\n"
+        "/textstring <%s> def\n"
+        "/ascent %f def\n"
+        "/descent %f def\n"
+        "/leftmargin %f def\n"
+        "/rightmargin %f def\n"
+        "/topmargin %f def\n"
+        "/bottommargin %f def\n"
+        "/pad %s def\n"
+        "/verbose %s def\n";
+
+    const char * const psFixed1 =
+        "FindFont fontsize scalefont\n"
+        "pad { dup dup\n"
+        "  /FontMatrix get 3 get /yscale exch def\n"
+        "  /FontBBox get dup\n"
+        "  1 get yscale mul neg /padbottom exch def\n"
+        "  3 get yscale mul /padtop exch def}\n"
+        "  {/padbottom 0 def /padtop 0 def}\n"
+        "  ifelse\n"
+        "setfont\n";
+
+    const char * const psFixed2 =
+        "0 0 moveto\n"
+        "textstring false charpath flattenpath pathbbox\n"
+        "/BBtop    exch def\n"
+        "/BBright  exch def\n"
+        "/BBbottom exch neg def\n"
+        "/BBleft   exch neg def\n"
+        "/max { 2 copy lt { exch } if pop } bind def\n"
+        "/yorigin descent padbottom max BBbottom max bottommargin add def\n"
+        "/xorigin leftmargin BBleft max def\n"
+        "/width xorigin BBright add rightmargin add def\n"
+        "/height ascent BBtop max padtop max topmargin add yorigin add def\n";
+
+    const char * const psFixed3 =
+        "<</PageSize [width height]>> setpagedevice\n"
+        "xorigin yorigin moveto\n"
+        "pensize 0 lt\n"
+        "  {textstring show}\n"
+        "  {pensize setlinewidth 0 setgray\n"
+        "  textstring true charpath stroke}\n"
+        "  ifelse\n"
+        "showpage\n";
+
+    const char * const psFixed4 =
+        "verbose\n"
+        "  {xorigin yorigin moveto\n"
+        "   [(width height) width height] ==\n"
+        "   [(ascent descent) height yorigin sub yorigin] ==\n"
+        "   [(bounding box) \n"
+        "     textstring false charpath flattenpath pathbbox] ==\n"
+        "   [(Fontname) FindFont dup /FontName\n"
+        "     known\n"
+        "       {/FontName get}\n"
+        "       {pop (anonymous)}\n"
+        "       ifelse]  ==}\n"
+        "  if";
 
     const char * retval;
-    const char * template;
-
-    if (cmdline.stroke < 0) 
-        template =
-            "/%s findfont\n"
-            "%d scalefont\n"
-            "setfont\n"
-            "12 36 moveto\n"
-            "<%s> show\n"
-            "showpage\n";
-    else 
-        template =
-            "/%s findfont\n"
-            "%d scalefont\n"
-            "setfont\n"
-            "12 36 moveto\n"
-            "%f setlinewidth\n"
-            "0 setgray\n"
-            "<%s> true charpath\n"
-            "stroke\n"
-            "showpage\n";
-
-    if (cmdline.stroke < 0)
-        pm_asprintf(&retval, template, cmdline.font, cmdline.fontsize, 
-                    cmdline.text);
-    else
-        pm_asprintf(&retval, template, cmdline.font, cmdline.fontsize, 
-                    cmdline.stroke, cmdline.text);
+    const char * psVariable;
+
+    pm_asprintf(&psVariable, psTemplate, cmdline.font,
+                cmdline.fontsize, cmdline.stroke, cmdline.text,
+                cmdline.ascent, cmdline.descent,
+                cmdline.leftmargin, cmdline.rightmargin,
+                cmdline.topmargin,  cmdline.bottommargin,
+                cmdline.pad ? "true" : "false",
+                cmdline.verbose ? "true" : "false" );
+
+    pm_asprintf(&retval, "%s%s%s%s%s", psVariable,
+                psFixed1, psFixed2, psFixed3, psFixed4);
+
+    pm_strfree(psVariable);
 
     return retval;
 }
 
 
 
-static const char *
-gsExecutableName() {
+static const char **
+gsArgList(const char *       const outputFilename,
+          struct CmdlineInfo const cmdline) {
+
+    unsigned int const maxArgCt = 50;
+
+    const char ** retval;
+    unsigned int argCt;  /* Number of arguments in 'retval' so far */
+
+    if (cmdline.res <= 0)
+         pm_error("Resolution (dpi) must be positive.");
 
-    const char * const which = "which gs";
+    if (cmdline.fontsize <= 0)
+         pm_error("Font size must be positive.");
 
-    static char buffer[BUFFER_SIZE];
+    MALLOCARRAY_NOFAIL(retval, maxArgCt+2);
 
-    FILE * f;
+    argCt = 0;  /* initial value */
 
-    memset(buffer, 0, BUFFER_SIZE);
+    pm_asprintf(&retval[argCt++], "ghostscript");
+    pm_asprintf(&retval[argCt++], "-r%d", cmdline.res);
+    pm_asprintf(&retval[argCt++], "-sDEVICE=pbmraw");
+    pm_asprintf(&retval[argCt++], "-sOutputFile=%s", outputFilename);
+    pm_asprintf(&retval[argCt++], "-q");
+    pm_asprintf(&retval[argCt++], "-dBATCH");
+    pm_asprintf(&retval[argCt++], "-dSAFER");
+    pm_asprintf(&retval[argCt++], "-dNOPAUSE");
+    pm_asprintf(&retval[argCt++], "-");
 
-    f = popen(which, "r");
-    if (!f)
-        pm_error("Can't find ghostscript");
+    retval[argCt++] = NULL;
 
-    fread(buffer, 1, BUFFER_SIZE, f);
-    if (buffer[strlen(buffer) - 1] == '\n')
-        buffer[strlen(buffer) - 1] = '\0';
-    pclose(f);
-    
-    if (buffer[0] != '/' && buffer[0] != '.')
-        pm_error("Can't find ghostscript");
+    assert(argCt < maxArgCt);
 
-    return buffer;
+    return retval;
 }
 
 
 
-static const char *
-cropExecutableName(void) {
+static void
+reportGhostScript(const char *  const executableNm,
+                  const char ** const argList) {
 
-    const char * const which = "which pnmcrop";
+    unsigned int i;
 
-    static char buffer[BUFFER_SIZE];
-    const char * retval;
+    pm_message("Running Ghostscript interpreter '%s'", executableNm);
 
-    FILE * f;
-
-    memset(buffer, 0, BUFFER_SIZE);
-
-    f = popen(which, "r");
-    if (!f)
-        retval = NULL;
-    else {
-        fread(buffer, 1, BUFFER_SIZE, f);
-        if (buffer[strlen(buffer) - 1] == '\n')
-            buffer[strlen(buffer) - 1] = 0;
-        pclose(f);
-            
-        if (buffer[0] != '/' && buffer[0] != '.') {
-            retval = NULL;
-            pm_message("Can't find pnmcrop");
-        } else
-            retval = buffer;
-    }
-    return retval;
+    pm_message("Program arguments:");
+
+    for (i = 0; argList[i]; ++i)
+        pm_message("  '%s'", argList[i]);
 }
 
 
 
-static const char *
-gsCommand(const char *       const psFname,
-          const char *       const outputFilename, 
-          struct cmdlineInfo const cmdline) {
+static void
+freeArgList(const char ** const argList) {
 
-    const char * retval;
-    double const x = (double) cmdline.res * 11;
-    double const y = (double) cmdline.res * 
-                     ((double) cmdline.fontsize * 2 + 72)  / 72;
-    
-    if (cmdline.res <= 0)
-         pm_error("Resolution (dpi) must be positive.");
-    
-    if (cmdline.fontsize <= 0)
-         pm_error("Font size must be positive.");
-    
-    /* The following checks are for guarding against overflows in this 
-       function.  Huge x,y values that pass these checks may be
-       rejected by the 'gs' program.
-    */
-    
-    if (x > (double) INT_MAX-10)
-         pm_error("Absurdly fine resolution: %u. Output width too large.",
-                   cmdline.res );
-    if (y > (double) INT_MAX-10)
-         pm_error("Absurdly fine resolution (%u) and/or huge font size (%u). "
-                  "Output height too large.", cmdline.res, cmdline.fontsize);
-         
-    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);
+    unsigned int i;
 
-    return retval;
+    for (i = 0; argList[i]; ++i)
+        pm_strfree(argList[i]);
+
+    free(argList);
 }
 
 
 
-static const char *
-cropCommand(const char * const inputFileName) {
+static void
+reportFontName(const char * const fontname) {
 
-    const char * retval;
-    const char * plainOpt = pm_plain_output ? "-plain" : "" ;
-    
-    if (cropExecutableName()) {
-        pm_asprintf(&retval, "%s -top -right %s %s", 
-                    cropExecutableName(), plainOpt, inputFileName);
-        if (retval == pm_strsol)
-            pm_error("Unable to allocate memory");
-    } else
-        retval = NULL;
+    pm_message("Font: '%s'", fontname);
 
-    return retval;
 }
 
 
 
 static void
-writeProgram(const char *       const psFname,
-             struct cmdlineInfo const cmdline) {
+reportMetrics(float const  width,
+              float const  height,
+              float const  ascent,
+              float const  descent,
+              float const  BBoxleft,
+              float const  BBoxbottom,
+              float const  BBoxright,
+              float const  BBoxtop) {
+
+    pm_message("-- Metrics in points.  Bottom left is (0,0) --");
+    pm_message("Width:   %f", width);
+    pm_message("Height:  %f", height);
+    pm_message("Ascent:  %f", ascent);
+    pm_message("Descent: %f", descent);
+    pm_message("BoundingBox_Left:   %f", BBoxleft);
+    pm_message("BoundingBox_Right:  %f", BBoxright);
+    pm_message("BoundingBox_Top:    %f", BBoxtop);
+    pm_message("BoundingBox_Bottom: %f", BBoxbottom);
 
-    const char * ps;
-    FILE * psfile;
+}
 
-    psfile = fopen(psFname, "w");
-    if (psfile == NULL)
-        pm_error("Can't open temp file '%s'.  Errno=%d (%s)",
-                 psFname, errno, strerror(errno));
 
-    ps = construct_postscript(cmdline);
 
-    if (cmdline.verbose)
-        pm_message("Postscript program = '%s'", ps);
-        
-    if (fwrite(ps, 1, strlen(ps), psfile) != strlen(ps))
-        pm_error("Can't write postscript to temp file");
+static void
+acceptGSoutput(int             const pipetosuckFd,
+               void *          const nullParams ) {
+/*-----------------------------------------------------------------------------
+  Accept text written to stdout by the PostScript program.
+
+  There are two kinds of output:
+    (1) Metrics and fontname reported, when verbose is on.
+    (2) Error messages from ghostscript.
+
+  We read one line at a time.
+
+  We cannot predict how long one line can be in case (2).  In practice
+  the "execute stack" report gets long.  We provide by setting lineBuffSize
+  to a large number.
+-----------------------------------------------------------------------------*/
+    unsigned int const lineBuffSize = 1024*32;
+    FILE *       const inFileP = fdopen(pipetosuckFd, "r");
+
+    float width, height, ascent, descent;
+    float BBoxleft, BBoxbottom, BBoxright, BBoxtop;
+    char * lineBuff;  /* malloc'd */
+    char fontname [2048];
+    bool fontnameReported, widthHeightReported;
+    bool ascentDescentReported, BBoxReported;
+
+    assert(nullParams == NULL);
+
+    fontnameReported      = FALSE; /* Initial value */
+    widthHeightReported   = FALSE; /* Initial value */
+    ascentDescentReported = FALSE; /* Initial value */
+    BBoxReported          = FALSE; /* Initial value */
 
-    fclose(psfile);
+    MALLOCARRAY_NOFAIL(lineBuff, lineBuffSize);
 
-    pm_strfree(ps);
+    while (fgets(lineBuff, lineBuffSize, inFileP) != NULL) {
+        unsigned int rWidthHeight, rAscentDescent, rBBox, rFontname;
+
+        rWidthHeight = sscanf(lineBuff, "[(width height) %f %f]",
+                              &width, &height);
+
+        rAscentDescent = sscanf(lineBuff, "[(ascent descent) %f %f]",
+                                &ascent, &descent);
+
+        rBBox =  sscanf(lineBuff, "[(bounding box) %f %f %f %f]",
+                        &BBoxleft, &BBoxbottom, &BBoxright, &BBoxtop);
+
+        rFontname = sscanf(lineBuff, "[(Fontname) /%2047s", fontname);
+
+        if (rFontname == 1)
+            fontnameReported = TRUE;
+        else if (rWidthHeight == 2)
+            widthHeightReported = TRUE;
+        else if (rAscentDescent == 2)
+            ascentDescentReported = TRUE;
+        else if (rBBox == 4)
+            BBoxReported = TRUE;
+        else
+            pm_message("[gs] %s", lineBuff);
+    }
+
+    if (fontnameReported) {
+        fontname[strlen(fontname)-1] = 0;
+        reportFontName(fontname);
+
+        if (widthHeightReported && ascentDescentReported && BBoxReported)
+            reportMetrics(width, height, ascent, descent,
+                          BBoxleft, BBoxbottom, BBoxright, BBoxtop);
+    }
+    fclose(inFileP);
+    pm_strfree(lineBuff);
 }
 
 
 
 static void
-executeProgram(const char *       const psFname, 
+executeProgram(const char *       const psProgram,
                const char *       const outputFname,
-               struct cmdlineInfo const cmdline) {
+               struct CmdlineInfo const cmdline) {
 
-    const char * com;
-    int rc;
+    const char *  const executableNm = "gs";
+    const char ** const argList = gsArgList(outputFname, cmdline);
+
+    struct bufferDesc feedBuffer;
+    int               termStatus;
+    unsigned int      bytesFed;
+
+    bytesFed = 0;  /* Initial value */
+
+    feedBuffer.buffer            = (unsigned char *) psProgram;
+    feedBuffer.size              = strlen(psProgram);
+    feedBuffer.bytesTransferredP = &bytesFed;
 
-    com = gsCommand(psFname, outputFname, cmdline);
-    if (com == NULL)
-        pm_error("Can't allocate memory for a 'ghostscript' command");
-    
     if (cmdline.verbose)
-        pm_message("Running Postscript interpreter '%s'", com);
+        reportGhostScript(executableNm, argList);
+
+    pm_system2_vp(executableNm,
+                  argList,
+                  &pm_feed_from_memory, &feedBuffer,
+                  cmdline.verbose ? &acceptGSoutput : &pm_accept_null, NULL,
+                  &termStatus);
+
+    if (termStatus != 0) {
+        const char * const msg = pm_termStatusDesc(termStatus);
 
-    rc = system(com);
-    if (rc != 0)
-        pm_error("Failed to run Ghostscript process.  rc=%d", rc);
+        pm_error("Failed to run Ghostscript process.  %s", msg);
 
-    pm_strfree(com);
+        pm_strfree(msg);
+    }
+    freeArgList(argList);
 }
 
 
 
 static void
-cropToStdout(const char * const inputFileName,
-             bool         const verbose) {
+writePbm(const char * const fileName,
+         FILE *       const ofP) {
+/*----------------------------------------------------------------------------
+  Write the PBM image that is in the file named 'fileName" to file *ofP.
+  I.e. pbmtopbm.
+
+  It's not a byte-for-byte copy because PBM allows the same image to be
+  represented many ways (all of which we can accept as our input), but we use
+  libnetpbm to write our output in its specific way.
+----------------------------------------------------------------------------*/
+    FILE * ifP;
+    int format;
+    int cols, rows, row ;
+    unsigned char * bitrow;
 
-    const char * com;
+    ifP = pm_openr(fileName);
+    pbm_readpbminit(ifP, &cols, &rows, &format);
 
-    com = cropCommand(inputFileName);
+    if (cols == 0 || rows == 0 || cols > INT_MAX - 10 || rows > INT_MAX - 10)
+        pm_error("Abnormal output from gs program.  "
+                 "width x height = %u x %u", cols, rows);
 
-    if (com == NULL) {
-        /* No pnmcrop.  So don't crop. */
-        pm_message("Can't find pnmcrop command, image will be large");
-        writeFileToStdout(inputFileName);
-    } else {
-        FILE * pnmcrop;
-
-        if (verbose)
-            pm_message("Running crop command '%s'", com);
-        
-        pnmcrop = popen(com, "r");
-        if (pnmcrop == NULL)
-            pm_error("Can't run pnmcrop process");
-        else {
-            char buf[2048];
-            bool eof;
-
-            eof = FALSE;
-            
-            while (!eof) {
-                int bytesRead;
-
-                bytesRead = fread(buf, 1, sizeof(buf), pnmcrop);
-                if (bytesRead > 0) {
-                    int rc;
-                    rc = fwrite(buf, 1, bytesRead, stdout);
-                    if (rc != bytesRead)
-                        pm_error("Can't write to stdout");
-                } else if (bytesRead == 0)
-                    eof = TRUE;
-                else
-                    pm_error("Failed to read output of Pnmcrop process.  "
-                             "Errno=%d (%s)", errno, strerror(errno));
-            }
-            fclose(pnmcrop);
-        }
-        pm_strfree(com);
+    pbm_writepbminit(ofP, cols, rows, 0);
+
+    bitrow = pbm_allocrow_packed(cols);
+
+    for (row = 0; row < rows; ++row) {
+        pbm_readpbmrow_packed(ifP, bitrow, cols, format);
+        pbm_writepbmrow_packed(ofP, bitrow, cols, 0);
     }
+    pbm_freerow_packed(bitrow);
+    pm_close(ifP);
 }
 
 
 
 static void
-createOutputFile(struct cmdlineInfo const cmdline) {
+generatePbm(struct CmdlineInfo const cmdline,
+            FILE *             const ofP) {
+
+    const char * const psProgram = postscriptProgram(cmdline);
+
+    const char * tempPbmFname;
+    FILE * pbmFileP;
+
+    pm_make_tmpfile(&pbmFileP, &tempPbmFname);
+    assert(pbmFileP != NULL && tempPbmFname != NULL);
+    fclose(pbmFileP);
+
+    executeProgram(psProgram, tempPbmFname, cmdline);
+
+    /* Although Ghostscript created a legal PBM file, it uses a different
+       implementation of the format from libnetpbm's canonical output format,
+       so instead of copying the content of 'tempPbmFname' to *ofP byte for
+       byte, we copy it as a PBM image.
+    */
+    writePbm(tempPbmFname, ofP);
+
+    unlink(tempPbmFname);
+    pm_strfree(tempPbmFname);
+    pm_strfree(psProgram);
+}
 
-    const char * const template = "./pstextpbm.%d.tmp.%s";
-    
-    const char * psFname;
-    const char * uncroppedPbmFname;
 
-    pm_asprintf(&psFname, template, getpid(), "ps");
-    if (psFname == NULL)
-        pm_error("Unable to allocate memory");
- 
-    writeProgram(psFname, cmdline);
 
-    pm_asprintf(&uncroppedPbmFname, template, getpid(), "pbm");
-    if (uncroppedPbmFname == NULL)
-        pm_error("Unable to allocate memory");
- 
-    executeProgram(psFname, uncroppedPbmFname, cmdline);
+static void
+dumpPsProgram(struct CmdlineInfo const cmdline) {
+
+    const char * psProgram;
 
-    unlink(psFname);
-    pm_strfree(psFname);
+    psProgram = postscriptProgram(cmdline);
 
-    cropToStdout(uncroppedPbmFname, cmdline.verbose);
+    puts(psProgram);
 
-    unlink(uncroppedPbmFname);
-    pm_strfree(uncroppedPbmFname);
+    pm_strfree(psProgram);
 }
 
 
 
-int 
-main(int argc, char *argv[]) {
+int
+main(int argc, const char *argv[]) {
 
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
 
-    pbm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
-    createOutputFile(cmdline);
+    if (cmdline.dump)
+        dumpPsProgram(cmdline);
+    else
+        generatePbm(cmdline, stdout);
 
     termCmdline(cmdline);
 
     return 0;
 }
+
+
+
diff --git a/generator/pgmcrater b/generator/pgmcrater
index 1c22ed70..c66f5576 100755
--- a/generator/pgmcrater
+++ b/generator/pgmcrater
@@ -37,6 +37,26 @@ use strict;
 
 use Getopt::Long;
 
+sub doVersionHack($) {
+    my ($argvR) = @_;
+
+    my $arg1 = $argvR->[0];
+
+    if (defined($arg1) && (($arg1 eq "--version") || ($arg1 eq "-version"))) {
+        my $termStatus = system('pamcrater', '--version');
+        exit($termStatus == 0 ? 0 : 1);
+    }
+}
+
+
+##############################################################################
+#
+#  MAINLINE
+#
+##############################################################################
+
+doVersionHack(\@ARGV);
+
 my @pgmcraterArgv = @ARGV;
 
 my $validOptions = GetOptions(
diff --git a/generator/pgmkernel.c b/generator/pgmkernel.c
index ec634c16..37072c38 100644
--- a/generator/pgmkernel.c
+++ b/generator/pgmkernel.c
@@ -223,11 +223,13 @@ main(int argc, const char * argv[]) {
 
         unsigned int col;
         for (col = 0; col < (cmdline.cols +1) / 2; ++col) {
+            double const epsilon = 1e-15;
             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));
+            gray const grayval =
+                ROUNDU(cmdline.maxval * (0.5 + normalized + epsilon));
 
             halfKernel[arow][col                   ] = grayval;
             halfKernel[arow][cmdline.cols - col - 1] = grayval;
diff --git a/generator/pgmmake.c b/generator/pgmmake.c
index f8f8b09c..ae706639 100644
--- a/generator/pgmmake.c
+++ b/generator/pgmmake.c
@@ -1,13 +1,18 @@
+#include <stdlib.h>
+#include <string.h>
+
 #include "pm_c_util.h"
 #include "mallocvar.h"
 #include "shhopt.h"
 #include "pgm.h"
 
-struct cmdlineInfo {
+
+
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    gray grayLevel;
+    double grayLevel;
     unsigned int cols;
     unsigned int rows;
     gray maxval;
@@ -15,9 +20,37 @@ struct cmdlineInfo {
 
 
 
+static double
+grayLevelFromArg(const char * const arg) {
+
+    double retval;
+
+    if (strlen(arg) < 1)
+        pm_error("Gray level argument is a null string");
+    else {
+        char * endPtr;
+
+        retval = strtod(arg, &endPtr);
+
+        if (*endPtr != '\0')
+            pm_error("Gray level argument '%s' is not a floating point number",
+                     arg);
+
+        if (retval < 0.0)
+            pm_error("You can't have a negative gray level (%f)", retval);
+        if (retval > 1.0)
+            pm_error("Gray level must be in the range [0.0, 1.0].  "
+                     "You specified %f", retval);
+
+    }
+    return retval;
+}
+
+
+
 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
@@ -34,7 +67,7 @@ parseCommandLine(int argc, char ** argv,
     unsigned int maxvalSpec;
     unsigned int option_def_index;
 
-    MALLOCARRAY(option_def, 100);
+    MALLOCARRAY_NOFAIL(option_def, 100);
 
     option_def_index = 0;   /* incremented by OPTENTRY */
     OPTENT3(0,   "maxval",    OPT_UINT, &cmdlineP->maxval, &maxvalSpec,    0);
@@ -43,11 +76,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 */
 
-    pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
-    free (option_def);
-
     if (!maxvalSpec)
         cmdlineP->maxval = PGM_MAXMAXVAL;
     else {
@@ -65,37 +96,36 @@ parseCommandLine(int argc, char ** argv,
         pm_error("Only 3 arguments allowed: gray level, width, height.  "
                  "You specified %d", argc-1);
     else {
-        double const grayLevel = atof(argv[1]);
-        if (grayLevel < 0.0)
-            pm_error("You can't have a negative gray level (%f)", grayLevel);
-        if (grayLevel > 1.0)
-            pm_error("Gray level must be in the range [0.0, 1.0].  "
-                     "You specified %f", grayLevel);
-        cmdlineP->grayLevel = ROUNDU(grayLevel * cmdlineP->maxval);
+        cmdlineP->grayLevel = grayLevelFromArg(argv[1]);
         cmdlineP->cols = pm_parse_width(argv[2]);
         cmdlineP->rows = pm_parse_height(argv[3]);
     }
+    free(option_def);
 }
 
 
 
 int
-main(int argc, char *argv[]) {
+main(int argc, const char ** const argv) {
 
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
     gray * grayrow;
     unsigned int col, row;
+    gray grayLevel;
 
-    pgm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
+    grayLevel = pgm_unnormalize(cmdline.grayLevel, cmdline.maxval);
+
     pgm_writepgminit(stdout, cmdline.cols, cmdline.rows, cmdline.maxval, 0);
+
     grayrow = pgm_allocrow(cmdline.cols);
 
     /* All rows are identical.  Fill once. */
     for (col = 0; col < cmdline.cols; ++col)
-        grayrow[col] = cmdline.grayLevel;
+        grayrow[col] = grayLevel;
 
     for (row = 0; row < cmdline.rows; ++row)
         pgm_writepgmrow(stdout, grayrow, cmdline.cols, cmdline.maxval, 0);
@@ -105,3 +135,5 @@ main(int argc, char *argv[]) {
 
     return 0;
 }
+
+
diff --git a/generator/pgmnoise.c b/generator/pgmnoise.c
index 442edc59..40d0e189 100644
--- a/generator/pgmnoise.c
+++ b/generator/pgmnoise.c
@@ -56,7 +56,7 @@ parseCommandLine(int argc, const char ** const argv,
 
     if (maxvalSpec) {
         if (cmdlineP->maxval > PGM_OVERALLMAXVAL)
-            pm_error("Maxval too large: %u.  Maximu is %u", 
+            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");
@@ -70,7 +70,7 @@ parseCommandLine(int argc, const char ** const argv,
     else {
         int const width  = atoi(argv[1]);
         int const height = atoi(argv[2]);
-        
+
         if (width <= 0)
             pm_error("Width must be positive, not %d", width);
         else
@@ -90,12 +90,12 @@ 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.
 -----------------------------------------------------------------------------*/
@@ -114,7 +114,7 @@ randPool(unsigned int const digits) {
         hold >>= digits;
         len   -= digits;
     } else {              /* Load another 31 bits into hold */
-        hold    = rand(); 
+        hold    = rand();
         retval |= (hold << len);
         hold >>=  (digits - len);
         len = 31 - digits + len;
@@ -164,11 +164,11 @@ pgmnoise(FILE *       const ofP,
             unsigned int col;
             for (col = 0; col < cols; ++col)
                 destrow[col] = randPool(bitLen);
-        } 
-        else { 
+        }
+        else {
             unsigned int col;
             for (col = 0; col < cols; ++col)
-                destrow[col] = rand() % (maxval + 1); 
+                destrow[col] = rand() % (maxval + 1);
         }
         pgm_writepgmrow(ofP, destrow, cols, maxval, 0);
     }
@@ -181,7 +181,7 @@ pgmnoise(FILE *       const ofP,
 int
 main(int          argc,
      const char * argv[]) {
-    
+
     struct cmdlineInfo cmdline;
 
     pm_proginit(&argc, argv);
@@ -194,3 +194,6 @@ main(int          argc,
 
     return 0;
 }
+
+
+
diff --git a/generator/pgmramp.c b/generator/pgmramp.c
index 225542fe..db32b9f0 100644
--- a/generator/pgmramp.c
+++ b/generator/pgmramp.c
@@ -35,7 +35,7 @@ static void
 parseCommandLine(int argc, char ** argv,
                  struct cmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
-  Convert program invocation arguments (argc,argv) into a format the 
+  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.
 
@@ -111,12 +111,23 @@ parseCommandLine(int argc, char ** argv,
 
 
 
+static int
+diffu(unsigned int const subtrahend,
+      unsigned int const subtractor) {
+
+    return (int)subtrahend - (int)subtractor;
+
+    /* (Not the conventional terminology, but better) */
+}
+
+
+
 int
 main(int argc, char *argv[]) {
 
     struct cmdlineInfo cmdline;
     gray *grayrow;
-    int rowso2, colso2;
+    unsigned int rowso2, colso2;
     unsigned int row;
 
     pgm_init( &argc, argv );
@@ -149,20 +160,17 @@ main(int argc, char *argv[]) {
                         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;
+                float const r = fabs((float)diffu(rowso2, row)) / rowso2;
+                float const c = fabs((float)diffu(colso2, col)) / colso2;
                 grayrow[col] =
                     cmdline.maxval - (r + c) / 2.0 * cmdline.maxval;
             } break;
 
             case RT_ELLIP: {
-                float const r = fabs((int)(rowso2 - row)) / rowso2;
-                float const c = fabs((int)(colso2 - col)) / colso2;
-                float v;
+                float const r = fabs((float)diffu(rowso2, row)) / rowso2;
+                float const c = fabs((float)diffu(colso2, col)) / colso2;
+                float const v = MAX(0.0f, MIN(1.0f, SQR(r) + SQR(c)));
 
-                v = r * r + c * c;
-                if ( v < 0.0 ) v = 0.0;
-                else if ( v > 1.0 ) v = 1.0;
                 grayrow[col] = cmdline.maxval - v * cmdline.maxval;
             } break;
             }
@@ -174,3 +182,6 @@ main(int argc, char *argv[]) {
     pm_close(stdout);
     return 0;
 }
+
+
+
diff --git a/generator/ppmcie.c b/generator/ppmcie.c
index 717ed13b..86325ba6 100644
--- a/generator/ppmcie.c
+++ b/generator/ppmcie.c
@@ -10,7 +10,7 @@
     granted,  without any conditions or restrictions.  This software is
     provided "as is" without express or implied warranty.
 
-    This program was called cietoppm in Walker's original work.  
+    This program was called cietoppm in Walker's original work.
     Because "cie" is not a graphics format, Bryan changed the name
     when he integrated it into the Netpbm package in March 2000.
 */
@@ -294,10 +294,10 @@ static struct colorSystem const
 */
 static struct colorSystem Customsystem = {
     "Custom",
-    0.64,  0.33,  0.30,  0.60,  0.15,  0.06,  
+    0.64,  0.33,  0.30,  0.60,  0.15,  0.06,
     IlluminantD65, GAMMA_REC709
 };
-    
+
 
 
 static void
@@ -344,11 +344,11 @@ xyz_to_rgb(const struct colorSystem * const cs,
     which   sums  to  the  desired  chromaticity.   If  the  requested
     chromaticity falls outside the  Maxwell  triangle  (color  gamut)
     formed  by the three primaries, one of the r, g, or b weights will
-    be negative.  
+    be negative.
 
     Caller can use constrain_rgb() to desaturate an outside-gamut
     color to the closest representation within the available
-    gamut. 
+    gamut.
 -----------------------------------------------------------------------------*/
     double xr, yr, zr, xg, yg, zg, xb, yb, zb;
     double xw, yw, zw;
@@ -542,7 +542,7 @@ drawTongueOutline(pixel ** const pixels,
 
         computeMonochromeColorLocation(wavelength, pxcols, pxrows, upvp,
                                        &icx, &icy);
-        
+
         if (wavelength > 380)
             ppmd_line(pixels, pixcols, pixrows, maxval,
                       B(lx, ly), B(icx, icy),
@@ -589,7 +589,7 @@ findTongue(pixel ** const pixels,
         int const leftEdge = i;
 
         *presentP = true;
-        
+
         for (j = pxcols - 1;
              j >= leftEdge && PPM_GETR(Bixels(row, j)) == 0;
              --j);
@@ -651,12 +651,12 @@ fillInTongue(pixel **                   const pixels,
                 xyz_to_rgb(cs, cx, cy, cz, &jr, &jg, &jb);
 
                 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;
 
@@ -688,7 +688,7 @@ drawYAxis(pixel **     const pixels,
           unsigned int const xBias,
           unsigned int const yBias,
           pixel        const axisColor) {
-              
+
     unsigned int const pxrows = pixrows - yBias;
 
     ppmd_line(pixels, pixcols, pixrows, maxval,
@@ -706,7 +706,7 @@ drawXAxis(pixel **     const pixels,
           unsigned int const xBias,
           unsigned int const yBias,
           pixel        const axisColor) {
-              
+
     unsigned int const pxcols = pixcols - xBias;
     unsigned int const pxrows = pixrows - yBias;
 
@@ -778,7 +778,7 @@ tickY(pixel **     const pixels,
         /* Pixel row where the top of the tick goes */
     unsigned int const tickThickness = Sz(3);
         /* Thickness of the tick in pixels */
-    
+
     char s[20];
 
     assert(tenth < 10);
@@ -970,7 +970,7 @@ plotBlackBodyCurve(pixel **                   const pixels,
 
                 /* Label selected tick marks with decreasing density. */
 
-                if (t <= 5000.1 || (t > 5000.0 && 
+                if (t <= 5000.1 || (t > 5000.0 &&
                                     ((((int) t) % 5000) == 0) &&
                                     t != 20000.0)) {
                     char bb[20];
@@ -980,7 +980,7 @@ plotBlackBodyCurve(pixel **                   const pixels,
                               B(lx - Sz(12), ly - Sz(4)), Sz(6), 0, bb,
                               PPMD_NULLDRAWPROC, (char *) &rgbcolor);
                 }
-  
+
             }
         }
         lx = xb;
@@ -998,7 +998,7 @@ overlappingLegend(bool const upvp,
 
     if (upvp)
         retval = (waveLength == 430 || waveLength == 640);
-    else 
+    else
         retval = (waveLength == 460 || waveLength == 630 || waveLength == 640);
     return retval;
 }
@@ -1054,7 +1054,7 @@ plotMonochromeWavelengths(
             /* Draw the tick mark */
             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))); 
+            ty = icy + ((x < 520) ? 0 : ((x >= 535) ? Sz(-1) : Sz(-2)));
             ppmd_line(pixels, pixcols, pixrows, maxval,
                       B(icx, icy), B(tx, ty),
                       PPMD_NULLDRAWPROC, (char *) &rgbcolor);
@@ -1113,7 +1113,7 @@ writeLabel(pixel **                   const pixels,
     char sysdesc[256];
 
     PPM_ASSIGN(rgbcolor, maxval, maxval, maxval);
-    
+
     pm_snprintf(sysdesc, sizeof(sysdesc),
                 "System: %s\n"
                 "Primary illuminants (X, Y)\n"
@@ -1198,9 +1198,9 @@ main(int          argc,
         } else if (pm_keymatch(argv[argn], "-smpte", 2)) {
             cs = &SMPTEsystem;
         } else if (pm_keymatch(argv[argn], "-hdtv", 2)) {
-            cs = &HDTVsystem;                 
+            cs = &HDTVsystem;
         } else if (pm_keymatch(argv[argn], "-cie", 1)) {
-            cs = &CIEsystem;                 
+            cs = &CIEsystem;
         } else if (pm_keymatch(argv[argn], "-black", 3)) {
             showBlack = true;         /* Show black body curve */
         } else if (pm_keymatch(argv[argn], "-wpoint", 2)) {
diff --git a/generator/ppmforge.c b/generator/ppmforge.c
index 8ea86429..390180e3 100644
--- a/generator/ppmforge.c
+++ b/generator/ppmforge.c
@@ -31,7 +31,7 @@
 
 */
 
-#define _XOPEN_SOURCE   /* get M_PI in math.h */
+#define _XOPEN_SOURCE 500  /* get M_PI in math.h */
 
 #include <math.h>
 #include <assert.h>
diff --git a/generator/ppmpat.c b/generator/ppmpat.c
index fe1a1d27..908c200f 100644
--- a/generator/ppmpat.c
+++ b/generator/ppmpat.c
@@ -10,15 +10,21 @@
 ** implied warranty.
 */
 
-#define _XOPEN_SOURCE  /* get M_PI in math.h */
+#define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
+                           /* get M_PI in math.h */
+#define _BSD_SOURCE  /* Make sure strdup() is in <string.h> */
+#define SPIROGRAPHS 0   /* Spirograph to be added soon */
 
 #include <assert.h>
 #include <math.h>
 #include <limits.h>
+#include <string.h>
 
 #include "pm_c_util.h"
 #include "mallocvar.h"
 #include "shhopt.h"
+#include "nstring.h"
 #include "ppm.h"
 #include "ppmdraw.h"
 
@@ -28,28 +34,138 @@ typedef enum {
     PAT_GINGHAM3,
     PAT_MADRAS,
     PAT_TARTAN,
+    PAT_ARGYLE1,
+    PAT_ARGYLE2,
     PAT_POLES,
     PAT_SQUIG,
     PAT_CAMO,
-    PAT_ANTICAMO
-} pattern;
+    PAT_ANTICAMO,
+    PAT_SPIRO1,
+    PAT_SPIRO2,
+    PAT_SPIRO3
+} Pattern;
 
-struct cmdlineInfo {
+typedef struct {
+/*----------------------------------------------------------------------------
+   An ordered list of colors with a cursor.
+-----------------------------------------------------------------------------*/
+    unsigned int count;
+    unsigned int index;
+        /* Current position in the list */
+    pixel *      color;
+        /* Malloced array 'count' in size. */
+} ColorTable;
+
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    pattern basePattern;
+    Pattern      basePattern;
     unsigned int width;
     unsigned int height;
+    unsigned int colorSpec;
+    ColorTable   colorTable;
     unsigned int randomseed;
     unsigned int randomseedSpec;
 };
 
 
+static void
+validateColorCount(Pattern      const basePattern,
+                   unsigned int const colorCount) {
+
+    if (colorCount == 0)
+        pm_error("-color: no colors specified");
+
+    switch (basePattern) {
+    case PAT_GINGHAM2:
+    case PAT_ARGYLE1:
+    case PAT_SPIRO1:
+        if (colorCount != 2)
+            pm_error("Wrong number of colors: %u. "
+                     "2 colors are required for the specified pattern.",
+                     colorCount);
+        break;
+    case PAT_GINGHAM3:
+    case PAT_MADRAS:
+    case PAT_TARTAN:
+    case PAT_ARGYLE2:
+        if (colorCount != 3)
+            pm_error("Wrong number of colors: %u. "
+                     "3 colors are required for the specified pattern.",
+                     colorCount);
+        break;
+    case PAT_POLES:
+        if (colorCount < 2)
+            pm_error("Too few colors: %u. "
+                     "At least 2 colors are required "
+                     "for the specified pattern.",
+                     colorCount);
+        break;
+    case PAT_SQUIG:
+    case PAT_CAMO:
+    case PAT_ANTICAMO:
+        if (colorCount < 3)
+            pm_error("Wrong number of colors: %u. "
+                     "At least 3 colors are required "
+                     "for the specified pattern.",
+                     colorCount);
+        break;
+
+    case PAT_SPIRO2:
+    case PAT_SPIRO3:
+    default:
+        pm_error("INTERNAL ERROR.");
+    }
+}
+
+
+
+static void
+parseColorOpt(const char ** const colorText,
+              ColorTable  * const colorTableP,
+              Pattern       const basePattern) {
+/*----------------------------------------------------------------------------
+    String-list argument to -color is a comma-separated array of
+    color names or values, e.g.:
+    "-color=red,white,blue"
+    "-color=rgb:ff/ff/ff,rgb:00/00/00,rgb:80/80/ff"
+
+    Input:
+      Color name/value string-list: colorText[]
+
+    Output values:
+      Color array: colorTableP->color[]
+      Number of colors found: colorTableP->colors
+----------------------------------------------------------------------------*/
+    unsigned int colorCount;
+    unsigned int i;
+    pixel * inColor;
+
+    for (colorCount = 0; colorText[colorCount] != NULL; ++colorCount)
+        ;
+
+    MALLOCARRAY(inColor, colorCount);
+
+    if (!inColor)
+        pm_error("Failed to allocate table space for %u colors "
+                 "specified by -color", colorCount);
+
+    for (i = 0; i < colorCount; ++i)
+        inColor[i] = ppm_parsecolor(colorText[i], PPM_MAXMAXVAL);
+
+    validateColorCount(basePattern, colorCount);
+
+    colorTableP->count = colorCount;
+    colorTableP->index = 0;  /* initial value */
+    colorTableP->color = inColor;
+}
+
+
 
 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.
@@ -60,15 +176,21 @@ parseCommandLine(int argc, const char ** argv,
     optStruct3 opt;
 
     unsigned int option_def_index;
+    const char ** colorText;
     unsigned int basePatternCount;
     unsigned int gingham2;
     unsigned int gingham3;
     unsigned int madras;
     unsigned int tartan;
+    unsigned int argyle1;
+    unsigned int argyle2;
     unsigned int poles;
     unsigned int squig;
     unsigned int camo;
     unsigned int anticamo;
+    unsigned int spiro1;
+    unsigned int spiro2;
+    unsigned int spiro3;
 
     MALLOCARRAY_NOFAIL(option_def, 100);
 
@@ -85,6 +207,10 @@ parseCommandLine(int argc, const char ** argv,
             &madras,     0);
     OPTENT3(0, "tartan",        OPT_FLAG,   NULL,
             &tartan,     0);
+    OPTENT3(0, "argyle1",       OPT_FLAG,   NULL,
+            &argyle1,     0);
+    OPTENT3(0, "argyle2",       OPT_FLAG,   NULL,
+            &argyle2,     0);
     OPTENT3(0, "poles",         OPT_FLAG,   NULL,
             &poles,      0);
     OPTENT3(0, "squig",         OPT_FLAG,   NULL,
@@ -93,7 +219,19 @@ parseCommandLine(int argc, const char ** argv,
             &camo,       0);
     OPTENT3(0, "anticamo",      OPT_FLAG,   NULL,
             &anticamo,   0);
-    OPTENT3(0, "randomseed",    OPT_UINT,   &cmdlineP->randomseed,
+#if SPIROGRAPHS != 0
+    OPTENT3(0, "spiro1",        OPT_FLAG,   NULL,
+            &spiro1,     0);
+    OPTENT3(0, "spiro2",        OPT_FLAG,   NULL,
+            &spiro1,     0);
+    OPTENT3(0, "spiro3",        OPT_FLAG,   NULL,
+            &spiro1,     0);
+#else
+    spiro1 = spiro2 = spiro3 = 0;
+#endif
+    OPTENT3(0, "color",         OPT_STRINGLIST, &colorText,
+            &cmdlineP->colorSpec,           0);
+    OPTENT3(0, "randomseed",    OPT_UINT,       &cmdlineP->randomseed,
             &cmdlineP->randomseedSpec,      0);
 
     opt.opt_table = option_def;
@@ -102,16 +240,14 @@ parseCommandLine(int argc, const char ** argv,
 
     pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+    free(option_def);
 
     basePatternCount =
-        gingham2 +
-        gingham3 +
-        madras +
-        tartan +
+        gingham2 + gingham3 + madras + tartan + argyle1 + argyle2 +
         poles +
         squig +
-        camo +
-        anticamo;
+        camo + anticamo +
+        spiro1 + spiro2 + spiro3;
 
     if (basePatternCount < 1)
         pm_error("You must specify a base pattern option such as -gingham2");
@@ -127,6 +263,10 @@ parseCommandLine(int argc, const char ** argv,
             cmdlineP->basePattern = PAT_MADRAS;
         else if (tartan)
             cmdlineP->basePattern = PAT_TARTAN;
+        else if (argyle1)
+            cmdlineP->basePattern = PAT_ARGYLE1;
+        else if (argyle2)
+            cmdlineP->basePattern = PAT_ARGYLE2;
         else if (poles)
             cmdlineP->basePattern = PAT_POLES;
         else if (squig)
@@ -135,9 +275,22 @@ parseCommandLine(int argc, const char ** argv,
             cmdlineP->basePattern = PAT_CAMO;
         else if (anticamo)
             cmdlineP->basePattern = PAT_ANTICAMO;
+        else if (spiro1)
+            cmdlineP->basePattern = PAT_SPIRO1;
+        else if (spiro2)
+            cmdlineP->basePattern = PAT_SPIRO2;
+        else if (spiro3)
+            cmdlineP->basePattern = PAT_SPIRO3;
         else
             assert(false);  /* Every possibility is accounted for */
     }
+
+    if (cmdlineP->colorSpec) {
+        parseColorOpt(colorText, &cmdlineP->colorTable, cmdlineP->basePattern);
+        free(colorText);
+    } else
+        cmdlineP->colorTable.count = 0;
+
     if (argc-1 != 2)
         pm_error("You must specify 2 non-option arguments: width and height "
                  "in pixels.  You specified %u", argc-1);
@@ -150,7 +303,15 @@ parseCommandLine(int argc, const char ** argv,
         if (cmdlineP->height < 1)
             pm_error("Height must be at least 1 pixel");
     }
-    free(option_def);
+}
+
+
+
+static void
+freeCmdline(struct CmdlineInfo const cmdline) {
+
+    if (cmdline.colorSpec)
+        free(cmdline.colorTable.color);
 }
 
 
@@ -169,7 +330,7 @@ validateComputableDimensions(unsigned int const cols,
       PPMD functions use signed integers for pixel positions
       (because they allow you to specify points off the canvas).
     */
-      
+
     if (cols > INT_MAX/4 || rows > INT_MAX/4 || rows > INT_MAX/cols)
         pm_error("Width and/or height are way too large: %u x %u",
                  cols, rows);
@@ -187,7 +348,7 @@ randomColor(pixval const maxval) {
                rand() % (maxval + 1),
                rand() % (maxval + 1)
         );
-    
+
     return p;
 }
 
@@ -242,12 +403,12 @@ averageTwoColors(pixel const p1,
 static ppmd_drawproc average_drawproc;
 
 static void
-average_drawproc(pixel **     const pixels, 
-                 int          const cols, 
-                 int          const rows, 
-                 pixval       const maxval, 
-                 int          const col, 
-                 int          const row, 
+average_drawproc(pixel **     const pixels,
+                 int          const cols,
+                 int          const rows,
+                 pixval       const maxval,
+                 int          const col,
+                 int          const row,
                  const void * const clientdata) {
 
     if (col >= 0 && col < cols && row >= 0 && row < rows)
@@ -257,6 +418,29 @@ average_drawproc(pixel **     const pixels,
 
 
 
+static void
+nextColor(ColorTable * const colorTableP) {
+/*----------------------------------------------------------------------------
+  Increment index, return it to 0 if we have used all the colors
+-----------------------------------------------------------------------------*/
+    colorTableP->index = (colorTableP->index + 1) % colorTableP->count;
+}
+
+
+
+static void
+nextColorBg(ColorTable * const colorTableP) {
+/*----------------------------------------------------------------------------
+  Increment index, return it to 1 if we have used all the colors (color[0] is
+  the background color, it's outside the cycle)
+-----------------------------------------------------------------------------*/
+    colorTableP->index = colorTableP->index % (colorTableP->count - 1) + 1;
+        /* Works when index == 0, but no callers rely on this. */
+
+}
+
+
+
 /*----------------------------------------------------------------------------
    Camouflage stuff
 -----------------------------------------------------------------------------*/
@@ -282,7 +466,7 @@ randomAnticamoColor(pixval const maxval) {
     case 3:
         PPM_ASSIGN(p, rand() % v2, rand() % v1 + v3, rand() % v2);
         break;
-        
+
     case 4:
     case 5:
         PPM_ASSIGN(p, rand() % v2, rand() % v2, rand() % v1 + v3);
@@ -306,7 +490,7 @@ randomAnticamoColor(pixval const maxval) {
         PPM_ASSIGN(p, rand() % v1 + v3, rand() % v1 + v3, rand() % v2);
         break;
     }
-    
+
     return p;
 }
 
@@ -335,7 +519,7 @@ randomCamoColor(pixval const maxval) {
         /* dark green */
         PPM_ASSIGN(p, rand() % v2, rand() % v2 + 3 * v1, rand() % v2);
         break;
-    
+
     case 6:
     case 7:
         /* brown */
@@ -362,15 +546,18 @@ rnduni(void) {
 
 
 static void
-clearBackground(pixel **     const pixels,
-                unsigned int const cols,
-                unsigned int const rows,
-                pixval       const maxval,
-                bool         const antiflag) {
+clearBackgroundCamo(pixel **     const pixels,
+                    unsigned int const cols,
+                    unsigned int const rows,
+                    pixval       const maxval,
+                    ColorTable * const colorTableP,
+                    bool         const antiflag) {
 
     pixel color;
 
-    if (antiflag)
+    if (colorTableP->count > 0) {
+        color = colorTableP->color[0];
+    } else if (antiflag)
         color = randomAnticamoColor(maxval);
     else
         color = randomCamoColor(maxval);
@@ -381,23 +568,29 @@ clearBackground(pixel **     const pixels,
 }
 
 
+
 static void
 camoFill(pixel **         const pixels,
          unsigned int     const cols,
          unsigned int     const rows,
          pixval           const maxval,
          struct fillobj * const fh,
+         ColorTable     * const colorTableP,
          bool             const antiflag) {
-         
+
     pixel color;
 
-    if (antiflag)
+    if (colorTableP->count > 0) {
+        assert(colorTableP->index < colorTableP->count);
+        color = colorTableP->color[colorTableP->index];
+        nextColorBg(colorTableP);
+    } else if (antiflag)
         color = randomAnticamoColor(maxval);
     else
         color = randomCamoColor(maxval);
 
     ppmd_fill(pixels, cols, rows, maxval, fh, PPMD_NULLDRAWPROC, &color);
-}        
+}
 
 
 
@@ -428,9 +621,9 @@ computeXsYs(int *        const xs,
     double const b = rnduni() * (MAX_ELLIPSE_FACTOR - MIN_ELLIPSE_FACTOR) +
         MIN_ELLIPSE_FACTOR;
     double const theta = rnduni() * 2.0 * M_PI;
-    
+
     unsigned int p;
-        
+
     for (p = 0; p < pointCt; ++p) {
         double const c = rnduni() * (MAX_POINT_FACTOR - MIN_POINT_FACTOR) +
             MIN_POINT_FACTOR;
@@ -448,14 +641,20 @@ static void
 camo(pixel **     const pixels,
      unsigned int const cols,
      unsigned int const rows,
+     ColorTable * const colorTableP,
      pixval       const maxval,
      bool         const antiflag) {
 
-    unsigned int const n = (rows * cols) / (BLOBRAD * BLOBRAD) * 5;
+    unsigned int const n = (rows * cols) / SQR(BLOBRAD) * 5;
 
     unsigned int i;
 
-    clearBackground(pixels, cols, rows, maxval, antiflag);
+    clearBackgroundCamo(pixels, cols, rows, maxval, colorTableP, antiflag);
+
+    if (colorTableP->count > 0) {
+        assert(colorTableP->count > 1);
+        colorTableP->index = 1;  /* Foreground colors start at 1 */
+    }
 
     for (i = 0; i < n; ++i) {
         unsigned int const pointCt =
@@ -475,9 +674,9 @@ camo(pixel **     const pixels,
         ppmd_polyspline(
             pixels, cols, rows, maxval, x0, y0, pointCt, xs, ys, x0, y0,
             ppmd_fill_drawproc, fh);
-        
-        camoFill(pixels, cols, rows, maxval, fh, antiflag);
-        
+
+        camoFill(pixels, cols, rows, maxval, fh, colorTableP, antiflag);
+
         ppmd_fill_destroy(fh);
     }
 }
@@ -485,19 +684,21 @@ camo(pixel **     const pixels,
 
 
 /*----------------------------------------------------------------------------
-   Gingham stuff
+   Plaid patterns
 -----------------------------------------------------------------------------*/
 
-
-
 static void
 gingham2(pixel **     const pixels,
          unsigned int const cols,
          unsigned int const rows,
+         ColorTable   const colorTable,
          pixval       const maxval) {
 
-    pixel const backcolor = randomDarkColor(maxval);
-    pixel const forecolor = randomBrightColor(maxval);
+    bool  const colorSpec = (colorTable.count > 0);
+    pixel const backcolor = colorSpec ?
+                            colorTable.color[0] : randomDarkColor(maxval);
+    pixel const forecolor = colorSpec ?
+                            colorTable.color[1] : randomBrightColor(maxval);
     unsigned int const colso2 = cols / 2;
     unsigned int const rowso2 = rows / 2;
 
@@ -524,15 +725,19 @@ static void
 gingham3(pixel **     const pixels,
          unsigned int const cols,
          unsigned int const rows,
+         ColorTable   const colorTable,
          pixval       const maxval) {
 
+    bool  const colorSpec = (colorTable.count > 0);
+    pixel const backcolor = colorSpec ?
+                            colorTable.color[0] : randomDarkColor(maxval);
+    pixel const fore1color = colorSpec ?
+                            colorTable.color[1] : randomBrightColor(maxval);
+    pixel const fore2color = colorSpec ?
+                            colorTable.color[2] : randomBrightColor(maxval);
     unsigned int const colso4 = cols / 4;
     unsigned int const rowso4 = rows / 4;
 
-    pixel const backcolor  = randomDarkColor(maxval);
-    pixel const fore1color = randomBrightColor(maxval);
-    pixel const fore2color = randomBrightColor(maxval);
-
     /* Warp. */
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 0, colso4, rows, PPMD_NULLDRAWPROC,
@@ -568,8 +773,16 @@ static void
 madras(pixel **     const pixels,
        unsigned int const cols,
        unsigned int const rows,
+       ColorTable   const colorTable,
        pixval       const maxval) {
 
+    bool  const colorSpec = (colorTable.count > 0);
+    pixel const backcolor = colorSpec ?
+                            colorTable.color[0] : randomDarkColor(maxval);
+    pixel const fore1color = colorSpec ?
+                            colorTable.color[1] : randomBrightColor(maxval);
+    pixel const fore2color = colorSpec ?
+                            colorTable.color[2] : randomBrightColor(maxval);
     unsigned int const cols2  = cols * 2 / 44;
     unsigned int const rows2  = rows * 2 / 44;
     unsigned int const cols3  = cols * 3 / 44;
@@ -580,9 +793,6 @@ madras(pixel **     const pixels,
     unsigned int const rows6a = rows12 / 2;
     unsigned int const cols6b = cols12 - cols6a;
     unsigned int const rows6b = rows12 - rows6a;
-    pixel const backcolor  = randomDarkColor(maxval);
-    pixel const fore1color = randomBrightColor(maxval);
-    pixel const fore2color = randomBrightColor(maxval);
 
     /* Warp. */
     ppmd_filledrectangle(
@@ -631,7 +841,7 @@ madras(pixel **     const pixels,
         pixels, cols, rows, maxval, 9 * cols2 + 3 * cols3 + cols6a + cols6b, 0,
         cols2, rows, PPMD_NULLDRAWPROC, &backcolor);
     ppmd_filledrectangle(
-        pixels, cols, rows, maxval, 10 * cols2 + 3 * cols3 + cols6a + cols6b, 
+        pixels, cols, rows, maxval, 10 * cols2 + 3 * cols3 + cols6a + cols6b,
         0, cols3, rows, PPMD_NULLDRAWPROC, &fore1color);
 
     /* Woof. */
@@ -681,7 +891,7 @@ madras(pixel **     const pixels,
         pixels, cols, rows, maxval, 0, 9 * rows2 + 3 * rows3 + rows6a + rows6b,
         cols, rows2, average_drawproc, &backcolor);
     ppmd_filledrectangle(
-        pixels, cols, rows, maxval, 0, 
+        pixels, cols, rows, maxval, 0,
         10 * rows2 + 3 * rows3 + rows6a + rows6b,
         cols, rows3, average_drawproc, &fore2color);
 }
@@ -692,8 +902,16 @@ static void
 tartan(pixel **     const pixels,
        unsigned int const cols,
        unsigned int const rows,
+       ColorTable   const colorTable,
        pixval       const maxval) {
 
+    bool  const colorSpec = (colorTable.count > 0);
+    pixel const backcolor = colorSpec ?
+                            colorTable.color[0] : randomDarkColor(maxval);
+    pixel const fore1color = colorSpec ?
+                            colorTable.color[1] : randomBrightColor(maxval);
+    pixel const fore2color = colorSpec ?
+                            colorTable.color[2] : randomBrightColor(maxval);
     unsigned int const cols1  = cols / 22;
     unsigned int const rows1  = rows / 22;
     unsigned int const cols3  = cols * 3 / 22;
@@ -704,9 +922,6 @@ tartan(pixel **     const pixels,
     unsigned int const rows5a = rows10 / 2;
     unsigned int const cols5b = cols10 - cols5a;
     unsigned int const rows5b = rows10 - rows5a;
-    pixel const backcolor  = randomDarkColor(maxval);
-    pixel const fore1color = randomBrightColor(maxval);
-    pixel const fore2color = randomBrightColor(maxval);
 
     /* Warp. */
     ppmd_filledrectangle(
@@ -763,6 +978,71 @@ tartan(pixel **     const pixels,
 
 
 
+static void
+drawAndFillDiamond(pixel **     const pixels,
+                   unsigned int const cols,
+                   unsigned int const rows,
+                   pixval       const maxval,
+                   pixel        const forecolor) {
+
+    unsigned int const colso2 = cols / 2;
+    unsigned int const rowso2 = rows / 2;
+
+    ppmd_pathbuilder * const pathBuilderP = ppmd_pathbuilder_create();
+
+    ppmd_pathbuilder_setBegPoint(pathBuilderP,
+                 ppmd_makePoint (colso2, 0));
+
+    ppmd_pathbuilder_addLineLeg(pathBuilderP,
+                 ppmd_makeLineLeg(ppmd_makePoint(cols-1, rowso2)));
+    ppmd_pathbuilder_addLineLeg(pathBuilderP,
+                 ppmd_makeLineLeg(ppmd_makePoint(colso2, rows-1)));
+    ppmd_pathbuilder_addLineLeg(pathBuilderP,
+                 ppmd_makeLineLeg(ppmd_makePoint(0,      rowso2)));
+    ppmd_pathbuilder_addLineLeg(pathBuilderP,
+                 ppmd_makeLineLeg(ppmd_makePoint(colso2, 0)));
+
+    ppmd_fill_path(pixels, cols, rows, maxval,
+                   ppmd_pathbuilder_pathP(pathBuilderP), forecolor);
+}
+
+
+
+static void
+argyle(pixel **     const pixels,
+       unsigned int const cols,
+       unsigned int const rows,
+       ColorTable   const colorTable,
+       pixval       const maxval,
+       bool         const stripes) {
+
+    bool  const colorSpec = (colorTable.count > 0);
+    pixel const backcolor = colorSpec ?
+        colorTable.color[0] : randomDarkColor(maxval);
+    pixel const forecolor = colorSpec ?
+        colorTable.color[1] : randomBrightColor(maxval);
+
+    /* Fill canvas with background to start */
+    ppmd_filledrectangle(
+        pixels, cols, rows, maxval, 0, 0, cols, rows, PPMD_NULLDRAWPROC,
+        &backcolor);
+
+    drawAndFillDiamond(pixels, cols, rows, maxval, forecolor);
+
+    if (stripes) {
+         /* Connect corners with thin stripes */
+         pixel const stripecolor =
+             colorSpec ? colorTable.color[2] : randomBrightColor(maxval);
+
+         ppmd_line(pixels, cols, rows, maxval, 0, 0, cols-1, rows-1,
+              PPMD_NULLDRAWPROC, (char *) &stripecolor);
+         ppmd_line(pixels, cols, rows, maxval, cols-1, 0, 0, rows-1,
+              PPMD_NULLDRAWPROC, (char *) &stripecolor);
+    }
+}
+
+
+
 /*----------------------------------------------------------------------------
    Poles stuff
 -----------------------------------------------------------------------------*/
@@ -780,14 +1060,21 @@ placeAndColorPolesRandomly(int *        const xs,
                            unsigned int const cols,
                            unsigned int const rows,
                            pixval       const maxval,
+                           ColorTable * const colorTableP,
                            unsigned int const poleCt) {
 
     unsigned int i;
 
     for (i = 0; i < poleCt; ++i) {
+
         xs[i] = rand() % cols;
         ys[i] = rand() % rows;
-        colors[i] = randomBrightColor(maxval);
+
+        if (colorTableP->count > 0) {
+            colors[i] = colorTableP->color[colorTableP->index];
+            nextColor(colorTableP);
+        } else
+            colors[i] = randomBrightColor(maxval);
     }
 }
 
@@ -799,8 +1086,8 @@ assignInterpolatedColor(pixel * const resultP,
                         double  const dist1,
                         pixel   const color2,
                         double  const dist2) {
-    
-    if (dist1 == 0) 
+
+    if (dist1 == 0)
         /* pixel is a pole */
         *resultP = color1;
     else {
@@ -809,7 +1096,7 @@ assignInterpolatedColor(pixel * const resultP,
         pixval const r = (PPM_GETR(color1)*dist2 + PPM_GETR(color2)*dist1)/sum;
         pixval const g = (PPM_GETG(color1)*dist2 + PPM_GETG(color2)*dist1)/sum;
         pixval const b = (PPM_GETB(color1)*dist2 + PPM_GETB(color2)*dist1)/sum;
-        
+
         PPM_ASSIGN(*resultP, r, g, b);
     }
 }
@@ -820,15 +1107,17 @@ static void
 poles(pixel **     const pixels,
       unsigned int const cols,
       unsigned int const rows,
+      ColorTable * const colorTableP,
       pixval       const maxval) {
 
     unsigned int const poleCt = MAX(2, MIN(MAXPOLES, cols * rows / 30000));
-    
+
     int xs[MAXPOLES], ys[MAXPOLES];
     pixel colors[MAXPOLES];
     unsigned int row;
 
-    placeAndColorPolesRandomly(xs, ys, colors, cols, rows, maxval, poleCt);
+    placeAndColorPolesRandomly(xs, ys, colors, cols, rows, maxval,
+                               colorTableP, poleCt);
 
     /* Interpolate points */
 
@@ -871,11 +1160,15 @@ poles(pixel **     const pixels,
 #define SQ_POINTS 7
 #define SQ_MAXCIRCLE_POINTS 5000
 
-static int sq_circlecount;
-static pixel sq_colors[SQ_MAXCIRCLE_POINTS];
-static ppmd_point sq_offs[SQ_MAXCIRCLE_POINTS];
-
+struct Squig {
+    unsigned int circleCt;
+    pixel        color[SQ_MAXCIRCLE_POINTS];
+    ppmd_point   off[SQ_MAXCIRCLE_POINTS];
+};
 
+typedef struct {
+    struct Squig * squigP;
+} SqClientData;
 
 static void
 validateSquigAspect(unsigned int const cols,
@@ -884,7 +1177,7 @@ validateSquigAspect(unsigned int const cols,
     if (cols / rows >= 25 || rows / cols >= 25)
         pm_error("Image too narrow.  Aspect ratio: %u/%u=%f "
                  "is outside accepted range: 0.04 - 25.0",
-                 cols, rows, (float)cols/rows ); 
+                 cols, rows, (float)cols/rows );
 
 }
 
@@ -902,14 +1195,18 @@ vectorSum(ppmd_point const a,
 static ppmd_drawprocp sqMeasureCircleDrawproc;
 
 static void
-sqMeasureCircleDrawproc(pixel**      const pixels, 
-                        unsigned int const cols, 
-                        unsigned int const rows, 
-                        pixval       const maxval, 
+sqMeasureCircleDrawproc(pixel**      const pixels,
+                        unsigned int const cols,
+                        unsigned int const rows,
+                        pixval       const maxval,
                         ppmd_point   const p,
                         const void * const clientdata) {
 
-    sq_offs[sq_circlecount++] = p;
+    const SqClientData * const sqClientDataP = clientdata;
+
+    struct Squig * const squigP = sqClientDataP->squigP;
+
+    squigP->off[squigP->circleCt++] = p;
 }
 
 
@@ -917,19 +1214,46 @@ sqMeasureCircleDrawproc(pixel**      const pixels,
 static ppmd_drawprocp sqRainbowCircleDrawproc;
 
 static void
-sqRainbowCircleDrawproc(pixel **     const pixels, 
-                        unsigned int const cols, 
-                        unsigned int const rows, 
-                        pixval       const maxval, 
+sqRainbowCircleDrawproc(pixel **     const pixels,
+                        unsigned int const cols,
+                        unsigned int const rows,
+                        pixval       const maxval,
                         ppmd_point   const p,
                         const void * const clientdata) {
 
+    const SqClientData * const sqClientDataP = clientdata;
+
+    struct Squig * const squigP = sqClientDataP->squigP;
+
     unsigned int i;
 
-    for (i = 0; i < sq_circlecount; ++i)
+    for (i = 0; i < squigP->circleCt; ++i)
         ppmd_point_drawprocp(
-            pixels, cols, rows, maxval, vectorSum(p, sq_offs[i]),
-            &sq_colors[i]);
+            pixels, cols, rows, maxval, vectorSum(p, squigP->off[i]),
+            &squigP->color[i]);
+}
+
+
+
+static void
+chooseSqPoleColors(ColorTable * const colorTableP,
+                   pixval       const maxval,
+                   pixel *      const color1P,
+                   pixel *      const color2P,
+                   pixel *      const color3P) {
+
+    if (colorTableP->count > 0) {
+        *color1P = colorTableP->color[colorTableP->index];
+        nextColor(colorTableP);
+        *color2P = colorTableP->color[colorTableP->index];
+        nextColor(colorTableP);
+        *color3P = colorTableP->color[colorTableP->index];
+        nextColor(colorTableP);
+    } else {
+        *color1P = randomBrightColor(maxval);
+        *color2P = randomBrightColor(maxval);
+        *color3P = randomBrightColor(maxval);
+    }
 }
 
 
@@ -937,16 +1261,19 @@ sqRainbowCircleDrawproc(pixel **     const pixels,
 static void
 sqAssignColors(unsigned int const circlecount,
                pixval       const maxval,
+               ColorTable * const colorTableP,
                pixel *      const colors) {
 
-    pixel const rc1 = randomBrightColor(maxval);
-    pixel const rc2 = randomBrightColor(maxval);
-    pixel const rc3 = randomBrightColor(maxval);
     float const cco3 = (circlecount - 1) / 3.0;
 
+    pixel rc1;
+    pixel rc2;
+    pixel rc3;
     unsigned int i;
 
-    for (i = 0; i < circlecount ; ++i) {
+    chooseSqPoleColors(colorTableP, maxval, &rc1, &rc2, &rc3);
+
+    for (i = 0; i < circlecount; ++i) {
         if (i < cco3) {
             float const frac = (float)i/cco3;
             PPM_ASSIGN(colors[i],
@@ -984,14 +1311,19 @@ sqAssignColors(unsigned int const circlecount,
 
 
 static void
-clearImageToBlack(pixel **     const pixels,
-                  unsigned int const cols,
-                  unsigned int const rows,
-                  pixval       const maxval) {
+clearBackgroundSquig(pixel **     const pixels,
+                     unsigned int const cols,
+                     unsigned int const rows,
+                     ColorTable * const colorTableP,
+                     pixval       const maxval) {
 
     pixel color;
 
-    PPM_ASSIGN(color, 0, 0, 0);
+    if (colorTableP->count > 0) {
+        color = colorTableP->color[0];
+        colorTableP->index = 1;
+    } else
+        PPM_ASSIGN(color, 0, 0, 0);
 
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 0, cols, rows, PPMD_NULLDRAWPROC,
@@ -1009,7 +1341,7 @@ chooseWrapAroundPoint(unsigned int const cols,
                       ppmd_point * const p1P,
                       ppmd_point * const p2P,
                       ppmd_point * const p3P) {
-                      
+
     switch (rand() % 4) {
     case 0:
         p1P->x = rand() % cols;
@@ -1091,27 +1423,37 @@ static void
 squig(pixel **     const pixels,
       unsigned int const cols,
       unsigned int const rows,
+      ColorTable * const colorTableP,
       pixval       const maxval) {
 
     int i;
 
     validateSquigAspect(cols, rows);
-    
-    clearImageToBlack(pixels, cols, rows, maxval);
+
+    clearBackgroundSquig(pixels, cols, rows, colorTableP, maxval);
 
     /* Draw the squigs. */
     ppmd_setlinetype(PPMD_LINETYPE_NODIAGS);
     ppmd_setlineclip(0);
+
     for (i = SQUIGS; i > 0; --i) {
         unsigned int const radius = (cols + rows) / 2 / (25 + i * 2);
 
+        struct Squig squig;
+
+        SqClientData sqClientData;
+
         ppmd_point c[SQ_POINTS];
         ppmd_point p0, p1, p2, p3;
-        sq_circlecount = 0;
+
+        squig.circleCt = 0;
+
+        sqClientData.squigP = &squig;
+
         ppmd_circlep(pixels, cols, rows, maxval,
                      ppmd_makePoint(0, 0), radius,
-                     sqMeasureCircleDrawproc, NULL);
-        sqAssignColors(sq_circlecount, maxval, sq_colors);
+                     sqMeasureCircleDrawproc, &sqClientData);
+        sqAssignColors(squig.circleCt, maxval, colorTableP, squig.color);
 
         chooseWrapAroundPoint(cols, rows, &c[0], &c[SQ_POINTS-1],
                               &p0, &p1, &p2, &p3);
@@ -1131,13 +1473,13 @@ squig(pixel **     const pixels,
 
         ppmd_linep(
             pixels, cols, rows, maxval, p0, p1,
-            sqRainbowCircleDrawproc, NULL);
+            sqRainbowCircleDrawproc, &sqClientData);
         ppmd_polysplinep(
             pixels, cols, rows, maxval, p1, SQ_POINTS, c, p2,
-            sqRainbowCircleDrawproc, NULL);
+            sqRainbowCircleDrawproc, &sqClientData);
         ppmd_linep(
             pixels, cols, rows, maxval, p2, p3,
-            sqRainbowCircleDrawproc, NULL);
+            sqRainbowCircleDrawproc, &sqClientData);
     }
 }
 
@@ -1146,50 +1488,68 @@ squig(pixel **     const pixels,
 int
 main(int argc, const char ** argv) {
 
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
     pixel ** pixels;
 
     pm_proginit(&argc, argv);
-    
+
     parseCommandLine(argc, argv, &cmdline);
 
     validateComputableDimensions(cmdline.width, cmdline.height);
-    
+
     srand(cmdline.randomseedSpec ? cmdline.randomseed : pm_randseed());
 
     pixels = ppm_allocarray(cmdline.width, cmdline.height);
 
     switch (cmdline.basePattern) {
     case PAT_GINGHAM2:
-        gingham2(pixels, cmdline.width, cmdline.height, PPM_MAXMAXVAL);
+        gingham2(pixels, cmdline.width, cmdline.height,
+                 cmdline.colorTable, PPM_MAXMAXVAL);
         break;
 
     case PAT_GINGHAM3:
-        gingham3(pixels, cmdline.width, cmdline.height, PPM_MAXMAXVAL);
+        gingham3(pixels, cmdline.width, cmdline.height,
+                 cmdline.colorTable, PPM_MAXMAXVAL);
         break;
 
     case PAT_MADRAS:
-        madras(pixels, cmdline.width, cmdline.height, PPM_MAXMAXVAL);
+        madras(pixels, cmdline.width, cmdline.height,
+               cmdline.colorTable, PPM_MAXMAXVAL);
         break;
 
     case PAT_TARTAN:
-        tartan(pixels, cmdline.width, cmdline.height, PPM_MAXMAXVAL);
+        tartan(pixels, cmdline.width, cmdline.height,
+               cmdline.colorTable, PPM_MAXMAXVAL);
+        break;
+
+    case PAT_ARGYLE1:
+        argyle(pixels, cmdline.width, cmdline.height,
+               cmdline.colorTable, PPM_MAXMAXVAL, FALSE);
+        break;
+
+    case PAT_ARGYLE2:
+        argyle(pixels, cmdline.width, cmdline.height,
+               cmdline.colorTable, PPM_MAXMAXVAL, TRUE);
         break;
 
     case PAT_POLES:
-        poles(pixels, cmdline.width, cmdline.height, PPM_MAXMAXVAL);
+        poles(pixels, cmdline.width, cmdline.height,
+              &cmdline.colorTable, PPM_MAXMAXVAL);
         break;
 
     case PAT_SQUIG:
-        squig(pixels, cmdline.width, cmdline.height, PPM_MAXMAXVAL);
+        squig(pixels, cmdline.width, cmdline.height,
+              &cmdline.colorTable, PPM_MAXMAXVAL);
         break;
 
     case PAT_CAMO:
-        camo(pixels, cmdline.width, cmdline.height, PPM_MAXMAXVAL, 0);
+        camo(pixels, cmdline.width, cmdline.height,
+             &cmdline.colorTable, PPM_MAXMAXVAL, 0);
         break;
 
     case PAT_ANTICAMO:
-        camo(pixels, cmdline.width, cmdline.height, PPM_MAXMAXVAL, 1);
+        camo(pixels, cmdline.width, cmdline.height,
+             &cmdline.colorTable, PPM_MAXMAXVAL, 1);
         break;
 
     default:
@@ -1201,6 +1561,8 @@ main(int argc, const char ** argv) {
 
     ppm_freearray(pixels, cmdline.height);
 
+    freeCmdline(cmdline);
+
     return 0;
 }
 
diff --git a/generator/ppmrainbow b/generator/ppmrainbow
index c0568d9b..e8a329ff 100755
--- a/generator/ppmrainbow
+++ b/generator/ppmrainbow
@@ -25,31 +25,57 @@ exec perl -w -x -S -- "$0" "$@"
 #!/usr/bin/perl
 use strict;
 use Getopt::Long;
+use File::Temp;
 
 my ($FALSE, $TRUE) = (0,1);
 
 (my $myname = $0) =~ s#\A.*/##;
 
+
+
+sub doVersionHack($) {
+    my ($argvR) = @_;
+
+    my $arg1 = $argvR->[0];
+
+    if (defined($arg1) && (($arg1 eq "--version") || ($arg1 eq "-version"))) {
+        my $termStatus = system('pgmramp', '--version');
+        exit($termStatus == 0 ? 0 : 1);
+    }
+}
+
+
+
 sub fatal($) {
     my ($msg) = @_;
 
-    print(STDERR "$msg\n");
+    print(STDERR "ppmrainbow: $msg\n");
     exit(1);
 }
 
-my ($Twid, $Thgt, $tmpdir, $norepeat, $verbose);
+
+
+##############################################################################
+#
+#                                 MAINLINE
+#
+##############################################################################
+
+doVersionHack(\@ARGV);
+
+my ($Twid, $Thgt, $tmpdir, $repeat, $verbose);
 
 # set defaults
 $Twid = 600;
 $Thgt = 8;
 $tmpdir = $ENV{"TMPDIR"} || "/tmp";
-$norepeat = $FALSE;
+$repeat = $TRUE;
 $verbose = $FALSE;
 
 GetOptions("width=i"   => \$Twid,
            "height=i"  => \$Thgt,
            "tmpdir=s"  => \$tmpdir,
-           "norepeat!" => \$norepeat,
+           "repeat!"   => \$repeat,
            "verbose!"  => \$verbose);
 
 if ($Twid < 1 || $Thgt < 1) {
@@ -59,7 +85,7 @@ my $verboseCommand = $verbose ? "set -x;" : "";
 
 if (@ARGV < 1) {
     fatal("You must specify at least one color as an argument");
-} elsif (@ARGV < 2 && $norepeat) {
+} elsif (@ARGV < 2 && ! $repeat) {
     fatal("With the -norepeat option, you must specify at least two colors " .
           "as arguments.");
 }
@@ -67,14 +93,11 @@ if (@ARGV < 1) {
 my @colorlist;
 
 @colorlist = @ARGV;
-if (!$norepeat) {
+if ($repeat) {
     push @colorlist, $ARGV[0];
 }
 
-my $ourtmp = "$tmpdir/ppmrainbow$$";
-mkdir($ourtmp, 0777) or
-    die("Unable to create directory for temporary files '$ourtmp");
-
+my $ourtmp = File::Temp::tempdir("$tmpdir/ppmrainbowXXXX", UNLINK=>1);
 
 my $widthRemaining;
 my $n;
@@ -92,7 +115,7 @@ while (@colorlist >= 2) {
     my $rc = system("$verboseCommand pgmramp -lr $w $Thgt | " .
                     "pgmtoppm \"$colorlist[0]-$colorlist[1]\" >$outfile");
     if ($rc != 0) {
-        fatal("pgmramp|pgmtoppm failed.");
+        fatal("pgmramp|pgmtoppm pipe failed.");
     }
     $widthRemaining -= $w;
     $n++;
diff --git a/generator/ppmwheel.c b/generator/ppmwheel.c
index ef5021f9..29e4730c 100644
--- a/generator/ppmwheel.c
+++ b/generator/ppmwheel.c
@@ -16,142 +16,245 @@
 #include <string.h>
 #include <math.h>
 
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "shhopt.h"
 #include "ppm.h"
 
 #ifndef PI
 #define PI  3.14159265358979323846
 #endif
 
-#ifndef ABS
-#define ABS(a) ((a) < 0 ? -(a) : (a))
-#endif
 
-static void 
-hsv_rgb(double const in_h, double const in_s, double const in_v, 
-        double * const r, double * const g, double * const b) {
+
+typedef enum {WT_HUE_VAL, WT_HUE_SAT, WT_PPMCIRC} WheelType;
+
+
+struct CmdlineInfo {
+    unsigned int diameter;
+    WheelType    wheelType;
+    pixval       maxval;
+};
+
+
+
+static void
+parseCommandLine(int argc, const char **argv,
+                 struct CmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
-   This is a stripped down hsv->rgb converter that works only for
-   Saturation of zero.
+  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.
 -----------------------------------------------------------------------------*/
-    double h, s, v;
-
-    h = in_h < 0.0 ? 0.0 : in_h > 360.0 ? 360.0 : in_h;
-
-    v = in_v < 0.0 ? 0.0 : in_v > 1.0 ? 1.0 : in_v;
-
-    s = in_s < 0.0 ? 0.0 : in_s > 1.0 ? 1.0 : in_s;
-
-    if (s != 0.0)
-        pm_error("Internal error: non-zero saturation");
-
-    if (h <= 60.0) {          /* from red to yellow */
-        *r = 1.0;
-        *g = h / 60.0;
-        *b = 0.0;
-    } else if ( h <= 120.0 ) {   /* from yellow to green */
-        *r = 1.0 - (h - 60.0) / 60.0;
-        *g = 1.0;
-        *b = 0.0;
-    } else if ( h <= 180.0 ) {   /* from green to cyan */
-        *r = 0.0;
-        *g = 1.0;
-        *b = (h - 120.0) / 60.0;
-    } else if ( h <= 240.0 ) {    /* from cyan to blue */
-        *r = 0.0;
-        *g = 1.0 - (h - 180.0) / 60.0;
-        *b = 1.0;
-    } else if ( h <= 300.0) {    /* from blue to magenta */
-        *r = (h - 240.0) / 60.0;
-        *g = 0.0;
-        *b = 1.0;
-    } else {                      /* from magenta to red */
-        *r = 1.0;
-        *g = 0.0;
-        *b = 1.0 - (h - 300.0) / 60.0;
+    optEntry * option_def;
+        /* Instructions to OptParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int maxvalSpec, huevalueSpec, huesaturationSpec;
+    unsigned int option_def_index;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;
+    OPTENT3(0, "maxval",         OPT_UINT,
+            &cmdlineP->maxval, &maxvalSpec,        0);
+    OPTENT3(0, "huevalue",       OPT_FLAG,
+            NULL,              &huevalueSpec,      0);
+    OPTENT3(0, "huesaturation",  OPT_FLAG,
+            NULL,              &huesaturationSpec, 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 (!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 ( v >= 0.5) {
-        v = 2.0 - 2.0 * v;
-        v = sqrt (v);
-        *r = 1.0 + v * (*r - 1.0);
-        *g = 1.0 + v * (*g - 1.0);
-        *b = 1.0 + v * (*b - 1.0);
+    if (huevalueSpec + huesaturationSpec > 1)
+        pm_error("You may specify at most one of "
+                 "-huevalue and -huesaturation");
+
+    cmdlineP->wheelType =
+        huevalueSpec      ? WT_HUE_VAL :
+        huesaturationSpec ? WT_HUE_SAT :
+        WT_PPMCIRC;
+
+    if (argc-1 != 1) {
+        pm_error("Need 1 argument diameter of the wheel in pixels");
     } else {
-        v *= 2.0;
-        v = sqrt (sqrt ( sqrt (v)));
-        *r *= v;
-        *g *= v;
-        *b *= v;
+        const char * const diameterArg = argv[1];
+
+        if (strlen(diameterArg) == 0)
+            pm_error("Diameter argument is a null string");
+        else {
+            long argNumber;
+            char * tailptr;
+            argNumber = strtol(diameterArg, &tailptr, 10);
+
+            if (*tailptr != '\0')
+                pm_error("You specified an invalid number as diameter: '%s'",
+                         diameterArg);
+            if (argNumber <= 0)
+                pm_error("Diameter must be positive.  You specified %ld.",
+                         argNumber);
+            if (argNumber < 4)
+                pm_error("Diameter must be at least 4.  You specified %ld",
+                         argNumber);
+
+            cmdlineP->diameter = argNumber;
+        }
     }
+    free(option_def);
 }
 
 
-int
-main(int argc, char *argv[]) {
-    pixel *orow;
-    int rows, cols;
-    pixval maxval;
-    unsigned int row;
-    unsigned int xcenter, ycenter, radius;
-    long diameter;
-    char * tailptr;
 
-    ppm_init( &argc, argv );
 
-    if (argc-1 != 1)
-        pm_error("Program takes one argument:  diameter of color wheel");
 
-    diameter = strtol(argv[1], &tailptr, 10);
-    if (strlen(argv[1]) == 0 || *tailptr != '\0')
-        pm_error("You specified an invalid diameter: '%s'", argv[1]);
-    if (diameter <= 0)
-        pm_error("Diameter must be positive.  You specified %ld.", diameter);
-    if (diameter < 4)
-        pm_error("Diameter must be at least 4.  You specified %ld", diameter);
 
-    cols = rows = diameter;
-    
-    orow = ppm_allocrow(cols);
 
-    maxval = PPM_MAXMAXVAL;
-    ppm_writeppminit(stdout, cols, rows, maxval, 0);
+static pixel
+ppmcircColor(pixel  const normalColor,
+             pixval const maxval,
+             double const d) {
+/*----------------------------------------------------------------------------
+   The color that Ppmcirc (by Peter Kirchgessner, not part of Netpbm) puts at
+   'd' units from the center where the normal color in a hue-value color wheel
+   is 'normalColor'.
+
+   We have no idea what the point of this is.
+-----------------------------------------------------------------------------*/
+    pixel retval;
+
+    if (d >= 0.5) {
+        double const scale = sqrt(2.0 - 2.0 * d);
+
+        PPM_ASSIGN(retval,
+                   maxval - scale * (maxval - normalColor.r/d),
+                   maxval - scale * (maxval - normalColor.g/d),
+                   maxval - scale * (maxval - normalColor.b/d));
+    } else if (d == 0.0) {
+        PPM_ASSIGN(retval, 0, 0, 0);
+    } else {
+        double const scale = sqrt(sqrt(sqrt(2.0 * d)))/d;
+        PPM_ASSIGN(retval,
+                   normalColor.r * scale,
+                   normalColor.g * scale,
+                   normalColor.b * scale);
+    }
+    return retval;
+}
+
 
-    radius = diameter/2 - 1;
 
-    xcenter = cols / 2;
-    ycenter = rows / 2;
+static pixel
+wheelColor(WheelType const wheelType,
+           double    const dx,
+           double    const dy,
+           double    const radius,
+           pixval    const maxval) {
+
+    double const dist = sqrt(SQR(dx) + SQR(dy));
+
+    pixel retval;
+
+    if (dist > radius) {
+        retval = ppm_whitepixel(maxval);
+    } else {
+        double const hue90 = atan2(dx, dy) / PI * 180.0;
+        struct hsv hsv;
+
+        hsv.h = hue90 < 0.0 ? 360.0 + hue90 : hue90;
+
+        switch (wheelType) {
+        case WT_HUE_SAT:
+            hsv.v = 1.0;
+            hsv.s = dist / radius;
+            retval = ppm_color_from_hsv(hsv, maxval);
+            break;
+        case WT_HUE_VAL:
+            hsv.s = 1.0;
+            hsv.v = dist / radius;
+            retval = ppm_color_from_hsv(hsv, maxval);
+            break;
+        case WT_PPMCIRC:
+            hsv.s = 1.0;
+            hsv.v = dist / radius;
+            {
+                pixel const hvColor = ppm_color_from_hsv(hsv, maxval);
+                retval = ppmcircColor(hvColor, maxval, dist/radius);
+            }
+            break;
+        }
+    }
+    return retval;
+}
+
+
+
+static void
+ppmwheel(WheelType    const wheelType,
+         unsigned int const diameter,
+         pixval       const maxval,
+         FILE *       const ofP) {
+
+    unsigned int const cols   = diameter;
+    unsigned int const rows   = diameter;
+    unsigned int const radius = diameter/2 - 1;
+    unsigned int const xcenter = cols / 2;
+    unsigned int const ycenter = rows / 2;
+
+    unsigned int row;
+    pixel * orow;
+
+    orow = ppm_allocrow(cols);
+
+    ppm_writeppminit(ofP, cols, rows, maxval, 0);
 
     for (row = 0; row < rows; ++row) {
         unsigned int col;
         for (col = 0; col < cols; ++col) {
             double const dx = (int)col - (int)xcenter;
             double const dy = (int)row - (int)ycenter;
-            double const dist = sqrt(dx*dx + dy*dy);
 
-            pixval r, g, b;
+            orow[col] = wheelColor(wheelType, dx, dy, radius, maxval);
+        }
+        ppm_writeppmrow(ofP, orow, cols, maxval, 0);
+    }
+    ppm_freerow(orow);
+}
 
-            if (dist > radius) {
-                r = g = b = maxval;
-            } else {
-                double hue, sat, val;
-                double dr, dg, db;
 
-                hue = atan2(dx, dy) / PI * 180.0;
-                if (hue < 0.0) 
-                    hue = 360.0 + hue;
-                sat = 0.0;
-                val = dist / radius;
 
-                hsv_rgb(hue, sat, val, &dr, &dg, &db);
+int
+main(int argc, const char ** argv) {
+
+    struct CmdlineInfo cmdline;
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ppmwheel(cmdline.wheelType, cmdline.diameter, cmdline.maxval, stdout);
 
-                r = (pixval)(maxval * dr);
-                g = (pixval)(maxval * dg);
-                b = (pixval)(maxval * db);
-            }
-            PPM_ASSIGN (orow[col], r, g, b );
-        }
-        ppm_writeppmrow(stdout, orow, cols, maxval, 0);
-    }
     pm_close(stdout);
-    exit(0);
+    return 0;
 }
+
+
diff --git a/lib/Makefile b/lib/Makefile
index 1e607ee5..bc758df4 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -27,7 +27,9 @@ endif
 
 LIBOBJECTS = libpm.o pmfileio.o fileio.o colorname.o \
 	libpamd.o \
-	libpbm1.o libpbm2.o libpbm3.o libpbmfont.o \
+	libpbm1.o libpbm2.o libpbm3.o \
+	libpbmfont0.o libpbmfont1.o libpbmfont2.o \
+	pbmfontdata0.o pbmfontdata1.o pbmfontdata2.o \
 	libpgm1.o libpgm2.o \
 	libppm1.o libppm2.o libppmcmap.o libppmcolor.o libppmfuzzy.o \
 	libppmd.o ppmdfont.o standardppmdfont.o path.o \
@@ -55,7 +57,7 @@ MANUALS3 = libnetpbm
 MANUALS5 = pbm pgm ppm pnm pam
 
 INTERFACE_HEADERS = colorname.h \
-	pam.h pamdraw.h pammap.h pbm.h pbmfont.h \
+	pam.h pamdraw.h pammap.h pbm.h pbmfont.h pbmfontdata.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 \
@@ -219,7 +221,7 @@ libpm.o: compile.h
 .PHONY: install.lib
 ifeq ($(NETPBMLIBTYPE),unixshared)
 # install a Unix-style shared library
-install.lib: $(PKGDIR)/lib $(PKGDIR)/link
+install.lib: $(PKGDIR)/lib $(PKGDIR)/sharedlink
 	cd $(PKGDIR)/lib ; rm -f libnetpbm.$(NETPBMLIBSUFFIX).$(MAJ).*
 	$(INSTALL) -c -m $(INSTALL_PERM_LIBD) \
 	  libnetpbm.$(NETPBMLIBSUFFIX).$(MAJ).$(MIN)  $(PKGDIR)/lib/
@@ -261,26 +263,27 @@ $(INTERFACE_HEADERS:%=%_installhdr): $(PKGDIR)/include/netpbm
 	  $(SRCDIR)/lib/$(@:%_installhdr=%) $(PKGDIR)/include/netpbm/
 
 .PHONY: install.staticlib
-install.staticlib: $(PKGDIR)/link
+install.staticlib: $(PKGDIR)/staticlink
 	$(INSTALL) -c -m $(INSTALL_PERM_LIBS) libnetpbm.$(STATICLIBSUFFIX) \
-	  $(PKGDIR)/link
+	  $(PKGDIR)/staticlink
 
 # Install a shared library stub -- the ".so" file used at link time to
 # prepare a program for dynamically linking a library at run time 
 .PHONY: install.sharedlibstub
-install.sharedlibstub: $(PKGDIR)/link
+install.sharedlibstub: $(PKGDIR)/sharedlink
 ifeq ($(NETPBMLIBTYPE),unixshared)
 # install the link-time (.so) links to the runtime libraries
-	cd $(PKGDIR)/link ; \
+	cd $(PKGDIR)/sharedlink ; \
           rm -f libnetpbm.$(NETPBMLIBSUFFIX); \
           $(SYMLINK) ../lib/libnetpbm.$(NETPBMLIBSUFFIX).$(MAJ) \
             libnetpbm.$(NETPBMLIBSUFFIX)
 endif
 ifeq ($(NETPBMLIBTYPE),dll)
-	$(INSTALL) -c -m $(INSTALL_PERM_LIBS) libnetpbm.dll.a $(PKGDIR)/link
+	$(INSTALL) -c -m $(INSTALL_PERM_LIBS) libnetpbm.dll.a \
+	  $(PKGDIR)/sharedlink
 endif
 ifeq ($(NETPBMLIBTYPE),dylib)
-	cd $(PKGDIR)/link/ ; \
+	cd $(PKGDIR)/sharedlink/ ; \
           rm -f libnetpbm.dylib; \
 	$(SYMLINK) ../lib/libnetpbm.$(MAJ).$(MIN).dylib libnetpbm.dylib
 endif
diff --git a/lib/colorname.c b/lib/colorname.c
index 83cf5d1a..fe580cb9 100644
--- a/lib/colorname.c
+++ b/lib/colorname.c
@@ -12,6 +12,7 @@
 ** implied warranty.
 */
 
+#define _DEFAULT_SOURCE 1  /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
@@ -20,8 +21,10 @@
 #include <string.h>
 #include <stdlib.h>
 #include <errno.h>
+#include <math.h>
 
 #include "netpbm/nstring.h"
+#include "netpbm/mallocvar.h"
 
 #include "colorname.h"
 
@@ -29,7 +32,7 @@ static int lineNo;
 
 
 
-void 
+void
 pm_canonstr(char * const arg) {
 /*----------------------------------------------------------------------------
    Modify string 'arg' to canonical form: lower case, no white space.
@@ -90,7 +93,7 @@ pm_openColornameFile(const char * const fileName, const int must_open) {
    environment variable whose name is RGB_ENV (e.g. "RGBDEF").  Except
    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
    exist), exit the program with an error message.  If 'must_open' is
@@ -108,7 +111,7 @@ pm_openColornameFile(const char * const fileName, const int must_open) {
                          "named %s, per the %s environment variable.  "
                          "errno = %d (%s)",
                          rgbdef, RGBENV, errno, strerror(errno));
-        } else {            
+        } else {
             /* The environment variable isn't set, so try the hardcoded
                default color name dictionary locations.
             */
@@ -119,7 +122,7 @@ pm_openColornameFile(const char * const fileName, const int must_open) {
                          "path '%s' "
                          "and Environment variable %s not set.  Set %s to "
                          "the pathname of your rgb.txt file or don't use "
-                         "color names.", 
+                         "color names.",
                          RGB_DB_PATH, RGBENV, RGBENV);
             }
         }
@@ -128,7 +131,7 @@ pm_openColornameFile(const char * const fileName, const int must_open) {
         if (f == NULL && must_open)
             pm_error("Can't open the color names dictionary file '%s'.  "
                      "errno = %d (%s)", fileName, errno, strerror(errno));
-        
+
     }
     lineNo = 0;
     return(f);
@@ -151,7 +154,7 @@ pm_colorget(FILE * const f) {
     bool eof;
     struct colorfile_entry retval;
     char * rc;
-    
+
     gotOne = FALSE;  /* initial value */
     eof = FALSE;
     while (!gotOne && !eof) {
@@ -162,15 +165,15 @@ pm_colorget(FILE * const f) {
         else {
             if (buf[0] != '#' && buf[0] != '\n' && buf[0] != '!' &&
                 buf[0] != '\0') {
-                if (sscanf(buf, "%ld %ld %ld %[^\n]", 
-                           &retval.r, &retval.g, &retval.b, colorname) 
+                if (sscanf(buf, "%ld %ld %ld %[^\n]",
+                           &retval.r, &retval.g, &retval.b, colorname)
                     == 4 )
                     gotOne = TRUE;
                 else {
                     if (buf[strlen(buf)-1] == '\n')
                         buf[strlen(buf)-1] = '\0';
                     pm_message("can't parse color names dictionary Line %d:  "
-                               "'%s'", 
+                               "'%s'",
                                lineNo, buf);
                 }
             }
@@ -186,65 +189,83 @@ pm_colorget(FILE * const f) {
 
 
 void
-pm_parse_dictionary_name(char    const colorname[], 
-                         pixval  const maxval,
-                         int     const closeOk,
-                         pixel * const colorP) {
+pm_parse_dictionary_namen(char   const colorname[],
+                          tuplen const color) {
 
-    FILE* f;
+    FILE * fP;
     bool gotit;
     bool colorfileExhausted;
-    struct colorfile_entry colorfile_entry;
+    struct colorfile_entry colorfileEntry;
     char * canoncolor;
-    pixval r,g,b;
 
-    f = pm_openColornameFile(NULL, TRUE);  /* exits if error */
-    canoncolor = pm_strdup(colorname);
+    fP = pm_openColornameFile(NULL, TRUE);  /* exits if error */
+    canoncolor = strdup(colorname);
 
     if (!canoncolor)
         pm_error("Failed to allocate memory for %u-byte color name",
                  (unsigned)strlen(colorname));
 
     pm_canonstr(canoncolor);
-    gotit = FALSE;
-    colorfileExhausted = FALSE;
-    while (!gotit && !colorfileExhausted) {
-        colorfile_entry = pm_colorget(f);
-        if (colorfile_entry.colorname) {
-            pm_canonstr(colorfile_entry.colorname);
-            if (strcmp( canoncolor, colorfile_entry.colorname) == 0)
+
+    for(gotit = FALSE, colorfileExhausted = FALSE;
+        !gotit && !colorfileExhausted; ) {
+
+        colorfileEntry = pm_colorget(fP);
+        if (colorfileEntry.colorname) {
+            pm_canonstr(colorfileEntry.colorname);
+            if (streq(canoncolor, colorfileEntry.colorname))
                 gotit = TRUE;
         } else
             colorfileExhausted = TRUE;
     }
-    fclose(f);
-    
+    fclose(fP);
+
     if (!gotit)
         pm_error("unknown color '%s'", colorname);
-    
-    /* Rescale from [0..255] if necessary. */
-    if (maxval != 255) {
-        r = colorfile_entry.r * maxval / 255;
-        g = colorfile_entry.g * maxval / 255;
-        b = colorfile_entry.b * maxval / 255;
-
-        if (!closeOk) {
-            if (r * 255 / maxval != colorfile_entry.r ||
-                g * 255 / maxval != colorfile_entry.g ||
-                b * 255 / maxval != colorfile_entry.b)
+
+    color[PAM_RED_PLANE] = (samplen)colorfileEntry.r / PAM_COLORFILE_MAXVAL;
+    color[PAM_GRN_PLANE] = (samplen)colorfileEntry.g / PAM_COLORFILE_MAXVAL;
+    color[PAM_BLU_PLANE] = (samplen)colorfileEntry.b / PAM_COLORFILE_MAXVAL;
+
+    free(canoncolor);
+}
+
+
+
+void
+pm_parse_dictionary_name(char    const colorname[],
+                         pixval  const maxval,
+                         int     const closeOk,
+                         pixel * const colorP) {
+
+    double const epsilon = 1.0/65536.0;
+
+    tuplen color;
+    pixval r, g, b;
+
+    MALLOCARRAY_NOFAIL(color, 3);
+
+    pm_parse_dictionary_namen(colorname, color);
+
+    r = ppm_unnormalize(color[PAM_RED_PLANE], maxval);
+    g = ppm_unnormalize(color[PAM_GRN_PLANE], maxval);
+    b = ppm_unnormalize(color[PAM_BLU_PLANE], maxval);
+
+    if (!closeOk) {
+        if (maxval != PAM_COLORFILE_MAXVAL) {
+            if (fabs((double)r / maxval - color[PAM_RED_PLANE]) > epsilon ||
+                fabs((double)g / maxval - color[PAM_GRN_PLANE]) > epsilon ||
+                fabs((double)b / maxval - color[PAM_BLU_PLANE]) > epsilon) {
                 pm_message("WARNING: color '%s' cannot be represented "
                            "exactly with a maxval of %u.  "
                            "Approximating as (%u,%u,%u).  "
-                           "The color dictionary uses maxval 255, so that "
-                           "maxval will always work.",
-                           colorname, maxval, r, g, b);
+                           "(The color dictionary uses maxval %u, so that "
+                           "maxval will always work).",
+                           colorname, maxval, r, g, b,
+                           PAM_COLORFILE_MAXVAL);
+            }
         }
-    } else {
-        r = colorfile_entry.r;
-        g = colorfile_entry.g;
-        b = colorfile_entry.b;
     }
-    free(canoncolor);
 
     PPM_ASSIGN(*colorP, r, g, b);
 }
diff --git a/lib/colorname.h b/lib/colorname.h
index 74583144..492df951 100644
--- a/lib/colorname.h
+++ b/lib/colorname.h
@@ -4,6 +4,7 @@
 #include <string.h>
 #include <stdio.h>
 #include <netpbm/ppm.h>
+#include <netpbm/pam.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -15,14 +16,19 @@ extern "C" {
 enum colornameFormat {PAM_COLORNAME_ENGLISH = 0,
                       PAM_COLORNAME_HEXOK   = 1};
 
+#define PAM_COLORFILE_MAXVAL 255
+
 struct colorfile_entry {
     long r, g, b;
+        /* Red, green, and blue components of color based on maxval
+           PAM_COLORFILE_MAXVAL
+        */
     char * colorname;
 };
 
 
 
-void 
+void
 pm_canonstr(char * const str);
 
 FILE *
@@ -32,7 +38,11 @@ struct colorfile_entry
 pm_colorget(FILE * const f);
 
 void
-pm_parse_dictionary_name(const char       colorname[], 
+pm_parse_dictionary_namen(char   const colorname[],
+                          tuplen const color);
+
+void
+pm_parse_dictionary_name(const char       colorname[],
                          pixval     const maxval,
                          int        const closeOk,
                          pixel *    const colorP);
diff --git a/lib/libpam.c b/lib/libpam.c
index cc6368e1..a8f140b3 100644
--- a/lib/libpam.c
+++ b/lib/libpam.c
@@ -12,7 +12,8 @@
    offset stuff.
 */
 #define _FILE_OFFSET_BITS 64
-#define _LARGE_FILES  
+#define _LARGE_FILES
+#define _DEFAULT_SOURCE 1  /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
@@ -107,7 +108,7 @@ validateComputableSize(struct pam * const pamP) {
                  INT_MAX - depth * sizeof(tuple *))
             pm_error("image width and depth (%u, %u) too large "
                      "to be processed.", pamP->width, depth);
-        
+
         if (depth > INT_MAX - 2)
             pm_error("image depth (%u) too large to be processed", depth);
         if (pamP->width > INT_MAX - 2)
@@ -129,7 +130,7 @@ pnm_allocpamtuple(const struct pam * const pamP) {
     retval = malloc(allocationDepth(pamP) * sizeof(retval[0]));
 
     if (retval == NULL)
-        pm_error("Out of memory allocating %u-plane tuple", 
+        pm_error("Out of memory allocating %u-plane tuple",
                  allocationDepth(pamP));
 
     return retval;
@@ -138,15 +139,15 @@ pnm_allocpamtuple(const struct pam * const pamP) {
 
 
 int
-pnm_tupleequal(const struct pam * const pamP, 
-               tuple              const comparand, 
+pnm_tupleequal(const struct pam * const pamP,
+               tuple              const comparand,
                tuple              const comparator) {
 
     unsigned int plane;
     bool equal;
 
     equal = TRUE;  /* initial value */
-    for (plane = 0; plane < pamP->depth; ++plane) 
+    for (plane = 0; plane < pamP->depth; ++plane)
         if (comparand[plane] != comparator[plane])
             equal = FALSE;
 
@@ -172,11 +173,11 @@ pnm_assigntuple(const struct pam * const pamP,
 static void
 scaleTuple(const struct pam * const pamP,
            tuple              const dest,
-           tuple              const source, 
+           tuple              const source,
            sample             const newmaxval) {
 
     unsigned int plane;
-    for (plane = 0; plane < pamP->depth; ++plane) 
+    for (plane = 0; plane < pamP->depth; ++plane)
         dest[plane] = pnm_scalesample(source[plane], pamP->maxval, newmaxval);
 }
 
@@ -185,7 +186,7 @@ scaleTuple(const struct pam * const pamP,
 void
 pnm_scaletuple(const struct pam * const pamP,
                tuple              const dest,
-               tuple              const source, 
+               tuple              const source,
                sample             const newmaxval) {
 
     scaleTuple(pamP, dest, source, newmaxval);
@@ -194,7 +195,7 @@ pnm_scaletuple(const struct pam * const pamP,
 
 
 void
-pnm_createBlackTuple(const struct pam * const pamP, 
+pnm_createBlackTuple(const struct pam * const pamP,
                      tuple *            const blackTupleP) {
 /*----------------------------------------------------------------------------
    Create a "black" tuple.  By that we mean a tuple all of whose elements
@@ -204,7 +205,7 @@ pnm_createBlackTuple(const struct pam * const pamP,
 
     *blackTupleP = pnm_allocpamtuple(pamP);
 
-    for (i = 0; i < pamP->depth; ++i) 
+    for (i = 0; i < pamP->depth; ++i)
         (*blackTupleP)[i] = 0;
 }
 
@@ -225,14 +226,14 @@ allocPamRow(const struct pam * const pamP) {
     tuple * tuplerow;
 
     tuplerow = malloc(pamP->width * (sizeof(tuple *) + bytesPerTuple));
-                      
+
     if (tuplerow != NULL) {
         /* Now we initialize the pointers to the individual tuples
-           to make this a regulation C two dimensional array.  
+           to make this a regulation C two dimensional array.
         */
         char * p;
         unsigned int col;
-        
+
         p = (char*) (tuplerow + pamP->width);  /* location of Tuple 0 */
         for (col = 0; col < pamP->width; ++col) {
                 tuplerow[col] = (tuple) p;
@@ -260,7 +261,7 @@ pnm_allocpamrow(const struct pam * const pamP) {
 
 
 
-static unsigned int 
+static unsigned int
 rowimagesize(const struct pam * const pamP) {
 
     /* If repeatedly calculating this turns out to be a significant
@@ -270,7 +271,7 @@ rowimagesize(const struct pam * const pamP) {
 
     if (PAM_FORMAT_TYPE(pamP->format) == PBM_TYPE)
         return pbm_packed_bytes(pamP->width);
-    else 
+    else
         return (pamP->width * pamP->bytes_per_sample * pamP->depth);
 }
 
@@ -306,7 +307,7 @@ pnm_freerowimage(unsigned char * const rowimage) {
 
 
 
-void 
+void
 pnm_scaletuplerow(const struct pam * const pamP,
                   tuple *            const destRow,
                   tuple *            const sourceRow,
@@ -320,26 +321,26 @@ pnm_scaletuplerow(const struct pam * const pamP,
             for (col = 0; col < pamP->width; ++col)
                 pnm_assigntuple(pamP, destRow[col], sourceRow[col]);
         }
-    } else {        
+    } else {
         unsigned int col;
-        for (col = 0; col < pamP->width; ++col) 
+        for (col = 0; col < pamP->width; ++col)
             scaleTuple(pamP, destRow[col], sourceRow[col], newMaxval);
     }
 }
 
- 
+
 
 tuple **
 pnm_allocpamarray(const struct pam * const pamP) {
-    
+
     tuple **tuplearray;
 
     /* If the speed of this is ever an issue, it might be sped up a little
        by allocating one large chunk.
     */
-    
+
     MALLOCARRAY(tuplearray, pamP->height);
-    if (tuplearray == NULL) 
+    if (tuplearray == NULL)
         pm_error("Out of memory allocating the row pointer section of "
                  "a %u row array", pamP->height);
     else {
@@ -352,14 +353,14 @@ pnm_allocpamarray(const struct pam * const pamP) {
             if (tuplearray[row] == NULL) {
                 unsigned int freerow;
                 outOfMemory = TRUE;
-                
+
                 for (freerow = 0; freerow < row; ++freerow)
                     pnm_freepamrow(tuplearray[row]);
             }
         }
         if (outOfMemory) {
             free(tuplearray);
-            
+
             pm_error("Out of memory allocating the %u rows %u columns wide by "
                      "%u planes deep", pamP->height, pamP->width,
                      allocationDepth(pamP));
@@ -382,7 +383,7 @@ pnm_freepamarray(tuple ** const tuplearray, const struct pam * const pamP) {
 
 
 
-void 
+void
 pnm_setminallocationdepth(struct pam * const pamP,
                           unsigned int const allocationDepth) {
 
@@ -393,7 +394,7 @@ pnm_setminallocationdepth(struct pam * const pamP,
                  pamP->len, (unsigned)PAM_STRUCT_SIZE(allocation_depth));
 
     pamP->allocation_depth = MAX(allocationDepth, pamP->depth);
-        
+
     validateComputableSize(pamP);
 }
 
@@ -401,13 +402,13 @@ pnm_setminallocationdepth(struct pam * const pamP,
 
 void
 pnm_setpamrow(const struct pam * const pamP,
-              tuple *            const tuplerow, 
+              tuple *            const tuplerow,
               sample             const value) {
 
     int col;
     for (col = 0; col < pamP->width; ++col) {
         int plane;
-        for (plane = 0; plane < pamP->depth; ++plane) 
+        for (plane = 0; plane < pamP->depth; ++plane)
             tuplerow[col][plane] = value;
     }
 }
@@ -420,9 +421,9 @@ pnm_setpamrow(const struct pam * const pamP,
 
 static void
 parseHeaderLine(const char buffer[],
-                char label[MAX_LABEL_LENGTH+1], 
+                char label[MAX_LABEL_LENGTH+1],
                 char value[MAX_VALUE_LENGTH+1]) {
-    
+
     int buffer_curs;
 
     buffer_curs = 0;
@@ -434,12 +435,12 @@ parseHeaderLine(const char buffer[],
         int label_curs;
         label_curs = 0;
         while (!ISSPACE(buffer[buffer_curs]) && buffer[buffer_curs] != '\0') {
-            if (label_curs < MAX_LABEL_LENGTH) 
+            if (label_curs < MAX_LABEL_LENGTH)
                 label[label_curs++] = buffer[buffer_curs];
             buffer_curs++;
         }
         label[label_curs] = '\0';  /* null terminate it */
-    }    
+    }
 
     /* Skip white space between label and value */
     while (ISSPACE(buffer[buffer_curs])) buffer_curs++;
@@ -451,7 +452,7 @@ parseHeaderLine(const char buffer[],
         /* Remove trailing white space from value[] */
         int value_curs;
         value_curs = strlen(value)-1;
-        while (value_curs >= 0 && ISSPACE(value[value_curs])) 
+        while (value_curs >= 0 && ISSPACE(value[value_curs]))
             value[value_curs--] = '\0';
     }
 }
@@ -493,10 +494,10 @@ parseHeaderUint(const char *   const valueString,
         if (errno != 0)
             pm_error("Too-large value for %s in "
                      "PAM file header: '%s'", name, valueString);
-        else if (*endptr != '\0') 
+        else if (*endptr != '\0')
             pm_error("Non-numeric value for %s in "
                      "PAM file header: '%s'", name, valueString);
-        else if (valueNum < 0) 
+        else if (valueNum < 0)
             pm_error("Negative value for %s in "
                      "PAM file header: '%s'", name, valueString);
         else if ((unsigned int)valueNum != valueNum)
@@ -582,7 +583,7 @@ processHeaderLine(char                const buffer[],
             }
             pamP->tuple_type[sizeof(pamP->tuple_type)-1] = '\0';
         }
-    } else 
+    } else
         pm_error("Unrecognized header line type: '%s'.  "
                  "Possible missing ENDHDR line?", label);
 }
@@ -643,13 +644,13 @@ readpaminitrest(struct pam * const pamP) {
 
     comments = strdup("");
 
-    { 
+    {
         int c;
-        /* Read off rest of 1st line -- probably just the newline after the 
-           magic number 
+        /* Read off rest of 1st line -- probably just the newline after the
+           magic number
         */
         while ((c = getc(pamP->file)) != -1 && c != '\n');
-    }    
+    }
 
     while (!headerSeen.endhdr) {
         char buffer[256];
@@ -664,7 +665,7 @@ readpaminitrest(struct pam * const pamP) {
                 appendComment(&comments, buffer);
             else if (pm_stripeq(buffer, ""));
                 /* Ignore it; it's a blank line */
-            else 
+            else
                 processHeaderLine(buffer, pamP, &headerSeen);
         }
     }
@@ -680,13 +681,13 @@ readpaminitrest(struct pam * const pamP) {
     if (!headerSeen.maxval)
         pm_error("No MAXVAL header line in PAM header");
 
-    if (pamP->height == 0) 
+    if (pamP->height == 0)
         pm_error("HEIGHT value is zero in PAM header");
-    if (pamP->width == 0) 
+    if (pamP->width == 0)
         pm_error("WIDTH value is zero in PAM header");
-    if (pamP->depth == 0) 
+    if (pamP->depth == 0)
         pm_error("DEPTH value is zero in PAM header");
-    if (pamP->maxval == 0) 
+    if (pamP->maxval == 0)
         pm_error("MAXVAL value is zero in PAM header");
     if (pamP->maxval > PAM_OVERALL_MAXVAL)
         pm_error("MAXVAL value (%lu) in PAM header is greater than %u",
@@ -696,9 +697,9 @@ readpaminitrest(struct pam * const pamP) {
 
 
 void
-pnm_readpaminitrestaspnm(FILE * const fileP, 
-                         int *  const colsP, 
-                         int *  const rowsP, 
+pnm_readpaminitrestaspnm(FILE * const fileP,
+                         int *  const colsP,
+                         int *  const rowsP,
                          gray * const maxvalP,
                          int *  const formatP) {
 /*----------------------------------------------------------------------------
@@ -742,7 +743,7 @@ pnm_readpaminitrestaspnm(FILE * const fileP,
 }
 
 
-                
+
 unsigned int
 pnm_bytespersample(sample const maxval) {
 /*----------------------------------------------------------------------------
@@ -870,17 +871,17 @@ interpretTupleType(struct pam * const pamP) {
 
 
 
-void 
-pnm_readpaminit(FILE *       const file, 
-                struct pam * const pamP, 
+void
+pnm_readpaminit(FILE *       const file,
+                struct pam * const pamP,
                 int          const size) {
 
-    if (size < PAM_STRUCT_SIZE(tuple_type)) 
+    if (size < PAM_STRUCT_SIZE(tuple_type))
         pm_error("pam object passed to pnm_readpaminit() is too small.  "
                  "It must be large "
                  "enough to hold at least up to the "
                  "'tuple_type' member, but according "
-                 "to the 'size' argument, it is only %d bytes long.", 
+                 "to the 'size' argument, it is only %d bytes long.",
                  size);
 
     pamP->size = size;
@@ -894,7 +895,7 @@ pnm_readpaminit(FILE *       const file,
     pamP->format = pm_readmagicnumber(file);
 
     switch (PAM_FORMAT_TYPE(pamP->format)) {
-    case PAM_TYPE: 
+    case PAM_TYPE:
         readpaminitrest(pamP);
         break;
     case PPM_TYPE: {
@@ -925,12 +926,12 @@ pnm_readpaminit(FILE *       const file,
         if (pamCommentP(pamP))
             *pamP->comment_p = strdup("");
         break;
-        
+
     default:
         pm_error("bad magic number 0x%x - not a PAM, PPM, PGM, or PBM file",
                  pamP->format);
     }
-    
+
     pamP->bytes_per_sample = pnm_bytespersample(pamP->maxval);
     pamP->plainformat = FALSE;
         /* See below for complex explanation of why this is FALSE. */
@@ -958,7 +959,7 @@ writeComments(const struct pam * const pamP) {
         for (p = &comment[0], startOfLine = TRUE; *p; ++p) {
             if (startOfLine)
                 fputc('#', pamP->file);
-                
+
             fputc(*p, pamP->file);
 
             if (*p == '\n')
@@ -985,7 +986,7 @@ writeComments(const struct pam * const pamP) {
    pam structure is what determines whether it is raw or plain.  So we
    set it false here so that it is false in the copied output pam
    structure.
-   
+
    Before 10.32, we set 'plainformat' according to the
    plainness of the input image, and thought it was a good
    idea for a program that reads a plain format input image to
@@ -996,7 +997,7 @@ writeComments(const struct pam * const pamP) {
    facilities to pam.
 */
 
-void 
+void
 pnm_writepaminit(struct pam * const pamP) {
 
     const char * tupleType;
@@ -1013,7 +1014,7 @@ pnm_writepaminit(struct pam * const pamP) {
                  "It must be large "
                  "enough to hold at least up through the "
                  "'bytes_per_sample' member, but according "
-                 "to its 'size' member, it is only %u bytes long.", 
+                 "to its 'size' member, it is only %u bytes long.",
                  pamP->size);
     if (pamP->len < PAM_STRUCT_SIZE(maxval))
         pm_error("pam object must contain members at least through 'maxval', "
@@ -1044,7 +1045,7 @@ pnm_writepaminit(struct pam * const pamP) {
     interpretTupleType(pamP);
 
     pamP->len = MIN(pamP->size, PAM_STRUCT_SIZE(opacity_plane));
-    
+
     switch (PAM_FORMAT_TYPE(pamP->format)) {
     case PAM_TYPE:
         /* See explanation below of why we ignore 'pm_plain_output' here. */
@@ -1064,13 +1065,13 @@ pnm_writepaminit(struct pam * const pamP) {
         */
         if (pamP->depth != 3)
             pm_error("pnm_writepaminit() got PPM format, but depth = %d "
-                     "instead of 3, as required for PPM.", 
+                     "instead of 3, as required for PPM.",
                      pamP->depth);
-        if (pamP->maxval > PPM_OVERALLMAXVAL) 
+        if (pamP->maxval > PPM_OVERALLMAXVAL)
             pm_error("pnm_writepaminit() got PPM format, but maxval = %ld, "
-                     "which exceeds the maximum allowed for PPM: %d", 
+                     "which exceeds the maximum allowed for PPM: %d",
                      pamP->maxval, PPM_OVERALLMAXVAL);
-        ppm_writeppminit(pamP->file, pamP->width, pamP->height, 
+        ppm_writeppminit(pamP->file, pamP->width, pamP->height,
                          (pixval) pamP->maxval, pamP->plainformat);
         break;
 
@@ -1081,9 +1082,9 @@ pnm_writepaminit(struct pam * const pamP) {
                      pamP->depth);
         if (pamP->maxval > PGM_OVERALLMAXVAL)
             pm_error("pnm_writepaminit() got PGM format, but maxval = %ld, "
-                     "which exceeds the maximum allowed for PGM: %d", 
+                     "which exceeds the maximum allowed for PGM: %d",
                      pamP->maxval, PGM_OVERALLMAXVAL);
-        pgm_writepgminit(pamP->file, pamP->width, pamP->height, 
+        pgm_writepgminit(pamP->file, pamP->width, pamP->height,
                          (gray) pamP->maxval, pamP->plainformat);
         break;
 
@@ -1092,10 +1093,10 @@ pnm_writepaminit(struct pam * const pamP) {
             pm_error("pnm_writepaminit() got PBM format, but depth = %d "
                      "instead of 1, as required for PBM.",
                      pamP->depth);
-        if (pamP->maxval != 1) 
+        if (pamP->maxval != 1)
             pm_error("pnm_writepaminit() got PBM format, but maxval = %ld "
                      "instead of 1, as required for PBM.", pamP->maxval);
-        pbm_writepbminit(pamP->file, pamP->width, pamP->height, 
+        pbm_writepbminit(pamP->file, pamP->width, pamP->height,
                          pamP->plainformat);
         break;
 
@@ -1125,29 +1126,29 @@ pnm_writepaminit(struct pam * const pamP) {
 
 
 void
-pnm_checkpam(const struct pam *   const pamP, 
-             enum pm_check_type   const checkType, 
+pnm_checkpam(const struct pam *   const pamP,
+             enum pm_check_type   const checkType,
              enum pm_check_code * const retvalP) {
 
     if (checkType != PM_CHECK_BASIC) {
         if (retvalP) *retvalP = PM_CHECK_UNKNOWN_TYPE;
     } else switch (PAM_FORMAT_TYPE(pamP->format)) {
     case PAM_TYPE: {
-        pm_filepos const need_raster_size = 
+        pm_filepos const need_raster_size =
             pamP->width * pamP->height * pamP->depth * pamP->bytes_per_sample;
         pm_check(pamP->file, checkType, need_raster_size, retvalP);
     }
         break;
     case PPM_TYPE:
-        pgm_check(pamP->file, checkType, pamP->format, 
+        pgm_check(pamP->file, checkType, pamP->format,
                   pamP->width, pamP->height, pamP->maxval, retvalP);
         break;
     case PGM_TYPE:
-        pgm_check(pamP->file, checkType, pamP->format, 
+        pgm_check(pamP->file, checkType, pamP->format,
                   pamP->width, pamP->height, pamP->maxval, retvalP);
         break;
     case PBM_TYPE:
-        pbm_check(pamP->file, checkType, pamP->format, 
+        pbm_check(pamP->file, checkType, pamP->format,
                   pamP->width, pamP->height, retvalP);
         break;
     default:
@@ -1157,7 +1158,7 @@ pnm_checkpam(const struct pam *   const pamP,
 
 
 
-void 
+void
 pnm_maketuplergb(const struct pam * const pamP,
                  tuple              const tuple) {
 
@@ -1171,29 +1172,27 @@ pnm_maketuplergb(const struct pam * const pamP,
 
 
 
-void 
+void
 pnm_makerowrgb(const struct pam * const pamP,
                tuple *            const tuplerow) {
-    
+
     if (pamP->depth < 3) {
         unsigned int col;
 
         if (allocationDepth(pamP) < 3)
             pm_error("allocation depth %u passed to pnm_makerowrgb().  "
                      "Must be at least 3.", allocationDepth(pamP));
-        
-        if (strncmp(pamP->tuple_type, "RGB", 3) != 0) {
-            for (col = 0; col < pamP->width; ++col) {
-                tuple const thisTuple = tuplerow[col];
-                thisTuple[2] = thisTuple[1] = thisTuple[0];
-            }
+
+        for (col = 0; col < pamP->width; ++col) {
+            tuple const thisTuple = tuplerow[col];
+            thisTuple[2] = thisTuple[1] = thisTuple[0];
         }
     }
 }
 
 
 
-void 
+void
 pnm_makearrayrgb(const struct pam * const pamP,
                  tuple **           const tuples) {
 
@@ -1202,7 +1201,7 @@ pnm_makearrayrgb(const struct pam * const pamP,
         if (allocationDepth(pamP) < 3)
             pm_error("allocation depth %u passed to pnm_makearrayrgb().  "
                      "Must be at least 3.", allocationDepth(pamP));
-        
+
         for (row = 0; row < pamP->height; ++row) {
             tuple * const tuplerow = tuples[row];
             unsigned int col;
@@ -1216,7 +1215,7 @@ pnm_makearrayrgb(const struct pam * const pamP,
 
 
 
-void 
+void
 pnm_makerowrgba(const struct pam * const pamP,
                 tuple *            const tuplerow) {
 /*----------------------------------------------------------------------------
@@ -1237,7 +1236,7 @@ pnm_makerowrgba(const struct pam * const pamP,
     } 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 {
@@ -1246,10 +1245,10 @@ pnm_makerowrgba(const struct pam * const pamP,
             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] = 
+                thisTuple[PAM_TRN_PLANE] =
                     pamP->have_opacity ? thisTuple[pamP->opacity_plane] :
                     pamP->maxval;
 
@@ -1263,7 +1262,7 @@ pnm_makerowrgba(const struct pam * const pamP,
 
 
 
-void 
+void
 pnm_addopacityrow(const struct pam * const pamP,
                   tuple *            const tuplerow) {
 /*----------------------------------------------------------------------------
@@ -1284,7 +1283,7 @@ pnm_addopacityrow(const struct pam * const pamP,
     } 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 {
@@ -1296,7 +1295,7 @@ pnm_addopacityrow(const struct pam * const pamP,
                 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;
         }
@@ -1387,7 +1386,7 @@ pnm_backgroundtuple(struct pam *  const pamP,
 
 
 /*=============================================================================
-   pm_system() Standard Input feeder and Standard Output accepter functions.   
+   pm_system() Standard Input feeder and Standard Output accepter functions.
 =============================================================================*/
 
 void
diff --git a/lib/libpam.h b/lib/libpam.h
index 9f8a34d0..2d4f29d7 100644
--- a/lib/libpam.h
+++ b/lib/libpam.h
@@ -7,9 +7,9 @@
 #include "pgm.h"
 
 void
-pnm_readpaminitrestaspnm(FILE * const fileP, 
-                         int *  const colsP, 
-                         int *  const rowsP, 
+pnm_readpaminitrestaspnm(FILE * const fileP,
+                         int *  const colsP,
+                         int *  const rowsP,
                          gray * const maxvalP,
                          int *  const formatP);
 
diff --git a/lib/libpamcolor.c b/lib/libpamcolor.c
index f3ca9a86..e1a24c66 100644
--- a/lib/libpamcolor.c
+++ b/lib/libpamcolor.c
@@ -12,27 +12,349 @@
    offset stuff.
 */
 #define _FILE_OFFSET_BITS 64
-#define _LARGE_FILES  
-
-#define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
-#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
+#define _LARGE_FILES
 
 #include <string.h>
 #include <limits.h>
+#include <math.h>
 
 #include "netpbm/pm_c_util.h"
+#include "netpbm/mallocvar.h"
+#include "netpbm/nstring.h"
+#include "netpbm/colorname.h"
+
+#include "netpbm/pam.h"
+#include "netpbm/ppm.h"
+
+
+
+static unsigned int
+hexDigitValue(char const digit) {
+
+    switch (digit) {
+    case '0': return 0;
+    case '1': return 1;
+    case '2': return 2;
+    case '3': return 3;
+    case '4': return 4;
+    case '5': return 5;
+    case '6': return 6;
+    case '7': return 7;
+    case '8': return 8;
+    case '9': return 9;
+    case 'a': case 'A': return 10;
+    case 'b': case 'B': return 11;
+    case 'c': case 'C': return 12;
+    case 'd': case 'D': return 13;
+    case 'e': case 'E': return 14;
+    case 'f': case 'F': return 15;
+    default:
+        pm_error("Invalid hex digit '%c'", digit);
+        return 0;  /* Defeat compiler warning */
+    }
+}
+
+
+
+static void
+parseHexDigits(const char *   const string,
+               char           const delim,
+               samplen *      const nP,
+               unsigned int * const digitCtP) {
+
+    unsigned int digitCt;
+    unsigned long n;
+    unsigned long range;
+        /* 16 for one hex digit, 256 for two hex digits, etc. */
+
+    for (digitCt = 0, n = 0, range = 1; string[digitCt] != delim; ) {
+        char const digit = string[digitCt];
+        if (digit == '\0')
+            pm_error("rgb: color spec '%s' ends prematurely", string);
+        else {
+            n = n * 16 + hexDigitValue(digit);
+            range *= 16;
+            ++digitCt;
+        }
+    }
+    if (range <= 1)
+        pm_error("No digits where hexadecimal number expected in rgb: "
+                 "color spec '%s'", string);
+
+    *nP = (samplen) n / (range-1);
+    *digitCtP = digitCt;
+}
+
 
-#include "pam.h"
-#include "ppm.h"
+
+static void
+parseNewHexX11(char   const colorname[],
+               tuplen const color) {
+/*----------------------------------------------------------------------------
+   Determine what color colorname[] specifies in the new style hex
+   color specification format (e.g. rgb:55/40/55).
+
+   Return that color as *colorP.
+
+   Assume colorname[] starts with "rgb:", but otherwise it might be
+   gibberish.
+-----------------------------------------------------------------------------*/
+    const char * cp;
+    unsigned int digitCt;
+
+    cp = &colorname[4];
+
+    parseHexDigits(cp, '/', &color[PAM_RED_PLANE], &digitCt);
+
+    cp += digitCt;
+    ++cp;  /* Skip the slash */
+
+    parseHexDigits(cp, '/', &color[PAM_GRN_PLANE], &digitCt);
+
+    cp += digitCt;
+    ++cp;  /* Skip the slash */
+
+    parseHexDigits(cp, '\0', &color[PAM_BLU_PLANE], &digitCt);
+}
+
+
+
+static bool
+isNormal(samplen const arg) {
+
+    return arg >= 0.0 && arg <= 1.0;
+}
+
+
+
+static void
+parseNewDecX11(const char * const colorname,
+               tuplen       const color) {
+
+    int rc;
+
+    rc = sscanf(colorname, "rgbi:%f/%f/%f",
+                &color[PAM_RED_PLANE],
+                &color[PAM_GRN_PLANE],
+                &color[PAM_BLU_PLANE]);
+
+    if (rc != 3)
+        pm_error("invalid color specifier '%s'", colorname);
+
+    if (!(isNormal(color[PAM_RED_PLANE]) &&
+          isNormal(color[PAM_GRN_PLANE]) &&
+          isNormal(color[PAM_BLU_PLANE]))) {
+        pm_error("invalid color specifier '%s' - "
+                 "values must be between 0.0 and 1.0", colorname);
+    }
+}
+
+
+
+static void
+parseInteger(const char * const colorname,
+             tuplen       const color) {
+
+    unsigned int maxval;
+    unsigned int r, g, b;
+    int rc;
+
+    rc = sscanf(colorname, "rgb-%u:%u/%u/%u", &maxval, &r, &g, &b);
+
+    if (rc != 4)
+        pm_error("invalid color specifier '%s'.  "
+                 "If it starts with \"rgb-\", then it must have the format "
+                 "rgb-<MAXVAL>:<RED>:<GRN>:<BLU>, "
+                 "where <MAXVAL>, <RED>, <GRN>, and <BLU> are "
+                 "unsigned integers",
+                 colorname);
+
+    if (maxval < 1 || maxval > PNM_OVERALLMAXVAL)
+        pm_error("Maxval in color specification '%s' is %u, "
+                 "which is invalid because it is not between "
+                 "1 and %u, inclusive",
+                 colorname, maxval, PNM_OVERALLMAXVAL);
+
+    if (r > maxval)
+        pm_error("Red value in color specification '%s' is %u, "
+                 "whcih is invalid because the specified maxval is %u",
+                 colorname, r, maxval);
+    if (g > maxval)
+        pm_error("Green value in color specification '%s' is %u, "
+                 "whcih is invalid because the specified maxval is %u",
+                 colorname, g, maxval);
+    if (b > maxval)
+        pm_error("Blue value in color specification '%s' is %u, "
+                 "whcih is invalid because the specified maxval is %u",
+                 colorname, b, maxval);
+
+    color[PAM_RED_PLANE] = (float)r/maxval;
+    color[PAM_GRN_PLANE] = (float)g/maxval;
+    color[PAM_BLU_PLANE] = (float)b/maxval;
+}
+
+
+
+static void
+parseOldX11(const char * const colorname,
+            tuplen       const color) {
+/*----------------------------------------------------------------------------
+   Return as *colorP the color specified by the old X11 style color
+   specififier colorname[] (e.g. #554055).
+-----------------------------------------------------------------------------*/
+    if (!pm_strishex(&colorname[1]))
+        pm_error("Non-hexadecimal characters in #-type color specification");
+
+    switch (strlen(colorname) - 1 /* (Number of hex digits) */) {
+    case 3:
+        color[PAM_RED_PLANE] = (samplen)hexDigitValue(colorname[1])/15;
+        color[PAM_GRN_PLANE] = (samplen)hexDigitValue(colorname[2])/15;
+        color[PAM_BLU_PLANE] = (samplen)hexDigitValue(colorname[3])/15;
+        break;
+
+    case 6:
+        color[PAM_RED_PLANE] =
+            ((samplen)(hexDigitValue(colorname[1]) << 4) +
+             (samplen)(hexDigitValue(colorname[2]) << 0))
+             / 255;
+        color[PAM_GRN_PLANE] =
+            ((samplen)(hexDigitValue(colorname[3]) << 4) +
+             (samplen)(hexDigitValue(colorname[4]) << 0))
+             / 255;
+        color[PAM_BLU_PLANE] =
+            ((samplen)(hexDigitValue(colorname[5]) << 4) +
+             (samplen)(hexDigitValue(colorname[6]) << 0))
+             / 255;
+        break;
+
+    case 9:
+        color[PAM_RED_PLANE] =
+            ((samplen)(hexDigitValue(colorname[1]) << 8) +
+             (samplen)(hexDigitValue(colorname[2]) << 4) +
+             (samplen)(hexDigitValue(colorname[3]) << 0))
+            / 4095;
+        color[PAM_GRN_PLANE] =
+            ((samplen)(hexDigitValue(colorname[4]) << 8) +
+             (samplen)(hexDigitValue(colorname[5]) << 4) +
+             (samplen)(hexDigitValue(colorname[6]) << 0))
+            / 4095;
+        color[PAM_BLU_PLANE] =
+            ((samplen)(hexDigitValue(colorname[7]) << 8) +
+             (samplen)(hexDigitValue(colorname[8]) << 4) +
+             (samplen)(hexDigitValue(colorname[9]) << 0))
+            / 4095;
+        break;
+
+    case 12:
+        color[PAM_RED_PLANE] =
+            ((samplen)(hexDigitValue(colorname[1]) << 12) +
+             (samplen)(hexDigitValue(colorname[2]) <<  8) +
+             (samplen)(hexDigitValue(colorname[3]) <<  4) +
+             (samplen)(hexDigitValue(colorname[4]) <<  0))
+            / 65535;
+        color[PAM_GRN_PLANE] =
+            ((samplen)(hexDigitValue(colorname[5]) << 12) +
+             (samplen)(hexDigitValue(colorname[6]) <<  8) +
+             (samplen)(hexDigitValue(colorname[7]) <<  4) +
+             (samplen)(hexDigitValue(colorname[8]) <<  0))
+            / 65535;
+        color[PAM_BLU_PLANE] =
+            ((samplen)(hexDigitValue(colorname[ 9]) << 12) +
+             (samplen)(hexDigitValue(colorname[10]) << 8) +
+             (samplen)(hexDigitValue(colorname[11]) << 4) +
+             (samplen)(hexDigitValue(colorname[12]) << 0))
+            / 65535;
+        break;
+
+    default:
+        pm_error("invalid color specifier '%s'", colorname);
+    }
+}
+
+
+
+static void
+parseOldX11Dec(const char* const colorname,
+               tuplen      const color) {
+
+    int rc;
+
+    rc = sscanf(colorname, "%f,%f,%f",
+                &color[PAM_RED_PLANE],
+                &color[PAM_GRN_PLANE],
+                &color[PAM_BLU_PLANE]);
+
+    if (rc != 3)
+        pm_error("invalid color specifier '%s'", colorname);
+
+    if (!(isNormal(color[PAM_RED_PLANE]) &&
+          isNormal(color[PAM_GRN_PLANE]) &&
+          isNormal(color[PAM_BLU_PLANE]))) {
+        pm_error("invalid color specifier '%s' - "
+                 "values must be between 0.0 and 1.0", colorname);
+    }
+}
+
+
+
+tuplen
+pnm_parsecolorn(const char * const colorname) {
+
+    tuplen retval;
+
+    MALLOCARRAY_NOFAIL(retval, 3);
+
+    if (strneq(colorname, "rgb:", 4))
+        /* It's a new-X11-style hexadecimal rgb specifier. */
+        parseNewHexX11(colorname, retval);
+    else if (strneq(colorname, "rgbi:", 5))
+        /* It's a new-X11-style decimal/float rgb specifier. */
+        parseNewDecX11(colorname, retval);
+    else if (strneq(colorname, "rgb-", 4))
+        /* It's a Netpbm-native decimal integer rgb specifier */
+        parseInteger(colorname, retval);
+    else if (colorname[0] == '#')
+        /* It's an old-X11-style hexadecimal rgb specifier. */
+        parseOldX11(colorname, retval);
+    else if ((colorname[0] >= '0' && colorname[0] <= '9') ||
+             colorname[0] == '.')
+        /* It's an old-style decimal/float rgb specifier. */
+        parseOldX11Dec(colorname, retval);
+    else
+        /* Must be a name from the X-style rgb file. */
+        pm_parse_dictionary_namen(colorname, retval);
+
+    return retval;
+}
+
+
+
+static void
+warnIfNotExact(const char * const colorname,
+               tuple        const rounded,
+               tuplen       const exact,
+               sample       const maxval,
+               unsigned int const plane) {
+
+    float const epsilon = 1.0/65536.0;
+
+    if (fabs((float)(rounded[plane] / maxval) - exact[plane]) > epsilon) {
+        pm_message("WARNING: Component %u of color '%s' is %f, "
+                   "which cannot be represented precisely with maxval %lu.  "
+                   "Approximating as %lu.",
+                   plane, colorname, exact[plane], maxval, rounded[plane]);
+    }
+}
 
 
 
 tuple
-pnm_parsecolor(const char * const colorname,
-               sample       const maxval) {
+pnm_parsecolor2(const char * const colorname,
+                sample       const maxval,
+                int          const closeOk) {
 
     tuple retval;
-    pixel color;
+    tuplen color;
     struct pam pam;
 
     pam.len = PAM_STRUCT_SIZE(bytes_per_sample);
@@ -42,17 +364,32 @@ pnm_parsecolor(const char * const colorname,
 
     retval = pnm_allocpamtuple(&pam);
 
-    color = ppm_parsecolor(colorname, maxval);
+    color = pnm_parsecolorn(colorname);
 
-    retval[PAM_RED_PLANE] = PPM_GETR(color);
-    retval[PAM_GRN_PLANE] = PPM_GETG(color);
-    retval[PAM_BLU_PLANE] = PPM_GETB(color);
+    pnm_unnormalizetuple(&pam, color, retval);
+
+    if (!closeOk) {
+        warnIfNotExact(colorname, retval, color, maxval, PAM_RED_PLANE);
+        warnIfNotExact(colorname, retval, color, maxval, PAM_GRN_PLANE);
+        warnIfNotExact(colorname, retval, color, maxval, PAM_BLU_PLANE);
+    }
+
+    free(color);
 
     return retval;
 }
 
 
 
+tuple
+pnm_parsecolor(const char * const colorname,
+               sample       const maxval) {
+
+    return pnm_parsecolor2(colorname, maxval, true);
+}
+
+
+
 const char *
 pnm_colorname(struct pam * const pamP,
               tuple        const color,
@@ -64,7 +401,7 @@ pnm_colorname(struct pam * const pamP,
 
     if (pamP->depth < 3)
         PPM_ASSIGN(colorp, color[0], color[0], color[0]);
-    else 
+    else
         PPM_ASSIGN(colorp,
                    color[PAM_RED_PLANE],
                    color[PAM_GRN_PLANE],
@@ -72,8 +409,8 @@ pnm_colorname(struct pam * const pamP,
 
     colorname = ppm_colorname(&colorp, pamP->maxval, hexok);
 
-    retval = strdup(colorname);
-    if (retval == NULL)
+    retval = pm_strdup(colorname);
+    if (retval == pm_strsol)
         pm_error("Couldn't get memory for color name string");
 
     return retval;
@@ -81,21 +418,279 @@ pnm_colorname(struct pam * const pamP,
 
 
 
+static tuple
+scaledRgb(struct pam * const pamP,
+          tuple        const color,
+          sample       const maxval) {
+
+    tuple scaledColor;
+
+    struct pam pam;
+
+    pam.size             = sizeof(pam);
+    pam.len              = PAM_STRUCT_SIZE(allocation_depth);
+    pam.maxval           = pamP->maxval;
+    pam.depth            = pamP->depth;
+    pam.allocation_depth = 3;
+
+    scaledColor = pnm_allocpamtuple(&pam);
+
+    pnm_scaletuple(&pam, scaledColor, color, maxval);
+
+    pnm_maketuplergb(&pam, scaledColor);
+
+    return scaledColor;
+}
+
+
+
+const char *
+pnm_colorspec_rgb_integer(struct pam * const pamP,
+                          tuple        const color,
+                          sample       const maxval) {
+
+    const char * retval;
+
+    tuple scaledColor = scaledRgb(pamP, color, maxval);
+
+    pm_asprintf(&retval, "rgb-%lu:%lu/%lu/%lu",
+                maxval,
+                scaledColor[PAM_RED_PLANE],
+                scaledColor[PAM_GRN_PLANE],
+                scaledColor[PAM_BLU_PLANE]
+        );
+
+    pnm_freepamtuple(scaledColor);
+
+    return retval;
+}
+
+
+
+const char *
+pnm_colorspec_rgb_norm(struct pam * const pamP,
+                       tuple        const color,
+                       unsigned int const digitCt) {
+
+    const char * retval;
+
+    tuple rgbColor;
+
+    tuplen normColor;
+
+    struct pam rgbPam;
+
+    rgbPam.size             = sizeof(rgbPam);
+    rgbPam.len              = PAM_STRUCT_SIZE(allocation_depth);
+    rgbPam.maxval           = pamP->maxval;
+    rgbPam.depth            = pamP->depth;
+    rgbPam.allocation_depth = 3;
+
+    rgbColor = pnm_allocpamtuple(&rgbPam);
+
+    pnm_assigntuple(&rgbPam, rgbColor, color);  /* initial value */
+
+    pnm_maketuplergb(&rgbPam, rgbColor);
+
+    normColor = pnm_allocpamtuplen(&rgbPam);
+
+    rgbPam.depth = 3;
+
+    pnm_normalizetuple(&rgbPam, rgbColor, normColor);
+
+    {
+        const char * format;
+        pm_asprintf(&format, "rgbi:%%.%uf/%%.%uf/%%.%uf",
+                    digitCt, digitCt, digitCt);
+
+        pm_asprintf(&retval, format,
+                    normColor[PAM_RED_PLANE],
+                    normColor[PAM_GRN_PLANE],
+                    normColor[PAM_BLU_PLANE]
+            );
+        pm_strfree(format);
+    }
+
+    pnm_freepamtuplen(normColor);
+    pnm_freepamtuple(rgbColor);
+
+    return retval;
+}
+
+
+
+const char *
+pnm_colorspec_rgb_x11(struct pam * const pamP,
+                      tuple        const color,
+                      unsigned int const hexDigitCt) {
+
+    const char * retval;
+
+    sample maxval;
+    const char * format;
+
+    switch(hexDigitCt) {
+    case 1:
+        maxval =    15;
+        format = "rgb:%01x:%01x:%01x";
+        break;
+    case 2:
+        maxval =   255;
+        format = "rgb:%02x:%02x:%02x";
+        break;
+    case 3:
+        maxval =  4095;
+        format = "rgb:%03x:%03x:%03x";
+        break;
+    case 4:
+        maxval = 65535;
+        format = "rgb:%04x:%04x:%04x";
+        break;
+    default:
+        pm_error("Invalid number of hex digits "
+                 "for X11 color specification: %u.  "
+                 "Must be 1, 2, 3, or 4", hexDigitCt);
+    }
+
+    {
+        tuple const scaledColor = scaledRgb(pamP, color, maxval);
+
+        pm_asprintf(&retval, format,
+                    scaledColor[PAM_RED_PLANE],
+                    scaledColor[PAM_GRN_PLANE],
+                    scaledColor[PAM_BLU_PLANE]
+            );
+
+        pnm_freepamtuple(scaledColor);
+    }
+    return retval;
+}
+
+
+
+const char *
+pnm_colorspec_dict(struct pam * const pamP,
+                   tuple        const color) {
+/*----------------------------------------------------------------------------
+   Return the name from the color dictionary of color 'color'.
+
+   Return it in newly allocated pm_strdrup storage.
+
+   If the color is not in the dictionary, or the dictionary doesn't even
+   exist (file not found in any of the possible places), return NULL.
+
+   The color dictionary uses maxval 255, so we match to that precision.
+   E.g. if a component of 'color' is 1000 out of maxval 65535 (which would be
+   3.9 out of maxval 255), we consider it a match to a component value of 4
+   in the color dictionary.
+-----------------------------------------------------------------------------*/
+    tuple scaledColor = scaledRgb(pamP, color, PAM_COLORFILE_MAXVAL);
+
+    FILE * dictFileP;
+    const char * colorname;
+
+    dictFileP = pm_openColornameFile(NULL, false);
+
+    if (dictFileP) {
+        bool eof;
+        for (colorname = NULL, eof = false; !colorname && !eof; ) {
+            struct colorfile_entry const ce = pm_colorget(dictFileP);
+
+            if (ce.colorname)  {
+                if (scaledColor[PAM_RED_PLANE] == (sample)ce.r &&
+                    scaledColor[PAM_GRN_PLANE] == (sample)ce.g &&
+                    scaledColor[PAM_BLU_PLANE] == (sample)ce.b) {
+                    colorname = pm_strdup(ce.colorname);
+                }
+            } else
+                eof = TRUE;
+        }
+
+        fclose(dictFileP);
+    } else
+        colorname = NULL;
+
+    pnm_freepamtuple(scaledColor);
+
+    return colorname;
+}
+
+
+
+const char *
+pnm_colorspec_dict_close(struct pam * const pamP,
+                         tuple        const color) {
+/*----------------------------------------------------------------------------
+   Return the name from the color dictionary of the color closst to 'color'.
+
+   Return it in newly allocated pm_strdrup storage.
+
+   If the color dictionary is empty, or the dictionary doesn't even exist
+   (file not found in any of the possible places), return a null string.
+   This is the only case in which we would return a null string, as the
+   color dictionary cannot define a null string color name.
+-----------------------------------------------------------------------------*/
+    tuple scaledColor = scaledRgb(pamP, color, PAM_COLORFILE_MAXVAL);
+
+    FILE * dictFileP;
+    static char colorname[200];
+
+    dictFileP = pm_openColornameFile(NULL, false);
+
+    if (dictFileP) {
+        unsigned int bestDiff;
+        bool eof;
+
+        for (bestDiff = 32767, eof = FALSE; !eof && bestDiff > 0; ) {
+            struct colorfile_entry const ce = pm_colorget(dictFileP);
+
+            if (ce.colorname)  {
+                unsigned int const thisDiff =
+                    abs((int)scaledColor[PAM_RED_PLANE] - (int)ce.r) +
+                    abs((int)scaledColor[PAM_GRN_PLANE] - (int)ce.g) +
+                    abs((int)scaledColor[PAM_BLU_PLANE] - (int)ce.b);
+
+                if (thisDiff < bestDiff) {
+                    bestDiff = thisDiff;
+                    STRSCPY(colorname, ce.colorname);
+                }
+            } else
+                eof = TRUE;
+        }
+
+        fclose(dictFileP);
+
+        if (bestDiff == 32767) {
+            /* Color file contain no entries, so we can't even pick a close
+               one
+            */
+            STRSCPY(colorname, "");
+        }
+    } else
+        STRSCPY(colorname, "");
+
+    pnm_freepamtuple(scaledColor);
+
+    return pm_strdup(colorname);
+}
+
+
+
 double pnm_lumin_factor[3] = {PPM_LUMINR, PPM_LUMING, PPM_LUMINB};
 
 void
-pnm_YCbCrtuple(tuple    const tuple, 
-               double * const YP, 
-               double * const CbP, 
+pnm_YCbCrtuple(tuple    const tuple,
+               double * const YP,
+               double * const CbP,
                double * const CrP) {
 /*----------------------------------------------------------------------------
-   Assuming that the tuple 'tuple' is of tupletype RGB, return the 
+   Assuming that the tuple 'tuple' is of tupletype RGB, return the
    Y/Cb/Cr representation of the color represented by the tuple.
 -----------------------------------------------------------------------------*/
     int const red = (int)tuple[PAM_RED_PLANE];
     int const grn = (int)tuple[PAM_GRN_PLANE];
     int const blu = (int)tuple[PAM_BLU_PLANE];
-    
+
     *YP  = (+ PPM_LUMINR * red + PPM_LUMING * grn + PPM_LUMINB * blu);
     *CbP = (- 0.16874 * red - 0.33126 * grn + 0.50000 * blu);
     *CrP = (+ 0.50000 * red - 0.41869 * grn - 0.08131 * blu);
@@ -103,11 +698,11 @@ pnm_YCbCrtuple(tuple    const tuple,
 
 
 
-void 
+void
 pnm_YCbCr_to_rgbtuple(const struct pam * const pamP,
                       tuple              const tuple,
                       double             const Y,
-                      double             const Cb, 
+                      double             const Cb,
                       double             const Cr,
                       int *              const overflowP) {
 
@@ -128,7 +723,7 @@ pnm_YCbCr_to_rgbtuple(const struct pam * const pamP,
         } else if (rgb[plane] < 0.0) {
             overflow = TRUE;
             tuple[plane] = 0;
-        } else 
+        } else
             tuple[plane] = (sample)rgb[plane];
     }
     if (overflowP)
diff --git a/lib/libpamn.c b/lib/libpamn.c
index 26dbbfe3..ae28283a 100644
--- a/lib/libpamn.c
+++ b/lib/libpamn.c
@@ -23,6 +23,43 @@
 
 
 
+static unsigned int
+allocationDepth(const struct pam * const pamP) {
+
+    unsigned int retval;
+
+    if (pamP->len >= PAM_STRUCT_SIZE(allocation_depth)) {
+        if (pamP->allocation_depth == 0)
+            retval = pamP->depth;
+        else {
+            if (pamP->depth > pamP->allocation_depth)
+                pm_error("'allocationDepth' (%u) is smaller than 'depth' (%u)",
+                         pamP->allocation_depth, pamP->depth);
+            retval = pamP->allocation_depth;
+        }
+    } else
+        retval = pamP->depth;
+    return retval;
+}
+
+
+
+tuplen
+pnm_allocpamtuplen(const struct pam * const pamP) {
+
+    tuplen retval;
+
+    retval = malloc(allocationDepth(pamP) * sizeof(retval[0]));
+
+    if (retval == NULL)
+        pm_error("Out of memory allocating %u-plane normalized tuple",
+                 allocationDepth(pamP));
+
+    return retval;
+}
+
+
+
 static void
 allocpamrown(const struct pam * const pamP,
              tuplen **          const tuplerownP,
@@ -32,7 +69,8 @@ allocpamrown(const struct pam * const pamP,
    overflow will not occur in our calculations.  NOTE: pnm_readpaminit()
    ensures this assumption is valid.
 -----------------------------------------------------------------------------*/
-    int const bytes_per_tuple = pamP->depth * sizeof(samplen);
+    unsigned int const bytes_per_tuple =
+        allocationDepth(pamP) * sizeof(samplen);
 
     tuplen * tuplerown;
     const char * error;
@@ -47,15 +85,16 @@ allocpamrown(const struct pam * const pamP,
         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));
+                    pamP->width, allocationDepth(pamP),
+                    (unsigned)sizeof(samplen));
     else {
         /* Now we initialize the pointers to the individual tuples to make this
            a regulation C two dimensional array.
         */
-        
+
         unsigned char * p;
         unsigned int i;
-        
+
         p = (unsigned char*) (tuplerown + pamP->width);
             /* location of Tuple 0 */
         for (i = 0; i < pamP->width; ++i) {
@@ -93,7 +132,7 @@ pnm_allocpamrown(const struct pam * const pamP) {
 
 
 static void
-readpbmrow(const struct pam * const pamP, 
+readpbmrow(const struct pam * const pamP,
            tuplen *           const tuplenrow) {
 
     bit * bitrow;
@@ -101,7 +140,7 @@ readpbmrow(const struct pam * const pamP,
     jmp_buf * origJmpbufP;
 
     bitrow = pbm_allocrow(pamP->width);
-    
+
     if (setjmp(jmpbuf) != 0) {
         pbm_freerow(bitrow);
         pm_setjmpbuf(origJmpbufP);
@@ -123,15 +162,15 @@ readpbmrow(const struct pam * const pamP,
 
 
 static void
-readpamrow(const struct pam * const pamP, 
+readpamrow(const struct pam * const pamP,
            tuplen *           const tuplenrow) {
 
     jmp_buf jmpbuf;
     jmp_buf * origJmpbufP;
     tuple * tuplerow;
-    
+
     tuplerow = pnm_allocpamrow(pamP);
-    
+
     if (setjmp(jmpbuf) != 0) {
         pnm_freepamrow(tuplerow);
         pm_setjmpbuf(origJmpbufP);
@@ -159,13 +198,13 @@ readpamrow(const struct pam * const pamP,
 
 
 
-void 
-pnm_readpamrown(const struct pam * const pamP, 
+void
+pnm_readpamrown(const struct pam * const pamP,
                 tuplen *           const tuplenrow) {
 
-    /* For speed, we don't check any of the inputs for consistency 
+    /* 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 
+       checking should have been done by a prior call to
        pnm_writepaminit().
     */
     assert(pamP->maxval != 0);
@@ -186,7 +225,7 @@ pnm_readpamrown(const struct pam * const pamP,
 
 
 static void
-writepbmrow(const struct pam * const pamP, 
+writepbmrow(const struct pam * const pamP,
             const tuplen *     const tuplenrow) {
 
     jmp_buf jmpbuf;
@@ -206,26 +245,26 @@ writepbmrow(const struct pam * const pamP,
 
         for (col = 0; col < pamP->width; ++col)
             bitrow[col] = tuplenrow[col][0] < 0.5 ? PBM_BLACK : PBM_WHITE;
-        pbm_writepbmrow(pamP->file, bitrow, pamP->width, 
+        pbm_writepbmrow(pamP->file, bitrow, pamP->width,
                         pamP->format == PBM_FORMAT);
 
         pm_setjmpbuf(origJmpbufP);
     }
     pbm_freerow(bitrow);
-} 
+}
 
 
 
 static void
-writepamrow(const struct pam * const pamP, 
+writepamrow(const struct pam * const pamP,
             const tuplen *     const tuplenrow) {
 
     jmp_buf jmpbuf;
     jmp_buf * origJmpbufP;
     tuple * tuplerow;
-    
+
     tuplerow = pnm_allocpamrow(pamP);
-    
+
     if (setjmp(jmpbuf) != 0) {
         pnm_freepamrow(tuplerow);
         pm_setjmpbuf(origJmpbufP);
@@ -240,7 +279,7 @@ writepamrow(const struct pam * const pamP,
             for (plane = 0; plane < pamP->depth; ++plane)
                 tuplerow[col][plane] = (sample)
                     (tuplenrow[col][plane] * pamP->maxval + 0.5);
-        }    
+        }
         pnm_writepamrow(pamP, tuplerow);
 
         pm_setjmpbuf(origJmpbufP);
@@ -250,13 +289,13 @@ writepamrow(const struct pam * const pamP,
 
 
 
-void 
-pnm_writepamrown(const struct pam * const pamP, 
+void
+pnm_writepamrown(const struct pam * const pamP,
                  const tuplen *     const tuplenrow) {
 
-    /* For speed, we don't check any of the inputs for consistency 
+    /* 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 
+       checking should have been done by a prior call to
        pnm_writepaminit().
     */
     assert(pamP->maxval != 0);
@@ -274,16 +313,16 @@ pnm_writepamrown(const struct pam * const pamP,
 
 tuplen **
 pnm_allocpamarrayn(const struct pam * const pamP) {
-    
+
     tuplen ** tuplenarray;
     const char * error;
 
     /* If the speed of this is ever an issue, it might be sped up a little
        by allocating one large chunk.
     */
-    
+
     MALLOCARRAY(tuplenarray, pamP->height);
-    if (tuplenarray == NULL) 
+    if (tuplenarray == NULL)
         pm_asprintf(&error,
                     "Out of memory allocating the row pointer section of "
                     "a %u row array", pamP->height);
@@ -317,7 +356,7 @@ pnm_allocpamarrayn(const struct pam * const pamP) {
 
 
 void
-pnm_freepamarrayn(tuplen **          const tuplenarray, 
+pnm_freepamarrayn(tuplen **          const tuplenarray,
                   const struct pam * const pamP) {
 
     int row;
@@ -329,9 +368,9 @@ pnm_freepamarrayn(tuplen **          const tuplenarray,
 
 
 
-tuplen** 
-pnm_readpamn(FILE *       const file, 
-             struct pam * const pamP, 
+tuplen**
+pnm_readpamn(FILE *       const file,
+             struct pam * const pamP,
              int          const size) {
 
     tuplen **tuplenarray;
@@ -339,9 +378,9 @@ pnm_readpamn(FILE *       const file,
     jmp_buf * origJmpbufP;
 
     pnm_readpaminit(file, pamP, size);
-    
+
     tuplenarray = pnm_allocpamarrayn(pamP);
-    
+
     if (setjmp(jmpbuf) != 0) {
         pnm_freepamarrayn(tuplenarray, pamP);
         pm_setjmpbuf(origJmpbufP);
@@ -351,7 +390,7 @@ pnm_readpamn(FILE *       const file,
 
         pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
 
-        for (row = 0; row < pamP->height; ++row) 
+        for (row = 0; row < pamP->height; ++row)
             pnm_readpamrown(pamP, tuplenarray[row]);
 
         pm_setjmpbuf(origJmpbufP);
@@ -361,20 +400,33 @@ pnm_readpamn(FILE *       const file,
 
 
 
-void 
-pnm_writepamn(struct pam * const pamP, 
+void
+pnm_writepamn(struct pam * const pamP,
               tuplen **    const tuplenarray) {
 
     unsigned int row;
 
     pnm_writepaminit(pamP);
-    
-    for (row = 0; row < pamP->height; ++row) 
+
+    for (row = 0; row < pamP->height; ++row)
         pnm_writepamrown(pamP, tuplenarray[row]);
 }
 
 
 
+samplen
+pnm_normalized_sample(struct pam * const pamP,
+                      sample       const sample) {
+    return (samplen)sample/pamP->maxval;
+}
+
+sample
+pnm_unnormalized_sample(struct pam * const pamP,
+                        samplen      const sampleVal) {
+    double const epsilon = 1e-6;
+    return (sample)((sampleVal + epsilon) * pamP->maxval + 0.5);
+}
+
 void
 pnm_normalizetuple(struct pam * const pamP,
                    tuple        const tuple,
@@ -382,8 +434,8 @@ pnm_normalizetuple(struct pam * const pamP,
 
     unsigned int plane;
 
-    for (plane = 0; plane < pamP->depth; ++plane) 
-        tuplen[plane] = (samplen)tuple[plane] / pamP->maxval;
+    for (plane = 0; plane < pamP->depth; ++plane)
+        tuplen[plane] = pnm_normalized_sample(pamP, tuple[plane]);
 }
 
 
@@ -395,8 +447,8 @@ pnm_unnormalizetuple(struct pam * const pamP,
 
     unsigned int plane;
 
-    for (plane = 0; plane < pamP->depth; ++plane) 
-        tuple[plane] = tuplen[plane] * pamP->maxval + 0.5;
+    for (plane = 0; plane < pamP->depth; ++plane)
+        tuple[plane] = pnm_unnormalized_sample(pamP, tuplen[plane]);
 }
 
 
@@ -412,7 +464,7 @@ pnm_normalizeRow(struct pam *             const pamP,
            once here so we can multiply many times later.
         */
     unsigned int plane;
-    
+
     for (plane = 0; plane < pamP->depth; ++plane) {
         if (transform && transform[plane]) {
             unsigned int col;
@@ -437,14 +489,14 @@ reversemap(samplen          const samplen,
 /*----------------------------------------------------------------------------
    Find the integer sample value that maps to the normalized samplen value
    'samplen' through the map 'transformMap'.  We interpret the map as
-   mapping the value N+1 to all the values transformMap[N] through 
+   mapping the value N+1 to all the values transformMap[N] through
    transformMap[N+1], and we expect transformMap[N+1] to be greater than
    transformMap[N] for all N.
 -----------------------------------------------------------------------------*/
     /* Do a binary search, since the values are in sorted (increasing)
        order
     */
-    
+
     sample low, high;
 
     low = 0; high = maxval;  /* Consider whole range to start */
@@ -471,18 +523,18 @@ pnm_unnormalizeRow(struct pam *             const pamP,
                    tuple *                  const tuplerow) {
 
     unsigned int plane;
-    
+
     for (plane = 0; plane < pamP->depth; ++plane) {
         if (transform && transform[plane]) {
             unsigned int col;
             for (col = 0; col < pamP->width; ++col)
-                tuplerow[col][plane] = 
-                    reversemap(tuplenrow[col][plane], 
+                tuplerow[col][plane] =
+                    reversemap(tuplenrow[col][plane],
                                transform[plane], pamP->maxval);
         } else {
             unsigned int col;
             for (col = 0; col < pamP->width; ++col)
-                tuplerow[col][plane] = 
+                tuplerow[col][plane] =
                     tuplenrow[col][plane] * pamP->maxval + 0.5;
         }
     }
@@ -500,13 +552,13 @@ gammaCommon(struct pam *  const pamP,
     unsigned int plane;
     unsigned int opacityPlane;
     int haveOpacity;
-    
+
     pnm_getopacity(pamP, &haveOpacity, &opacityPlane);
 
     for (plane = 0; plane < pamP->depth; ++plane) {
         if (haveOpacity && plane == opacityPlane) {
             /* It's an opacity (alpha) plane, which means there is
-               no gamma adjustment in it.  
+               no gamma adjustment in it.
             */
         } else {
             unsigned int col;
@@ -550,7 +602,7 @@ applyopacityCommon(enum applyUnapply const applyUnapply,
 -----------------------------------------------------------------------------*/
     unsigned int opacityPlane;
     int haveOpacity;
-    
+
     pnm_getopacity(pamP, &haveOpacity, &opacityPlane);
 
     if (haveOpacity) {
@@ -605,6 +657,63 @@ pnm_unapplyopacityrown(struct pam * const pamP,
 
 
 
+void
+pnm_maketuplergbn(const struct pam * const pamP,
+                  tuplen             const tuple) {
+
+    if (allocationDepth(pamP) < 3)
+        pm_error("allocation depth %u passed to pnm_maketuplergb().  "
+                 "Must be at least 3.", allocationDepth(pamP));
+
+    if (pamP->depth < 3)
+        tuple[2] = tuple[1] = tuple[0];
+}
+
+
+
+void
+pnm_makerowrgbn(const struct pam * const pamP,
+                tuplen *           const tuplerow) {
+
+    if (pamP->depth < 3) {
+        unsigned int col;
+
+        if (allocationDepth(pamP) < 3)
+            pm_error("allocation depth %u passed to pnm_makerowrgb().  "
+                     "Must be at least 3.", allocationDepth(pamP));
+
+        for (col = 0; col < pamP->width; ++col) {
+            tuplen const thisTuple = tuplerow[col];
+            thisTuple[2] = thisTuple[1] = thisTuple[0];
+        }
+    }
+}
+
+
+
+void
+pnm_makearrayrgbn(const struct pam * const pamP,
+                  tuplen **          const tuples) {
+
+    if (pamP->depth < 3) {
+        unsigned int row;
+        if (allocationDepth(pamP) < 3)
+            pm_error("allocation depth %u passed to pnm_makearrayrgb().  "
+                     "Must be at least 3.", allocationDepth(pamP));
+
+        for (row = 0; row < pamP->height; ++row) {
+            tuplen * const tuplerow = tuples[row];
+            unsigned int col;
+            for (col = 0; col < pamP->width; ++col) {
+                tuplen const thisTuple = tuplerow[col];
+                thisTuple[2] = thisTuple[1] = thisTuple[0];
+            }
+        }
+    }
+}
+
+
+
 static void
 fillInMap(pnm_transformMap const ungammaTransformMap,
           sample           const maxval,
@@ -657,7 +766,7 @@ createUngammaMapOffset(const struct pam * const pamP,
                     retval[plane] = NULL;
                 else
                     retval[plane] = ungammaTransformMap;
-            }            
+            }
             fillInMap(ungammaTransformMap, pamP->maxval, offset);
         } else {
             free(retval);
diff --git a/lib/libpbm2.c b/lib/libpbm2.c
index f199c51a..a35004f9 100644
--- a/lib/libpbm2.c
+++ b/lib/libpbm2.c
@@ -18,7 +18,7 @@
 #include "fileio.h"
 #include "pam.h"
 
-static bit 
+static bit
 getbit (FILE * const file) {
     char ch;
 
@@ -28,7 +28,7 @@ getbit (FILE * const file) {
 
     if ( ch != '0' && ch != '1' )
         pm_error( "junk in file where bits should be" );
-    
+
     return ( ch == '1' ) ? 1 : 0;
 }
 
@@ -157,9 +157,9 @@ pbm_readpbmrow( FILE * const file,
 
 
 void
-pbm_readpbmrow_packed(FILE *          const fileP, 
+pbm_readpbmrow_packed(FILE *          const fileP,
                       unsigned char * const packedBits,
-                      int             const cols, 
+                      int             const cols,
                       int             const format) {
 
     switch(format) {
@@ -182,20 +182,20 @@ pbm_readpbmrow_packed(FILE *          const fileP,
     case RPBM_FORMAT: {
         unsigned int bytesReadCt;
         bytesReadCt = fread(packedBits, 1, pbm_packed_bytes(cols), fileP);
-             
+
         if (bytesReadCt < pbm_packed_bytes(cols)) {
-            if (feof(fileP)) 
-                if (bytesReadCt == 0) 
+            if (feof(fileP))
+                if (bytesReadCt == 0)
                     pm_error("Attempt to read a raw PBM image row, but "
                              "no more rows left in file.");
                 else
                     pm_error("EOF in the middle of a raw PBM row.");
-            else 
+            else
                 pm_error("I/O error reading raw PBM row");
         }
     }
     break;
-    
+
     default:
         pm_error("Internal error in pbm_readpbmrow_packed.");
     }
@@ -205,7 +205,7 @@ pbm_readpbmrow_packed(FILE *          const fileP,
 
 void
 pbm_readpbmrow_bitoffset(FILE *          const ifP,
-                         unsigned char * const packedBits, 
+                         unsigned char * const packedBits,
                          int             const cols,
                          int             const format,
                          unsigned int    const offset) {
@@ -237,7 +237,7 @@ pbm_readpbmrow_bitoffset(FILE *          const ifP,
         /* Target slot doesn't start on byte boundary; right-shift. */
         unsigned char carryover;
         unsigned int i;
-  
+
         carryover = (origHead >> lsh) << lsh;
 
         for (i = 0; i <= last; ++i) {
@@ -246,7 +246,7 @@ pbm_readpbmrow_bitoffset(FILE *          const ifP,
             carryover = t;
         }
     }
-  
+
     if ((cols + rsh) % 8 > 0) {
         /* Adjust rightmost char */
         unsigned int  const trs = (cols + rsh) % 8;
diff --git a/lib/libpbm3.c b/lib/libpbm3.c
index c8389824..0144abe2 100644
--- a/lib/libpbm3.c
+++ b/lib/libpbm3.c
@@ -40,9 +40,9 @@
 
 
 void
-pbm_writepbminit(FILE * const fileP, 
-                 int    const cols, 
-                 int    const rows, 
+pbm_writepbminit(FILE * const fileP,
+                 int    const cols,
+                 int    const rows,
                  int    const forceplain) {
 
     if (!forceplain && !pm_plain_output) {
@@ -55,14 +55,20 @@ pbm_writepbminit(FILE * const fileP,
 
 static void
 writePackedRawRow(FILE *                const fileP,
-                  const unsigned char * const packed_bits,
-                  int                   const cols) {
+                  const unsigned char * const packedBits,
+                  unsigned int          const cols) {
 
-    int bytesWritten;
-    bytesWritten = fwrite(packed_bits, 1, pbm_packed_bytes(cols), fileP);
-    if (bytesWritten < pbm_packed_bytes(cols)) 
-        pm_error("I/O error writing packed row to raw PBM file.");
-} 
+    unsigned int const packedByteCt = pbm_packed_bytes(cols);
+
+    size_t writtenByteCt;
+
+    writtenByteCt = fwrite(packedBits, 1, packedByteCt, fileP);
+    if (writtenByteCt < packedByteCt)
+        pm_error("I/O error writing packed row to raw PBM file.  "
+                 "(Attempted fwrite() of %u packed bytes; "
+                 "only %u got written)",
+                 packedByteCt, (unsigned)writtenByteCt);
+}
 
 
 
@@ -83,21 +89,21 @@ packBitsWithSse2(  FILE *          const fileP,
 -----------------------------------------------------------------------------*/
     /*
       We use 2 SSE registers.
-    
+
       The key machine instructions are:
-        
+
       PCMPGTB128  Packed CoMPare Greater Than Byte
-    
+
         Compares 16 bytes in parallel
         Result is x00 if greater than, xFF if not for each byte
 
-    
-      PMOVMSKB128 Packed MOVe MaSK Byte 
-    
+
+      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 
+        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.
 
@@ -133,7 +139,7 @@ packBitsWithSse2(  FILE *          const fileP,
             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;
         }
     }
@@ -142,10 +148,10 @@ packBitsWithSse2(  FILE *          const fileP,
         unsigned int i, j;
 
         bit128.v16 = bit128.v16 ^ bit128.v16;
-    
-        for (i = 0, j = col ; j < cols; ++i, ++j) 
+
+        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);
@@ -211,22 +217,22 @@ packPartialBytes(const bit *     const bitrow,
                  unsigned int    const cols,
                  unsigned int    const nextCol,
                  unsigned char * const packedBits) {
-              
+
     /* routine for partial byte at the end of packedBits[]
        Prior to addition of the above enhancement,
        this method was used for the entire process
-    */                   
-    
+    */
+
     unsigned int col;
     int bitshift;
     unsigned char item;
-    
+
     bitshift = 7;  /* initial value */
     item = 0;      /* initial value */
     for (col = nextCol; col < cols; ++col, --bitshift)
         if (bitrow[col] != 0)
             item |= 1 << bitshift;
-    
+
     packedBits[col/8] = item;
 }
 
@@ -252,7 +258,7 @@ writePbmRowRaw(FILE *      const fileP,
         pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
 
         switch (PACKBITS_SSE) {
-        case 2: 
+        case 2:
             packBitsWithSse2(fileP, bitrow, packedBits, cols);
             break;
         default: {
@@ -273,9 +279,9 @@ writePbmRowRaw(FILE *      const fileP,
 
 static void
 writePbmRowPlain(FILE *      const fileP,
-                 const bit * const bitrow, 
+                 const bit * const bitrow,
                  int         const cols) {
-    
+
     int col, charcount;
 
     charcount = 0;
@@ -293,9 +299,9 @@ writePbmRowPlain(FILE *      const fileP,
 
 
 void
-pbm_writepbmrow(FILE *       const fileP, 
-                const bit *  const bitrow, 
-                int          const cols, 
+pbm_writepbmrow(FILE *       const fileP,
+                const bit *  const bitrow,
+                int          const cols,
                 int          const forceplain) {
 
     if (!forceplain && !pm_plain_output)
@@ -307,9 +313,9 @@ pbm_writepbmrow(FILE *       const fileP,
 
 
 void
-pbm_writepbmrow_packed(FILE *                const fileP, 
+pbm_writepbmrow_packed(FILE *                const fileP,
                        const unsigned char * const packedBits,
-                       int                   const cols, 
+                       int                   const cols,
                        int                   const forceplain) {
 
     if (!forceplain && !pm_plain_output)
@@ -327,11 +333,11 @@ pbm_writepbmrow_packed(FILE *                const fileP,
             pm_longjmp();
         } else {
             unsigned int col;
-            
+
             pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
 
-            for (col = 0; col < cols; ++col) 
-                bitrow[col] = 
+            for (col = 0; col < cols; ++col)
+                bitrow[col] =
                     packedBits[col/8] & (0x80 >> (col%8)) ?
                     PBM_BLACK : PBM_WHITE;
 
@@ -395,13 +401,13 @@ pbm_writepbmrow_bitoffset(FILE *          const fileP,
     bool const carryover = (csh == 0 || rsh + csh > 8);
         /* TRUE:  Input comes from colByteCnt bytes and one extra byte.
            FALSE: Input comes from colByteCnt bytes.  For example:
-           TRUE:  xxxxxxii iiiiiiii iiiiiiii iiixxxxx  cols=21, offset=6 
+           TRUE:  xxxxxxii iiiiiiii iiiiiiii iiixxxxx  cols=21, offset=6
            FALSE: xiiiiiii iiiiiiii iiiiiixx ________  cols=21, offset=1
 
            We treat these differently for in the FALSE case the byte after
            last (indicated by ________) may not exist.
         */
-       
+
     if (rsh > 0) {
         unsigned int const shiftBytes =  carryover ? colByteCnt : colByteCnt-1;
 
@@ -412,26 +418,26 @@ pbm_writepbmrow_bitoffset(FILE *          const fileP,
         if (!carryover)
             window[last] = window[last] << rsh;
     }
-      
+
     if (csh > 0)
         window[last] = leftBits(window[last], csh);
-          
+
     pbm_writepbmrow_packed(fileP, window, cols, 0);
 }
 
 
 
 void
-pbm_writepbm(FILE * const fileP, 
-             bit ** const bits, 
-             int    const cols, 
-             int    const rows, 
+pbm_writepbm(FILE * const fileP,
+             bit ** const bits,
+             int    const cols,
+             int    const rows,
              int    const forceplain) {
 
     int row;
 
     pbm_writepbminit(fileP, cols, rows, forceplain);
-    
+
     for (row = 0; row < rows; ++row)
         pbm_writepbmrow(fileP, bits[row], cols, forceplain);
 }
diff --git a/lib/libpbmfont.c b/lib/libpbmfont.c
deleted file mode 100644
index d3551e78..00000000
--- a/lib/libpbmfont.c
+++ /dev/null
@@ -1,1603 +0,0 @@
-/* libpbm5.c - pbm utility library part 5
-**
-** Font routines.
-**
-** BDF font code Copyright 1993 by George Phillips.
-**
-** 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.
-*/
-
-#include <assert.h>
-#include <string.h>
-#include <ctype.h>
-
-#include "netpbm/pm_c_util.h"
-#include "netpbm/mallocvar.h"
-#include "netpbm/nstring.h"
-
-#include "pbmfont.h"
-#include "pbm.h"
-
-static unsigned int const firstCodePoint = 32;
-    /* This is the code point of the first character in a pbmfont font.
-       In ASCII, it is a space.
-    */
-
-static unsigned int const nCharsInFont = 96;
-    /* The number of characters in a pbmfont font.  A pbmfont font defines
-       characters at position 32 (ASCII space) through 127, so that's 96.
-    */
-
-/* The default font, packed in hex so this source file doesn't get huge.
-** You can replace this with your own font using pbm_dumpfont().
-*/
-#define DEFAULTFONT_ROWS 155
-#define DEFAULTFONT_COLS 112
-static unsigned long defaultfont_bits[DEFAULTFONT_ROWS][(DEFAULTFONT_COLS+31)/32] = {
-    {0x00000000L,0x20000c00L,0x10000000L,0x00000000L},
-    {0xc600a000L,0x42000810L,0x00000002L,0x00000063L},
-    {0x6c00a000L,0x45000810L,0x00000002L,0x00000036L},
-    {0x6c00a000L,0x88800808L,0xf2e1dee2L,0x00000036L},
-    {0x54000000L,0x80000800L,0x11122442L,0x0000002aL},
-    {0x54000001L,0x00000800L,0x11122442L,0x0000002aL},
-    {0x54000001L,0x00000800L,0x11122282L,0x0000002aL},
-    {0x44000102L,0x00000800L,0x11122382L,0x00000022L},
-    {0xee000102L,0x00000800L,0x11e1e102L,0x00000077L},
-    {0x00000204L,0x00000800L,0x11002102L,0x00000000L},
-    {0x00000000L,0x00000c00L,0x11002102L,0x00000000L},
-    {0x00000000L,0x003f8000L,0xe3807600L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x02000080L,0x00040000L,0x00120000L,0x00000001L},
-    {0x04000082L,0x828e1838L,0x20210100L,0x00000002L},
-    {0x04000082L,0x82912448L,0x20210100L,0x00000002L},
-    {0x08000082L,0x8fd01940L,0x404087c2L,0x00000004L},
-    {0x08000080L,0x050c0622L,0x00408102L,0x00000004L},
-    {0x10000080L,0x05061874L,0x0040828fL,0x00008008L},
-    {0x10000080L,0x1f912688L,0x00408002L,0x00000008L},
-    {0x20000000L,0x0a11098cL,0x00408002L,0x00000010L},
-    {0x20000080L,0x0a0e0672L,0x00210000L,0x00000010L},
-    {0x40000000L,0x00040000L,0x00210000L,0x00000020L},
-    {0x00000000L,0x00000000L,0x00120000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x004e0838L,0x7023e1cfL,0x00008000L},
-    {0x00000000L,0x00913844L,0x88620208L,0x00008000L},
-    {0x08000000L,0x00910844L,0x08a20401L,0x00000004L},
-    {0x10000000L,0x01110844L,0x08a20401L,0x00000008L},
-    {0x20000000L,0x01110808L,0x3123c781L,0x00000010L},
-    {0x400003e0L,0x02110810L,0x0a202441L,0x00000020L},
-    {0x20000000L,0x02110820L,0x0bf02442L,0x00000010L},
-    {0x10008000L,0x04110844L,0x88242442L,0x00000008L},
-    {0x08008002L,0x040e3e7cL,0x7073c382L,0x00000004L},
-    {0x00010000L,0x08000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x0000e1c0L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00011220L,0x00000000L,0x70e38f87L,0x00000000L},
-    {0x20011220L,0x00020020L,0x89108448L,0x00008010L},
-    {0x10011220L,0x00040010L,0x09314448L,0x00008008L},
-    {0x0800e221L,0x02083e08L,0x11514788L,0x00000004L},
-    {0x040111e0L,0x00100004L,0x2153e448L,0x00000002L},
-    {0x08011020L,0x00083e08L,0x213a2448L,0x00008004L},
-    {0x10011040L,0x02040010L,0x01022448L,0x00008008L},
-    {0x2000e381L,0x02020020L,0x20e77f87L,0x00000010L},
-    {0x00000000L,0x04000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x3803e7efL,0xc73bbe3dL,0xdb863ce7L,0x0000001cL},
-    {0x44011224L,0x48910808L,0x91036648L,0x00008022L},
-    {0x4c011285L,0x48910808L,0xa1036648L,0x00008026L},
-    {0x54011387L,0x081f0808L,0xc102a548L,0x0000802aL},
-    {0x54011285L,0x09910808L,0xe102a548L,0x0000802aL},
-    {0x4e011204L,0x08910848L,0x9112a4c8L,0x00008027L},
-    {0x40011224L,0x08910848L,0x891224c8L,0x00008020L},
-    {0x3803e7efL,0x073bbe31L,0xcff77e47L,0x0000001cL},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000003L,0x00000000L},
-    {0x0003e1cfL,0x87bff7efL,0xdfbf77c2L,0x00000000L},
-    {0x00013224L,0x48a4a244L,0x89122442L,0x00000000L},
-    {0x00011224L,0x4824a244L,0xa8a14482L,0x00000000L},
-    {0x00013227L,0x8e04226cL,0xa8414102L,0x00000000L},
-    {0x0001e224L,0x83842228L,0xa8a08102L,0x00000000L},
-    {0x00010224L,0x40842228L,0xd8a08242L,0x00000000L},
-    {0x00010224L,0x48843638L,0x51108442L,0x00000000L},
-    {0x0003c1ceL,0x6f1f1c10L,0x53b9c7c2L,0x00000000L},
-    {0x00000060L,0x00000000L,0x00000002L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000003L,0x00000000L},
-    {0xfe000000L,0x00000000L,0x00000000L,0x0000007fL},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00010180L,0x000000c0L,0x003001c0L,0x00000000L},
-    {0x08008081L,0x00040040L,0x00100200L,0x00000004L},
-    {0x10008082L,0x80040040L,0x00100200L,0x00000008L},
-    {0x10004084L,0x40023c78L,0x70f1c7c7L,0x00004008L},
-    {0x10004080L,0x00000244L,0x89122208L,0x00008008L},
-    {0x20002080L,0x00001e44L,0x8113e208L,0x00008010L},
-    {0x10002080L,0x00002244L,0x81120208L,0x00008008L},
-    {0x10001080L,0x00002244L,0x89122208L,0x00008008L},
-    {0x10001080L,0x00001db8L,0x70e9c787L,0x00008008L},
-    {0x10000880L,0x00000000L,0x00000000L,0x00008008L},
-    {0x08000180L,0x00000000L,0x00000000L,0x00008004L},
-    {0x00000000L,0x1fc00000L,0x00000007L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00030080L,0x981c0000L,0x00000000L,0x00000000L},
-    {0x20010000L,0x08040000L,0x00000000L,0x00000010L},
-    {0x10010000L,0x08040000L,0x00000000L,0x00000008L},
-    {0x10016387L,0x898474b8L,0x72e1d5c7L,0x00000008L},
-    {0x10019080L,0x8a042a64L,0x89122208L,0x00008008L},
-    {0x08011080L,0x8c042a44L,0x89122207L,0x00000004L},
-    {0x10011080L,0x8a042a44L,0x89122200L,0x00008008L},
-    {0x10011080L,0x89042a44L,0x89122208L,0x00008008L},
-    {0x1003bbe0L,0x98dfebe6L,0x71e1e787L,0x00000008L},
-    {0x10000000L,0x80000000L,0x01002000L,0x00000008L},
-    {0x20000000L,0x80000000L,0x01002000L,0x00000010L},
-    {0x00000007L,0x00000000L,0x03807000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00008000L,0x00000000L,0x10410000L,0x00000000L},
-    {0x00008000L,0x00000000L,0x20408000L,0x00000000L},
-    {0x0001f66eL,0xfdfbf77cL,0x20408000L,0x00000000L},
-    {0x24008224L,0x488a2248L,0x20408240L,0x00000012L},
-    {0x54008224L,0x4a842210L,0x40404540L,0x0000002aL},
-    {0x48008222L,0x8a8a1420L,0x20408480L,0x00000024L},
-    {0x00008a23L,0x85111c44L,0x20408000L,0x00000000L},
-    {0x000071d1L,0x0531887cL,0x20408000L,0x00000000L},
-    {0x00000000L,0x00000800L,0x20408000L,0x00000000L},
-    {0x00000000L,0x00000800L,0x10410000L,0x00000000L},
-    {0x00000000L,0x00003000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x00000000L,0x00000000L,0x00000000L},
-    {0x00000000L,0x20000c00L,0x10000000L,0x00000000L},
-    {0xc600a000L,0x42000810L,0x00000002L,0x00000063L},
-    {0x6c00a000L,0x45000810L,0x00000002L,0x00000036L},
-    {0x6c00a000L,0x88800808L,0xf2e1dee2L,0x00000036L},
-    {0x54000000L,0x80000800L,0x11122442L,0x0000002aL},
-    {0x54000001L,0x00000800L,0x11122442L,0x0000002aL},
-    {0x54000001L,0x00000800L,0x11122282L,0x0000002aL},
-    {0x44000102L,0x00000800L,0x11122382L,0x00000022L},
-    {0xee000102L,0x00000800L,0x11e1e102L,0x00000077L},
-    {0x00000204L,0x00000800L,0x11002102L,0x00000000L},
-    {0x00000000L,0x00000c00L,0x11002102L,0x00000000L},
-    {0x00000000L,0x003f8000L,0xe3807600L,0x00000000L}
-    };
-
-/* A default BDF font */
-/* Not as nicely compacted as the PBM font, oh well. */
-
-static struct glyph _g[190] = {
- { 1, 1, 0, 0, 3, "\0" },
- { 1, 9, 1, 0, 3, "\1\1\1\1\1\1\1\0\1" },
- { 3, 3, 1, 6, 5, "\1\0\1\1\0\1\1\0\1" },
- { 5, 8, 0, 0, 6, "\0\1\0\1\0\0\1\0\1\0\1\1\1\1\1\0\1\0\1\0\0\1\0\1\0\1\1\1\1\1\0\1\0\1\0\0\1\0\1\0" },
- { 5, 11, 0, -1, 6, "\0\0\1\0\0\0\1\1\1\0\1\0\1\0\1\1\0\1\0\0\0\1\1\0\0\0\0\1\1\0\0\0\1\0\1\0\0\1\0\1\1\0\1\0\1\0\1\1\1\0\0\0\1\0\0" },
- { 8, 9, 0, 0, 9, "\0\1\1\0\0\0\1\1\1\0\0\1\1\1\1\0\1\0\0\1\0\1\0\0\0\1\1\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\1\0\1\1\0\0\0\1\0\1\0\0\1\0\1\0\0\1\0\0\1\0\1\0\0\0\1\1\0" },
- { 9, 9, 0, 0, 10, "\0\0\0\1\1\0\0\0\0\0\0\1\0\0\1\0\0\0\0\0\1\0\0\1\0\0\0\0\0\0\1\1\0\1\1\1\0\1\1\1\1\0\0\1\0\1\1\0\0\1\1\1\0\0\1\0\0\0\0\1\0\0\0\1\1\0\0\1\1\1\0\1\0\1\1\1\1\0\1\1\0" },
- { 2, 3, 1, 6, 4, "\1\1\0\1\1\0" },
- { 3, 12, 1, -3, 5, "\0\0\1\0\1\0\0\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1" },
- { 3, 12, 0, -3, 5, "\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\0\0\1\0\1\0\0" },
- { 5, 5, 0, 4, 6, "\0\0\1\0\0\1\0\1\0\1\0\1\1\1\0\1\0\1\0\1\0\0\1\0\0" },
- { 5, 5, 1, 1, 7, "\0\0\1\0\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\0\1\0\0" },
- { 2, 3, 0, -2, 3, "\0\1\0\1\1\0" },
- { 5, 1, 1, 3, 8, "\1\1\1\1\1" },
- { 1, 1, 1, 0, 3, "\1" },
- { 3, 9, 0, 0, 3, "\0\0\1\0\0\1\0\0\1\0\1\0\0\1\0\0\1\0\1\0\0\1\0\0\1\0\0" },
- { 5, 9, 0, 0, 6, "\0\1\1\1\0\1\1\0\1\1\1\0\0\0\1\1\0\0\0\1\1\0\0\0\1\1\0\0\0\1\1\0\0\0\1\1\1\0\1\1\0\1\1\1\0" },
- { 4, 9, 0, 0, 6, "\0\0\1\0\0\1\1\0\1\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\1\1\1" },
- { 5, 9, 0, 0, 6, "\0\1\1\1\0\1\0\0\0\1\0\0\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\1\1\1\1\1" },
- { 5, 9, 0, 0, 6, "\0\1\1\1\0\1\0\0\0\1\0\0\0\0\1\0\0\0\1\0\0\1\1\1\0\0\0\0\0\1\0\0\0\0\1\1\0\0\0\1\0\1\1\1\0" },
- { 5, 9, 0, 0, 6, "\0\0\0\1\0\0\0\1\1\0\0\0\1\1\0\0\1\0\1\0\0\1\0\1\0\1\0\0\1\0\1\1\1\1\1\0\0\0\1\0\0\0\0\1\0" },
- { 5, 9, 0, 0, 6, "\0\0\1\1\1\0\1\0\0\0\0\1\0\0\0\0\1\1\1\0\0\0\0\1\1\0\0\0\0\1\0\0\0\0\1\1\0\0\1\1\0\1\1\1\0" },
- { 5, 9, 0, 0, 6, "\0\0\0\1\1\0\1\1\0\0\0\1\0\0\0\1\1\1\1\0\1\0\0\1\1\1\0\0\0\1\1\0\0\0\1\1\1\0\0\1\0\1\1\1\0" },
- { 5, 9, 0, 0, 6, "\1\1\1\1\1\1\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\0\0\1\0\0\0" },
- { 5, 9, 0, 0, 6, "\0\1\1\1\0\1\0\0\0\1\1\0\0\0\1\1\1\0\0\1\0\1\1\1\0\1\0\0\1\1\1\0\0\0\1\1\0\0\0\1\0\1\1\1\0" },
- { 5, 9, 0, 0, 6, "\0\1\1\1\0\1\0\0\1\1\1\0\0\0\1\1\0\0\0\1\1\1\0\0\1\0\1\1\1\1\0\0\0\1\0\0\0\1\1\0\1\1\0\0\0" },
- { 1, 6, 1, 0, 3, "\1\0\0\0\0\1" },
- { 2, 8, 0, -2, 3, "\0\1\0\0\0\0\0\0\0\0\0\1\0\1\1\0" },
- { 6, 5, 0, 1, 8, "\0\0\0\0\1\1\0\0\1\1\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1" },
- { 5, 3, 1, 2, 7, "\1\1\1\1\1\0\0\0\0\0\1\1\1\1\1" },
- { 6, 5, 1, 1, 8, "\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\1\1\0\0\1\1\0\0\0\0" },
- { 4, 9, 0, 0, 5, "\0\1\1\0\1\0\0\1\0\0\0\1\0\0\1\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\0\0\1\0\0" },
- { 10, 11, 1, -2, 11, "\0\0\0\0\1\1\1\1\0\0\0\0\1\1\0\0\0\0\1\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\1\1\0\1\0\1\1\0\0\1\0\0\1\0\0\1\1\0\1\0\0\0\1\0\0\1\1\0\1\0\0\1\0\0\1\0\1\0\1\0\0\1\0\0\1\0\1\0\0\1\1\0\1\1\0\0\0\1\0\0\0\0\0\0\0\0\0\0\1\1\1\1\1\0\0\0" },
- { 9, 9, 0, 0, 9, "\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\1" },
- { 7, 9, 0, 0, 8, "\1\1\1\1\1\1\0\0\1\0\0\0\1\1\0\1\0\0\0\0\1\0\1\0\0\0\1\1\0\1\1\1\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\1\0\1\0\0\0\1\1\1\1\1\1\1\1\0" },
- { 7, 9, 0, 0, 8, "\0\0\1\1\1\0\1\0\1\1\0\0\1\1\0\1\0\0\0\0\1\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\1\0\1\1\0\0\1\1\0\0\1\1\1\1\0" },
- { 8, 9, 0, 0, 9, "\1\1\1\1\1\1\0\0\0\1\0\0\0\1\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\0\0\0\1\1\0\1\1\1\1\1\1\0\0" },
- { 7, 9, 0, 0, 8, "\1\1\1\1\1\1\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\1\1\1" },
- { 7, 9, 0, 0, 8, "\1\1\1\1\1\1\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\1\1\1\0\0\0" },
- { 8, 9, 0, 0, 9, "\0\0\1\1\1\0\1\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\0\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
- { 8, 9, 0, 0, 9, "\1\1\1\0\0\1\1\1\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\1\1\1\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\1\1\1\0\0\1\1\1" },
- { 3, 9, 0, 0, 4, "\1\1\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
- { 4, 9, 0, 0, 4, "\0\1\1\1\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\1\0\1\0\1\1\0\0" },
- { 8, 9, 0, 0, 8, "\1\1\1\0\1\1\1\0\0\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\1\1\1\0\0\0\0\0\1\0\1\1\0\0\0\0\1\0\0\1\1\0\0\0\1\0\0\0\1\1\0\1\1\1\0\0\1\1\1" },
- { 6, 9, 0, 0, 7, "\1\1\1\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\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\1\1\1\1\1\1\1" },
- { 11, 9, 0, 0, 11, "\1\1\0\0\0\0\0\0\0\1\1\0\1\1\0\0\0\0\0\1\1\0\0\1\1\0\0\0\0\0\1\1\0\0\1\0\1\0\0\0\1\0\1\0\0\1\0\1\0\0\0\1\0\1\0\0\1\0\0\1\0\1\0\0\1\0\0\1\0\0\1\0\1\0\0\1\0\0\1\0\0\0\1\0\0\0\1\0\1\1\1\0\0\1\0\0\1\1\1" },
- { 9, 9, 0, 0, 9, "\1\1\0\0\0\0\1\1\1\0\1\1\0\0\0\0\1\0\0\1\1\0\0\0\0\1\0\0\1\0\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\0\1\0\0\1\0\0\0\0\1\1\0\1\1\1\0\0\0\0\1\0" },
- { 8, 9, 0, 0, 9, "\0\0\1\1\1\1\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
- { 7, 9, 0, 0, 7, "\1\1\1\1\1\1\0\0\1\0\0\0\1\1\0\1\0\0\0\0\1\0\1\0\0\0\1\1\0\1\1\1\1\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\1\1\0\0\0\0" },
- { 8, 11, 0, -2, 9, "\0\0\1\1\1\1\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\0\0\1\1" },
- { 8, 9, 0, 0, 8, "\1\1\1\1\1\1\0\0\0\1\0\0\0\1\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\1\1\0\0\1\1\1\1\1\0\0\0\1\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\1\0\1\1\1\0\0\0\1\1" },
- { 6, 9, 0, 0, 7, "\0\1\1\1\0\1\1\0\0\0\1\1\1\0\0\0\0\1\0\1\1\0\0\0\0\0\1\1\1\0\0\0\0\0\1\1\1\0\0\0\0\1\1\1\0\0\1\1\1\0\1\1\1\0" },
- { 7, 9, 0, 0, 7, "\1\1\1\1\1\1\1\1\0\0\1\0\0\1\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\1\1\0\0" },
- { 8, 9, 0, 0, 8, "\1\1\1\0\0\1\1\1\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
- { 9, 9, 0, 0, 9, "\1\1\1\0\0\0\1\1\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0" },
- { 12, 9, 0, 0, 12, "\1\1\1\0\1\1\1\0\0\1\1\1\0\1\0\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\0\1\1\0\1\1\1\0\1\0\0\0\0\0\1\0\1\0\1\0\1\0\0\0\0\0\1\1\0\0\1\1\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0" },
- { 8, 9, 0, 0, 8, "\1\1\1\0\0\1\1\1\0\1\0\0\0\0\1\0\0\0\1\0\0\1\0\0\0\0\1\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\1\0\1\1\0\0\0\0\1\0\0\1\0\0\0\1\0\0\0\0\1\0\1\1\1\0\0\1\1\1" },
- { 9, 9, 0, 0, 9, "\1\1\1\0\0\0\1\1\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\0\1\0\0\0\0\0\1\1\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\1\1\0\0\0" },
- { 7, 9, 0, 0, 8, "\1\1\1\1\1\1\1\1\0\0\0\0\1\1\0\0\0\0\1\1\0\0\0\0\1\1\0\0\0\0\1\1\0\0\0\0\1\1\0\0\0\0\1\1\0\0\0\0\0\1\0\0\0\0\0\1\1\1\1\1\1\1\1" },
- { 3, 12, 1, -3, 5, "\1\1\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1" },
- { 3, 9, 0, 0, 3, "\1\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\1" },
- { 3, 12, 0, -3, 5, "\1\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1\1" },
- { 5, 5, 0, 4, 6, "\0\0\1\0\0\0\1\0\1\0\0\1\0\1\0\1\0\0\0\1\1\0\0\0\1" },
- { 6, 1, 0, -3, 6, "\1\1\1\1\1\1" },
- { 2, 3, 1, 6, 4, "\0\1\1\0\1\1" },
- { 5, 6, 1, 0, 6, "\0\1\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" },
- { 5, 9, 0, 0, 6, "\1\1\0\0\0\0\1\0\0\0\0\1\0\0\0\0\1\1\1\0\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\1\1\0" },
- { 4, 6, 1, 0, 5, "\0\1\1\0\1\0\0\1\1\0\0\0\1\0\0\0\1\0\0\1\0\1\1\0" },
- { 5, 9, 1, 0, 6, "\0\0\1\1\0\0\0\0\1\0\0\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" },
- { 5, 6, 1, 0, 6, "\0\1\1\0\0\1\0\0\1\0\1\1\1\1\0\1\0\0\0\0\1\1\0\0\1\0\1\1\1\0" },
- { 3, 9, 0, 0, 3, "\0\0\1\0\1\0\0\1\0\1\1\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0" },
- { 5, 9, 1, -3, 6, "\0\1\1\1\1\1\0\0\1\0\1\0\0\1\0\1\1\1\0\0\0\1\0\0\0\0\1\1\1\0\1\0\0\0\1\1\0\0\0\1\0\1\1\1\0" },
- { 6, 9, 0, 0, 6, "\1\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1\1\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1\0\1\1" },
- { 3, 9, 0, 0, 3, "\0\1\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
- { 2, 12, 0, -3, 3, "\0\1\0\0\0\0\1\1\0\1\0\1\0\1\0\1\0\1\0\1\0\1\1\0" },
- { 6, 9, 0, 0, 6, "\1\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\0\1\0\0\0\1\1\0\0\0\0\1\0\1\0\0\0\1\0\0\1\0\0\1\0\0\1\1" },
- { 3, 9, 0, 0, 3, "\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
- { 9, 6, 0, 0, 9, "\1\0\1\1\0\1\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1\0\1\1\0\1\1" },
- { 6, 6, 0, 0, 6, "\1\0\1\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1\0\1\1" },
- { 4, 6, 1, 0, 6, "\0\1\1\0\1\0\0\1\1\0\0\1\1\0\0\1\1\0\0\1\0\1\1\0" },
- { 5, 9, 0, -3, 6, "\1\1\1\1\0\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\1\1\0\0\1\0\0\0\0\1\0\0\0\1\1\1\0\0" },
- { 5, 9, 1, -3, 6, "\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\1\0\0\0\0\1\0\0\0\0\1\0\0\0\1\1\1" },
- { 4, 6, 0, 0, 4, "\1\0\1\1\0\1\1\0\0\1\0\0\0\1\0\0\0\1\0\0\1\1\1\0" },
- { 4, 6, 1, 0, 6, "\0\1\1\1\1\0\0\1\1\1\0\0\0\0\1\1\1\0\0\1\1\1\1\0" },
- { 4, 7, 0, 0, 4, "\0\1\0\0\1\1\1\1\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\1\1" },
- { 6, 6, 0, 0, 6, "\1\1\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\1\0\1" },
- { 6, 6, 0, 0, 6, "\1\1\0\0\1\1\0\1\0\0\1\0\0\1\0\1\1\0\0\1\0\1\0\0\0\0\1\1\0\0\0\0\1\0\0\0" },
- { 9, 6, 0, 0, 9, "\1\1\1\0\1\1\0\1\1\0\1\0\0\1\0\0\1\0\0\1\1\0\1\0\1\1\0\0\0\1\0\1\0\1\0\0\0\0\1\1\0\1\0\0\0\0\0\1\0\0\1\0\0\0" },
- { 5, 6, 1, 0, 6, "\1\1\0\1\1\0\1\0\1\0\0\0\1\0\0\0\0\1\0\0\0\1\0\1\0\1\1\0\1\1" },
- { 6, 9, 0, -3, 6, "\1\1\0\0\1\1\0\1\0\0\1\0\0\1\0\1\1\0\0\1\0\1\0\0\0\0\1\1\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\1\0\0\0\0\1\1\0\0\0\0" },
- { 4, 6, 1, 0, 6, "\1\1\1\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1\1\1" },
- { 4, 12, 1, -3, 6, "\0\0\1\1\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\1\1" },
- { 1, 9, 1, 0, 3, "\1\1\1\1\1\1\1\1\1" },
- { 4, 12, 0, -3, 6, "\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\1\1\0\0" },
- { 6, 2, 0, 3, 7, "\0\1\1\0\0\1\1\0\0\1\1\0" },
- { 1, 9, 1, -3, 4, "\1\0\1\1\1\1\1\1\1" },
- { 5, 8, 1, -1, 6, "\0\0\0\0\1\0\1\1\1\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\1\0\0\1\0\1\1\1\0\1\0\0\0\0" },
- { 5, 9, 0, 0, 6, "\0\0\1\1\0\0\1\0\0\1\0\1\0\0\0\0\1\0\0\0\1\1\1\1\0\0\1\0\0\0\0\1\0\0\0\1\1\1\0\1\1\1\0\1\1" },
- { 6, 7, 1, 1, 7, "\1\0\0\0\0\1\0\1\1\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1\1\0\1\0\0\0\0\1" },
- { 5, 9, 0, 0, 6, "\1\0\0\0\1\1\0\0\0\1\0\1\0\1\0\0\1\0\1\0\1\1\1\1\1\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\1\1\0" },
- { 1, 9, 1, 0, 3, "\1\1\1\0\0\1\1\1\1" },
- { 4, 12, 1, -3, 6, "\0\1\1\1\1\0\0\1\1\1\0\0\0\1\1\0\1\0\1\1\1\0\0\1\1\0\0\1\1\1\0\1\0\1\1\0\0\0\1\1\1\0\0\1\1\1\1\0" },
- { 3, 1, 0, 7, 3, "\1\0\1" },
- { 9, 9, 1, 0, 11, "\0\0\0\1\1\1\0\0\0\0\1\1\0\0\0\1\1\0\0\1\0\1\1\1\0\1\0\1\0\1\0\0\1\0\0\1\1\0\1\0\0\0\0\0\1\1\0\1\0\0\1\0\0\1\0\1\0\1\1\1\0\1\0\0\1\1\0\0\0\1\1\0\0\0\0\1\1\1\0\0\0" },
- { 3, 6, 1, 3, 5, "\1\1\0\0\0\1\1\1\1\1\0\1\0\0\0\1\1\1" },
- { 5, 5, 1, 0, 7, "\0\0\1\0\1\0\1\0\1\0\1\0\1\0\0\0\1\0\1\0\0\0\1\0\1" },
- { 6, 4, 1, 1, 8, "\1\1\1\1\1\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1" },
- { 4, 1, 1, 3, 6, "\1\1\1\1" },
- { 9, 9, 1, 0, 11, "\0\0\0\1\1\1\0\0\0\0\1\1\0\0\0\1\1\0\0\1\0\1\1\1\0\1\0\1\0\0\1\0\0\1\0\1\1\0\0\1\1\1\0\0\1\1\0\0\1\0\1\0\0\1\1\1\0\1\0\1\0\1\0\0\1\1\0\0\0\1\1\0\0\0\1\1\1\1\0\0\0" },
- { 4, 1, 0, 7, 4, "\1\1\1\1" },
- { 4, 4, 0, 5, 5, "\0\1\1\0\1\0\0\1\1\0\0\1\0\1\1\0" },
- { 5, 7, 1, 0, 7, "\0\0\1\0\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\0\1\0\0\0\0\0\0\0\1\1\1\1\1" },
- { 4, 5, 0, 4, 4, "\0\1\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1\1" },
- { 3, 5, 0, 4, 4, "\1\1\1\0\0\1\0\1\0\0\0\1\1\1\0" },
- { 2, 2, 1, 7, 4, "\0\1\1\0" },
- { 6, 9, 0, -3, 6, "\1\1\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\1\0\0\0\0\0\1\0\0\0\0\0\1\1\0\0\0" },
- { 6, 12, 0, -3, 7, "\0\1\1\1\1\1\1\1\1\0\1\0\1\1\1\0\1\0\1\1\1\0\1\0\1\1\1\0\1\0\0\1\1\0\1\0\0\0\1\0\1\0\0\0\1\0\1\0\0\0\1\0\1\0\0\0\1\0\1\0\0\0\1\0\1\0\0\0\1\0\1\0" },
- { 1, 1, 1, 3, 3, "\1" },
- { 3, 3, 0, -3, 3, "\0\1\0\0\0\1\1\1\1" },
- { 3, 5, 0, 4, 4, "\0\1\0\1\1\0\0\1\0\0\1\0\1\1\1" },
- { 3, 6, 1, 3, 5, "\0\1\0\1\0\1\1\0\1\0\1\0\0\0\0\1\1\1" },
- { 5, 5, 0, 0, 7, "\1\0\1\0\0\0\1\0\1\0\0\0\1\0\1\0\1\0\1\0\1\0\1\0\0" },
- { 9, 9, 0, 0, 9, "\0\1\0\0\0\0\1\0\0\1\1\0\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\1\1\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\0\0\1\0\1\0\1\0\0\0\1\0\0\1\1\1\1\0\0\1\0\0\0\0\1\0" },
- { 9, 9, 0, 0, 9, "\0\1\0\0\0\0\1\0\0\1\1\0\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\1\1\0\1\0\1\1\0\0\0\0\1\0\1\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\1\1\1\1" },
- { 9, 9, 0, 0, 9, "\1\1\1\0\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\0\0\0\0\1\1\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\0\0\1\0\1\0\1\0\0\0\1\0\0\1\1\1\1\0\0\1\0\0\0\0\1\0" },
- { 4, 9, 0, -3, 5, "\0\0\1\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\1\0\0\1\0\0\0\1\0\0\1\0\1\1\0" },
- { 9, 12, 0, 0, 9, "\0\0\0\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\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\1" },
- { 9, 12, 0, 0, 9, "\0\0\0\0\0\1\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\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\1" },
- { 9, 12, 0, 0, 9, "\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\1" },
- { 9, 12, 0, 0, 9, "\0\0\0\0\1\0\1\0\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\1" },
- { 9, 11, 0, 0, 9, "\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\1" },
- { 9, 12, 0, 0, 9, "\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\1" },
- { 10, 9, 0, 0, 11, "\0\0\1\1\1\1\1\1\1\1\0\0\0\1\1\0\0\0\0\1\0\0\1\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\1\0\0\1\0\0\1\1\1\1\1\0\0\1\1\1\1\0\0\0\1\0\0\1\0\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\0\0\1\1\1\1\1\1" },
- { 7, 12, 0, -3, 8, "\0\0\1\1\1\0\1\0\1\1\0\0\1\1\0\1\0\0\0\0\1\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\1\0\1\1\0\0\1\1\0\0\1\1\1\1\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\1\1\1\0\0" },
- { 7, 12, 0, 0, 8, "\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\1\1\1\1\1\1\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\1\1\1" },
- { 7, 12, 0, 0, 8, "\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\1\1\1\1\1\1\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\1\1\1" },
- { 7, 12, 0, 0, 8, "\0\0\0\1\0\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\1\1\1\1\1\1\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\1\1\1" },
- { 7, 11, 0, 0, 8, "\0\0\1\0\1\0\0\0\0\0\0\0\0\0\1\1\1\1\1\1\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\1\1\1" },
- { 3, 12, 0, 0, 4, "\1\0\0\0\1\0\0\0\0\1\1\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
- { 3, 12, 0, 0, 4, "\0\0\1\0\1\0\0\0\0\1\1\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
- { 3, 12, 0, 0, 4, "\0\1\0\1\0\1\0\0\0\1\1\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
- { 3, 11, 0, 0, 4, "\1\0\1\0\0\0\1\1\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
- { 8, 9, 0, 0, 9, "\1\1\1\1\1\1\0\0\0\1\0\0\0\1\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\0\1\1\1\1\0\0\0\0\1\0\1\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\0\0\0\1\1\0\1\1\1\1\1\1\0\0" },
- { 9, 12, 0, 0, 9, "\0\0\0\0\1\0\1\0\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\1\1\0\0\0\0\1\1\1\0\1\1\0\0\0\0\1\0\0\1\1\0\0\0\0\1\0\0\1\0\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\0\1\0\0\1\0\0\0\0\1\1\0\1\1\1\0\0\0\0\1\0" },
- { 8, 12, 0, 0, 9, "\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\1\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
- { 8, 12, 0, 0, 9, "\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\1\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
- { 8, 12, 0, 0, 9, "\0\0\0\1\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\1\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
- { 8, 12, 0, 0, 9, "\0\0\0\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\1\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
- { 8, 11, 0, 0, 9, "\0\0\1\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\1\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
- { 5, 5, 1, 1, 7, "\1\0\0\0\1\0\1\0\1\0\0\0\1\0\0\0\1\0\1\0\1\0\0\0\1" },
- { 9, 10, 0, 0, 9, "\0\0\0\0\0\0\0\0\1\0\0\1\1\1\1\0\1\0\0\1\1\0\0\1\1\0\0\0\1\0\0\0\1\1\0\0\1\0\0\0\1\0\0\1\0\1\0\0\0\1\0\0\1\0\1\0\0\1\0\0\0\1\0\0\1\1\0\0\0\1\0\0\0\1\1\0\0\1\1\0\0\1\0\1\1\1\1\0\0\0" },
- { 8, 12, 0, 0, 8, "\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\0\1\1\1\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
- { 8, 12, 0, 0, 8, "\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\0\1\1\1\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
- { 8, 12, 0, 0, 8, "\0\0\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\0\1\1\1\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
- { 8, 11, 0, 0, 8, "\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\0\1\1\1\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
- { 9, 12, 0, 0, 9, "\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\0\0\1\1\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\0\1\0\0\0\0\0\1\1\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\1\1\0\0\0" },
- { 7, 9, 0, 0, 7, "\1\1\1\0\0\0\0\0\1\0\0\0\0\0\0\1\1\1\1\1\0\0\1\0\0\0\1\1\0\1\0\0\0\0\1\0\1\0\0\0\1\1\0\1\1\1\1\1\0\0\1\0\0\0\0\0\1\1\1\0\0\0\0" },
- { 6, 9, 0, 0, 6, "\0\0\1\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\0\0\0\1\1\1\0\0\0\1\0\0\1\0\0\1\0\0\0\1\0\1\0\0\0\1\1\1\0\1\1\0" },
- { 5, 9, 1, 0, 6, "\0\1\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" },
- { 5, 9, 1, 0, 6, "\0\0\0\1\0\0\0\1\0\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" },
- { 5, 9, 1, 0, 6, "\0\0\1\0\0\0\1\0\1\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" },
- { 5, 9, 1, 0, 6, "\0\1\0\1\0\1\0\1\0\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" },
- { 5, 8, 1, 0, 6, "\0\1\0\1\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" },
- { 5, 9, 1, 0, 6, "\0\0\1\0\0\0\1\0\1\0\0\0\1\0\0\0\1\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" },
- { 8, 6, 1, 0, 9, "\0\1\1\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1\1\1\1\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\0\1\1\0\1\1\1\0" },
- { 4, 9, 1, -3, 5, "\0\1\1\0\1\0\0\1\1\0\0\0\1\0\0\0\1\0\0\1\0\1\1\0\0\1\0\0\0\0\1\0\1\1\1\0" },
- { 5, 9, 1, 0, 6, "\0\1\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\1\1\1\1\0\1\0\0\0\0\1\1\0\0\1\0\1\1\1\0" },
- { 5, 9, 1, 0, 6, "\0\0\1\0\0\0\1\0\0\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\1\1\1\1\0\1\0\0\0\0\1\1\0\0\1\0\1\1\1\0" },
- { 5, 9, 1, 0, 6, "\0\0\1\0\0\0\1\0\1\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\1\1\1\1\0\1\0\0\0\0\1\1\0\0\1\0\1\1\1\0" },
- { 5, 8, 1, 0, 6, "\0\1\0\1\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\1\1\1\1\0\1\0\0\0\0\1\1\0\0\1\0\1\1\1\0" },
- { 3, 9, 0, 0, 3, "\1\0\0\0\1\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
- { 3, 9, 0, 0, 3, "\0\1\0\1\0\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
- { 3, 9, 0, 0, 3, "\0\1\0\1\0\1\0\0\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
- { 3, 8, 0, 0, 3, "\1\0\1\0\0\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
- { 4, 9, 1, 0, 6, "\0\1\0\0\0\1\1\1\1\0\1\0\0\1\1\1\1\0\0\1\1\0\0\1\1\0\0\1\1\0\0\1\0\1\1\0" },
- { 6, 9, 0, 0, 6, "\0\0\1\0\1\0\0\1\0\1\0\0\0\0\0\0\0\0\1\0\1\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1\0\1\1" },
- { 4, 9, 1, 0, 6, "\0\1\0\0\0\0\1\0\0\0\0\0\0\1\1\0\1\0\0\1\1\0\0\1\1\0\0\1\1\0\0\1\0\1\1\0" },
- { 4, 9, 1, 0, 6, "\0\0\1\0\0\1\0\0\0\0\0\0\0\1\1\0\1\0\0\1\1\0\0\1\1\0\0\1\1\0\0\1\0\1\1\0" },
- { 4, 9, 1, 0, 6, "\0\0\1\0\0\1\0\1\0\0\0\0\0\1\1\0\1\0\0\1\1\0\0\1\1\0\0\1\1\0\0\1\0\1\1\0" },
- { 4, 9, 1, 0, 6, "\0\1\0\1\1\0\1\0\0\0\0\0\0\1\1\0\1\0\0\1\1\0\0\1\1\0\0\1\1\0\0\1\0\1\1\0" },
- { 4, 8, 1, 0, 6, "\1\0\1\0\0\0\0\0\0\1\1\0\1\0\0\1\1\0\0\1\1\0\0\1\1\0\0\1\0\1\1\0" },
- { 5, 5, 1, 1, 7, "\0\0\1\0\0\0\0\0\0\0\1\1\1\1\1\0\0\0\0\0\0\0\1\0\0" },
- { 6, 7, 0, -1, 6, "\0\0\1\1\0\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1\0\0\1\0\0\0\0\0" },
- { 6, 9, 0, 0, 6, "\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\1\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\1\0\1" },
- { 6, 9, 0, 0, 6, "\0\0\0\1\0\0\0\0\1\0\0\0\0\0\0\0\0\0\1\1\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\1\0\1" },
- { 6, 9, 0, 0, 6, "\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\1\1\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\1\0\1" },
- { 6, 8, 0, 0, 6, "\0\1\0\1\0\0\0\0\0\0\0\0\1\1\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\1\0\1" },
- { 6, 12, 0, -3, 6, "\0\0\0\0\1\0\0\0\0\1\0\0\0\0\0\0\0\0\1\1\0\0\1\1\0\1\0\0\1\0\0\1\0\1\1\0\0\1\0\1\0\0\0\0\1\1\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\1\0\0\0\0\1\1\0\0\0\0" },
- { 5, 12, 0, -3, 6, "\1\1\0\0\0\0\1\0\0\0\0\1\0\0\0\0\1\1\1\0\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\1\1\0\0\1\0\0\0\0\1\0\0\0\1\1\1\0\0" },
- { 6, 11, 0, -3, 6, "\0\1\0\0\1\0\0\0\0\0\0\0\1\1\0\0\1\1\0\1\0\0\1\0\0\1\0\1\1\0\0\1\0\1\0\0\0\0\1\1\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\1\0\0\0\0\1\1\0\0\0\0" }
-};
-
-static struct font default_bdffont = { 14, 15, -1, -3, {
- 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,
- _g + 0,
- _g + 1,
- _g + 2,
- _g + 3,
- _g + 4,
- _g + 5,
- _g + 6,
- _g + 7,
- _g + 8,
- _g + 9,
- _g + 10,
- _g + 11,
- _g + 12,
- _g + 13,
- _g + 14,
- _g + 15,
- _g + 16,
- _g + 17,
- _g + 18,
- _g + 19,
- _g + 20,
- _g + 21,
- _g + 22,
- _g + 23,
- _g + 24,
- _g + 25,
- _g + 26,
- _g + 27,
- _g + 28,
- _g + 29,
- _g + 30,
- _g + 31,
- _g + 32,
- _g + 33,
- _g + 34,
- _g + 35,
- _g + 36,
- _g + 37,
- _g + 38,
- _g + 39,
- _g + 40,
- _g + 41,
- _g + 42,
- _g + 43,
- _g + 44,
- _g + 45,
- _g + 46,
- _g + 47,
- _g + 48,
- _g + 49,
- _g + 50,
- _g + 51,
- _g + 52,
- _g + 53,
- _g + 54,
- _g + 55,
- _g + 56,
- _g + 57,
- _g + 58,
- _g + 59,
- _g + 60,
- _g + 61,
- _g + 62,
- _g + 63,
- _g + 64,
- _g + 65,
- _g + 66,
- _g + 67,
- _g + 68,
- _g + 69,
- _g + 70,
- _g + 71,
- _g + 72,
- _g + 73,
- _g + 74,
- _g + 75,
- _g + 76,
- _g + 77,
- _g + 78,
- _g + 79,
- _g + 80,
- _g + 81,
- _g + 82,
- _g + 83,
- _g + 84,
- _g + 85,
- _g + 86,
- _g + 87,
- _g + 88,
- _g + 89,
- _g + 90,
- _g + 91,
- _g + 92,
- _g + 93,
- _g + 94,
- 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,
- _g + 95,
- _g + 96,
- _g + 97,
- _g + 98,
- _g + 99,
- _g + 100,
- _g + 101,
- _g + 102,
- _g + 103,
- _g + 104,
- _g + 105,
- _g + 106,
- _g + 107,
- _g + 108,
- _g + 109,
- _g + 110,
- _g + 111,
- _g + 112,
- _g + 113,
- _g + 114,
- _g + 115,
- _g + 116,
- _g + 117,
- _g + 118,
- _g + 119,
- _g + 120,
- _g + 121,
- _g + 122,
- _g + 123,
- _g + 124,
- _g + 125,
- _g + 126,
- _g + 127,
- _g + 128,
- _g + 129,
- _g + 130,
- _g + 131,
- _g + 132,
- _g + 133,
- _g + 134,
- _g + 135,
- _g + 136,
- _g + 137,
- _g + 138,
- _g + 139,
- _g + 140,
- _g + 141,
- _g + 142,
- _g + 143,
- _g + 144,
- _g + 145,
- _g + 146,
- _g + 147,
- _g + 148,
- _g + 149,
- _g + 150,
- _g + 151,
- _g + 152,
- _g + 153,
- _g + 154,
- _g + 155,
- _g + 156,
- _g + 157,
- _g + 158,
- _g + 159,
- _g + 160,
- _g + 161,
- _g + 162,
- _g + 163,
- _g + 164,
- _g + 165,
- _g + 166,
- _g + 167,
- _g + 168,
- _g + 169,
- _g + 170,
- _g + 171,
- _g + 172,
- _g + 173,
- _g + 174,
- _g + 175,
- _g + 176,
- _g + 177,
- _g + 178,
- _g + 179,
- _g + 180,
- _g + 181,
- _g + 182,
- _g + 183,
- _g + 184,
- _g + 185,
- _g + 186,
- _g + 187,
- _g + 188,
- _g + 189
- }
-};
-
-
-
-struct font *
-pbm_defaultfont(const char * const name) {
-/*----------------------------------------------------------------------------
-   Generate the built-in font with name 'name'.
------------------------------------------------------------------------------*/
-    struct font * retval;
-
-    if (strcmp(name, "bdf") == 0)
-        retval = &default_bdffont;
-    else {
-        bit** defaultfont;
-        unsigned int row;
-
-        if (strcmp(name, "fixed") != 0)
-            pm_error( "built-in font name unknown, try 'bdf' or 'fixed'");
-
-        defaultfont = pbm_allocarray(DEFAULTFONT_COLS, DEFAULTFONT_ROWS);
-        for (row =  0; row < DEFAULTFONT_ROWS; ++row) {
-            unsigned int col;
-            for (col = 0; col < DEFAULTFONT_COLS; col += 32) {
-                int scol;
-                unsigned long l;
-
-                l = defaultfont_bits[row][col / 32]; /* initial value */
-
-                for (scol = MIN( col + 32, DEFAULTFONT_COLS) - 1;
-                     scol >= (int)col; 
-                     --scol) {
-                    if (l & 0x01)
-                        defaultfont[row][scol] = 1;
-                    else
-                        defaultfont[row][scol] = 0;
-                    l >>= 1;
-                }
-            }
-        }
-        retval = 
-            pbm_dissectfont((const bit **)defaultfont,
-                            DEFAULTFONT_ROWS, DEFAULTFONT_COLS);
-    }
-    return retval;
-}
-
-
-
-static void
-findFirstBlankRow(const bit **   const font,
-                  unsigned int   const fcols,
-                  unsigned int   const frows,
-                  unsigned int * const browP) {
-
-    unsigned int row;
-    bool foundBlank;
-
-    for (row = 0, foundBlank = false; row < frows / 6 && !foundBlank; ++row) {
-        unsigned int col;
-        bit col0Value = font[row][0];
-        bool rowIsBlank;
-        rowIsBlank = true;  /* initial assumption */
-        for (col = 1; col < fcols; ++col)
-            if (font[row][col] != col0Value)
-                rowIsBlank = false;
-
-        if (rowIsBlank) {
-            foundBlank = true;
-            *browP = row;
-        }
-    }
-
-    if (!foundBlank)
-        pm_error("couldn't find blank pixel row in font");
-}
-
-
-
-static void
-findFirstBlankCol(const bit **   const font,
-                  unsigned int   const fcols,
-                  unsigned int   const frows,
-                  unsigned int * const bcolP) {
-
-    unsigned int col;
-    bool foundBlank;
-
-    for (col = 0, foundBlank = false; col < fcols / 6 && !foundBlank; ++col) {
-        unsigned int row;
-        bit row0Value = font[0][col];
-        bool colIsBlank;
-        colIsBlank = true;  /* initial assumption */
-        for (row = 1; row < frows; ++row)
-            if (font[row][col] != row0Value)
-                colIsBlank = false;
-
-        if (colIsBlank) {
-            foundBlank = true;
-            *bcolP = col;
-        }
-    }
-
-    if (!foundBlank)
-        pm_error("couldn't find blank pixel column in font");
-}
-
-
-
-static void
-computeCharacterSize(const bit **   const font,
-                     unsigned int   const fcols,
-                     unsigned int   const frows,
-                     unsigned int * const cellWidthP,
-                     unsigned int * const cellHeightP,
-                     unsigned int * const charWidthP,
-                     unsigned int * const charHeightP) {
-
-    unsigned int firstBlankRow;
-    unsigned int firstBlankCol;
-    unsigned int heightLast11Rows;
-
-    findFirstBlankRow(font, fcols, frows, &firstBlankRow);
-
-    findFirstBlankCol(font, fcols, frows, &firstBlankCol);
-
-    heightLast11Rows = frows - firstBlankRow;
-
-    if (heightLast11Rows % 11 != 0)
-        pm_error("The rows of characters in the font do not appear to "
-                 "be all the same height.  The last 11 rows are %u pixel "
-                 "rows high (from pixel row %u up to %u), "
-                 "which is not a multiple of 11.",
-                 heightLast11Rows, firstBlankRow, frows);
-    else {
-        unsigned int widthLast15Cols;
-
-        *cellHeightP = heightLast11Rows / 11;
-
-        widthLast15Cols = fcols - firstBlankCol;
-
-        if (widthLast15Cols % 15 != 0)
-            pm_error("The columns of characters in the font do not appear to "
-                     "be all the same width.  "
-                     "The last 15 columns are %u pixel "
-                     "columns wide (from pixel col %u up to %u), "
-                     "which is not a multiple of 15.",
-                     widthLast15Cols, firstBlankCol, fcols);
-        else {
-            *cellWidthP = widthLast15Cols / 15;
-
-            *charWidthP = firstBlankCol;
-            *charHeightP = firstBlankRow;
-        }
-    }
-}
-
-
-
-struct font*
-pbm_dissectfont(const bit ** const font,
-                unsigned int const frows,
-                unsigned int const fcols) {
-    /*
-       This routine expects a font bitmap representing the following text:
-      
-       (0,0)
-          M ",/^_[`jpqy| M
-      
-          /  !"#$%&'()*+ /
-          < ,-./01234567 <
-          > 89:;<=>?@ABC >
-          @ DEFGHIJKLMNO @
-          _ PQRSTUVWXYZ[ _
-          { \]^_`abcdefg {
-          } hijklmnopqrs }
-          ~ tuvwxyz{|}~  ~
-      
-          M ",/^_[`jpqy| M
-      
-       The bitmap must be cropped exactly to the edges.
-      
-       The characters in the border you see are irrelevant except for
-       character size compuations.  The 12 x 8 array in the center is
-       the font.  The top left character there belongs to code point
-       0, and the code points increase in standard reading order, so
-       the bottom right character is code point 127.  You can't define
-       code points < 32 or > 127 with this font format.
-
-       The characters in the top and bottom border rows must include a
-       character with the lowest reach of any in the font (e.g. "y",
-       "_") and one with the highest reach (e.g. '"').  The characters
-       in the left and right border columns must include characters
-       with the rightmost and leftmost reach of any in the font
-       (e.g. "M" for both).
-
-       The border must be separated from the font by one blank text
-       row or text column.
-      
-       The dissection works by finding the first blank row and column;
-       i.e the lower right corner of the "M" in the upper left corner
-       of the matrix.  That gives the height and width of the
-       maximum-sized character, which is not too useful.  But the
-       distance from there to the opposite side is an integral
-       multiple of the cell size, and that's what we need.  Then it's
-       just a matter of filling in all the coordinates.  */
-    
-    unsigned int cellWidth, cellHeight;
-        /* Dimensions in pixels of each cell of the font -- that
-           includes the glyph and the white space above and to the
-           right of it.  Each cell is a tile of the font image.  The
-           top character row and left character row don't count --
-           those cells are smaller because they are missing the white
-           space.
-        */
-    unsigned int charWidth, charHeight;
-        /* Maximum dimensions of glyph itself, inside its cell */
-
-    int row, col, ch, r, c, i;
-    struct font * fn;
-    struct glyph * glyph;
-    char* bmap;
-
-    computeCharacterSize(font, fcols, frows,
-                         &cellWidth, &cellHeight, &charWidth, &charHeight);
-
-    /* Now convert to a general font */
-
-    MALLOCVAR(fn);
-    if (fn == NULL)
-        pm_error("out of memory allocating font structure");
-
-    fn->maxwidth  = charWidth;
-    fn->maxheight = charHeight;
-    fn->x = fn->y = 0;
-
-    fn->oldfont = font;
-    fn->frows = frows;
-    fn->fcols = fcols;
-    
-    /* Initialize all character positions to "undefined."  Those that
-       are defined in the font will be filled in below.
-    */
-    for (i = 0; i < 256; i++)
-        fn->glyph[i] = NULL;
-
-    MALLOCARRAY(glyph, nCharsInFont);
-    if ( glyph == NULL )
-        pm_error( "out of memory allocating glyphs" );
-    
-    bmap = (char*) malloc( fn->maxwidth * fn->maxheight * nCharsInFont );
-    if ( bmap == (char*) 0)
-        pm_error( "out of memory allocating glyph data" );
-
-    /* Now fill in the 0,0 coords. */
-    row = cellHeight * 2;
-    col = cellWidth * 2;
-    for (i = 0; i < firstCodePoint; ++i)
-        fn->glyph[i] = NULL;
-
-    for ( ch = 0; ch < nCharsInFont; ++ch ) {
-        glyph[ch].width = fn->maxwidth;
-        glyph[ch].height = fn->maxheight;
-        glyph[ch].x = glyph[ch].y = 0;
-        glyph[ch].xadd = cellWidth;
-
-        for ( r = 0; r < glyph[ch].height; ++r )
-            for ( c = 0; c < glyph[ch].width; ++c )
-                bmap[r * glyph[ch].width + c] = font[row + r][col + c];
-    
-        glyph[ch].bmap = bmap;
-        bmap += glyph[ch].width * glyph[ch].height;
-
-        fn->glyph[firstCodePoint + ch] = &glyph[ch];
-
-        col += cellWidth;
-        if ( col >= cellWidth * 14 ) {
-            col = cellWidth * 2;
-            row += cellHeight;
-        }
-    }
-    for (i = firstCodePoint + nCharsInFont; i < 256; ++i)
-        fn->glyph[i] = NULL;
-    
-    return fn;
-}
-
-
-
-struct font*
-pbm_loadfont(const char * const filename)
-{
-    FILE* fp;
-    struct font* fn;
-    char line[256];
-
-    fp = pm_openr( filename );
-    fgets(line, 256, fp);
-    pm_close( fp );
-
-    if (line[0] == PBM_MAGIC1 && 
-        (line[1] == PBM_MAGIC2 || line[1] == RPBM_MAGIC2)) {
-        return pbm_loadpbmfont( filename );
-    } else if (!strncmp(line, "STARTFONT", 9)) {
-        if (!(fn = pbm_loadbdffont( filename )))
-          pm_error( "could not load BDF font file" );
-        return fn;
-    } else {
-      pm_error( "font file not in a recognized format ");
-      return NULL;  /* should never reach here */
-  }
-}
-
-
-
-struct font *
-pbm_loadpbmfont(const char * const filename) {
-
-    FILE * ifP;
-    bit ** font;
-    int fcols, frows;
-
-    ifP = pm_openr(filename);
-    font = pbm_readpbm(ifP, &fcols, &frows);
-    pm_close(ifP);
-    return pbm_dissectfont((const bit **)font, frows, fcols);
-}
-
-
-
-void
-pbm_dumpfont( fn )
-    struct font* fn;
-{
-    /* Dump out font as C source code. */
-    int row, col, scol, lperrow;
-    unsigned long l;
-
-    if (fn->oldfont) {
-    printf( "#define DEFAULTFONT_ROWS %d\n", fn->frows );
-    printf( "#define DEFAULTFONT_COLS %d\n", fn->fcols );
-    printf( "static unsigned long defaultfont_bits[DEFAULTFONT_ROWS][(DEFAULTFONT_COLS+31)/32] = {\n" );
-    for ( row = 0; row < fn->frows; ++row )
-        {
-        lperrow = 0;
-        for ( col = 0; col < fn->fcols; col += 32 )
-        {
-        if ( lperrow == 0 )
-            printf( "    {" );
-        else if ( lperrow % 6 == 0 )
-            {
-            printf( ",\n     " );
-            lperrow = 0;
-            }
-        else
-            printf( "," );
-        l = 0;
-        for ( scol = col; scol < MIN( col + 32, fn->fcols ); ++scol )
-            {
-            l <<= 1;
-            if ( fn->oldfont[row][scol] )
-            l |= 1;
-            }
-        printf( "0x%08lxL", l );
-        ++lperrow;
-        }
-        printf( "}%s\n", row == fn->frows - 1 ? "" : "," );
-        }
-    printf( "    };\n" );
-    }
-    else {
-    struct glyph* glyph;
-    int i, j, ng;
-
-    ng = 0;
-    for (i = 0; i < 256; i++)
-        if (fn->glyph[i])
-            ng++;
-
-    printf("static struct glyph _g[%d] = {\n", ng);
-    for (i = 0; i < 256; i++) {
-        if (!(glyph = fn->glyph[i]))
-            continue;
-
-        printf(" { %d, %d, %d, %d, %d, \"", glyph->width, glyph->height,
-            glyph->x, glyph->y, glyph->xadd);
-
-        for (j = 0; j < glyph->width * glyph->height; j++)
-            if (glyph->bmap[j])
-                printf("\\1");
-            else
-                printf("\\0");
-        
-        ng--;
-        printf("\" }%s\n", ng ? "," : "");
-    }
-    printf("};\n");
-
-    printf("static struct font default_bdffont = { %d, %d, %d, %d, {\n",
-        fn->maxwidth, fn->maxheight, fn->x, fn->y);
-
-    for (i = 0; i < 256; i++) {
-        if (fn->glyph[i])
-            printf(" _g + %d", ng++);
-        else
-            printf(" 0");
-        
-        if (i != 255) printf(",");
-        printf("\n");
-    }
-
-    printf(" }\n};\n");
-    exit(0);
-
-    }
-
-}
-
-
-/* Routines for loading a BDF font file */
-
-
-typedef struct {
-/*----------------------------------------------------------------------------
-   This is an object for reading lines of a font file.  It reads and tokenizes
-   them into words.
------------------------------------------------------------------------------*/
-    FILE * ifP;
-
-    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];
-    n = 0;
-
-    while (*p) {
-        if (ISSPACE(*p))
-            *p++ = '\0';
-        else {
-            words[n++] = p;
-            if (n >= maxWordCt-1)
-                break;
-            while (*p && !ISSPACE(*p))
-                ++p;
-        }
-    }
-    words[n] = NULL;
-}
-
-
-
-static void
-readline_read(readline * const readlineP,
-              bool *     const eofP) {
-/*----------------------------------------------------------------------------
-   Read a nonblank line from the file.  Make its contents available
-   as readlineP->arg[].
-
-   Return *eofP == true iff there is no nonblank line before EOF or we
-   are unable to read the file.
------------------------------------------------------------------------------*/
-    bool gotLine;
-    bool error;
-
-    for (gotLine = false, error = false; !gotLine && !error; ) {
-        char * rc;
-
-        rc = fgets(readlineP->line, 1024, readlineP->ifP);
-        if (rc == NULL)
-            error = true;
-        else {
-            tokenize(readlineP->line,
-                     readlineP->arg, ARRAY_SIZE(readlineP->arg));
-            if (readlineP->arg[0] != NULL)
-                gotLine = true;
-        }
-    }
-    *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(readline *      const readlineP,
-           unsigned int    const glyphWidth,
-           unsigned int    const glyphHeight,
-           const char *    const charName,
-           unsigned char * const bmap) {
-
-    int n;
-    unsigned int bmapIndex;
-
-    bmapIndex = 0;
-           
-    for (n = glyphHeight; n > 0; --n) {
-        bool eof;
-        const char * error;
-
-        readline_read(readlineP, &eof);
-
-        if (eof)
-            pm_error("End of file in bitmap for character '%s' in BDF "
-                     "font file.", charName);
-
-        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);
-        }
-    }
-}
-
-
-
-static void
-createBmap(unsigned int  const glyphWidth,
-           unsigned int  const glyphHeight,
-           readline *    const readlineP,
-           const char *  const charName,
-           const char ** const bmapP) {
-
-    unsigned char * bmap;
-    bool eof;
-    
-    if (glyphWidth > 0 && UINT_MAX / glyphWidth < glyphHeight)
-        pm_error("Ridiculously large glyph");
-
-    MALLOCARRAY(bmap, glyphWidth * glyphHeight);
-
-    if (!bmap)
-        pm_error("no memory for font glyph byte map");
-
-    readline_read(readlineP, &eof);
-    if (eof)
-        pm_error("End of file encountered reading font glyph byte map from "
-                 "BDF font file.");
-    
-    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(readlineP->arg[0], "BITMAP"))
-        pm_error("'%s' found where BITMAP expected in definition of "
-                 "character '%s' in BDF font file.",
-                 readlineP->arg[0], charName);
-
-    assert(streq(readlineP->arg[0], "BITMAP"));
-
-    readBitmap(readlineP, glyphWidth, glyphHeight, charName, bmap);
-
-    *bmapP = (char *)bmap;
-}
-
-
-
-static void
-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;
-
-    readline_read(readlineP, &eof);
-
-    if (eof)
-        pm_error("EOF in BDF font file where '%s' expected", 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(readline * const readlineP) {
-/*----------------------------------------------------------------------------
-  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) {
-        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(readlineP->arg[0], "ENDCHAR");
-    }                        
-}
-
-
-
-static void
-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
-   code, e.g. in ASCII, 48 is '0').
-
-   But if the statement doesn't give an acceptable codepoint return
-   *badCodepointP == TRUE.
------------------------------------------------------------------------------*/
-
-    bool gotCodepoint;
-    bool badCodepoint;
-    unsigned int codepoint;
-
-    if (!arg[1])
-        pm_error("Invalid ENCODING statement - no arguments");
-    if (atoi(arg[1]) >= 0) {
-        codepoint = atoi(arg[1]);
-        gotCodepoint = true;
-    } else {
-        if (arg[2]) {
-            codepoint = atoi(arg[2]);
-            gotCodepoint = true;
-        } else
-            gotCodepoint = false;
-    }
-    if (gotCodepoint) {
-        if (codepoint > 255)
-            badCodepoint = true;
-        else
-            badCodepoint = false;
-    } else
-        badCodepoint = true;
-
-    *badCodepointP = badCodepoint;
-    *codepointP    = codepoint;
-}
-
-
-
-static void
-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 (!readlineP->arg[1])
-        pm_error("Invalid CHARS line - no arguments");
-
-    nCharacters = atoi(readlineP->arg[1]);
-
-    nCharsDone = 0;
-
-    while (nCharsDone < nCharacters) {
-        bool eof;
-
-        readline_read(readlineP, &eof);
-        if (eof)
-            pm_error("End of file after CHARS reading BDF font file");
-
-        if (streq(readlineP->arg[0], "COMMENT")) {
-            /* ignore */
-        } else if (!streq(readlineP->arg[0], "STARTCHAR"))
-            pm_error("no STARTCHAR after CHARS in BDF font file");
-        else if (!readlineP->arg[1])
-            pm_error("Invalid STARTCHAR - no arguments");
-        else {
-            const char * const charName = readlineP->arg[1];
-
-            struct glyph * glyphP;
-            unsigned int codepoint;
-            bool badCodepoint;
-
-            assert(streq(readlineP->arg[0], "STARTCHAR"));
-
-            MALLOCVAR(glyphP);
-
-            if (glyphP == NULL)
-                pm_error("no memory for font glyph for '%s' character",
-                         charName);
-
-            readEncoding(readlineP, &codepoint, &badCodepoint);
-
-            if (badCodepoint)
-                skipCharacter(readlineP);
-            else {
-                readExpectedStatement(readlineP, "SWIDTH");
-                    
-                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);
-                
-
-                readExpectedStatement(readlineP, "ENDCHAR");
-
-                assert(codepoint < 256); /* Ensured by readEncoding() */
-
-                fontP->glyph[codepoint] = glyphP;
-            }
-            ++nCharsDone;
-        }
-    }
-}
-
-
-
-static void
-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 */
-
-    assert(readlineP->arg[0] != NULL);  /* Entry condition */
-
-    if (streq(readlineP->arg[0], "COMMENT")) {
-        /* ignore */
-    } else if (streq(readlineP->arg[0], "SIZE")) {
-        /* ignore */
-    } else if (streq(readlineP->arg[0], "STARTPROPERTIES")) {
-        /* Read off the properties and ignore them all */
-        unsigned int propCount;
-        unsigned int i;
-
-        if (!readlineP->arg[1])
-            pm_error("Invalid STARTPROPERTIES statement - no arguments");
-        propCount = atoi(readlineP->arg[1]);
-
-        for (i = 0; i < propCount; ++i) {
-            bool eof;
-            readline_read(readlineP, &eof);
-            if (eof)
-                pm_error("End of file after STARTPROPERTIES in BDF font file");
-        }
-    } else if (streq(readlineP->arg[0], "FONTBOUNDINGBOX")) {
-        if (!readlineP->arg[1])
-            pm_error("Invalid FONTBOUNDINGBOX  statement - no arguments");
-        fontP->maxwidth  = atoi(readlineP->arg[1]);
-        if (!readlineP->arg[2])
-            pm_error("Invalid FONTBOUNDINGBOX  statement - only 1 argument");
-        fontP->maxheight = atoi(readlineP->arg[2]);
-        if (!readlineP->arg[3])
-            pm_error("Invalid FONTBOUNDINGBOX  statement - only 2 arguments");
-        fontP->x         = atoi(readlineP->arg[3]);
-        if (!readlineP->arg[4])
-            pm_error("Invalid FONTBOUNDINGBOX  statement - only 3 arguments");
-        fontP->y         = atoi(readlineP->arg[4]);
-    } else if (streq(readlineP->arg[0], "ENDFONT")) {
-        *endOfFontP = true;
-    } else if (streq(readlineP->arg[0], "CHARS")) {
-        processChars(readlineP, fontP);
-    } else {
-        /* ignore */
-    }
-}
-
-
-
-struct font *
-pbm_loadbdffont(const char * const name) {
-
-    FILE * ifP;
-    readline readline;
-    struct font * fontP;
-    bool endOfFont;
-
-    ifP = fopen(name, "rb");
-    if (!ifP)
-        pm_error("Unable to open BDF font file name '%s'.  errno=%d (%s)",
-                 name, errno, strerror(errno));
-
-    readline_init(&readline, ifP);
-
-    MALLOCVAR(fontP);
-    if (fontP == NULL)
-        pm_error("no memory for font");
-
-    fontP->oldfont = 0;
-    { 
-        /* Initialize all characters to nonexistent; we will fill the ones we
-           find in the bdf file later.
-        */
-        unsigned int i;
-        for (i = 0; i < 256; ++i) 
-            fontP->glyph[i] = NULL;
-    }
-    fontP->x = fontP->y = 0;
-
-    readExpectedStatement(&readline, "STARTFONT");
-
-    endOfFont = FALSE;
-
-    while (!endOfFont) {
-        bool eof;
-        readline_read(&readline, &eof);
-        if (eof)
-            pm_error("End of file before ENDFONT statement in BDF font file");
-
-        processBdfFontLine(&readline, fontP, &endOfFont);
-    }
-    return fontP;
-}
diff --git a/lib/libpbmfont0.c b/lib/libpbmfont0.c
new file mode 100644
index 00000000..503c7ee7
--- /dev/null
+++ b/lib/libpbmfont0.c
@@ -0,0 +1,335 @@
+/*
+**
+** Font routines.
+**
+** Wide character stuff written by Akira Urushibata in 2018 and contributed
+** to the public domain.
+**
+** Also copyright (C) 1991 by Jef Poskanzer and licensed to the public as
+** follows.
+**
+** 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 <string.h>
+
+#include "netpbm/pm_c_util.h"
+#include "netpbm/mallocvar.h"
+#include "netpbm/nstring.h"
+
+#include "pbm.h"
+#include "pbmfont.h"
+#include "pbmfontdata.h"
+
+
+struct font *
+pbm_defaultfont(const char * const name) {
+/*----------------------------------------------------------------------------
+   Generate the built-in font with name 'name'.
+-----------------------------------------------------------------------------*/
+    struct font * retval;
+
+    if (streq(name, "bdf"))
+        retval = &pbm_defaultBdffont;
+    else if (streq(name, "fixed"))
+        retval = &pbm_defaultFixedfont;
+    else
+        pm_error( "built-in font name unknown, try 'bdf' or 'fixed'");
+
+    return retval;
+}
+
+
+
+struct font2 *
+pbm_defaultfont2(const char * const requestedFontName) {
+
+    struct font2 * font2P;
+    struct font2 * retval = NULL; /* initial value */
+    unsigned int i;
+
+    for (i = 0; retval == NULL; ++i) {
+        const char * longName;
+        const char * shortName;
+        font2P = (struct font2 * ) pbm_builtinFonts[i];
+        if (font2P == NULL)
+            break;
+
+        longName = font2P->name;
+        shortName = &longName[strlen("builtin ")];
+
+        if (streq(shortName, requestedFontName))
+             retval = font2P;
+    }
+
+    if (retval == NULL)
+        pm_error("No builtin font named %s", requestedFontName);
+
+    return retval;
+}
+
+
+
+static void
+selectFontType(const    char * const filename,
+               PM_WCHAR        const maxmaxglyph,
+               unsigned int    const isWide,
+               struct font  ** const fontPP,
+               struct font2 ** const font2PP) {
+
+    FILE * fileP;
+    struct font  * fontP  = NULL; /* initial value */
+    struct font2 * font2P = NULL; /* initial value */
+    char line[10] = "\0\0\0\0\0\0\0\0\0\0";
+        /* Initialize to suppress Valgrind error which is reported when file
+           is empty or very small.
+        */
+
+    fileP = pm_openr(filename);
+    fgets(line, 10, fileP);
+    pm_close(fileP);
+
+    if (line[0] == PBM_MAGIC1 &&
+        (line[1] == PBM_MAGIC2 || line[1] == RPBM_MAGIC2)) {
+        if (isWide == TRUE)
+            font2P = pbm_loadpbmfont2(filename);
+        else
+            fontP  = pbm_loadpbmfont(filename);
+        if (fontP == NULL && font2P == NULL)
+            pm_error("could not load PBM font file");
+
+    } else if (!strncmp(line, "STARTFONT", 9)) {
+        if (isWide == TRUE)
+            font2P = pbm_loadbdffont2(filename, maxmaxglyph);
+        else
+            fontP = pbm_loadbdffont(filename);
+        if (fontP == NULL && font2P == NULL)
+            pm_error("could not load BDF font file");
+
+    } else {
+        pm_error("font file not in a recognized format.  Does not start "
+                 "with the signature of a PBM file or BDF font file");
+        assert(false);
+        fontP = NULL;  /* defeat compiler warning */
+    }
+
+    if (isWide)
+        *font2PP = font2P;
+    else
+        *fontPP = fontP;
+}
+
+
+
+struct font *
+pbm_loadfont(const    char * const filename) {
+
+    struct font  * fontP;
+    struct font2 * font2P;
+
+    selectFontType(filename, PM_FONT_MAXGLYPH, FALSE, &fontP, &font2P);
+    return fontP;
+}
+
+
+
+struct font2 *
+pbm_loadfont2(const    char * const filename,
+              PM_WCHAR        const maxmaxglyph) {
+
+    struct font  * fontP;
+    struct font2 * font2P;
+
+    selectFontType(filename, maxmaxglyph, TRUE, &fontP, &font2P);
+    return font2P;
+}
+
+
+
+void
+pbm_createbdffont2_base(struct font2 ** const font2PP,
+                        PM_WCHAR        const maxmaxglyph) {
+
+    struct font2 * font2P;
+
+    MALLOCVAR(font2P);
+    if (font2P == NULL)
+        pm_error("no memory for font");
+
+    MALLOCARRAY(font2P->glyph, maxmaxglyph + 1);
+    if (font2P->glyph == NULL)
+        pm_error("no memory for font glyphs");
+
+    /* Initialize */
+    font2P->size = sizeof (struct font2);
+    font2P->len  = PBM_FONT2_STRUCT_SIZE(charset_string);
+
+    /*  Caller should overwrite following fields as necessary */
+    font2P->oldfont = NULL;
+    font2P->fcols = font2P->frows = 0;
+    font2P->selector = NULL;
+    font2P->default_char = 0;
+    font2P->default_char_defined = FALSE;
+    font2P->total_chars = font2P->chars = 0;
+    font2P->name = NULL;
+    font2P->charset = ENCODING_UNKNOWN;
+    font2P->charset_string = NULL;
+
+    *font2PP = font2P;
+}
+
+
+
+static void
+destroyGlyphData(struct glyph ** const glyph,
+                 PM_WCHAR        const maxglyph) {
+/*----------------------------------------------------------------------------
+  Free glyph objects and bitmap objects.
+
+  This does not work when an object is "shared" through multiple pointers
+  referencing an identical address and thus pointing to a common glyph
+  or bitmap object.
+-----------------------------------------------------------------------------*/
+
+    PM_WCHAR i;
+
+    for(i = 0; i <= maxglyph; ++i) {
+        if (glyph[i]!=NULL) {
+            free((void *) (glyph[i]->bmap));
+            free(glyph[i]);
+      }
+    }
+}
+
+
+void
+pbm_destroybdffont2_base(struct font2 * const font2P) {
+/*----------------------------------------------------------------------------
+  Free font2 structure, but not the glyph data
+---------------------------------------------------------------------------- */
+
+    free(font2P->selector);
+
+    pm_strfree(font2P->name);
+    pm_strfree(font2P->charset_string);
+    free(font2P->glyph);
+
+    if (font2P->oldfont !=NULL)
+       pbm_freearray(font2P->oldfont, font2P->frows);
+
+    free((void *)font2P);
+
+}
+
+
+
+void
+pbm_destroybdffont2(struct font2 * const font2P) {
+/*----------------------------------------------------------------------------
+  Free font2 structure and glyph data
+
+  Examines the 'load_fn' field to check whether the object is fixed data.
+  Do nothing if 'load_fn' is 'FIXED_DATA'.
+---------------------------------------------------------------------------- */
+
+    if (font2P->load_fn != FIXED_DATA) {
+        destroyGlyphData(font2P->glyph, font2P->maxglyph);
+        pbm_destroybdffont2_base(font2P);
+    }
+}
+
+
+
+void
+pbm_destroybdffont(struct font * const fontP) {
+/*----------------------------------------------------------------------------
+  Free font structure and glyph data.
+
+  For freeing a structure created by pbm_loadbdffont() or pbm_loadpbmfont().
+---------------------------------------------------------------------------- */
+
+    destroyGlyphData(fontP->glyph, PM_FONT_MAXGLYPH);
+
+    if (fontP->oldfont !=NULL)
+       pbm_freearray(fontP->oldfont, fontP->frows);
+
+    free(fontP);
+}
+
+
+
+struct font2 *
+pbm_expandbdffont(const struct font * const fontP) {
+/*----------------------------------------------------------------------------
+  Convert a traditional 'font' structure into an expanded 'font2' structure.
+
+  After calling this function *fontP may be freed, but not the individual
+  glyph data: fontP->glyph[0...255] .
+
+  Using this function on static data is not recommended.  Rather add
+  the extra fields to make a font2 structure.  See file pbmfontdata1.c
+  for an example.
+
+  The returned object is in new malloc'ed storage, in many pieces.
+
+  Destroy with pbm_destroybdffont2() if *fontP is read from a file.
+
+  Destroy with pbm_destroybdffont2_base() if *fontP is static data
+  and you desire to defy the above-stated recommendation.
+
+  The general function for conversion in the opposite direction
+  'font2' => 'font' is font2ToFont() in libpbmfont2.c .  It is currently
+  declared as static.
+ ---------------------------------------------------------------------------*/
+    PM_WCHAR maxglyph, codePoint;
+    unsigned int nCharacters;
+    struct font2 * font2P;
+
+    pbm_createbdffont2_base(&font2P, PM_FONT_MAXGLYPH);
+
+    font2P->maxwidth  = fontP->maxwidth;
+    font2P->maxheight = fontP->maxheight;
+
+    font2P->x = fontP->x;
+    font2P->y = fontP->y;
+
+    /* Hunt for max non-NULL entry in glyph table */
+    for (codePoint = PM_FONT_MAXGLYPH;
+         fontP->glyph[codePoint] == NULL && codePoint > 0; --codePoint)
+        ;
+
+    maxglyph = font2P->maxglyph = codePoint;
+    assert (0 <= maxglyph && maxglyph <= PM_FONT_MAXGLYPH);
+
+    if (maxglyph == 0 && fontP->glyph[0] == NULL)
+        pm_error("no glyphs loaded");
+
+    REALLOCARRAY(font2P->glyph, font2P->maxglyph + 1);
+
+    for (codePoint = 0; codePoint <= maxglyph; ++codePoint) {
+        font2P->glyph[codePoint] = fontP->glyph[codePoint];
+
+        if (font2P->glyph[codePoint] != NULL)
+           ++nCharacters;
+    }
+
+    font2P->oldfont = fontP->oldfont;
+    font2P->fcols = fontP->fcols;
+    font2P->frows = fontP->frows;
+
+    font2P->bit_format = PBM_FORMAT;
+    font2P->total_chars = font2P->chars = nCharacters;
+    font2P->load_fn = CONVERTED_TYPE1_FONT;
+    /* Caller should be overwrite the above to a more descriptive
+       value */
+    return font2P;
+}
+
+
+
diff --git a/lib/libpbmfont1.c b/lib/libpbmfont1.c
new file mode 100644
index 00000000..2d269da7
--- /dev/null
+++ b/lib/libpbmfont1.c
@@ -0,0 +1,359 @@
+/*
+**
+** Routines for loading a PBM sheet font file
+**
+** 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.
+*/
+
+#include <assert.h>
+#include <string.h>
+
+#include "netpbm/pm_c_util.h"
+#include "netpbm/mallocvar.h"
+#include "netpbm/nstring.h"
+
+#include "pbm.h"
+#include "pbmfont.h"
+
+
+/*----------------------------------------------------------------------------
+
+  The routines in this file reads a font bitmap representing
+  the following text:
+
+  (0,0)
+     M ",/^_[`jpqy| M
+
+     /  !"#$%&'()*+ /
+     < ,-./01234567 <
+     > 89:;<=>?@ABC >
+     @ DEFGHIJKLMNO @
+     _ PQRSTUVWXYZ[ _
+     { \]^_`abcdefg {
+     } hijklmnopqrs }
+     ~ tuvwxyz{|}~  ~
+
+     M ",/^_[`jpqy| M
+
+  The bitmap must be cropped exactly to the edges.
+
+  The characters in the border you see are irrelevant except for
+  character size compuations.  The 12 x 8 array in the center is
+  the font.  The top left character there belongs to code point
+  0, and the code points increase in standard reading order, so
+  the bottom right character is code point 127.  You can't define
+  code points < 32 or > 127 with this font format.
+
+  The characters in the top and bottom border rows must include a
+  character with the lowest reach of any in the font (e.g. "y",
+  "_") and one with the highest reach (e.g. '"').  The characters
+  in the left and right border columns must include characters
+  with the rightmost and leftmost reach of any in the font
+  (e.g. "M" for both).
+
+  The border must be separated from the font by one blank text
+  row or text column.
+-----------------------------------------------------------------------------*/
+
+
+static unsigned int const firstCodePoint = 32;
+    /* This is the code point of the first character in a pbmfont font.
+       In ASCII, it is a space.
+    */
+
+static unsigned int const nCharsInFont = 96;
+    /* The number of characters in a pbmfont font.  A pbmfont font defines
+       characters at position 32 (ASCII space) through 127, so that's 96.
+    */
+
+
+static void
+findFirstBlankRow(const bit **   const font,
+                  unsigned int   const fcols,
+                  unsigned int   const frows,
+                  unsigned int * const browP) {
+
+    unsigned int row;
+    bool foundBlank;
+
+    for (row = 0, foundBlank = false; row < frows / 6 && !foundBlank; ++row) {
+        unsigned int col;
+        bit col0Value = font[row][0];
+        bool rowIsBlank;
+        rowIsBlank = true;  /* initial assumption */
+        for (col = 1; col < fcols; ++col)
+            if (font[row][col] != col0Value)
+                rowIsBlank = false;
+
+        if (rowIsBlank) {
+            foundBlank = true;
+            *browP = row;
+        }
+    }
+
+    if (!foundBlank)
+        pm_error("couldn't find blank pixel row in font");
+}
+
+
+
+static void
+findFirstBlankCol(const bit **   const font,
+                  unsigned int   const fcols,
+                  unsigned int   const frows,
+                  unsigned int * const bcolP) {
+
+    unsigned int col;
+    bool foundBlank;
+
+    for (col = 0, foundBlank = false; col < fcols / 6 && !foundBlank; ++col) {
+        unsigned int row;
+        bit row0Value = font[0][col];
+        bool colIsBlank;
+        colIsBlank = true;  /* initial assumption */
+        for (row = 1; row < frows; ++row)
+            if (font[row][col] != row0Value)
+                colIsBlank = false;
+
+        if (colIsBlank) {
+            foundBlank = true;
+            *bcolP = col;
+        }
+    }
+
+    if (!foundBlank)
+        pm_error("couldn't find blank pixel column in font");
+}
+
+
+
+static void
+computeCharacterSize(const bit **   const font,
+                     unsigned int   const fcols,
+                     unsigned int   const frows,
+                     unsigned int * const cellWidthP,
+                     unsigned int * const cellHeightP,
+                     unsigned int * const charWidthP,
+                     unsigned int * const charHeightP) {
+
+    unsigned int firstBlankRow;
+    unsigned int firstBlankCol;
+    unsigned int heightLast11Rows;
+
+    findFirstBlankRow(font, fcols, frows, &firstBlankRow);
+
+    findFirstBlankCol(font, fcols, frows, &firstBlankCol);
+
+    heightLast11Rows = frows - firstBlankRow;
+
+    if (heightLast11Rows % 11 != 0)
+        pm_error("The rows of characters in the font do not appear to "
+                 "be all the same height.  The last 11 rows are %u pixel "
+                 "rows high (from pixel row %u up to %u), "
+                 "which is not a multiple of 11.",
+                 heightLast11Rows, firstBlankRow, frows);
+    else {
+        unsigned int widthLast15Cols;
+
+        *cellHeightP = heightLast11Rows / 11;
+
+        widthLast15Cols = fcols - firstBlankCol;
+
+        if (widthLast15Cols % 15 != 0)
+            pm_error("The columns of characters in the font do not appear to "
+                     "be all the same width.  "
+                     "The last 15 columns are %u pixel "
+                     "columns wide (from pixel col %u up to %u), "
+                     "which is not a multiple of 15.",
+                     widthLast15Cols, firstBlankCol, fcols);
+        else {
+            *cellWidthP = widthLast15Cols / 15;
+
+            *charWidthP = firstBlankCol;
+            *charHeightP = firstBlankRow;
+        }
+    }
+}
+
+
+
+struct font*
+pbm_dissectfont(const bit ** const fontsheet,
+                unsigned int const frows,
+                unsigned int const fcols) {
+/*----------------------------------------------------------------------------
+  Dissect PBM sheet font data, create a font structre,
+  load bitmap data into it.
+
+  Return value is a pointer to the newly created font structure
+
+  The input bitmap data is in memory, in one byte per pixel format.
+
+  The dissection works by finding the first blank row and column;
+  i.e the lower right corner of the "M" in the upper left corner
+  of the matrix.  That gives the height and width of the
+  maximum-sized character, which is not too useful.  But the
+  distance from there to the opposite side is an integral
+  multiple of the cell size, and that's what we need.  Then it's
+  just a matter of filling in all the coordinates.
+
+  Struct font has fields 'oldfont', 'fcols', 'frows' for backward
+  compability.  If there is any need to load data stored in this format
+  feed the above three, in order, as arguments to this function:
+
+    pbm_dissectfont(oldfont, fcols, frows);
+ ----------------------------------------------------------------------------*/
+
+    unsigned int cellWidth, cellHeight;
+        /* Dimensions in pixels of each cell of the font -- that
+           includes the glyph and the white space above and to the
+           right of it.  Each cell is a tile of the font image.  The
+           top character row and left character row don't count --
+           those cells are smaller because they are missing the white
+           space.
+        */
+    unsigned int charWidth, charHeight;
+        /* Maximum dimensions of glyph itself, inside its cell */
+
+    int row, col, ch, r, c, i;
+    struct font * fn;
+
+    computeCharacterSize(fontsheet, fcols, frows,
+                         &cellWidth, &cellHeight, &charWidth, &charHeight);
+
+    /* Now convert to a general font */
+
+    MALLOCVAR(fn);
+    if (fn == NULL)
+        pm_error("out of memory allocating font structure");
+
+    fn->maxwidth  = charWidth;
+    fn->maxheight = charHeight;
+    fn->x = fn->y = 0;
+
+    fn->oldfont = fontsheet;
+    fn->frows = frows;
+    fn->fcols = fcols;
+
+    /* Now fill in the 0,0 coords. */
+    row = cellHeight * 2;
+    col = cellWidth  * 2;
+
+    /* Load individual glyphs */
+    for ( ch = 0; ch < nCharsInFont; ++ch ) {
+        /* Allocate memory separately for each glyph.
+           pbm_loadbdffont2() does this in exactly the same manner.
+         */
+        struct glyph * const glyph =
+             (struct glyph *) malloc (sizeof (struct glyph));
+        char * const bmap = (char*) malloc(fn->maxwidth * fn->maxheight);
+
+        if ( bmap == NULL || glyph == NULL )
+            pm_error( "out of memory allocating glyph data" );
+
+        glyph->width  = fn->maxwidth;
+        glyph->height = fn->maxheight;
+        glyph->x = glyph->y = 0;
+        glyph->xadd = cellWidth;
+
+        for ( r = 0; r < glyph->height; ++r )
+            for ( c = 0; c < glyph->width; ++c )
+                bmap[r * glyph->width + c] = fontsheet[row + r][col + c];
+
+        glyph->bmap = bmap;
+        fn->glyph[firstCodePoint + ch] = glyph;
+
+        col += cellWidth;
+        if ( col >= cellWidth * 14 ) {
+            col = cellWidth * 2;
+            row += cellHeight;
+        }
+    }
+
+    /* Initialize all remaining character positions to "undefined." */
+    for (i = 0; i < firstCodePoint; ++i)
+        fn->glyph[i] = NULL;
+
+    for (i = firstCodePoint + nCharsInFont; i <= PM_FONT_MAXGLYPH; ++i)
+        fn->glyph[i] = NULL;
+
+    return fn;
+}
+
+
+
+
+struct font *
+pbm_loadpbmfont(const char * const filename) {
+/*----------------------------------------------------------------------------
+  Read PBM font sheet data from file 'filename'.
+  Load data into font structure.
+
+  When done with object, free with pbm_destroybdffont().
+-----------------------------------------------------------------------------*/
+
+    FILE * ifP;
+    bit ** fontsheet;
+    int fcols, frows;
+    struct font * retval;
+
+    ifP = pm_openr(filename);
+
+    fontsheet = pbm_readpbm(ifP, &fcols, &frows);
+
+    if ((fcols - 1) / 16 >= pbm_maxfontwidth() ||
+        (frows - 1) / 12 >= pbm_maxfontheight())
+        pm_error("Absurdly large PBM font file: %s", filename);
+    else if (fcols < 31 || frows < 23) {
+        /* Need at least one pixel per character, and this is too small to
+           have that.
+        */
+        pm_error("PBM font file '%s' too small to be a font file: %u x %u.  "
+                 "Minimum sensible size is 31 x 23",
+                 filename, fcols, frows);
+    }
+
+    pm_close(ifP);
+
+    retval = pbm_dissectfont((const bit **)fontsheet, frows, fcols);
+    return (retval);
+
+}
+
+
+
+struct font2 *
+pbm_loadpbmfont2(const char * const filename) {
+/*----------------------------------------------------------------------------
+  Like pbm_loadpbmfont, but return a pointer to struct font2.
+
+  When done with object, free with pbm_destroybdffont2().
+-----------------------------------------------------------------------------*/
+
+    const struct font * const pbmfont = pbm_loadpbmfont(filename);
+    struct font2 * const retval  = pbm_expandbdffont(pbmfont);
+
+    free ((void *)pbmfont);
+
+    /* Overwrite some fields */
+
+    retval->load_fn = LOAD_PBMSHEET;
+    retval->default_char = (PM_WCHAR) ' ';
+    retval->default_char_defined = TRUE;
+    retval->name = pm_strdup("(PBM sheet font has no name)");
+    retval->charset = ISO646_1991_IRV;
+    retval->charset_string = pm_strdup("ASCII");
+    retval->total_chars = retval->chars = nCharsInFont;
+
+    return(retval);
+}
+
+
+
diff --git a/lib/libpbmfont2.c b/lib/libpbmfont2.c
new file mode 100644
index 00000000..b354e91b
--- /dev/null
+++ b/lib/libpbmfont2.c
@@ -0,0 +1,1051 @@
+/*
+**
+** Font routines.
+**
+** Wide character stuff written by Akira Urushibata in 2018 and contributed
+** to the public domain.
+**
+** BDF font code by George Phillips, copyright 1993
+**
+** 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.
+**
+** BDF font specs available from:
+** https://partners.adobe.com/public/developer/en/font/5005.BDF_Spec.pdf
+** Glyph Bitmap Distribution Format (BDF) Specification
+** Version 2.2
+** 22 March 1993
+** Adobe Developer Support
+*/
+
+#include <assert.h>
+#include <string.h>
+
+#include "netpbm/pm_c_util.h"
+#include "netpbm/mallocvar.h"
+#include "netpbm/nstring.h"
+
+#include "pbmfont.h"
+#include "pbm.h"
+
+/*----------------------------------------------------------------------------
+  Routines for loading a BDF font file
+-----------------------------------------------------------------------------*/
+
+/* The following are not recognized in individual glyph data; library
+   routines do a pm_error if they see one:
+
+   Vertical writing systems: DWIDTH1, SWIDTH1, VVECTOR, METRICSET,
+   CONTENTVERSION.
+
+   The following is not recognized and is thus ignored at the global level:
+   DWIDTH
+*/
+
+
+#define MAXBDFLINE 1024
+
+/* Official Adobe document says max length of string is 65535 characters.
+   However the value 1024 is sufficient for practical uses.
+*/
+
+typedef struct {
+/*----------------------------------------------------------------------------
+   This is an object for reading lines of a font file.  It reads and tokenizes
+   them into words.
+-----------------------------------------------------------------------------*/
+    FILE * ifP;
+
+    char line[MAXBDFLINE+1];
+        /* 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[7];
+        /* These are the words; each entry is a pointer into line[] (above) */
+
+    unsigned int wordCt;
+} Readline;
+
+
+
+static void
+readline_init(Readline * const readlineP,
+              FILE *     const ifP) {
+
+    readlineP->ifP = ifP;
+
+    readlineP->arg[0] = NULL;
+    readlineP->wordCt = 0;
+}
+
+
+
+static void
+tokenize(char *         const s,
+         const char **  const words,
+         unsigned int   const wordsSz,
+         unsigned int * const wordCtP) {
+/*----------------------------------------------------------------------------
+   Chop up 's' into words by changing space characters to NUL.  Return as
+   'words' an array of pointers to the beginnings of those words in 's'.
+   Terminate the words[] list with a null pointer.
+
+   'wordsSz' is the number of elements of space in 'words'.  If there are more
+   words in 's' than will fit in that space (including the terminating null
+   pointer), ignore the excess on the right.
+
+   '*wordCtP' is the number elements actually found.
+-----------------------------------------------------------------------------*/
+    unsigned int n;  /* Number of words in words[] so far */
+    char * p;
+
+    p = &s[0];
+    n = 0;
+
+    while (*p) {
+        if (!ISGRAPH(*p)) {
+            if(!ISSPACE(*p)) {
+              /* Control chars excluding 09 - 0d (space), 80-ff */
+            pm_message("Warning: non-ASCII character '%x' in "
+                       "BDF font file", *p);
+            }
+            *p++ = '\0';
+        }
+        else {
+            words[n++] = p;
+            if (n >= wordsSz - 1)
+                break;
+            while (*p && ISGRAPH(*p))
+                ++p;
+        }
+    }
+    assert(n <= wordsSz - 1);
+    words[n] = NULL;
+    *wordCtP = n;
+}
+
+
+
+static void
+readline_read(Readline * const readlineP,
+              bool *     const eofP) {
+/*----------------------------------------------------------------------------
+   Read a nonblank line from the file.  Make its contents available
+   as readlineP->arg[].
+
+   Return *eofP == true iff there is no nonblank line before EOF or we
+   are unable to read the file.
+-----------------------------------------------------------------------------*/
+    bool gotLine;
+    bool error;
+
+    for (gotLine = false, error = false; !gotLine && !error; ) {
+        char * rc;
+
+        rc = fgets(readlineP->line, MAXBDFLINE+1, readlineP->ifP);
+        if (rc == NULL)
+            error = true;
+        else {
+            tokenize(readlineP->line, readlineP->arg,
+                     ARRAY_SIZE(readlineP->arg), &readlineP->wordCt);
+            if (readlineP->arg[0] != NULL)
+                gotLine = true;
+        }
+    }
+    *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(Readline *      const readlineP,
+           unsigned int    const glyphWidth,
+           unsigned int    const glyphHeight,
+           const char *    const charName,
+           unsigned char * const bmap) {
+
+    int n;
+    unsigned int bmapIndex;
+
+    bmapIndex = 0;
+
+    for (n = glyphHeight; n > 0; --n) {
+        bool eof;
+        const char * error;
+
+        readline_read(readlineP, &eof);
+
+        if (eof)
+            pm_error("End of file in bitmap for character '%s' in BDF "
+                     "font file.", charName);
+
+        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);
+        }
+    }
+}
+
+
+
+static void
+createBmap(unsigned int  const glyphWidth,
+           unsigned int  const glyphHeight,
+           Readline *    const readlineP,
+           const char *  const charName,
+           const char ** const bmapP) {
+
+    unsigned char * bmap;
+    bool eof;
+
+    if (glyphWidth > 0 && UINT_MAX / glyphWidth < glyphHeight)
+        pm_error("Ridiculously large glyph");
+
+    MALLOCARRAY(bmap, glyphWidth * glyphHeight);
+
+    if (!bmap)
+        pm_error("no memory for font glyph byte map");
+
+    readline_read(readlineP, &eof);
+    if (eof)
+        pm_error("End of file encountered reading font glyph byte map from "
+                 "BDF font file.");
+
+    if (streq(readlineP->arg[0], "ATTRIBUTES")) {
+        /* ATTRIBUTES is defined in Glyph Bitmap Distribution Format (BDF)
+           Specification Version 2.1, but not in Version 2.2.
+        */
+        bool eof;
+        readline_read(readlineP, &eof);
+        if (eof)
+            pm_error("End of file encountered after ATTRIBUTES in BDF "
+                     "font file.");
+    }
+    if (!streq(readlineP->arg[0], "BITMAP"))
+        pm_error("'%s' found where BITMAP expected in definition of "
+                 "character '%s' in BDF font file.",
+                 readlineP->arg[0], charName);
+
+    assert(streq(readlineP->arg[0], "BITMAP"));
+
+    readBitmap(readlineP, glyphWidth, glyphHeight, charName, bmap);
+
+    *bmapP = (char *)bmap;
+}
+
+
+
+static void
+validateWordCount(Readline *    const readlineP,
+                  unsigned int  const nWords) {
+
+    if( readlineP->wordCt != nWords )
+        pm_error("Wrong number of arguments in '%s' line in BDF font file",
+                 readlineP->arg[0]);
+
+    /* We assume that the first word in line 'arg[0]' is a valid string */
+
+}
+
+
+static void
+readExpectedStatement(Readline *    const readlineP,
+                      const char *  const expected,
+                      unsigned int  const nWords) {
+/*----------------------------------------------------------------------------
+  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").  Check for the number
+  of words: 'nWords'.  If either condition is not met, fail the program.
+-----------------------------------------------------------------------------*/
+
+    bool eof;
+
+    readline_read(readlineP, &eof);
+
+    if (eof)
+        pm_error("EOF in BDF font file where '%s' expected", 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);
+
+    validateWordCount(readlineP, nWords);
+
+}
+
+
+
+static void
+skipCharacter(Readline * const readlineP) {
+/*----------------------------------------------------------------------------
+  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) {
+        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(readlineP->arg[0], "ENDCHAR");
+    }
+}
+
+
+
+static int
+wordToInt(const char * const word) {
+
+    unsigned int absValue;
+
+    int retval;
+
+    const char * error;
+    const int sign = (word[0] == '-') ? -1 : +1;
+    const char * const absString = (sign == -1) ? &word[1] : word;
+    /* No leading spaces allowed in 'word' */
+
+    if (!ISDIGIT(absString[0]))
+      error = "Non-digit character encountered";
+
+    else {
+        pm_string_to_uint(absString, &absValue, &error);
+        if (error == NULL && absValue > INT_MAX)
+            error = "Out of range";
+    }
+
+    if (error != NULL)
+        pm_error ("Error reading numerical argument in "
+                  "BDF font file: %s %s %s", error, word, absString);
+
+    retval = sign * absValue;
+    assert (INT_MIN < retval && retval < INT_MAX);
+
+    return retval;
+}
+
+
+
+static void
+interpEncoding(const char **  const arg,
+               unsigned int * const codepointP,
+               bool *         const badCodepointP,
+               PM_WCHAR       const maxmaxglyph) {
+/*----------------------------------------------------------------------------
+   With arg[] being the ENCODING statement from the font, return as
+   *codepointP the codepoint that it indicates (code point is the character
+   code, e.g. in ASCII, 48 is '0').
+
+   But if the statement doesn't give an acceptable codepoint return
+   *badCodepointP == TRUE.
+
+   'maxmaxglyph' is the maximum codepoint in the font.
+-----------------------------------------------------------------------------*/
+    bool gotCodepoint;
+    bool badCodepoint;
+    unsigned int codepoint;
+
+    if (wordToInt(arg[1]) >= 0) {
+        codepoint = wordToInt(arg[1]);
+        gotCodepoint = true;
+    } else {
+      if (wordToInt(arg[1]) == -1 && arg[2] != NULL) {
+            codepoint = wordToInt(arg[2]);
+            gotCodepoint = true;
+        } else
+            gotCodepoint = false;
+    }
+    if (gotCodepoint) {
+        if (codepoint > maxmaxglyph)
+            badCodepoint = true;
+        else
+            badCodepoint = false;
+    } else
+        badCodepoint = true;
+
+    *badCodepointP = badCodepoint;
+    *codepointP    = codepoint;
+}
+
+
+
+static void
+readEncoding(Readline *     const readlineP,
+             unsigned int * const codepointP,
+             bool *         const badCodepointP,
+             PM_WCHAR       const maxmaxglyph) {
+
+    bool eof;
+    const char * expected = "ENCODING";
+
+    readline_read(readlineP, &eof);
+
+    if (eof)
+        pm_error("EOF in BDF font file where '%s' expected", 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);
+    else if(readlineP->wordCt != 2 &&  readlineP->wordCt != 3)
+        pm_error("Wrong number of arguments in '%s' line in BDF font file",
+                 readlineP->arg[0]);
+
+    interpEncoding(readlineP->arg, codepointP, badCodepointP, maxmaxglyph);
+}
+
+
+
+static void
+validateFontLimits(const struct font2 * const font2P) {
+
+    assert(pbm_maxfontheight() > 0 && pbm_maxfontwidth() > 0);
+
+    if (font2P->maxwidth  <= 0 ||
+        font2P->maxheight <= 0 ||
+        font2P->maxwidth  > pbm_maxfontwidth()  ||
+        font2P->maxheight > pbm_maxfontheight() ||
+        -font2P->x + 1 > font2P->maxwidth ||
+        -font2P->y + 1 > font2P->maxheight ||
+        font2P->x > font2P->maxwidth  ||
+        font2P->y > font2P->maxheight ||
+        font2P->x + font2P->maxwidth  > pbm_maxfontwidth() ||
+        font2P->y + font2P->maxheight > pbm_maxfontheight()
+        ) {
+
+        pm_error("Global font metric(s) out of bounds.");
+    }
+
+    if (font2P->maxglyph > PM_FONT2_MAXGLYPH)
+        pm_error("Internal error.  Glyph table too large: %u glyphs; "
+                 "Maximum possible in Netpbm is %u",
+                 (unsigned int) font2P->maxglyph, PM_FONT2_MAXGLYPH);
+}
+
+
+
+static void
+validateGlyphLimits(const struct font2 * const font2P,
+                    const struct glyph * const glyphP,
+                    const char *         const charName) {
+
+    /* Some BDF files code space with zero width and height,
+       no bitmap data and just the xadd value.
+       We allow zero width and height, iff both are zero.
+
+       Some BDF files have individual glyphs with a BBX value which
+       exceeds the global maximum stated by FONTBOUNDINGBOX.
+       Abort with error when this is encountered.
+       It seems some programs including emacs and bdftopcf tolerate
+       this violation.
+    */
+
+    if (((glyphP->width == 0 || glyphP->height == 0) &&
+         !(glyphP->width == 0 && glyphP->height == 0)) ||
+        glyphP->width  > font2P->maxwidth  ||
+        glyphP->height > font2P->maxheight ||
+        glyphP->x < font2P->x ||
+        glyphP->y < font2P->y ||
+        glyphP->x + (int) glyphP->width  > font2P->x + font2P->maxwidth  ||
+        glyphP->y + (int) glyphP->height > font2P->y + font2P->maxheight ||
+        glyphP->xadd > pbm_maxfontwidth() ||
+        glyphP->xadd + MAX(glyphP->x,0) + (int) glyphP->width >
+        pbm_maxfontwidth()
+        ) {
+
+        pm_error("Font metric(s) for char '%s' out of bounds.\n", charName);
+    }
+}
+
+
+
+static void
+processChars(Readline *     const readlineP,
+             struct font2 * const font2P) {
+/*----------------------------------------------------------------------------
+   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 *font2P.
+-----------------------------------------------------------------------------*/
+    unsigned int const nCharacters = wordToInt(readlineP->arg[1]);
+
+    unsigned int nCharsDone;
+    unsigned int nCharsValid;
+
+    for (nCharsDone = 0, nCharsValid = 0;
+         nCharsDone < nCharacters; ) {
+
+        bool eof;
+
+        readline_read(readlineP, &eof);
+        if (eof)
+            pm_error("End of file after CHARS reading BDF font file");
+
+        if (streq(readlineP->arg[0], "COMMENT")) {
+            /* ignore */
+        } else if (!streq(readlineP->arg[0], "STARTCHAR"))
+            pm_error("%s detected where \'STARTCHAR\' expected "
+                     "in BDF font file", readlineP->arg[0] );
+        else {
+            const char * charName;
+
+            struct glyph * glyphP;
+            unsigned int codepoint;
+            bool badCodepoint;
+
+            if (readlineP->wordCt < 2)
+                pm_error("Wrong number of arguments in STARTCHAR line "
+                         "in BDF font file");
+            /* Character name may contain spaces: there may be more than
+               three words in the line.
+             */
+            charName = pm_strdup(readlineP->arg[1]);
+
+            assert(streq(readlineP->arg[0], "STARTCHAR"));
+
+            MALLOCVAR(glyphP);
+
+            if (glyphP == NULL)
+                pm_error("no memory for font glyph for '%s' character",
+                         charName);
+
+            readEncoding(readlineP, &codepoint, &badCodepoint,
+                         font2P->maxmaxglyph);
+
+            if (badCodepoint)
+                skipCharacter(readlineP);
+            else {
+                if (codepoint < font2P->maxglyph) {
+                    if (font2P->glyph[codepoint] != NULL)
+                        pm_error("Multiple definition of code point %u "
+                                 "in BDF font file", (unsigned int) codepoint);
+                    else
+                        pm_message("Reverse order detected in BDF file. "
+                                   "Code point %u defined after %u",
+                                    (unsigned int) codepoint,
+                                    (unsigned int) font2P->maxglyph);
+                } else {
+                    /* Initialize all characters in the gap to nonexistent */
+                    unsigned int i;
+                    unsigned int const oldMaxglyph = font2P->maxglyph;
+                    unsigned int const newMaxglyph = codepoint;
+
+                    for (i = oldMaxglyph + 1; i < newMaxglyph; ++i)
+                        font2P->glyph[i] = NULL;
+
+                    font2P->maxglyph = newMaxglyph;
+                    }
+
+                readExpectedStatement(readlineP, "SWIDTH", 3);
+
+                readExpectedStatement(readlineP, "DWIDTH", 3);
+                glyphP->xadd = wordToInt(readlineP->arg[1]);
+
+                readExpectedStatement(readlineP, "BBX", 5);
+                glyphP->width  = wordToInt(readlineP->arg[1]);
+                glyphP->height = wordToInt(readlineP->arg[2]);
+                glyphP->x      = wordToInt(readlineP->arg[3]);
+                glyphP->y      = wordToInt(readlineP->arg[4]);
+
+                validateGlyphLimits(font2P, glyphP, charName);
+
+                createBmap(glyphP->width, glyphP->height, readlineP, charName,
+                           &glyphP->bmap);
+
+                readExpectedStatement(readlineP, "ENDCHAR", 1);
+
+                assert(codepoint <= font2P->maxmaxglyph);
+                /* Ensured by readEncoding() */
+
+                font2P->glyph[codepoint] = glyphP;
+                pm_strfree(charName);
+
+                ++nCharsValid;
+            }
+            ++nCharsDone;
+        }
+    }
+    font2P->chars = nCharsValid;
+    font2P->total_chars = nCharacters;
+}
+
+
+
+static void
+processBdfFontNameLine(Readline     * const readlineP,
+                       struct font2 * const font2P) {
+
+    if (font2P->name)  /* We've already processed a FONT line */
+        pm_error("Multiple FONT lines in BDF font file");
+    else {
+        char * buffer;
+
+        MALLOCARRAY(buffer, MAXBDFLINE+1);
+
+        if (!buffer)
+            pm_error("Failed to get memory for %u-character font name buffer",
+                     MAXBDFLINE+1);
+
+        if (readlineP->wordCt == 1)
+            strcpy(buffer, "(no name)");
+
+        else {
+            unsigned int tokenCt;
+
+            buffer[0] ='\0';
+
+            for (tokenCt=1;
+                 tokenCt < ARRAY_SIZE(readlineP->arg) &&
+                     readlineP->arg[tokenCt] != NULL; ++tokenCt) {
+                strcat(buffer, " ");
+                strcat(buffer, readlineP->arg[tokenCt]);
+            }
+        }
+        font2P->name = buffer;
+    }
+}
+
+
+
+static void
+loadCharsetString(const char *  const registry,
+                  const char *  const encoding,
+                  const char ** const charsetStringP) {
+
+    char * dest;
+    unsigned int inCt, outCt;
+
+    dest = malloc(strlen(registry) + strlen(encoding) + 1);
+
+    if (!dest)
+        pm_error("no memory to load CHARSET_REGISTRY and CHARSET_ENCODING "
+               "from BDF file");
+
+    for (inCt = outCt = 0; inCt < strlen(registry); ++inCt) {
+        char const c = registry[inCt];
+        if (isgraph(c) && c != '"')
+            dest[outCt++] = c;
+    }
+    dest[outCt++] = '-';
+
+    for (inCt = 0; inCt < strlen(encoding); ++inCt) {
+        char const c = encoding[inCt];
+        if (isgraph(c) && c != '"')
+            dest[outCt++] = c;
+    }
+
+    dest[outCt] = '\0';
+    *charsetStringP = dest;
+}
+
+
+
+
+static unsigned int const maxTokenLen = 60;
+
+
+
+static void
+doCharsetRegistry(Readline *    const readlineP,
+                  bool *        const gotRegistryP,
+                  const char ** const registryP) {
+
+    if (*gotRegistryP)
+        pm_error("Multiple CHARSET_REGISTRY lines in BDF font file");
+    else if (readlineP->arg[2] != NULL)
+        pm_message("CHARSET_REGISTRY in BDF font file is not "
+                   "a single word.  Ignoring extra element(s) %s ...",
+                   readlineP->arg[2]);
+    else if (strlen(readlineP->arg[1]) > maxTokenLen)
+        pm_message("CHARSET_REGISTRY in BDF font file is too long. "
+                   "Truncating");
+
+    *registryP = pm_strdup(readlineP->arg[1]);
+    *gotRegistryP = true;
+}
+
+
+
+static void
+doCharsetEncoding(Readline *    const readlineP,
+                  bool *        const gotEncodingP,
+                  const char ** const encodingP) {
+
+    if (*gotEncodingP)
+        pm_error("Multiple CHARSET_ENCODING lines in BDF font file");
+    else if (readlineP->arg[2] != NULL)
+        pm_message("CHARSET_ENCODING in BDF font file is not "
+                   "a single word.  Ignoring extra element(s) %s ...",
+                   readlineP->arg[2]);
+    else if (strlen(readlineP->arg[1]) > maxTokenLen)
+        pm_message("CHARSET_ENCODING in BDF font file is too long. "
+                   "Truncating");
+
+    *encodingP = pm_strdup(readlineP->arg[1]);
+    *gotEncodingP = true;
+}
+
+
+
+static void
+doDefaultChar(Readline * const readlineP,
+              bool *     const gotDefaultCharP,
+              PM_WCHAR * const defaultCharP) {
+
+    if (*gotDefaultCharP)
+        pm_error("Multiple DEFAULT_CHAR lines in BDF font file");
+    else if (readlineP->arg[1] == NULL)
+        pm_error("Malformed DEFAULT_CHAR line in BDF font file");
+    else {
+        *defaultCharP = (PM_WCHAR) wordToInt(readlineP->arg[1]);
+        *gotDefaultCharP = true;
+    }
+}
+
+
+
+static void
+processBdfPropertyLine(Readline     * const readlineP,
+                       struct font2 * const font2P) {
+
+    bool gotRegistry;
+    const char * registry;
+    bool gotEncoding;
+    const char * encoding;
+    bool gotDefaultChar;
+    PM_WCHAR defaultChar;
+    unsigned int propCt;
+    unsigned int commentCt;
+    unsigned int propTotal;
+
+    validateWordCount(readlineP, 2);   /* STARTPROPERTIES n */
+
+    propTotal = wordToInt(readlineP->arg[1]);
+
+    gotRegistry    = false;  /* initial value */
+    gotEncoding    = false;  /* initial value */
+    gotDefaultChar = false;  /* initial value */
+
+    propCt    = 0;  /* initial value */
+    commentCt = 0;  /* initial value */
+
+    do {
+        bool eof;
+
+        readline_read(readlineP, &eof);
+        if (eof)
+            pm_error("End of file after STARTPROPERTIES in BDF font file");
+        else if (streq(readlineP->arg[0], "CHARSET_REGISTRY") &&
+                 readlineP->arg[1] != NULL) {
+            doCharsetRegistry(readlineP, &gotRegistry, &registry);
+        } else if (streq(readlineP->arg[0], "CHARSET_ENCODING") &&
+                   readlineP->arg[1] != NULL) {
+            doCharsetEncoding(readlineP, &gotEncoding, &encoding);
+        } else if (streq(readlineP->arg[0], "DEFAULT_CHAR")) {
+            doDefaultChar(readlineP, &gotDefaultChar, &defaultChar);
+        } else if (streq(readlineP->arg[0], "COMMENT")) {
+            ++commentCt;
+        }
+        ++propCt;
+
+    } while (!streq(readlineP->arg[0], "ENDPROPERTIES"));
+
+    --propCt; /* Subtract one for ENDPROPERTIES line */
+
+    if (propCt != propTotal && propCt - commentCt != propTotal)
+      /* Some BDF files have COMMENTs in the property section and leave
+         them out of the count.
+         Others just give a wrong count.
+       */
+        pm_message ("Note: wrong number of property lines in BDF font file. "
+                    "STARTPROPERTIES line says %u, actual count: %u. "
+                    "Proceeding.",
+                    propTotal, propCt);
+
+
+    if (gotRegistry && gotEncoding)
+        loadCharsetString(registry, encoding, &font2P->charset_string);
+    else if (gotRegistry != gotEncoding) {
+        pm_message ("CHARSET_%s absent or incomplete in BDF font file. "
+                    "Ignoring CHARSET_%s.",
+                    gotEncoding ? "REGISTRY" : "ENCODING",
+                    gotEncoding ? "ENCODING" : "REGISTRY");
+    }
+    if (gotRegistry)
+        pm_strfree(registry);
+    if (gotEncoding)
+        pm_strfree(encoding);
+
+    if (gotDefaultChar) {
+        font2P->default_char         = defaultChar;
+        font2P->default_char_defined = true;
+    }
+
+}
+
+
+static void
+processBdfFontLine(Readline     * const readlineP,
+                   struct font2 * const font2P,
+                   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 */
+
+    assert(readlineP->arg[0] != NULL);  /* Entry condition */
+
+    if (streq(readlineP->arg[0], "FONT")) {
+        processBdfFontNameLine(readlineP, font2P);
+    } else if (streq(readlineP->arg[0], "COMMENT")) {
+        /* ignore */
+    } else if (streq(readlineP->arg[0], "SIZE")) {
+        /* ignore */
+    } else if (streq(readlineP->arg[0], "STARTPROPERTIES")) {
+      if (font2P->maxwidth == 0)
+      pm_error("Encountered STARTROPERTIES before FONTBOUNDINGBOX "
+               "in BDF font file");
+      else
+        processBdfPropertyLine(readlineP, font2P);
+    } else if (streq(readlineP->arg[0], "FONTBOUNDINGBOX")) {
+        validateWordCount(readlineP,5);
+
+        font2P->maxwidth  = wordToInt(readlineP->arg[1]);
+        font2P->maxheight = wordToInt(readlineP->arg[2]);
+        font2P->x = wordToInt(readlineP->arg[3]);
+        font2P->y = wordToInt(readlineP->arg[4]);
+        validateFontLimits(font2P);
+    } else if (streq(readlineP->arg[0], "ENDFONT")) {
+        *endOfFontP = true;
+    } else if (streq(readlineP->arg[0], "CHARS")) {
+      if (font2P->maxwidth == 0)
+      pm_error("Encountered CHARS before FONTBOUNDINGBOX "
+                   "in BDF font file");
+      else {
+        validateWordCount(readlineP, 2);  /* CHARS n */
+        processChars(readlineP, font2P);
+      }
+    } else {
+        /* ignore */
+    }
+
+}
+
+
+
+struct font2 *
+pbm_loadbdffont2(const char * const filename,
+                 PM_WCHAR     const maxmaxglyph) {
+/*----------------------------------------------------------------------------
+   Read a BDF font file "filename" as a 'font2' structure.  A 'font2'
+   structure is more expressive than a 'font' structure, most notably in that
+   it can handle wide code points and many more glyphs.
+
+   Codepoints up to maxmaxglyph inclusive are valid in the file.
+
+   The returned object is in new malloc'ed storage, in many pieces.
+   When done with, destroy with pbm_destroybdffont2().
+-----------------------------------------------------------------------------*/
+
+    FILE *         ifP;
+    Readline       readline;
+    struct font2 * font2P;
+    bool           endOfFont;
+
+    ifP = fopen(filename, "rb");
+    if (!ifP)
+        pm_error("Unable to open BDF font file name '%s'.  errno=%d (%s)",
+                 filename, errno, strerror(errno));
+
+    readline_init(&readline, ifP);
+
+    pbm_createbdffont2_base(&font2P, maxmaxglyph);
+
+    font2P->maxglyph = 0;
+        /* Initial value.  Increases as new characters are loaded */
+    font2P->glyph[0] = NULL;
+        /* Initial value.  Overwrite later if codepoint 0 is defined. */
+
+    font2P->maxmaxglyph = maxmaxglyph;
+
+    /* Initialize some values - to be overwritten if actual values are
+       stated in BDF file */
+    font2P->maxwidth = font2P->maxheight = font2P->x = font2P->y = 0;
+    font2P->name = font2P->charset_string = NULL;
+    font2P->chars = font2P->total_chars = 0;
+    font2P->default_char = 0;
+    font2P->default_char_defined = FALSE;
+
+    readExpectedStatement(&readline, "STARTFONT", 2);
+
+    endOfFont = FALSE;
+
+    while (!endOfFont) {
+        bool eof;
+        readline_read(&readline, &eof);
+        if (eof)
+            pm_error("End of file before ENDFONT statement in BDF font file");
+
+        processBdfFontLine(&readline, font2P, &endOfFont);
+    }
+    fclose(ifP);
+
+    if(font2P->chars == 0)
+        pm_error("No glyphs found in BDF font file "
+                 "in codepoint range 0 - %u", (unsigned int) maxmaxglyph);
+
+    REALLOCARRAY(font2P->glyph, font2P->maxglyph + 1);
+
+    font2P->bit_format = PBM_FORMAT;
+    font2P->load_fn = LOAD_BDFFILE;
+    font2P->charset = ENCODING_UNKNOWN;
+    font2P->oldfont = NULL;  /* Legacy field */
+    font2P->fcols = font2P->frows = 0;  /* Legacy fields */
+
+    return font2P;
+}
+
+
+static struct font *
+font2ToFont(const struct font2 * const font2P) {
+            struct font  * fontP;
+            unsigned int   codePoint;
+
+    MALLOCVAR(fontP);
+    if (fontP == NULL)
+        pm_error("no memory for font");
+
+    fontP->maxwidth  = font2P->maxwidth;
+    fontP->maxheight = font2P->maxheight;
+
+    fontP->x = font2P->x;
+    fontP->y = font2P->y;
+
+    for (codePoint = 0; codePoint <= font2P->maxglyph; ++codePoint)
+        fontP->glyph[codePoint] = font2P->glyph[codePoint];
+
+    /* font2P->maxglyph is typically 255 (PM_FONT_MAXGLYPH) or larger.
+       But in some rare cases it is smaller.
+       If an ASCII-only font is read, it will be 126 or 127.
+
+       Set remaining codepoints up to PM_FONT_MAXGLYPH, if any, to NULL
+    */
+
+    for ( ; codePoint <= PM_FONT_MAXGLYPH; ++codePoint)
+        fontP->glyph[codePoint] = NULL;
+
+    /* Give values to legacy fields */
+    fontP->oldfont = font2P->oldfont;
+    fontP->fcols = font2P->fcols;
+    fontP->frows = font2P->frows;
+
+    return fontP;
+}
+
+
+
+struct font *
+pbm_loadbdffont(const char * const filename) {
+/*----------------------------------------------------------------------------
+   Read a BDF font file "filename" into a traditional font structure.
+
+   Codepoints up to 255 (PM_FONT_MAXGLYPH) are valid.
+
+   Can handle ASCII, ISO-8859-1, ISO-8859-2, ISO-8859-15, etc.
+
+   The returned object is in new malloc'ed storage, in many pieces.
+   Destroy with pbm_destroybdffont().
+-----------------------------------------------------------------------------*/
+    struct font  * fontP;
+    struct font2 * const font2P = pbm_loadbdffont2(filename, PM_FONT_MAXGLYPH);
+
+    fontP = font2ToFont(font2P);
+
+    /* Free the base structure which was created by pbm_loadbdffont2() */
+    pbm_destroybdffont2_base(font2P);
+
+    return fontP;
+}
+
+
+
diff --git a/lib/libpbmfontdump.c b/lib/libpbmfontdump.c
new file mode 100644
index 00000000..f0c950f7
--- /dev/null
+++ b/lib/libpbmfontdump.c
@@ -0,0 +1,96 @@
+/*
+**
+** Font routines.
+**
+** BDF font code Copyright 1993 by George Phillips.
+**
+** 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.
+**
+** BDF font specs available from:
+** https://partners.adobe.com/public/developer/en/font/5005.BDF_Spec.pdf
+** Glyph Bitmap Distribution Format (BDF) Specification
+** Version 2.2
+** 22 March 1993
+** Adobe Developer Support
+*/
+
+#include <assert.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "netpbm/pm_c_util.h"
+#include "netpbm/mallocvar.h"
+#include "netpbm/nstring.h"
+
+#include "pbmfont.h"
+#include "pbm.h"
+
+
+void
+pbm_dumpfont(struct font * const fontP,
+             FILE *        const ofP) {
+/*----------------------------------------------------------------------------
+  Dump out font as C source code.
+-----------------------------------------------------------------------------*/
+    unsigned int i;
+    unsigned int ng;
+
+    if (fontP->oldfont)
+        pm_message("Netpbm no longer has the capability to generate "
+                   "a font in long hexadecimal data format");
+
+    for (i = 0, ng = 0; i < PM_FONT_MAXGLYPH +1; ++i) {
+        if (fontP->glyph[i])
+            ++ng;
+    }
+
+    printf("static struct glyph _g[%d] = {\n", ng);
+
+    for (i = 0; i < PM_FONT_MAXGLYPH + 1; ++i) {
+        struct glyph * const glyphP = fontP->glyph[i];
+        if (glyphP) {
+            unsigned int j;
+            printf(" { %d, %d, %d, %d, %d, \"", glyphP->width, glyphP->height,
+                   glyphP->x, glyphP->y, glyphP->xadd);
+
+            for (j = 0; j < glyphP->width * glyphP->height; ++j) {
+                if (glyphP->bmap[j])
+                    printf("\\1");
+                else
+                    printf("\\0");
+            }
+            --ng;
+            printf("\" }%s\n", ng ? "," : "");
+        }
+    }
+    printf("};\n");
+
+    printf("struct font XXX_font = { %d, %d, %d, %d, {\n",
+           fontP->maxwidth, fontP->maxheight, fontP->x, fontP->y);
+
+    {
+        unsigned int i;
+
+        for (i = 0; i < PM_FONT_MAXGLYPH + 1; ++i) {
+            if (fontP->glyph[i])
+                printf(" _g + %d", ng++);
+            else
+                printf(" NULL");
+
+            if (i != PM_FONT_MAXGLYPH) printf(",");
+            printf("\n");
+        }
+    }
+
+    printf(" }\n};\n");
+}
+
+
+
diff --git a/lib/libpgm.h b/lib/libpgm.h
index 7523faaf..eb292c80 100644
--- a/lib/libpgm.h
+++ b/lib/libpgm.h
@@ -7,9 +7,9 @@
 #include "pgm.h"
 
 void
-pgm_readpgminitrest(FILE * const file, 
-                    int *  const colsP, 
-                    int *  const rowsP, 
+pgm_readpgminitrest(FILE * const file,
+                    int *  const colsP,
+                    int *  const rowsP,
                     gray * const maxvalP);
 
 #endif
diff --git a/lib/libpm.c b/lib/libpm.c
index 4374bbe3..47a2f498 100644
--- a/lib/libpm.c
+++ b/lib/libpm.c
@@ -8,7 +8,7 @@
   Netpbm library subroutines.
 **************************************************************************/
 
-#define _BSD_SOURCE          /* Make sure strdup is in string.h */
+#define _DEFAULT_SOURCE      /* New name for SVID & BSD source defines */
 #define _XOPEN_SOURCE 500    /* Make sure ftello, fseeko are defined */
 
 #include "netpbm/pm_config.h"
@@ -44,7 +44,7 @@ const char * pm_progname;
 int pm_plain_output;
     /* Boolean: programs should produce output in plain format */
 
-static bool pm_showmessages;  
+static bool pm_showmessages;
     /* Programs should display informational messages (because the user didn't
        specify the --quiet option).
     */
@@ -67,7 +67,7 @@ static pm_usererrormsgfn * userErrorMsgFn = NULL;
     */
 
 static pm_usermessagefn * userMessageFn = NULL;
-    /* A function to call to issue an error message.
+    /* A function to call to issue a non-error message.
 
        NULL means use the library default: print to Standard Error
     */
@@ -248,7 +248,7 @@ pm_errormsg(const char format[], ...) {
     va_start(args, format);
 
     pm_vasprintf(&msg, format, args);
-    
+
     errormsg(msg);
 
     pm_strfree(msg);
@@ -266,7 +266,7 @@ pm_error(const char format[], ...) {
     va_start(args, format);
 
     pm_vasprintf(&msg, format, args);
-    
+
     errormsg(msg);
 
     pm_strfree(msg);
@@ -355,7 +355,7 @@ pm_allocarray(int const cols,
 
 
 void
-pm_freearray(char ** const rowIndex, 
+pm_freearray(char ** const rowIndex,
              int     const rows) {
 
     void * const rowIndexVoid = rowIndex;
@@ -368,8 +368,8 @@ pm_freearray(char ** const rowIndex,
 /* Case-insensitive keyword matcher. */
 
 int
-pm_keymatch(const char *       const strarg, 
-            const char * const keywordarg, 
+pm_keymatch(const char *       const strarg,
+            const char * const keywordarg,
             int          const minchars) {
     int len;
     const char * keyword;
@@ -438,7 +438,8 @@ pm_maxvaltobits(int const maxval) {
         return 16;
     else
         pm_error( "maxval of %d is too large!", maxval );
-        return -1;  /* Should never come here */
+
+    assert(false);
 }
 
 int
@@ -448,7 +449,7 @@ pm_bitstomaxval(int const bits) {
 
 
 unsigned int PURE_FN_ATTR
-pm_lcm(unsigned int const x, 
+pm_lcm(unsigned int const x,
        unsigned int const y,
        unsigned int const z,
        unsigned int const limit) {
@@ -471,7 +472,7 @@ pm_lcm(unsigned int const x,
            candidate <= limit)
         candidate += biggest;
 
-    if (candidate > limit) 
+    if (candidate > limit)
         candidate = limit;
 
     return candidate;
@@ -498,7 +499,7 @@ pm_init(const char * const progname,
 #ifdef HAVE_SETMODE
     /* Set the stdin and stdout mode to binary.  This means nothing on Unix,
        but matters on Windows.
-       
+
        Note that stdin and stdout aren't necessarily image files.  In
        particular, stdout is sometimes text for human consumption,
        typically printed on the terminal.  Binary mode isn't really
@@ -506,7 +507,7 @@ pm_init(const char * const progname,
        any knowledge of how stdin and stdout are being used because it is
        easy.  But we do make an exception for the case that we know the
        file is a terminal, to get a little closer to doing the right
-       thing.  
+       thing.
     */
     if (!isatty(0)) setmode(0,O_BINARY);  /* Standard Input */
     if (!isatty(1)) setmode(1,O_BINARY);  /* Standard Output */
@@ -516,13 +517,51 @@ pm_init(const char * const progname,
 
 
 
+static const char *
+dtMsg(time_t const dateTime) {
+/*----------------------------------------------------------------------------
+   Text for the version message to indicate datetime 'dateTime'.
+-----------------------------------------------------------------------------*/
+    struct tm * const brokenTimeP = localtime(&dateTime);
+
+    char buffer[100];
+
+    strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", brokenTimeP);
+
+    return pm_strdup(buffer);
+}
+
+
+
 static void
 showVersion(void) {
-    pm_message( "Using libnetpbm from Netpbm Version: %s", NETPBM_VERSION );
-#if defined(COMPILE_TIME) && defined(COMPILED_BY)
-    pm_message( "Compiled %s by user \"%s\"",
-                COMPILE_TIME, COMPILED_BY );
+
+    pm_message("Using libnetpbm from Netpbm Version: %s", NETPBM_VERSION);
+
+    /* SOURCE_DATETIME is defined when the user wants a reproducible build,
+       so wants the source code modification datetime instead of the build
+       datetime in the object code.
+    */
+#if defined(SOURCE_DATETIME)
+    {
+        const char * const sourceDtMsg = dtMsg(SOURCE_DATETIME);
+        pm_message("Built from source dated %s", sourceDtMsg);
+        pm_strfree(sourceDtMsg);
+    }
+#else
+  #if defined(BUILD_DATETIME)
+    {
+        const char * const buildDtMsg = dtMsg(BUILD_DATETIME);
+        pm_message("Built at %s", buildDtMsg);
+        pm_strfree(buildDtMsg);
+    }
+  #endif
 #endif
+
+#if defined(COMPILED_BY)
+    pm_message("Built by %s", COMPILED_BY);
+#endif
+
 #ifdef BSD
     pm_message( "BSD defined" );
 #endif /*BSD*/
@@ -540,7 +579,7 @@ showVersion(void) {
         pm_message( "RGB_ENV='%s'", RGBENV );
         rgbdef = getenv(RGBENV);
         if( rgbdef )
-            pm_message( "RGBENV= '%s' (env vbl set to '%s')", 
+            pm_message( "RGBENV= '%s' (env vbl set to '%s')",
                         RGBENV, rgbdef );
         else
             pm_message( "RGBENV= '%s' (env vbl is unset)", RGBENV);
@@ -553,7 +592,7 @@ static void
 showNetpbmHelp(const char progname[]) {
 /*----------------------------------------------------------------------------
   Tell the user where to get help for this program, assuming it is a Netpbm
-  program (a program that comes with the Netpbm package, as opposed to a 
+  program (a program that comes with the Netpbm package, as opposed to a
   program that just uses the Netpbm libraries).
 
   Tell him to go to the URL listed in the Netpbm configuration file.
@@ -571,9 +610,9 @@ showNetpbmHelp(const char progname[]) {
 
     if (getenv("NETPBM_CONF"))
         netpbmConfigFileName = getenv("NETPBM_CONF");
-    else 
+    else
         netpbmConfigFileName = "/etc/netpbm";
-    
+
     netpbmConfigFile = fopen(netpbmConfigFileName, "r");
     if (netpbmConfigFile == NULL) {
         pm_message("Unable to open Netpbm configuration file '%s'.  "
@@ -627,14 +666,14 @@ parseCommonOptions(int *         const argcP,
 
     for (inCursor = 1, outCursor = 1; inCursor < *argcP; ++inCursor) {
             if (strcaseeq(argv[inCursor], "-quiet") ||
-                strcaseeq(argv[inCursor], "--quiet")) 
+                strcaseeq(argv[inCursor], "--quiet"))
                 *showMessagesP = false;
             else if (strcaseeq(argv[inCursor], "-version") ||
-                     strcaseeq(argv[inCursor], "--version")) 
+                     strcaseeq(argv[inCursor], "--version"))
                 *showVersionP = true;
             else if (strcaseeq(argv[inCursor], "-help") ||
                      strcaseeq(argv[inCursor], "--help") ||
-                     strcaseeq(argv[inCursor], "-?")) 
+                     strcaseeq(argv[inCursor], "-?"))
                 *showHelpP = true;
             else if (strcaseeq(argv[inCursor], "-plain") ||
                      strcaseeq(argv[inCursor], "--plain"))
@@ -689,7 +728,7 @@ pm_proginit(int *         const argcP,
         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 
+        /* If we can figure out a way to distinguish Netpbm programs from
            other programs using the Netpbm libraries, we can do better here.
         */
         if (0)
@@ -703,7 +742,7 @@ pm_proginit(int *         const argcP,
 void
 pm_setMessage(int   const newState,
               int * const oldStateP) {
-    
+
     if (oldStateP)
         *oldStateP = pm_showmessages;
 
@@ -724,7 +763,7 @@ static void
 extractAfterLastSlash(const char * const fullPath,
                       char *       const retval,
                       size_t       const retvalSize) {
-    
+
     char * slashPos;
 
     /* Chop any directories off the left end */
@@ -737,10 +776,17 @@ extractAfterLastSlash(const char * const fullPath,
         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;
+
+static void
+chopOffExe(char * const arg) {
+/*----------------------------------------------------------------------------
+  Chop any .exe off the right end of 'arg'.
+-----------------------------------------------------------------------------*/
+    if (strlen(arg) >= 4 && strcmp(arg+strlen(arg)-4, ".exe") == 0)
+        arg[strlen(arg)-4] = 0;
 }
 
 
@@ -748,13 +794,13 @@ extractAfterLastSlash(const char * const fullPath,
 char *
 pm_arg0toprogname(const char arg0[]) {
 /*----------------------------------------------------------------------------
-   Given a value for argv[0] (a command name or file name passed to a 
+   Given a value for argv[0] (a command name or file name passed to a
    program in the standard C calling sequence), return the name of the
    Netpbm program to which it refers.
 
    In the most ordinary case, this is simply the argument itself.
 
-   But if the argument contains a slash, it is the part of the argument 
+   But if the argument contains a slash, it is the part of the argument
    after the last slash, and if there is a .exe on it (as there is for
    DJGPP), that is removed.
 
@@ -775,6 +821,7 @@ pm_arg0toprogname(const char arg0[]) {
 #else
     static char retval[MAX_RETVAL_SIZE+1];
     extractAfterLastSlash(arg0, retval, sizeof(retval));
+    chopOffExe(retval);
 #endif
 
     return retval;
@@ -801,7 +848,7 @@ pm_parse_width(const char * const arg) {
     unsigned int width;
     const char * error;
 
-    pm_interpret_uint(arg, &width, &error);
+    pm_string_to_uint(arg, &width, &error);
 
     if (error) {
         pm_error("'%s' is invalid as an image width.  %s", arg, error);
@@ -826,7 +873,7 @@ pm_parse_height(const char * const arg) {
     unsigned int height;
     const char * error;
 
-    pm_interpret_uint(arg, &height, &error);
+    pm_string_to_uint(arg, &height, &error);
 
     if (error) {
         pm_error("'%s' is invalid as an image height.  %s", arg, error);
diff --git a/lib/libpnm3.c b/lib/libpnm3.c
index 0426ebcb..3970c734 100644
--- a/lib/libpnm3.c
+++ b/lib/libpnm3.c
@@ -10,6 +10,9 @@
 ** implied warranty.
 */
 
+#include <stdbool.h>
+#include <assert.h>
+
 #include "pnm.h"
 #include "ppm.h"
 #include "pgm.h"
@@ -199,22 +202,22 @@ pnm_blackxel(xelval const maxval,
     default:
         pm_error("Invalid format %d passed to pnm_blackxel()", format);
     }
-    
+
     return retval;
 }
 
 
 
 void
-pnm_invertxel(xel*   const xP, 
-              xelval const maxval, 
+pnm_invertxel(xel*   const xP,
+              xelval const maxval,
               int    const format) {
 
     switch (PNM_FORMAT_TYPE(format)) {
     case PPM_TYPE:
-        PPM_ASSIGN(*xP, 
+        PPM_ASSIGN(*xP,
                    maxval - PPM_GETR(*xP),
-                   maxval - PPM_GETG(*xP), 
+                   maxval - PPM_GETG(*xP),
                    maxval - PPM_GETB(*xP));
         break;
 
@@ -234,145 +237,199 @@ pnm_invertxel(xel*   const xP,
 
 
 void
-pnm_promoteformat( xel** xels, int cols, int rows, xelval maxval, int format, xelval newmaxval, int newformat )
-    {
-    int row;
+pnm_promoteformat(xel ** const xels,
+                  int    const cols,
+                  int    const rows,
+                  xelval const maxval,
+                  int    const format,
+                  xelval const newmaxval,
+                  int    const newformat) {
 
-    for ( row = 0; row < rows; ++row )
-    pnm_promoteformatrow(
-        xels[row], cols, maxval, format, newmaxval, newformat );
-    }
+    unsigned int row;
 
-void
-pnm_promoteformatrow( xel* xelrow, int cols, xelval maxval, int format, xelval newmaxval, int newformat )
-    {
-    register int col;
-    register xel* xP;
-
-    if ( ( PNM_FORMAT_TYPE(format) == PPM_TYPE &&
-       ( PNM_FORMAT_TYPE(newformat) == PGM_TYPE ||
-         PNM_FORMAT_TYPE(newformat) == PBM_TYPE ) ) ||
-     ( PNM_FORMAT_TYPE(format) == PGM_TYPE &&
-       PNM_FORMAT_TYPE(newformat) == PBM_TYPE ) )
-    pm_error( "pnm_promoteformatrow: can't promote downwards!" );
-
-    /* Are we promoting to the same type? */
-    if ( PNM_FORMAT_TYPE(format) == PNM_FORMAT_TYPE(newformat) )
-    {
-    if ( PNM_FORMAT_TYPE(format) == PBM_TYPE )
-        return;
-    if ( newmaxval < maxval )
-        pm_error(
-       "pnm_promoteformatrow: can't decrease maxval - try using pnmdepth" );
-    if ( newmaxval == maxval )
-        return;
-    /* Increase maxval. */
-    switch ( PNM_FORMAT_TYPE(format) )
-        {
-        case PGM_TYPE:
-        for ( col = 0, xP = xelrow; col < cols; ++col, ++xP )
-        PNM_ASSIGN1(
-            *xP, (int) PNM_GET1(*xP) * newmaxval / maxval );
-        break;
+    for (row = 0; row < rows; ++row)
+        pnm_promoteformatrow(
+            xels[row], cols, maxval, format, newmaxval, newformat);
+}
 
-        case PPM_TYPE:
-        for ( col = 0, xP = xelrow; col < cols; ++col, ++xP )
-        PPM_DEPTH( *xP, *xP, maxval, newmaxval );
-        break;
 
-        default:
-        pm_error( "Invalid old format passed to pnm_promoteformatrow()" );
-        }
-    return;
-    }
 
-    /* We must be promoting to a higher type. */
-    switch ( PNM_FORMAT_TYPE(format) )
-    {
-    case PBM_TYPE:
-    switch ( PNM_FORMAT_TYPE(newformat) )
-        {
-        case PGM_TYPE:
-        for ( col = 0, xP = xelrow; col < cols; ++col, ++xP )
-        if ( PNM_GET1(*xP) == 0 )
-            PNM_ASSIGN1( *xP, 0 );
-        else
-            PNM_ASSIGN1( *xP, newmaxval );
-        break;
-
-        case PPM_TYPE:
-        for ( col = 0, xP = xelrow; col < cols; ++col, ++xP )
-        if ( PNM_GET1(*xP) == 0 )
-            PPM_ASSIGN( *xP, 0, 0, 0 );
-        else
-            PPM_ASSIGN( *xP, newmaxval, newmaxval, newmaxval );
-        break;
-
-        default:
-        pm_error( "Invalid new format passed to pnm_promoteformatrow()" );
+void
+pnm_promoteformatrow(xel *  const xelrow,
+                     int    const cols,
+                     xelval const maxval,
+                     int    const format,
+                     xelval const newmaxval,
+                     int    const newformat) {
+
+    if ((PNM_FORMAT_TYPE(format) == PPM_TYPE &&
+         (PNM_FORMAT_TYPE(newformat) == PGM_TYPE ||
+          PNM_FORMAT_TYPE(newformat) == PBM_TYPE)) ||
+        (PNM_FORMAT_TYPE(format) == PGM_TYPE &&
+         PNM_FORMAT_TYPE(newformat) == PBM_TYPE)) {
+
+        pm_error( "pnm_promoteformatrow: can't promote downwards!" );
+    } else if (PNM_FORMAT_TYPE(format) == PNM_FORMAT_TYPE(newformat)) {
+        /* We're promoting to the same type - but not necessarily maxval */
+        if (PNM_FORMAT_TYPE(format) == PBM_TYPE) {
+            /* PBM doesn't have maxval, so this is idempotent */
+        } else if (newmaxval < maxval)
+            pm_error("pnm_promoteformatrow: can't decrease maxval - "
+                     "try using pamdepth");
+        else if (newmaxval == maxval) {
+            /* Same type, same maxval => idempotent function */
+        } else {
+            /* Increase maxval. */
+            switch (PNM_FORMAT_TYPE(format)) {
+            case PGM_TYPE: {
+                unsigned int col;
+                for (col = 0; col < cols; ++col)
+                    PNM_ASSIGN1(xelrow[col],
+                                PNM_GET1(xelrow[col]) * newmaxval / maxval);
+            } break;
+
+            case PPM_TYPE: {
+                unsigned int col;
+                for (col = 0; col < cols; ++col)
+                    PPM_DEPTH(xelrow[col], xelrow[col], maxval, newmaxval);
+            } break;
+
+            default:
+                pm_error("Invalid old format passed to "
+                         "pnm_promoteformatrow()");
+            }
         }
-    break;
+    } else {
+        /* Promote to a higher type. */
+        switch (PNM_FORMAT_TYPE(format)) {
+        case PBM_TYPE:
+            switch (PNM_FORMAT_TYPE(newformat)) {
+            case PGM_TYPE: {
+                unsigned int col;
+                for (col = 0; col < cols; ++col) {
+                    if (PNM_GET1(xelrow[col]) == 0)
+                        PNM_ASSIGN1(xelrow[col], 0);
+                    else
+                        PNM_ASSIGN1(xelrow[col], newmaxval);
+                }
+            } break;
+
+            case PPM_TYPE: {
+                unsigned int col;
+                for (col = 0; col < cols; ++col) {
+                    if (PNM_GET1(xelrow[col]) == 0)
+                        PPM_ASSIGN(xelrow[col], 0, 0, 0);
+                    else
+                        PPM_ASSIGN(xelrow[col],
+                                   newmaxval, newmaxval, newmaxval );
+                }
+            } break;
+
+            default:
+                pm_error("Invalid new format passed to "
+                         "pnm_promoteformatrow()");
+            }
+            break;
 
-    case PGM_TYPE:
-    switch ( PNM_FORMAT_TYPE(newformat) )
-        {
-        case PPM_TYPE:
-        if ( newmaxval < maxval )
-        pm_error(
-       "pnm_promoteformatrow: can't decrease maxval - try using pnmdepth" );
-        if ( newmaxval == maxval )
-        {
-        for ( col = 0, xP = xelrow; col < cols; ++col, ++xP )
-            PPM_ASSIGN(
-            *xP, PNM_GET1(*xP), PNM_GET1(*xP), PNM_GET1(*xP) );
-        }
-        else
-        { /* Increase maxval. */
-        for ( col = 0, xP = xelrow; col < cols; ++col, ++xP )
-            PPM_ASSIGN(
-            *xP, (int) PNM_GET1(*xP) * newmaxval / maxval,
-            (int) PNM_GET1(*xP) * newmaxval / maxval,
-            (int) PNM_GET1(*xP) * newmaxval / maxval );
-        }
-        break;
+        case PGM_TYPE:
+            switch (PNM_FORMAT_TYPE(newformat)) {
+            case PPM_TYPE:
+                if (newmaxval < maxval)
+                    pm_error("pnm_promoteformatrow: can't decrease maxval - "
+                             "try using pamdepth");
+                else if (newmaxval == maxval) {
+                    unsigned int col;
+                    for (col = 0; col < cols; ++col) {
+                        PPM_ASSIGN(xelrow[col],
+                                   PNM_GET1(xelrow[col]),
+                                   PNM_GET1(xelrow[col]),
+                                   PNM_GET1(xelrow[col]));
+                    }
+                } else {
+                    /* Increase maxval. */
+                    unsigned int col;
+                    for (col = 0; col < cols; ++col) {
+                        PPM_ASSIGN(xelrow[col],
+                                   PNM_GET1(xelrow[col]) * newmaxval / maxval,
+                                   PNM_GET1(xelrow[col]) * newmaxval / maxval,
+                                   PNM_GET1(xelrow[col]) * newmaxval / maxval);
+                    }
+                }
+                break;
+
+            default:
+                pm_error("Invalid new format passed to "
+                         "pnm_promoteformatrow()");
+            }
+            break;
 
         default:
-        pm_error( "Invalid new format passed to pnm_promoteformatrow()" );
+            pm_error("Invalid old format passed to pnm_promoteformatrow()");
         }
-    break;
-
-    default:
-        pm_error( "Invalid old format passed to pnm_promoteformatrow()" );
-    }
     }
+}
+
 
 
 pixel
-pnm_xeltopixel(xel const inputxel,
+pnm_xeltopixel(xel const inputXel,
                int const format) {
-    
-    pixel outputpixel;
+
+    pixel outputPixel;
 
     switch (PNM_FORMAT_TYPE(format)) {
     case PPM_TYPE:
-        PPM_ASSIGN(outputpixel,
-                   PPM_GETR(inputxel),
-                   PPM_GETG(inputxel),
-                   PPM_GETB(inputxel));
+        PPM_ASSIGN(outputPixel,
+                   PNM_GETR(inputXel),
+                   PNM_GETG(inputXel),
+                   PNM_GETB(inputXel));
         break;
     case PGM_TYPE:
     case PBM_TYPE:
-        PPM_ASSIGN(outputpixel,
-                   PNM_GET1(inputxel),
-                   PNM_GET1(inputxel),
-                   PNM_GET1(inputxel));
+        PPM_ASSIGN(outputPixel,
+                   PNM_GET1(inputXel),
+                   PNM_GET1(inputXel),
+                   PNM_GET1(inputXel));
         break;
     default:
         pm_error("Invalid format code %d passed to pnm_xeltopixel()",
                  format);
     }
 
-    return outputpixel;
+    return outputPixel;
+}
+
+
+
+xel
+pnm_pixeltoxel(pixel const inputPixel) {
+
+    return inputPixel;
+}
+
+
+
+xel
+pnm_graytoxel(gray const inputGray) {
+
+    xel outputXel;
+
+    PNM_ASSIGN1(outputXel, inputGray);
+
+    return outputXel;
+}
+
+
+xel
+pnm_bittoxel(bit    const inputBit,
+             xelval const maxval) {
+
+    switch (inputBit) {
+    case PBM_BLACK: return pnm_blackxel(maxval, PBM_TYPE); break;
+    case PBM_WHITE: return pnm_whitexel(maxval, PBM_TYPE); break;
+    default:
+        assert(false);
+    }
 }
 
 
@@ -413,6 +470,6 @@ pnm_parsecolorxel(const char * const colorName,
         pm_error("Invalid format code %d passed to pnm_parsecolorxel()",
                  format);
     }
-    
+
     return retval;
 }
diff --git a/lib/libppm1.c b/lib/libppm1.c
index c1eb152c..ccc8adb5 100644
--- a/lib/libppm1.c
+++ b/lib/libppm1.c
@@ -14,7 +14,7 @@
    offset stuff.
 */
 #define _FILE_OFFSET_BITS 64
-#define _LARGE_FILES  
+#define _LARGE_FILES
 
 #include <string.h>
 #include <stdio.h>
@@ -56,7 +56,7 @@ ppm_init(int * const argcP, char ** const argv) {
 
 
 void
-ppm_nextimage(FILE * const fileP, 
+ppm_nextimage(FILE * const fileP,
               int *  const eofP) {
     pm_nextimage(fileP, eofP);
 }
@@ -64,9 +64,9 @@ ppm_nextimage(FILE * const fileP,
 
 
 void
-ppm_readppminitrest(FILE *   const fileP, 
-                    int *    const colsP, 
-                    int *    const rowsP, 
+ppm_readppminitrest(FILE *   const fileP,
+                    int *    const colsP,
+                    int *    const rowsP,
                     pixval * const maxvalP) {
     unsigned int maxval;
 
@@ -79,7 +79,7 @@ ppm_readppminitrest(FILE *   const fileP,
     if (maxval > PPM_OVERALLMAXVAL)
         pm_error("maxval of input image (%u) is too large.  "
                  "The maximum allowed by the PPM format is %u.",
-                 maxval, PPM_OVERALLMAXVAL); 
+                 maxval, PPM_OVERALLMAXVAL);
     if (maxval == 0)
         pm_error("maxval of input image is zero.");
 
@@ -114,10 +114,10 @@ validateComputableSize(unsigned int const cols,
 
 
 void
-ppm_readppminit(FILE *   const fileP, 
-                int *    const colsP, 
-                int *    const rowsP, 
-                pixval * const maxvalP, 
+ppm_readppminit(FILE *   const fileP,
+                int *    const colsP,
+                int *    const rowsP,
+                pixval * const maxvalP,
                 int *    const formatP) {
 
     int realFormat;
@@ -156,10 +156,10 @@ ppm_readppminit(FILE *   const fileP,
 
 
 static void
-readPpmRow(FILE *       const fileP, 
-           pixel *      const pixelrow, 
-           unsigned int const cols, 
-           pixval       const maxval, 
+readPpmRow(FILE *       const fileP,
+           pixel *      const pixelrow,
+           unsigned int const cols,
+           pixval       const maxval,
            int          const format) {
 
     unsigned int col;
@@ -168,7 +168,7 @@ readPpmRow(FILE *       const fileP,
         pixval const r = pm_getuint(fileP);
         pixval const g = pm_getuint(fileP);
         pixval const b = pm_getuint(fileP);
-        
+
         if (r > maxval)
             pm_error("Red sample value %u is greater than maxval (%u)",
                      r, maxval);
@@ -178,7 +178,7 @@ readPpmRow(FILE *       const fileP,
         if (b > maxval)
             pm_error("Blue sample value %u is greater than maxval (%u)",
                      b, maxval);
-        
+
         PPM_ASSIGN(pixelrow[col], r, g, b);
     }
 }
@@ -194,7 +194,7 @@ interpRasterRowRaw(const unsigned char * const rowBuffer,
     unsigned int bufferCursor;
 
     bufferCursor = 0;  /* start at beginning of rowBuffer[] */
-        
+
     if (bytesPerSample == 1) {
         unsigned int col;
         for (col = 0; col < cols; ++col) {
@@ -208,16 +208,16 @@ interpRasterRowRaw(const unsigned char * const rowBuffer,
         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);
         }
     }
@@ -231,7 +231,7 @@ validateRppmRow(pixel *       const pixelrow,
                 pixval        const maxval,
                 const char ** const errorP) {
 /*----------------------------------------------------------------------------
-  Check for sample values above maxval in input.  
+  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
@@ -272,20 +272,20 @@ validateRppmRow(pixel *       const pixelrow,
 
 
 static void
-readRppmRow(FILE *       const fileP, 
-            pixel *      const pixelrow, 
-            unsigned int const cols, 
-            pixval       const maxval, 
+readRppmRow(FILE *       const fileP,
+            pixel *      const pixelrow,
+            unsigned int const cols,
+            pixval       const maxval,
             int          const format) {
 
     unsigned int const bytesPerSample = maxval < 256 ? 1 : 2;
     unsigned int const bytesPerRow    = cols * 3 * bytesPerSample;
-        
+
     unsigned char * rowBuffer;
     const char * error;
-    
+
     MALLOCARRAY(rowBuffer, bytesPerRow);
-        
+
     if (rowBuffer == NULL)
         pm_asprintf(&error, "Unable to allocate memory for row buffer "
                     "for %u columns", cols);
@@ -293,7 +293,7 @@ readRppmRow(FILE *       const fileP,
         ssize_t rc;
 
         rc = fread(rowBuffer, 1, bytesPerRow, fileP);
-    
+
         if (feof(fileP))
             pm_asprintf(&error, "Unexpected EOF reading row of PPM image.");
         else if (ferror(fileP))
@@ -323,10 +323,10 @@ readRppmRow(FILE *       const fileP,
 
 
 static void
-readPgmRow(FILE *       const fileP, 
-           pixel *      const pixelrow, 
-           unsigned int const cols, 
-           pixval       const maxval, 
+readPgmRow(FILE *       const fileP,
+           pixel *      const pixelrow,
+           unsigned int const cols,
+           pixval       const maxval,
            int          const format) {
 
     jmp_buf jmpbuf;
@@ -341,7 +341,7 @@ readPgmRow(FILE *       const fileP,
         pm_longjmp();
     } else {
         unsigned int col;
-    
+
         pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
 
         pgm_readpgmrow(fileP, grayrow, cols, maxval, format);
@@ -358,10 +358,10 @@ readPgmRow(FILE *       const fileP,
 
 
 static void
-readPbmRow(FILE *       const fileP, 
-           pixel *      const pixelrow, 
-           unsigned int const cols, 
-           pixval       const maxval, 
+readPbmRow(FILE *       const fileP,
+           pixel *      const pixelrow,
+           unsigned int const cols,
+           pixval       const maxval,
            int          const format) {
 
     jmp_buf jmpbuf;
@@ -395,10 +395,10 @@ readPbmRow(FILE *       const fileP,
 
 
 void
-ppm_readppmrow(FILE *  const fileP, 
-               pixel * const pixelrow, 
-               int     const cols, 
-               pixval  const maxval, 
+ppm_readppmrow(FILE *  const fileP,
+               pixel * const pixelrow,
+               int     const cols,
+               pixval  const maxval,
                int     const format) {
 
     switch (format) {
@@ -432,9 +432,9 @@ ppm_readppmrow(FILE *  const fileP,
 
 
 pixel**
-ppm_readppm(FILE *   const fileP, 
-            int *    const colsP, 
-            int *    const rowsP, 
+ppm_readppm(FILE *   const fileP,
+            int *    const colsP,
+            int *    const rowsP,
             pixval * const maxvalP) {
 
     jmp_buf jmpbuf;
@@ -472,11 +472,11 @@ ppm_readppm(FILE *   const fileP,
 
 
 void
-ppm_check(FILE *               const fileP, 
-          enum pm_check_type   const checkType, 
-          int                  const format, 
-          int                  const cols, 
-          int                  const rows, 
+ppm_check(FILE *               const fileP,
+          enum pm_check_type   const checkType,
+          int                  const format,
+          int                  const cols,
+          int                  const rows,
           pixval               const maxval,
           enum pm_check_code * const retvalP) {
 
@@ -484,7 +484,7 @@ ppm_check(FILE *               const fileP,
         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 (checkType != PM_CHECK_BASIC) {
         if (retvalP)
             *retvalP = PM_CHECK_UNKNOWN_TYPE;
@@ -495,10 +495,10 @@ ppm_check(FILE *               const fileP,
     } else if (format != RPPM_FORMAT) {
         if (retvalP)
             *retvalP = PM_CHECK_UNCHECKABLE;
-    } else {        
+    } else {
         pm_filepos const bytesPerRow    = cols * 3 * (maxval > 255 ? 2 : 1);
         pm_filepos const needRasterSize = rows * bytesPerRow;
-        
+
         pm_check(fileP, checkType, needRasterSize, retvalP);
     }
 }
diff --git a/lib/libppmcolor.c b/lib/libppmcolor.c
index aee8fd83..c0a88dc2 100644
--- a/lib/libppmcolor.c
+++ b/lib/libppmcolor.c
@@ -9,9 +9,6 @@
 ** implied warranty.
 */
 
-#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 <stdlib.h>
 #include <string.h>
@@ -22,376 +19,31 @@
 #include "netpbm/nstring.h"
 #include "ppm.h"
 #include "colorname.h"
+#include "pam.h"
 
 
-static void
-computeHexTable(int hexit[]) {
-
-    unsigned int i;
-
-    for ( i = 0; i < 256; ++i )
-        hexit[i] = -1;
-
-    hexit['0'] = 0;
-    hexit['1'] = 1;
-    hexit['2'] = 2;
-    hexit['3'] = 3;
-    hexit['4'] = 4;
-    hexit['5'] = 5;
-    hexit['6'] = 6;
-    hexit['7'] = 7;
-    hexit['8'] = 8;
-    hexit['9'] = 9;
-    hexit['a'] = hexit['A'] = 10;
-    hexit['b'] = hexit['B'] = 11;
-    hexit['c'] = hexit['C'] = 12;
-    hexit['d'] = hexit['D'] = 13;
-    hexit['e'] = hexit['E'] = 14;
-    hexit['f'] = hexit['F'] = 15;
-}
-
+pixel
+ppm_parsecolor2(const char * const colorname,
+                pixval       const maxval,
+                int          const closeOk) {
 
+    tuple const color = pnm_parsecolor2(colorname, maxval, closeOk);
 
-static long
-invRgbnorm(pixval       const rgb,
-           pixval       const maxval,
-           unsigned int const hexDigits) {
-/*----------------------------------------------------------------------------
-  This is the inverse of 'rgbnorm', below.
------------------------------------------------------------------------------*/
-    long retval;
-
-    switch (hexDigits) {
-    case 1:
-        retval = (long)((double) rgb * 15 / maxval + 0.5);
-        break;
-    case 2:
-        retval = (long) ((double) rgb * 255 / maxval + 0.5);
-        break;
-    case 3:
-        retval = (long) ((double) rgb * 4095 / maxval + 0.5);
-        break;
-    case 4:
-        retval = (long) ((double) rgb * 65535UL / maxval + 0.5);
-        break;
-    default:
-        pm_message("Internal error in invRgbnorm()");
-        abort();
-    }
-    return retval;
-}
-
+    pixel retval;
 
+    PPM_PUTR(retval, color[PAM_RED_PLANE]);
+    PPM_PUTG(retval, color[PAM_GRN_PLANE]);
+    PPM_PUTB(retval, color[PAM_BLU_PLANE]);
 
-static pixval
-rgbnorm(long         const rgb, 
-        pixval       const maxval, 
-        unsigned int const hexDigitCount, 
-        bool         const closeOk,
-        const char * const colorname) {
-/*----------------------------------------------------------------------------
-   Normalize the color (r, g, or b) value 'rgb', which was specified
-   with 'hexDigitCount' digits, to a maxval of 'maxval'.  If the
-   number of digits isn't valid, issue an error message and identify
-   the complete color color specification in error as 'colorname'.
-
-   For example, if the user says "0ff" and the maxval is 100,
-   then rgb is 0xff, n is 3, and our result is 
-   0xff / (16**3-1) * 100 = 6.
------------------------------------------------------------------------------*/
-    pixval retval;
-
-    switch (hexDigitCount) {
-    case 0:
-        pm_error("A hexadecimal color specifier in color '%s' is "
-                 "an empty string", colorname);
-        break;
-    case 1:
-        retval = (pixval)((double) rgb * maxval / 15 + 0.5);
-        break;
-    case 2:
-        retval = (pixval) ((double) rgb * maxval / 255 + 0.5);
-        break;
-    case 3:
-        retval = (pixval) ((double) rgb * maxval / 4095 + 0.5);
-        break;
-    case 4:
-        retval = (pixval) ((double) rgb * maxval / 65535L + 0.5);
-        break;
-    default:
-        pm_error("color specifier '%s' has too many digits", colorname);
-    }
+    free(color);
 
-    if (!closeOk) {
-        long const newrgb = invRgbnorm(retval, maxval, hexDigitCount);
-        if (newrgb != rgb)
-            pm_message("WARNING: Component 0x%lx of color '%s' "
-                       "cannot be represented precisely with maxval %u.  "
-                       "Approximating as %u.",
-                       rgb, colorname, maxval, retval);
-    }
     return retval;
 }
 
 
 
-static void
-parseHexDigits(const char *   const string,
-               char           const delim,
-               int            const hexit[],
-               pixval *       const nP,
-               unsigned int * const digitCountP) {
-
-    unsigned int digitCount;
-    pixval n;
-    
-    digitCount = 0;  /* initial value */
-    n = 0;           /* initial value */
-    while (string[digitCount] != delim) {
-        char const digit = string[digitCount];
-        if (digit == '\0')
-            pm_error("rgb: color spec ends prematurely");
-        else {
-            int const hexval = hexit[(unsigned int)digit];
-            if (hexval == -1)
-                pm_error("Invalid hex digit in rgb: color spec: 0x%02x",
-                         digit);
-            n = n * 16 + hexval;
-            ++digitCount;
-        }
-    }
-    *nP = n;
-    *digitCountP = digitCount;
-}
-
-
-
-static void
-parseNewHexX11(char       const colorname[], 
-               pixval     const maxval,
-               bool       const closeOk,
-               pixel *    const colorP) {
-/*----------------------------------------------------------------------------
-   Determine what color colorname[] specifies in the new style hex
-   color specification format (e.g. rgb:55/40/55).
-
-   Return that color as *colorP.
-
-   Assume colorname[] starts with "rgb:", but otherwise it might be
-   gibberish.
------------------------------------------------------------------------------*/
-    int hexit[256];
-
-    const char * cp;
-    pixval n;
-    unsigned int digitCount;
-    pixval rNorm, gNorm, bNorm;
-
-    computeHexTable(hexit);
-
-    cp = &colorname[4];
-
-    parseHexDigits(cp, '/', hexit, &n, &digitCount);
-
-    rNorm = rgbnorm(n, maxval, digitCount, closeOk, colorname);
-
-    cp += digitCount;
-    ++cp;  /* Skip the slash */
-
-    parseHexDigits(cp, '/', hexit, &n, &digitCount);
-
-    gNorm = rgbnorm(n, maxval, digitCount, closeOk, colorname);
-
-    cp += digitCount;
-    ++cp;  /* Skip the slash */
-
-    parseHexDigits(cp, '\0', hexit, &n, &digitCount);
-
-    bNorm = rgbnorm(n, maxval, digitCount, closeOk, colorname);
-
-    PPM_ASSIGN(*colorP, rNorm, gNorm, bNorm);
-}
-
-
-
-static void
-parseNewDecX11(char       const colorname[], 
-               pixval     const maxval,
-               bool       const closeOk,
-               pixel *    const colorP) {
-
-    float const epsilon = 1.0/65536.0;
-    float fr, fg, fb;
-    pixval rNorm, gNorm, bNorm;
-
-    if (sscanf( colorname, "rgbi:%f/%f/%f", &fr, &fg, &fb) != 3)
-        pm_error("invalid color specifier '%s'", colorname);
-    if (fr < 0.0 || fr > 1.0 || fg < 0.0 || fg > 1.0 
-        || fb < 0.0 || fb > 1.0)
-        pm_error("invalid color specifier '%s' - "
-                 "values must be between 0.0 and 1.0", colorname );
-
-    rNorm = fr * maxval + 0.5;
-    gNorm = fg * maxval + 0.5;
-    bNorm = fb * maxval + 0.5;
-
-    if (!closeOk) {
-        if (fabs((double)rNorm/maxval - fr) > epsilon ||
-            fabs((double)gNorm/maxval - fg) > epsilon ||
-            fabs((double)bNorm/maxval - fb) > epsilon)
-            pm_message("WARNING: Color '%s' cannot be represented "
-                       "precisely with maxval %u.  "
-                       "Approximating as (%u,%u,%u).",
-                       colorname, maxval, rNorm, gNorm, bNorm);
-    }
-    PPM_ASSIGN(*colorP, rNorm, gNorm, bNorm);
-}
-
-
-
-static void
-parseOldX11(char       const colorname[], 
-            pixval     const maxval,
-            bool       const closeOk,
-            pixel *    const colorP) {
-/*----------------------------------------------------------------------------
-   Return as *colorP the color specified by the old X11 style color
-   specififier colorname[] (e.g. #554055).
------------------------------------------------------------------------------*/
-    int hexit[256];
-    long r,g,b;
-    pixval rNorm, gNorm, bNorm;
-    
-    computeHexTable(hexit);
-
-    if (!pm_strishex(&colorname[1]))
-        pm_error("Non-hexadecimal characters in #-type color specification");
-
-    switch (strlen(colorname) - 1 /* (Number of hex digits) */) {
-    case 3:
-        r = hexit[(int)colorname[1]];
-        g = hexit[(int)colorname[2]];
-        b = hexit[(int)colorname[3]];
-        rNorm = rgbnorm(r, maxval, 1, closeOk, colorname);
-        gNorm = rgbnorm(g, maxval, 1, closeOk, colorname);
-        bNorm = rgbnorm(b, maxval, 1, closeOk, colorname);
-        break;
-
-    case 6:
-        r = (hexit[(int)colorname[1]] << 4 ) + hexit[(int)colorname[2]];
-        g = (hexit[(int)colorname[3]] << 4 ) + hexit[(int)colorname[4]];
-        b = (hexit[(int)colorname[5]] << 4 ) + hexit[(int)colorname[6]];
-        rNorm = rgbnorm(r, maxval, 2, closeOk, colorname);
-        gNorm = rgbnorm(g, maxval, 2, closeOk, colorname);
-        bNorm = rgbnorm(b, maxval, 2, closeOk, colorname);
-        break;
-
-    case 9:
-        r = (hexit[(int)colorname[1]] << 8) +
-            (hexit[(int)colorname[2]] << 4) +
-            (hexit[(int)colorname[3]] << 0);
-        g = (hexit[(int)colorname[4]] << 8) + 
-            (hexit[(int)colorname[5]] << 4) +
-            (hexit[(int)colorname[6]] << 0);
-        b = (hexit[(int)colorname[7]] << 8) + 
-            (hexit[(int)colorname[8]] << 4) +
-            (hexit[(int)colorname[9]] << 0);
-        rNorm = rgbnorm(r, maxval, 3, closeOk, colorname);
-        gNorm = rgbnorm(g, maxval, 3, closeOk, colorname);
-        bNorm = rgbnorm(b, maxval, 3, closeOk, colorname);
-        break;
-
-    case 12:
-        r = (hexit[(int)colorname[1]] << 12) + 
-            (hexit[(int)colorname[2]] <<  8) +
-            (hexit[(int)colorname[3]] <<  4) + hexit[(int)colorname[4]];
-        g = (hexit[(int)colorname[5]] << 12) + 
-            (hexit[(int)colorname[6]] <<  8) +
-            (hexit[(int)colorname[7]] <<  4) + hexit[(int)colorname[8]];
-        b = (hexit[(int)colorname[9]] << 12) + 
-            (hexit[(int)colorname[10]] << 8) +
-            (hexit[(int)colorname[11]] << 4) + hexit[(int)colorname[12]];
-        rNorm = rgbnorm(r, maxval, 4, closeOk, colorname);
-        gNorm = rgbnorm(g, maxval, 4, closeOk, colorname);
-        bNorm = rgbnorm(b, maxval, 4, closeOk, colorname);
-        break;
-
-    default:
-        pm_error("invalid color specifier '%s'", colorname);
-    }
-    PPM_ASSIGN(*colorP, rNorm, gNorm, bNorm);
-}
-
-
-
-
-static void
-parseOldX11Dec(const char       colorname[], 
-               pixval     const maxval,
-               bool       const closeOk,
-               pixel *    const colorP) {
-
-    float const epsilon = 1.0/65536.0;
-
-    float fr, fg, fb;
-    pixval rNorm, gNorm, bNorm;
-
-    if (sscanf(colorname, "%f,%f,%f", &fr, &fg, &fb) != 3)
-        pm_error("invalid color specifier '%s'", colorname);
-    if (fr < 0.0 || fr > 1.0 || fg < 0.0 || fg > 1.0 
-        || fb < 0.0 || fb > 1.0)
-        pm_error("invalid color specifier '%s' - "
-                 "values must be between 0.0 and 1.0", colorname );
-
-    rNorm = fr * maxval + 0.5;
-    gNorm = fg * maxval + 0.5;
-    bNorm = fb * maxval + 0.5;
-
-    if (!closeOk) {
-        if (fabs((float)rNorm/maxval - fr) > epsilon ||
-            fabs((float)gNorm/maxval - fg) > epsilon ||
-            fabs((float)bNorm/maxval - fb) > epsilon)
-            pm_message("WARNING: Color '%s' cannot be represented "
-                       "precisely with maxval %u.  "
-                       "Approximating as (%u,%u,%u).",
-                       colorname, maxval, rNorm, gNorm, bNorm);
-    }
-    PPM_ASSIGN(*colorP, rNorm, gNorm, bNorm);
-}
-
-
-
-pixel
-ppm_parsecolor2(const char * const colorname,
-                pixval       const maxval,
-                int          const closeOk) {
-
-    pixel color;
-    
-    if (strncmp(colorname, "rgb:", 4) == 0)
-        /* It's a new-X11-style hexadecimal rgb specifier. */
-        parseNewHexX11(colorname, maxval, closeOk, &color);
-    else if (strncmp(colorname, "rgbi:", 5) == 0)
-        /* It's a new-X11-style decimal/float rgb specifier. */
-        parseNewDecX11(colorname, maxval, closeOk, &color);
-    else if (colorname[0] == '#')
-        /* It's an old-X11-style hexadecimal rgb specifier. */
-        parseOldX11(colorname, maxval, closeOk, &color);
-    else if ((colorname[0] >= '0' && colorname[0] <= '9') ||
-             colorname[0] == '.')
-        /* It's an old-style decimal/float rgb specifier. */
-        parseOldX11Dec(colorname, maxval, closeOk, &color);
-    else 
-        /* Must be a name from the X-style rgb file. */
-        pm_parse_dictionary_name(colorname, maxval, closeOk, &color);
-    
-    return color;
-}
-
-
-
 pixel
-ppm_parsecolor(const char * const colorname, 
+ppm_parsecolor(const char * const colorname,
                pixval       const maxval) {
 
     return ppm_parsecolor2(colorname, maxval, TRUE);
@@ -400,13 +52,14 @@ ppm_parsecolor(const char * const colorname,
 
 
 char *
-ppm_colorname(const pixel * const colorP, 
-              pixval        const maxval, 
+ppm_colorname(const pixel * const colorP,
+              pixval        const maxval,
               int           const hexok)   {
 
     int r, g, b;
     FILE * f;
     static char colorname[200];
+        /* Null string means no suitable name so far */
 
     if (maxval == 255) {
         r = PPM_GETR(*colorP);
@@ -419,33 +72,57 @@ ppm_colorname(const pixel * const colorP,
     }
 
     f = pm_openColornameFile(NULL, !hexok);
-    if (f != NULL) {
-        int best_diff, this_diff;
+
+    if (!f)
+        STRSCPY(colorname, "");
+    else {
+        int bestDiff;
         bool eof;
 
-        best_diff = 32767;
-        eof = FALSE;
-        while (!eof && best_diff > 0 ) {
+        for (bestDiff = 32767, eof = FALSE;
+             !eof && bestDiff > 0; ) {
             struct colorfile_entry const ce = pm_colorget(f);
             if (ce.colorname)  {
-                this_diff = abs(r - ce.r) + abs(g - ce.g) + abs(b - ce.b);
-                if (this_diff < best_diff) {
-                    best_diff = this_diff;
-                    strcpy(colorname, ce.colorname);
+                int const thisDiff =
+                    abs(r - (int)ce.r) +
+                    abs(g - (int)ce.g) +
+                    abs(b - (int)ce.b);
+
+                if (thisDiff < bestDiff) {
+                    bestDiff = thisDiff;
+                    STRSCPY(colorname, ce.colorname);
                 }
             } else
                 eof = TRUE;
         }
         fclose(f);
-        if (best_diff != 32767 && (best_diff == 0 || ! hexok))
-            return colorname;
+
+        if (bestDiff == 32767) {
+            /* Color file contain no entries, so we can't even pick a close
+               one
+            */
+            STRSCPY(colorname, "");
+        } else if (bestDiff > 0 && hexok) {
+            /* We didn't find an exact match and user is willing to accept
+               hex, so we don't have to use an approximate match.
+            */
+            STRSCPY(colorname, "");
+        }
     }
 
-    /* Color lookup failed, but caller is willing to take an X11-style
-       hex specifier, so return that.
-    */
-    sprintf(colorname, "#%02x%02x%02x", r, g, b);
-    return colorname;}
+    if (streq(colorname, "")) {
+        if (hexok) {
+            /* Color lookup failed, but caller is willing to take an X11-style
+               hex specifier, so return that.
+            */
+            sprintf(colorname, "#%02x%02x%02x", r, g, b);
+        } else {
+            pm_error("Couldn't find any name colors at all");
+        }
+    }
+
+    return colorname;
+}
 
 
 
@@ -508,14 +185,14 @@ processColorfileEntry(struct colorfile_entry const ce,
             /* The color is already in the hash, which means we saw it
                earlier in the file.  We prefer the first name that the
                file gives for each color, so we just ignore the
-               current entry.  
+               current entry.
             */
             *errorP = NULL;
         } else {
             ppm_addtocolorhash(cht, &color, *colornameIndexP);
-            colornames[*colornameIndexP] = strdup(ce.colorname);
+            colornames[*colornameIndexP] = pm_strdup(ce.colorname);
             colors[*colornameIndexP] = color;
-            if (colornames[*colornameIndexP] == NULL)
+            if (colornames[*colornameIndexP] == pm_strsol)
                 pm_asprintf(errorP, "Unable to allocate space for color name");
             else {
                 *errorP = NULL;
@@ -538,15 +215,14 @@ openColornameFile(const char *  const fileName,
 
     if (setjmp(jmpbuf) != 0) {
         pm_asprintf(errorP, "Failed to open color name file");
-        pm_setjmpbuf(origJmpbufP);
-        pm_longjmp();
     } else {
+        pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
+
         *filePP = pm_openColornameFile(fileName, mustOpen);
 
         *errorP = NULL;  /* Would have longjmped if there were a problem */
-
-        pm_setjmpbuf(origJmpbufP);
     }
+    pm_setjmpbuf(origJmpbufP);
 }
 
 
@@ -576,15 +252,15 @@ readOpenColorFile(FILE *          const colorFileP,
 
     while (!done && !*errorP) {
         struct colorfile_entry const ce = pm_colorget(colorFileP);
-        
-        if (!ce.colorname)  
+
+        if (!ce.colorname)
             done = TRUE;
-        else 
+        else
             processColorfileEntry(ce, cht, colornames, colors,
                                   &nColorsDone, errorP);
     }
     *nColorsP = nColorsDone;
-    
+
     if (*errorP) {
         unsigned int colorIndex;
 
@@ -630,13 +306,13 @@ readColorFile(const char *    const fileName,
         } else {
             readOpenColorFile(colorFileP, nColorsP, colornames, colors, cht,
                               errorP);
-            
+
             fclose(colorFileP);
         }
     }
 }
 
-    
+
 
 static void
 readcolordict(const char *      const fileName,
@@ -657,14 +333,14 @@ readcolordict(const char *      const fileName,
         pixel * colors;
 
         MALLOCARRAY(colors, MAXCOLORNAMES);
-        
+
         if (colors == NULL)
             pm_asprintf(errorP, "Unable to allocate space for color table.");
         else {
             colorhash_table cht;
 
             cht = allocColorHash();
-            
+
             if (cht == NULL)
                 pm_asprintf(errorP, "Unable to allocate space for color hash");
             else {
@@ -732,7 +408,7 @@ ppm_readcolordict(const char *      const fileName,
     if (error) {
         pm_errormsg("%s", error);
         pm_strfree(error);
-        ppm_freecolorhash(cht);
+        pm_longjmp();
     } else {
         if (chtP)
             *chtP = cht;
@@ -754,9 +430,9 @@ ppm_readcolordict(const char *      const fileName,
 
 
 void
-ppm_readcolornamefile(const char *      const fileName, 
+ppm_readcolornamefile(const char *      const fileName,
                       int               const mustOpen,
-                      colorhash_table * const chtP, 
+                      colorhash_table * const chtP,
                       const char ***    const colornamesP) {
 
     ppm_readcolordict(fileName, mustOpen, NULL, colornamesP, NULL, chtP);
@@ -778,7 +454,7 @@ ppm_freecolornames(const char ** const colornames) {
 
 
 
-static unsigned int 
+static unsigned int
 nonnegative(unsigned int const arg) {
 
     if ((int)(arg) < 0)
@@ -790,8 +466,8 @@ nonnegative(unsigned int const arg) {
 
 
 pixel
-ppm_color_from_ycbcr(unsigned int const y, 
-                     int          const cb, 
+ppm_color_from_ycbcr(unsigned int const y,
+                     int          const cb,
                      int          const cr) {
 /*----------------------------------------------------------------------------
    Return the color that has luminance 'y', blue chrominance 'cb', and
@@ -805,12 +481,12 @@ ppm_color_from_ycbcr(unsigned int const y,
 -----------------------------------------------------------------------------*/
     pixel retval;
 
-    PPM_ASSIGN(retval, 
+    PPM_ASSIGN(retval,
                y + 1.4022 * cr,
                nonnegative(y - 0.7145 * cr - 0.3456 * cb),
                y + 1.7710 * cb
         );
-    
+
     return retval;
 }
 
@@ -876,10 +552,10 @@ ppm_color_from_hsv(struct hsv const hsv,
             pm_error("Invalid H value passed to color_from_HSV: %f", hsv.h);
         }
     }
-    PPM_ASSIGN(retval, 
-               ROUNDU(R * maxval),
-               ROUNDU(G * maxval),
-               ROUNDU(B * maxval));
+    PPM_ASSIGN(retval,
+               ppm_unnormalize(R, maxval),
+               ppm_unnormalize(G, maxval),
+               ppm_unnormalize(B, maxval));
 
     return retval;
 }
diff --git a/lib/libppmd.c b/lib/libppmd.c
index 262679ec..a94ff107 100644
--- a/lib/libppmd.c
+++ b/lib/libppmd.c
@@ -850,7 +850,7 @@ ppmd_spline4p(pixel **       const pixels,
 /*----------------------------------------------------------------------------
    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
+   'endPt0' and 'ctlPt0' is tangent to the curve at 'endPt0' and the
    length of that line controls "enthusiasm," whatever that is.
    Same for 'endPt1' and 'ctlPt1'.
 -----------------------------------------------------------------------------*/
@@ -981,7 +981,7 @@ 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
+       have drawing not modify the fillobj, i.e. it passes
        const fillobj * to the drawing program.
     */
     struct fillState * stateP;
@@ -1116,14 +1116,13 @@ continueSegment(struct fillState * const stateP,
 
 
 
-
 /* 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.
+   But ppmd_fill_drawprocp() 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.
diff --git a/lib/libppmfuzzy.c b/lib/libppmfuzzy.c
index f1573c68..902d684d 100644
--- a/lib/libppmfuzzy.c
+++ b/lib/libppmfuzzy.c
@@ -338,7 +338,7 @@ matchBk(pixel     const color,
              );
 
     (*bkMatchP)[BKCOLOR_BROWN]  =
-	fzOr(
+        fzOr(
              fzAnd(fzOr(hueAround015, hueAround360),
                    fzAnd(fzNot(satVeryLow), fzOr(valLow, valMedium))),
              fzAnd(hueAround015, satLow)
diff --git a/lib/libsystem.c b/lib/libsystem.c
index fd3c52ec..bf2416a4 100644
--- a/lib/libsystem.c
+++ b/lib/libsystem.c
@@ -1,18 +1,23 @@
 /*=============================================================================
                                  pm_system
 ===============================================================================
-   This is the library subroutine pm_system().  It is just like Standard C
-   Library system(), except that you can supply routines for it to run to
-   generate the Standard Input for the executed shell command and to accept
-   the Standard Output from it.  system(), by contrast, always sets up the
-   current Standard Input and Standard Output as the Standard Input and
-   Standard Output of the shell command.
+   This is the pm_system() family of subroutines.
+
+   pm_system() is just like Standard C Library system(), except that you can
+   supply routines for it to run to generate the Standard Input for the
+   executed shell command and to accept the Standard Output from it.
+   system(), by contrast, always sets up the current Standard Input and
+   Standard Output as the Standard Input and Standard Output of the shell
+   command.
+
+   pm_system_lp() and pm_system_vp() are similar, but exec an OS-level program
+   (i.e. exec a program) rather than run a shell command.
 
    By Bryan Henderson, San Jose CA  2002.12.14.
 
    Contributed to the public domain.
 =============================================================================*/
-#define _XOPEN_SOURCE
+#define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE  /* Make SIGWINCH defined on OpenBSD */
 
 #include <stdarg.h>
@@ -26,6 +31,7 @@
 
 #include "netpbm/pm_c_util.h"
 #include "netpbm/mallocvar.h"
+#include "netpbm/nstring.h"
 #include "pm.h"
 #include "pm_system.h"
 
@@ -164,7 +170,7 @@ spawnProcessor(const char *  const progName,
 /*----------------------------------------------------------------------------
    Create a process to run program 'progName' with arguments
    argArray[] (terminated by NULL element).  Pass file descriptor
-   'stdinFd' to the shell as Standard Input.
+   'stdinFd' to the process as Standard Input.
 
    if 'stdoutFdP' is NULL, have that process write its Standard Output to
    the current process' Standard Output.
@@ -313,26 +319,44 @@ signalName(unsigned int const signalClass) {
 
 
 
-static void
-cleanupProcessorProcess(pid_t const processorPid) {
+const char *
+pm_termStatusDesc(int const termStatusArg) {
+/*----------------------------------------------------------------------------
+   English description of  process termination status 'termStatus'.
+-----------------------------------------------------------------------------*/
+    const char * retval;
+
+    /* WIFEXITED, etc. do not work with a constant argument in older GNU C
+       library.  Compilation fails with "attempt to assign read-only
+       location".  This is because The GNU C library has some magic to allow
+       for a BSD 'union wait' (instead of int) argument to WIFEXITED.  The
+       magic involves defining a variable with 'typeof' the argument and
+       assigning to that variable.
+       
+       To work around this, we make sure the argument is not constant.
+    */
 
-    int terminationStatus;
-    waitpid(processorPid, &terminationStatus, 0);
+    int termStatus = termStatusArg;
 
-    if (WIFEXITED(terminationStatus)) {
-        int const exitStatus = WEXITSTATUS(terminationStatus);
+    if (WIFEXITED(termStatus)) {
+        int const exitStatus = WEXITSTATUS(termStatus);
 
-        if (exitStatus != 0)
-            pm_message("Shell process exited with abnormal exit status %u.  ",
-                       exitStatus);
-    } else if (WIFSIGNALED(terminationStatus)) {
-        pm_message("Shell process was killed by a Class %u (%s) signal.",
-                   WTERMSIG(terminationStatus),
-                   signalName(WTERMSIG(terminationStatus)));
+        if (exitStatus == 0)
+            pm_asprintf(&retval, "Process exited normally");
+        else
+            pm_asprintf(&retval,
+                        "Process exited with abnormal exit status %u.  ",
+                        exitStatus);
+    } else if (WIFSIGNALED(termStatus)) {
+        pm_asprintf(&retval, "Process was killed by a Class %u (%s) signal.",
+                    WTERMSIG(termStatus),
+                    signalName(WTERMSIG(termStatus)));
     } else {
-        pm_message("Shell process died, but its termination status "
-                   "0x%x  doesn't make sense", terminationStatus);
+        pm_asprintf(&retval, "Process died, but its termination status "
+                    "0x%x  doesn't make sense", termStatus);
     }
+
+    return retval;
 }
 
 
@@ -347,7 +371,7 @@ cleanupFeederProcess(pid_t const feederPid) {
         if (WTERMSIG(status) == SIGPIPE)
             pm_message("WARNING: "
                        "Standard Input feeder process was terminated by a "
-                       "SIGPIPE signal because the shell command closed its "
+                       "SIGPIPE signal because the program closed its "
                        "Standard Input before the Standard Input feeder was "
                        "through feeding it.");
         else
@@ -369,12 +393,13 @@ cleanupFeederProcess(pid_t const feederPid) {
 
 
 void
-pm_system_vp(const char *    const progName,
-             const char **   const argArray,
-             void stdinFeeder(int, void *),
-             void *          const feederParm,
-             void stdoutAccepter(int, void *),
-             void *          const accepterParm) {
+pm_system2_vp(const char *    const progName,
+              const char **   const argArray,
+              void stdinFeeder(int, void *),
+              void *          const feederParm,
+              void stdoutAccepter(int, void *),
+              void *          const accepterParm,
+              int *           const termStatusP) {
 /*----------------------------------------------------------------------------
    Run a program in a child process.  Feed its Standard Input with a
    pipe, which is fed by the routine 'stdinFeeder' with parameter
@@ -387,6 +412,9 @@ pm_system_vp(const char *    const progName,
 
    Run the program 'progName' with arguments argArray[] (terminated by NULL
    element).  That includes arg0.
+
+   Return as *termStatusP the termination status of the processor process
+   (the one running the program named 'progName').
 -----------------------------------------------------------------------------*/
     /* If 'stdinFeeder' is non-NULL, we create a child process to run
        'stdinFeeder' and create a pipe from that process as the
@@ -416,6 +444,7 @@ pm_system_vp(const char *    const progName,
         */
     pid_t feederPid;
     pid_t processorPid;
+    int termStatus;
 
     if (stdinFeeder) {
         createPipeFeeder(stdinFeeder, feederParm, &progStdinFd, &feederPid);
@@ -452,10 +481,118 @@ pm_system_vp(const char *    const progName,
         close(progStdinFd);
     }
 
-    cleanupProcessorProcess(processorPid);
+    waitpid(processorPid, &termStatus, 0);
 
     if (feederPid) 
         cleanupFeederProcess(feederPid);
+
+    *termStatusP = termStatus;
+}
+
+
+
+void
+pm_system2_lp(const char *    const progName,
+              void stdinFeeder(int, void *),
+              void *          const feederParm,
+              void stdoutAccepter(int, void *),
+              void *          const accepterParm,
+              int *           const termStatusP,
+              ...) {
+/*----------------------------------------------------------------------------
+  Same as pm_system_vp() except with arguments as variable arguments
+  instead of an array.
+
+  N.B. the first variable argument is the program's arg 0; the last
+  variable argument must be NULL.
+-----------------------------------------------------------------------------*/
+    va_list args;
+    bool endOfArgs;
+    const char ** argArray;
+    unsigned int n;
+
+    va_start(args, termStatusP);
+
+    endOfArgs = FALSE;
+    argArray = NULL;
+
+    for (endOfArgs = FALSE, argArray = NULL, n = 0;
+         !endOfArgs;
+        ) {
+        const char * const arg = va_arg(args, const char *);
+        
+        REALLOCARRAY(argArray, n+1);
+
+        argArray[n++] = arg;
+
+        if (!arg)
+            endOfArgs = TRUE;
+    }
+
+    va_end(args);
+
+    pm_system2_vp(progName, argArray,
+                  stdinFeeder, feederParm, stdoutAccepter, accepterParm,
+                  termStatusP);
+
+    free(argArray);
+}
+
+
+
+void
+pm_system2(void stdinFeeder(int, void *),
+           void *          const feederParm,
+           void stdoutAccepter(int, void *),
+           void *          const accepterParm,
+           const char *    const shellCommand,
+           int *           const termStatusP) {
+/*----------------------------------------------------------------------------
+   Run a shell and have it run command 'shellCommand'.  Feed its
+   Standard Input with a pipe, which is fed by the routine
+   'stdinFeeder' with parameter 'feederParm'.  Process its Standard
+   Output with the routine 'stdoutAccepter' with parameter 'accepterParm'.
+
+   But if 'stdinFeeder' is NULL, just feed the shell our own Standard
+   Input.  And if 'stdoutFeeder' is NULL, just send its Standard Output
+   to our own Standard Output.
+
+   Return as *termStatusP the termination status of the processor process
+   (the one running the program named 'progName').
+-----------------------------------------------------------------------------*/
+    pm_system2_lp("/bin/sh", 
+                  stdinFeeder, feederParm, stdoutAccepter, accepterParm,
+                  termStatusP,
+                  "sh", "-c", shellCommand, NULL);
+}
+
+
+
+void
+pm_system_vp(const char *    const progName,
+             const char **   const argArray,
+             void stdinFeeder(int, void *),
+             void *          const feederParm,
+             void stdoutAccepter(int, void *),
+             void *          const accepterParm) {
+/*----------------------------------------------------------------------------
+   Same as pm_system2_vp(), except instead of returning the termination
+   status, we just issue a message (pm_message) describing it.
+-----------------------------------------------------------------------------*/
+    int termStatus;
+
+    pm_system2_vp(progName, argArray,
+                  stdinFeeder, feederParm,
+                  stdoutAccepter, accepterParm,
+                  &termStatus);
+
+    if (termStatus != 0) {
+        const char * const msg = pm_termStatusDesc(termStatus);
+
+        pm_message("%s", msg);
+
+        pm_strfree(msg);
+    }
 }
 
 
@@ -468,8 +605,11 @@ pm_system_lp(const char *    const progName,
              void *          const accepterParm,
              ...) {
 /*----------------------------------------------------------------------------
-  same as pm_system_vp() except with arguments as variable arguments
+  Same as pm_system_vp() except with arguments as variable arguments
   instead of an array.
+
+  N.B. the first variable argument is the program's arg 0; the last
+  variable argument must be NULL.
 -----------------------------------------------------------------------------*/
     va_list args;
     bool endOfArgs;
@@ -511,19 +651,62 @@ pm_system(void stdinFeeder(int, void *),
           void *          const accepterParm,
           const char *    const shellCommand) {
 /*----------------------------------------------------------------------------
-   Run a shell and have it run command 'shellCommand'.  Feed its
-   Standard Input with a pipe, which is fed by the routine
-   'stdinFeeder' with parameter 'feederParm'.  Process its Standard
-   Output with the routine 'stdoutAccepter' with parameter 'accepterParm'.
-
-   But if 'stdinFeeder' is NULL, just feed the shell our own Standard
-   Input.  And if 'stdoutFeeder' is NULL, just send its Standard Output
-   to our own Standard Output.
+   Same as pm_system2(), except instead of returning the termination status,
+   we just issue a message (pm_message) describing it.
 -----------------------------------------------------------------------------*/
+    int termStatus;
+
+    pm_system2(stdinFeeder, feederParm, stdoutAccepter, accepterParm,
+               shellCommand,
+               &termStatus);
+
+    if (termStatus != 0) {
+        const char * const msg = pm_termStatusDesc(termStatus);
+
+        pm_message("%s", msg);
+
+        pm_strfree(msg);
+    }
+}
+
+
+
+void
+pm_feed_null(int    const pipeToFeedFd,
+             void * const feederParm) {
+
+}
 
-    pm_system_lp("/bin/sh", 
-                 stdinFeeder, feederParm, stdoutAccepter, accepterParm,
-                 "sh", "-c", shellCommand, NULL);
+
+
+void
+pm_accept_null(int    const pipetosuckFd,
+               void * const accepterParm ) {
+
+    size_t const bufferSize = 4096;
+
+    unsigned char * buffer;
+
+    MALLOCARRAY(buffer, bufferSize);
+
+    if (buffer) {
+        bool eof;
+
+        for (eof = false; !eof; ) {
+            ssize_t rc;
+
+            rc = read(pipetosuckFd, buffer, bufferSize);
+
+            if (rc < 0) {
+                /* No way to report the problem; just say we're done */
+                eof = true;
+            } else if (rc == 0)
+                /* eof */
+                eof = true;
+        }
+        free(buffer);
+    }
+    close(pipetosuckFd);
 }
 
 
@@ -532,7 +715,7 @@ void
 pm_feed_from_memory(int    const pipeToFeedFd,
                     void * const feederParm) {
 
-    struct bufferDesc * const inputBufferP = feederParm;
+    pm_bufferDesc * const inputBufferP = feederParm;
     
     FILE * const outFileP = fdopen(pipeToFeedFd, "w");
     
@@ -556,7 +739,7 @@ void
 pm_accept_to_memory(int             const pipetosuckFd,
                     void *          const accepterParm ) {
 
-    struct bufferDesc * const outputBufferP = accepterParm;
+    pm_bufferDesc * const outputBufferP = accepterParm;
     
     FILE * const inFileP = fdopen(pipetosuckFd, "r");
 
@@ -570,3 +753,6 @@ pm_accept_to_memory(int             const pipetosuckFd,
     if (outputBufferP->bytesTransferredP)
         *(outputBufferP->bytesTransferredP) = bytesTransferred;
 }
+
+
+
diff --git a/lib/pam.h b/lib/pam.h
index c58e36a2..0055858b 100644
--- a/lib/pam.h
+++ b/lib/pam.h
@@ -29,8 +29,8 @@ struct pam {
     /* This structure describes an open PAM image file.  It consists
        entirely of information that belongs in the header of a PAM image
        and filesystem information.  It does not contain any state
-       information about the processing of that image.  
-       
+       information about the processing of that image.
+
        This is not considered to be an opaque object.  The user of Netbpm
        libraries is free to access and set any of these fields whenever
        appropriate.  The structure exists to make coding of function calls
@@ -41,11 +41,11 @@ struct pam {
        backward compatibility between library functions and calling programs
        as this structure grows.
        */
-    unsigned int size;   
+    unsigned int size;
         /* The storage size of this entire structure, in bytes */
-    unsigned int len;    
+    unsigned int len;
         /* The length, in bytes, of the information in this structure.
-           The information starts in the first byte and is contiguous.  
+           The information starts in the first byte and is contiguous.
            This cannot be greater than 'size'
 
            Use PAM_STRUCT_SIZE() to compute or interpret a value for this.
@@ -76,12 +76,12 @@ struct pam {
            have plain and raw variations.
         */
     int height;  /* Height of image in rows */
-    int width;   
+    int width;
         /* Width of image in number of columns (tuples per row) */
-    unsigned int depth;   
+    unsigned int depth;
         /* Depth of image (number of samples in each tuple). */
     sample maxval;  /* Maximum defined value for a sample */
-    unsigned int bytes_per_sample;  
+    unsigned int bytes_per_sample;
         /* Number of bytes used to represent each sample in the image file.
            Note that this is strictly a function of 'maxval'.  It is in a
            a separate member for computational speed.
@@ -100,7 +100,7 @@ struct pam {
 
            The purpose of this is to make it possible for a program to
            change the type of a tuple to one with more or fewer
-           planes.  
+           planes.
 
            0 means the allocation depth is the same as the image depth.
         */
@@ -141,7 +141,7 @@ struct pam {
 #define PAM_HAVE_ALLOCATION_DEPTH 1
 #define PAM_HAVE_COMMENT_P 1
 
-/* PAM_STRUCT_SIZE(x) tells you how big a struct pam is up through the 
+/* PAM_STRUCT_SIZE(x) tells you how big a struct pam is up through the
    member named x.  This is useful in conjunction with the 'len' value
    to determine which fields are present in the structure.
 */
@@ -172,7 +172,7 @@ struct pam {
     /* These are values of samples in a PAM image that represents a black
        and white bitmap image.  They are the values of black and white,
        respectively.  For example, if you use pnm_readpamrow() to read a
-       row from a PBM file, the black pixels get returned as 
+       row from a PBM file, the black pixels get returned as
        PAM_PBM_BLACK.
     */
 
@@ -192,7 +192,7 @@ struct pam {
 #define PAM_GRAY_TRN_PLANE 1
     /* For a "GRAYSCALE" tuple type, this is the transparency plane */
 
-typedef sample *tuple;  
+typedef sample *tuple;
     /* A tuple in a PAM.  This is an array such that tuple[i-1] is the
        ith sample (element) in the tuple.  It's dimension is the depth
        of the image (see pam.depth above).
@@ -203,7 +203,7 @@ typedef sample *tuple;
 /* Note: xv uses the same "P7" signature for its thumbnail images (it
    started using it years before PAM and unbeknownst to the designer
    of PAM).  But these images are still easily distinguishable from
-   PAMs 
+   PAMs
 */
 #define PAM_MAGIC1 'P'
 #define PAM_MAGIC2 '7'
@@ -221,9 +221,9 @@ struct pamtuples {
 
 
 typedef float * pnm_transformMap;
-    /* This is an array of transform maps.  transform[N] is the 
+    /* This is an array of transform maps.  transform[N] is the
        array that is the map for Plane N.
-   
+
        Transform maps define a transformation between PAM sample value
        to normalized libnetpbm "samplen" value, i.e. what you get back
        from pnm_readpamrown() or pass to pnm_writepamrown().
@@ -244,7 +244,7 @@ typedef float * pnm_transformMap;
        obvious table lookup.  The samplen -> sample transformation is
        more complicated -- if the samplen value is between map[N]
        and map[N+1], then the sample value is N.  And only transforms
-       where map[N+1] > map[N] are allowed.  
+       where map[N+1] > map[N] are allowed.
     */
 
 /* Declarations of library functions. */
@@ -257,8 +257,8 @@ unsigned int
 pnm_bytespersample(sample const maxval);
 
 int
-pnm_tupleequal(const struct pam * const pamP, 
-               tuple              const comparand, 
+pnm_tupleequal(const struct pam * const pamP,
+               tuple              const comparand,
                tuple              const comparator);
 
 void
@@ -267,14 +267,14 @@ pnm_assigntuple(const struct pam * const pamP,
                 tuple              const source);
 
 static __inline__ sample
-pnm_scalesample(sample const source, 
-                sample const oldmaxval, 
+pnm_scalesample(sample const source,
+                sample const oldmaxval,
                 sample const newmaxval) {
 
     if (oldmaxval == newmaxval)
         /* Fast path for common case */
         return source;
-    else 
+    else
         return (source * newmaxval + (oldmaxval/2)) / oldmaxval;
 }
 
@@ -283,24 +283,24 @@ pnm_scalesample(sample const source,
 void
 pnm_scaletuple(const struct pam * const pamP,
                tuple              const dest,
-               tuple              const source, 
+               tuple              const source,
                sample             const newmaxval);
 
-void 
+void
 pnm_scaletuplerow(const struct pam * const pamP,
                   tuple *            const destRow,
                   tuple *            const sourceRow,
                   sample             const newMaxval);
 
-void 
+void
 pnm_maketuplergb(const struct pam * const pamP,
                  tuple              const tuple);
 
-void 
+void
 pnm_makerowrgb(const struct pam * const pamP,
                tuple *            const tuplerow);
 
-void 
+void
 pnm_makearrayrgb(const struct pam * const pamP,
                  tuple **           const tuples);
 
@@ -336,13 +336,13 @@ pnm_allocpamarray(const struct pam * const pamP);
 void
 pnm_freepamarray(tuple ** const tuplearray, const struct pam * const pamP);
 
-void 
+void
 pnm_setminallocationdepth(struct pam * const pamP,
                           unsigned int const allocationDepth);
 
 void
-pnm_setpamrow(const struct pam * const pam, 
-              tuple *            const tuplerow, 
+pnm_setpamrow(const struct pam * const pam,
+              tuple *            const tuplerow,
               sample             const value);
 
 unsigned char *
@@ -351,20 +351,20 @@ pnm_allocrowimage(const struct pam * const pamP);
 void
 pnm_freerowimage(unsigned char * const rowimage);
 
-void 
-pnm_readpaminit(FILE *       const file, 
-                struct pam * const pamP, 
+void
+pnm_readpaminit(FILE *       const file,
+                struct pam * const pamP,
                 int          const size);
 
-void 
+void
 pnm_readpamrow(const struct pam * const pamP, tuple* const tuplerow);
 
-tuple ** 
-pnm_readpam(FILE *       const file, 
-            struct pam * const pamP, 
+tuple **
+pnm_readpam(FILE *       const file,
+            struct pam * const pamP,
             int          const size);
 
-void 
+void
 pnm_writepaminit(struct pam * const pamP);
 
 void
@@ -373,19 +373,19 @@ pnm_formatpamrow(const struct pam * const pamP,
                  unsigned char *    const outbuf,
                  unsigned int *     const rowSizeP);
 
-void 
+void
 pnm_writepamrow(const struct pam * const pamP, const tuple * const tuplerow);
 
 void
-pnm_writepamrowmult(const struct pam * const pamP, 
+pnm_writepamrowmult(const struct pam * const pamP,
                     const tuple *      const tuplerow,
                     unsigned int       const rptcnt);
 
-void 
+void
 pnm_writepam(struct pam * const pamP, tuple ** const tuplearray);
 
 void
-pnm_checkpam(const struct pam *   const pamP, 
+pnm_checkpam(const struct pam *   const pamP,
              enum pm_check_type   const checkType,
              enum pm_check_code * const retvalP);
 
@@ -401,6 +401,11 @@ typedef float samplen;
 typedef samplen *tuplen;
     /* Same as 'tuple', except using normalized samples. */
 
+tuplen
+pnm_allocpamtuplen(const struct pam * const pamP);
+
+#define pnm_freepamtuplen(tuplen) pm_freerow((char*) tuplen)
+
 tuplen *
 pnm_allocpamrown(const struct pam * const pamP);
 
@@ -409,30 +414,37 @@ pnm_allocpamrown(const struct pam * const pamP);
 tuplen *
 pnm_allocpamrown(const struct pam * const pamP);
 
-void 
-pnm_readpamrown(const struct pam * const pamP, 
+void
+pnm_readpamrown(const struct pam * const pamP,
                 tuplen *           const tuplenrow);
 
-void 
-pnm_writepamrown(const struct pam * const pamP, 
+void
+pnm_writepamrown(const struct pam * const pamP,
                  const tuplen *     const tuplenrow);
 
 tuplen **
 pnm_allocpamarrayn(const struct pam * const pamP);
 
 void
-pnm_freepamarrayn(tuplen **          const tuplenarray, 
+pnm_freepamarrayn(tuplen **          const tuplenarray,
                   const struct pam * const pamP);
 
-tuplen** 
-pnm_readpamn(FILE *       const file, 
-             struct pam * const pamP, 
+tuplen**
+pnm_readpamn(FILE *       const file,
+             struct pam * const pamP,
              int          const size);
 
-void 
-pnm_writepamn(struct pam * const pamP, 
+void
+pnm_writepamn(struct pam * const pamP,
               tuplen **    const tuplenarray);
 
+samplen
+pnm_normalized_sample(struct pam * const pamP,
+                      sample       const sample);
+
+sample
+pnm_unnormalized_sample(struct pam * const pamP,
+                        samplen      const sampleVal);
 
 void
 pnm_normalizetuple(struct pam * const pamP,
@@ -477,6 +489,18 @@ void
 pnm_unapplyopacityrown(struct pam * const pamP,
                        tuplen *     const tuplenrow);
 
+void
+pnm_maketuplergbn(const struct pam * const pamP,
+                  tuplen             const tuple);
+
+void
+pnm_makerowrgbn(const struct pam * const pamP,
+                tuplen *           const tuplerow);
+
+void
+pnm_makearrayrgbn(const struct pam * const pamP,
+                  tuplen **          const tuples);
+
 pnm_transformMap *
 pnm_creategammatransform(const struct pam * const pamP);
 
@@ -490,26 +514,57 @@ pnm_createungammatransform(const struct pam * const pamP);
 #define pnm_freeungammatransform pnm_freegammatransform;
 
 tuple
+pnm_parsecolor2(const char * const colorname,
+                sample       const maxval,
+                int          const closeOk);
+
+tuple
 pnm_parsecolor(const char * const colorname,
                sample       const maxval);
 
+tuplen
+pnm_parsecolorn(const char * const colorname);
+
 const char *
 pnm_colorname(struct pam * const pamP,
               tuple        const color,
               int          const hexok);
 
-extern double 
+const char *
+pnm_colorspec_rgb_integer(struct pam * const pamP,
+                          tuple        const color,
+                          sample       const maxval);
+
+const char *
+pnm_colorspec_rgb_norm(struct pam * const pamP,
+                       tuple        const color,
+                       unsigned int const digitCt);
+
+const char *
+pnm_colorspec_rgb_x11(struct pam * const pamP,
+                      tuple        const color,
+                      unsigned int const hexDigitCt);
+
+const char *
+pnm_colorspec_dict(struct pam * const pamP,
+                   tuple        const color);
+
+const char *
+pnm_colorspec_dict_close(struct pam * const pamP,
+                         tuple        const color);
+
+extern double
 pnm_lumin_factor[3];
 
 void
-pnm_YCbCrtuple(const tuple tuple, 
+pnm_YCbCrtuple(const tuple tuple,
                double * const YP, double * const CbP, double * const CrP);
 
-void 
+void
 pnm_YCbCr_to_rgbtuple(const struct pam * const pamP,
                       tuple              const tuple,
                       double             const Y,
-                      double             const Cb, 
+                      double             const Cb,
                       double             const Cr,
                       int *              const overflowP);
 
diff --git a/lib/path.c b/lib/path.c
index 10ae92d2..8d53eb9e 100644
--- a/lib/path.c
+++ b/lib/path.c
@@ -58,13 +58,156 @@
 /* NOTE NOTE NOTE
 
    In all the path logic below, we call the direction of increasing row
-   number "up" because we think of the raster as standard Cartesian
+   number "up" because we think of the raster as a standard Cartesian
    plane.  So visualize the image as upside down in the first quadrant
    of the Cartesian plane -- the real top left corner of the image is at
    the origin.
 */
 
 
+ppmd_pathleg
+ppmd_makeLineLeg(ppmd_point const point) {
+
+    ppmd_pathleg retval;
+
+    retval. type = PPMD_PATHLEG_LINE;
+
+    retval.u.linelegparms.end = point;
+
+    return retval;
+}
+
+
+
+ppmd_pathbuilder *
+ppmd_pathbuilder_create() {
+
+    ppmd_pathbuilder * retval;
+
+    MALLOCVAR(retval);
+
+    if (!retval)
+        pm_error("Failed to allocate memory "
+                 "for a ppmd_pathuilder structure");
+
+    retval->path.version = 0;
+    retval->path.legCount = 0;
+    retval->path.legSize = sizeof(ppmd_pathleg);
+    retval->path.legs = NULL;
+
+    retval->begIsSet = false;
+    retval->legsAreAutoAllocated = true;
+    retval->legsAllocSize = 0;
+
+    return retval;
+}
+
+
+
+void
+ppmd_pathbuilder_destroy(ppmd_pathbuilder * const pathBuilderP) {
+
+    if (pathBuilderP->legsAreAutoAllocated) {
+        if (pathBuilderP->path.legs)
+            free(pathBuilderP->path.legs);
+    }
+    free(pathBuilderP);
+}
+
+
+
+void
+ppmd_pathbuilder_setLegArray(ppmd_pathbuilder * const pathBuilderP,
+                             ppmd_pathleg *     const legs,
+                             unsigned int       const legCount) {
+
+    if (pathBuilderP->path.legs)
+        pm_error("Legs array is already set up");
+
+    if (legCount < 1)
+        pm_error("Leg array size must be at least one leg in size");
+
+    if (legs == NULL)
+        pm_error("Leg array pointer is null");
+
+    pathBuilderP->legsAreAutoAllocated = false;
+    
+    pathBuilderP->legsAllocSize = legCount;
+
+    pathBuilderP->path.legs = legs;
+}
+
+
+
+void
+ppmd_pathbuilder_preallocLegArray(ppmd_pathbuilder * const pathBuilderP,
+                                  unsigned int       const legCount) {
+
+    if (pathBuilderP->path.legs)
+        pm_error("Legs array is already set up");
+
+    if (legCount < 1)
+        pm_error("Leg array size must be at least one leg in size");
+
+    MALLOCARRAY(pathBuilderP->path.legs, legCount);
+
+    if (!pathBuilderP->path.legs)
+        pm_error("Unable to allocate memory for %u legs", legCount);
+
+    pathBuilderP->legsAllocSize = legCount;
+}
+
+
+
+void
+ppmd_pathbuilder_setBegPoint(ppmd_pathbuilder * const pathBuilderP,
+                             ppmd_point         const begPoint) {
+
+    pathBuilderP->path.begPoint = begPoint;
+    
+    pathBuilderP->begIsSet = true;
+}
+
+
+
+void
+ppmd_pathbuilder_addLineLeg(ppmd_pathbuilder * const pathBuilderP,
+                            ppmd_pathleg       const leg) {
+
+    if (!pathBuilderP->begIsSet)
+        pm_error("Attempt to add a leg to a path when the "
+                 "beginning point of the path has not been set");
+
+    if (pathBuilderP->path.legCount + 1 > pathBuilderP->legsAllocSize) {
+        if (pathBuilderP->legsAreAutoAllocated) {
+            pathBuilderP->legsAllocSize =
+                MAX(16, pathBuilderP->legsAllocSize * 2);
+
+            REALLOCARRAY(pathBuilderP->path.legs, 
+                         pathBuilderP->legsAllocSize);
+
+            if (pathBuilderP->path.legs == NULL)
+                pm_error("Unable to allocate memory for %u legs", 
+                         pathBuilderP->legsAllocSize);
+        } else
+            pm_error("Out of space in user-supplied legs array "
+                     "(has space for %u legs)", pathBuilderP->legsAllocSize);
+    }
+
+    assert(pathBuilderP->path.legCount + 1 <= pathBuilderP->legsAllocSize);
+
+    pathBuilderP->path.legs[pathBuilderP->path.legCount++] = leg;
+}
+
+
+
+const ppmd_path *
+ppmd_pathbuilder_pathP(ppmd_pathbuilder * const pathBuilderP) {
+
+    return &pathBuilderP->path;
+}
+
+
 
 static bool
 pointEqual(ppmd_point const comparator,
@@ -411,12 +554,12 @@ fillLeg(ppmd_point  const begPoint,
 
 
 void
-ppmd_fill_path(pixel **      const pixels, 
-               int           const cols, 
-               int           const rows, 
-               pixval        const maxval,
-               ppmd_path *   const pathP,
-               pixel         const color) {
+ppmd_fill_path(pixel **          const pixels, 
+               int               const cols, 
+               int               const rows, 
+               pixval            const maxval,
+               const ppmd_path * const pathP,
+               pixel             const color) {
 /*----------------------------------------------------------------------------
    Draw a path which defines a closed figure (or multiple closed figures)
    and fill it in.
diff --git a/lib/pbmfont.h b/lib/pbmfont.h
index bedf57b9..c8b3934b 100644
--- a/lib/pbmfont.h
+++ b/lib/pbmfont.h
@@ -10,6 +10,70 @@ extern "C" {
 } /* to fake out automatic code indenters */
 #endif
 
+
+/* Maximum dimensions for fonts */
+
+#define  pbm_maxfontwidth()  65535
+#define  pbm_maxfontheight() 65535
+    /* These limits are not in the official Adobe BDF definition, but
+       should never be a problem for practical purposes, considering that
+       a 65536 x 65536 glyph occupies 4G pixels.
+
+       Note that the maximum line length allowed in a BDF file imposes
+       another restriction.
+    */
+
+typedef wchar_t PM_WCHAR;
+    /* Precaution to make adjustments, if necessary, for systems with
+       unique wchar_t.
+    */
+
+#define PM_FONT_MAXGLYPH 255
+
+#define PM_FONT2_MAXGLYPH 65535
+    /* Upper limit of codepoint value.
+
+       This is large enough to handle Unicode Plane 0 (Basic Multilingual
+       Plane: BMP) which defines the great majority of characters used in
+       modern languages.
+
+       This can be set to a higher value at some cost to efficiency.
+       As of Unicode v. 11.0.0 planes up to 16 are defined.
+    */
+
+enum pbmFontLoad { FIXED_DATA           = 0,
+                   LOAD_PBMSHEET        = 1,
+                   LOAD_BDFFILE         = 2,
+                   CONVERTED_TYPE1_FONT = 9 };
+
+static const char * const pbmFontOrigin[10] =
+                 {"Fixed data",                                   /* 0 */
+                  "Loaded from PBM sheet by libnetpbm",           /* 1 */
+                  "Loaded from BDF file by libnetpbm",            /* 2 */
+                  NULL, NULL, NULL, NULL, NULL, NULL,
+                  "Expanded from type 1 font structure by libnetpbm"}; /* 9 */
+
+enum pbmFontEncoding { ENCODING_UNKNOWN = 0,
+                       ISO646_1991_IRV = 1,   /* ASCII */
+                       ISO_8859_1 = 1000, ISO_8859_2, ISO_8859_3, ISO_8859_4,
+                       ISO_8859_5,   ISO_8859_6,   ISO_8859_7,   ISO_8859_8,
+                       ISO_8859_9,   ISO_8859_10,  ISO_8859_11,  ISO_8859_12,
+                       ISO_8859_13,  ISO_8859_14,  ISO_8859_15,  ISO_8859_16,
+                       ISO_10646 = 2000 };
+
+/* For future use */
+
+/* In addition to the above, the following CHARSET_REGISTRY-CHARSET_ENCODING
+   values have been observed in actual BDF files:
+
+  ADOBE-FONTSPECIFIC, DEC-DECTECH, GOST19768.74-1, IS13194-DEVANAGARI,
+  JISX0201.1976-0, KOI8-C, KOI8-R, MISC-FONTSPECIFIC,
+  MULEARABIC-0, MULEARABIC-1, MULEARABIC-2, MULEIPA-1, MULELAO-1,
+  OMRON_UDC_ZH-0, TIS620.2529-0, TIS620.2529-1, VISCII1-1, VISCII1.1-1,
+  XTIS-0
+ */
+
+
 struct glyph {
     /* A glyph consists of white borders and the "central glyph" which
        can be anything, but normally does not have white borders because
@@ -41,38 +105,244 @@ struct glyph {
            the top half and white on the bottom, this is an array of
            800 bytes, with the first 400 having value 0x01 and the
            last 400 having value 0x00.
+
+           Do not share bmap objects among glyphs if using
+           pbm_destroybdffont() or pbm_destroybdffont2() to free
+           the font/font2 structure.
         */
 };
 
+
 struct font {
     /* This describes a combination of font and character set.  Given
        an code point in the range 0..255, this structure describes the
        glyph for that character.
     */
-    int maxwidth, maxheight;
+    unsigned int maxwidth, maxheight;
     int x;
-        /* ?? Not used by Pbmtext */
+        /* The minimum value of glyph.font.  The left edge of the glyph
+           in the glyph set which advances furthest to the left. */
     int y;
         /* Amount of white space that should be added between lines of
            this font.  Can be negative.
         */
     struct glyph * glyph[256];
-        /* glyph[i] is the glyph for code point i */
+        /* glyph[i] is the glyph for code point i.
+           Glyph objects must be unique for pbm_destroybdffont() to work.
+        */
     const bit ** oldfont;
         /* for compatibility with old pbmtext routines */
         /* oldfont is NULL if the font is BDF derived */
     int fcols, frows;
 };
 
-struct font* pbm_defaultfont(const char* const which);
-struct font*
+
+struct font2 {
+    /* Font structure for expanded character set.
+       Code points in the range 0...maxmaxglyph are loaded.
+       Loaded code point is in the range 0..maxglyph .
+     */
+
+    /* 'size' and 'len' are necessary in order to provide forward and
+       backward compatibility between library functions and calling programs
+       as this structure grows.  See struct pam in pam.h.
+     */
+    unsigned int size;
+        /* The storage size of this entire structure, in bytes */
+
+    unsigned int len;
+        /* The length, in bytes, of the information in this structure.
+           The information starts in the first byte and is contiguous.
+           This cannot be greater than 'size'
+        */
+
+    int maxwidth, maxheight;
+
+    int x;
+         /* The minimum value of glyph.font.  The left edge of the glyph in
+            the glyph set which advances furthest to the left.
+         */
+    int y;
+        /* Amount of white space that should be added between lines of this
+           font.  Can be negative.
+        */
+
+    struct glyph ** glyph;
+        /* glyph[i] is the glyph for code point i
+
+           Glyph objects must be unique for pbm_destroybdffont2() to work.
+           For example space and non-break-space are often identical at the
+           image data level; they must be loaded into separate memory
+           locations if using pbm_destroybdffont2().
+         */
+
+    PM_WCHAR maxglyph;
+        /* max code point for glyphs, including vacant slots max value of
+           above i
+        */
+
+    void * selector;
+        /* Reserved
+
+           Bit array or structure indicating which code points to load.
+
+           When NULL, all available code points up to maxmaxglyph, inclusive
+           are loaded.
+       */
+
+    PM_WCHAR maxmaxglyph;
+        /* Code points above this value are not loaded, even if they occur
+           in the BDF font file
+        */
+
+    const bit ** oldfont;
+        /* For compatibility with old pbmtext routines.
+           Valid only when data is in the form of a PBM sheet
+        */
+
+    unsigned int fcols, frows;
+        /* For compatibility with old pbmtext routines.
+           Valid only when oldfont is non-NULL
+        */
+
+    unsigned int bit_format;
+        /* PBM_FORMAT:   glyph data: 1 byte per pixel (like P1, but not ASCII)
+           RPBM_FORMAT:  glyph data: 1 bit per pixel
+           Currently only PBM_FORMAT is possible
+        */
+
+    unsigned int total_chars;
+        /* Number of glyphs defined in font file, as stated in the CHARS line
+           of the BDF file PBM sheet font.  Always 96
+        */
+
+    unsigned int chars;
+        /* Number of glyphs actually loaded into structure
+
+           Maximum: total_chars
+
+           Less than total_chars when a subset of the file is loaded
+           PBM sheet font: always 96 */
+
+    enum pbmFontLoad load_fn;
+        /* Description of the function that created the structure and loaded
+           the glyph data
+
+           Used to choose a string to show in verbose messages.
+
+           FIXED_DATA (==0) means memory for this structure was not
+           dynamically allocated by a function; all data is hardcoded in
+           source code and resides in static data.  See file pbmfontdata1.c
+        */
+
+    PM_WCHAR default_char;
+        /* Code index of what to show when there is no glyph for a requested
+           code Available in many BDF fonts between STARPROPERTIES -
+           ENDPROPERTIES.
+
+           Set to value read from BDF font file.
+
+           Common values are 0, 32, 8481, 32382, 33, 159, 255.
+        */
+
+    unsigned int default_char_defined;
+        /* boolean
+           TRUE: above field is valid; DEFAULT_CHAR is defined in font file.
+           FALSE: font file has no DEFAULT_CHAR field.
+        */
+
+    const char * name;
+        /* Name of the font.  Available in BDF fonts.
+           NULL means no name.
+        */
+
+    enum pbmFontEncoding charset;
+        /* Reserved for future use.
+           Set by analyzing following charset_string.
+        */
+
+    const char * charset_string;
+        /* Charset registry and encoding.
+           Available in most BDF fonts between STARPROPERTIES - ENDPROPERTIES.
+           NULL means no name.
+        */
+};
+
+
+/* PBM_FONT2_STRUCT_SIZE(x) tells you how big a struct font2 is up
+   through the member named x.  This is useful in conjunction with the
+   'len' value to determine which fields are present in the structure.
+*/
+
+/* Some compilers are really vigilant and recognize it as an error
+   to cast a 64 bit address to a 32 bit type.  Hence the roundabout
+   casting.  See PAM_MEMBER_OFFSET in pam.h .
+*/
+
+
+#define PBM_FONT2_MEMBER_OFFSET(mbrname) \
+  ((size_t)(unsigned long)(char*)&((struct font2 *)0)->mbrname)
+#define PBM_FONT2_MEMBER_SIZE(mbrname) \
+  sizeof(((struct font2 *)0)->mbrname)
+#define PBM_FONT2_STRUCT_SIZE(mbrname) \
+  (PBM_FONT2_MEMBER_OFFSET(mbrname) + PBM_FONT2_MEMBER_SIZE(mbrname))
+
+
+struct font *
+pbm_defaultfont(const char* const which);
+
+struct font2 *
+pbm_defaultfont2(const char* const which);
+
+struct font *
 pbm_dissectfont(const bit ** const font,
                 unsigned int const frows,
                 unsigned int const fcols);
-struct font* pbm_loadfont(const char * const filename);
-struct font* pbm_loadpbmfont(const char * const filename);
-struct font* pbm_loadbdffont(const char * const filename);
-void pbm_dumpfont(struct font * const fnP);
+
+struct font *
+pbm_loadfont(const char * const filename);
+
+struct font2 *
+pbm_loadfont2(const    char * const filename,
+              PM_WCHAR        const maxmaxglyph);
+
+struct font *
+pbm_loadpbmfont(const char * const filename);
+
+struct font2 *
+pbm_loadpbmfont2(const char * const filename);
+
+struct font *
+pbm_loadbdffont(const char * const filename);
+
+struct font2 *
+pbm_loadbdffont2(const char * const filename,
+                 PM_WCHAR     const maxmaxglyph);
+
+struct font2 *
+pbm_loadbdffont2_select(const char * const filename,
+                        PM_WCHAR     const maxmaxglyph,
+                        const void * const selector);
+
+void
+pbm_createbdffont2_base(struct font2 ** const font2P,
+                        PM_WCHAR        const maxmaxglyph);
+
+void
+pbm_destroybdffont(struct font * const fontP);
+
+void
+pbm_destroybdffont2_base(struct font2 * const font2P);
+
+void
+pbm_destroybdffont2(struct font2 * const font2P);
+
+struct font2 *
+pbm_expandbdffont(const struct font * const font);
+
+void
+pbm_dumpfont(struct font * const fontP,
+             FILE *        const ofP);
 
 #ifdef __cplusplus
 }
diff --git a/lib/pbmfontdata.h b/lib/pbmfontdata.h
new file mode 100644
index 00000000..7ac63abc
--- /dev/null
+++ b/lib/pbmfontdata.h
@@ -0,0 +1,7 @@
+extern struct font pbm_defaultFixedfont;
+extern struct font pbm_defaultBdffont;
+
+extern struct font2 const pbm_defaultFixedfont2;
+extern struct font2 const pbm_defaultBdffont2;
+
+extern struct font2 const * pbm_builtinFonts[];
diff --git a/lib/pbmfontdata0.c b/lib/pbmfontdata0.c
new file mode 100644
index 00000000..dfafc317
--- /dev/null
+++ b/lib/pbmfontdata0.c
@@ -0,0 +1,9 @@
+#include "pbm.h"
+#include "pbmfont.h"
+#include "pbmfontdata.h"
+
+struct font2 const * pbm_builtinFonts[] = {
+    &pbm_defaultFixedfont2,
+    &pbm_defaultBdffont2,
+    NULL,
+};
diff --git a/lib/pbmfontdata1.c b/lib/pbmfontdata1.c
new file mode 100644
index 00000000..ab6ce28d
--- /dev/null
+++ b/lib/pbmfontdata1.c
@@ -0,0 +1,269 @@
+#include "pbmfont.h"
+#include "pbmfontdata.h"
+
+/* Default fixed-width font
+   All glyphs fit into a 7 x 12 rectangular cell.
+   Raster data is provided completely.
+
+   This kind of font, commonly used on early bitmap displays, is
+   sometimes called "cell-char".
+
+
+   Note: In the current version default_fixedfont is fixed data, which
+   means it is always accessible, without any preparatory action.
+   However, the storage format may change in future versions.
+
+   To ensure future compatibility call pbm_defaultfont() before accessing
+   default_fixedfont.
+
+   The Netpbm development tool 'genfontc' generates C source code like this
+   from a libnetpbm font file or builtin font.
+*/
+
+static struct glyph glFxd[96] = {
+/*  32 character   */
+{7,12,0,0,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\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  33 character ! */
+{7,12,0,0,7,"\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\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" },
+/*  34 character " */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\1\0\0\0\0\1\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\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" },
+/*  35 character # */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\1\0\0\1\1\1\1\1\1\0\0\1\0\1\0\0\0\0\1\0\1\0\0\1\1\1\1\1\1\0\0\1\0\1\0\0\0\0\1\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" },
+/*  36 character $ */
+{7,12,0,0,7,"\0\0\0\1\0\0\0\0\0\1\1\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\1\1\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  37 character % */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\1\1\0\0\0\0\1\0\0\1\0\0\0\0\1\1\0\0\1\0\0\0\0\1\1\0\0\0\1\1\0\0\0\0\1\0\0\1\1\0\0\0\0\1\0\0\1\0\0\0\0\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  38 character & */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\1\1\1\0\0\0\1\0\0\1\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\1\0\1\1\1\0\1\0\1\0\0\0\1\0\0\1\0\0\0\1\1\0\0\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" },
+/*  39 character ' */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\1\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\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" },
+/*  40 character ( */
+{7,12,0,0,7,"\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0" },
+/*  41 character ) */
+{7,12,0,0,7,"\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  42 character * */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\0\0\1\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\0\0\0\0\0\0\0\0\0\0\0" },
+/*  43 character + */
+{7,12,0,0,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\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\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" },
+/*  44 character , */
+{7,12,0,0,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\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\0\0\0\0\1\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" },
+/*  45 character - */
+{7,12,0,0,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\0\0\0\0\0\0\0\0\0\0\0\1\1\1\1\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\0\0\0\0\0\0\0\0\0\0" },
+/*  46 character . */
+{7,12,0,0,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\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\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  47 character / */
+{7,12,0,0,7,"\0\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\1\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" },
+/*  48 character 0 */
+{7,12,0,0,7,"\0\0\1\1\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  49 character 1 */
+{7,12,0,0,7,"\0\0\0\1\0\0\0\0\1\1\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  50 character 2 */
+{7,12,0,0,7,"\0\0\1\1\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\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\0\0\0\1\0\0\1\1\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  51 character 3 */
+{7,12,0,0,7,"\0\0\1\1\1\0\0\0\1\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  52 character 4 */
+{7,12,0,0,7,"\0\0\0\0\1\0\0\0\0\0\1\1\0\0\0\0\1\0\1\0\0\0\0\1\0\1\0\0\0\1\0\0\1\0\0\1\0\0\0\1\0\0\1\1\1\1\1\1\0\0\0\0\0\1\0\0\0\0\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  53 character 5 */
+{7,12,0,0,7,"\0\1\1\1\1\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\1\1\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  54 character 6 */
+{7,12,0,0,7,"\0\0\0\1\1\1\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\1\1\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  55 character 7 */
+{7,12,0,0,7,"\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\1\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" },
+/*  56 character 8 */
+{7,12,0,0,7,"\0\0\1\1\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\1\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  57 character 9 */
+{7,12,0,0,7,"\0\0\1\1\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\1\1\1\0\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\1\1\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" },
+/*  58 character : */
+{7,12,0,0,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\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\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" },
+/*  59 character ; */
+{7,12,0,0,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\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\1\0\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  60 character < */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\0\0\0\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\0\0\1\0\0\0\0\0\0\0\1\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" },
+/*  61 character = */
+{7,12,0,0,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\0\0\0\0\1\1\1\1\1\0\0\0\0\0\0\0\0\0\1\1\1\1\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\0\0\0" },
+/*  62 character > */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\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\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  63 character ? */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\1\1\1\0\0\0\1\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\1\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" },
+/*  64 character @ */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\1\1\1\0\0\0\1\0\0\0\1\0\0\1\0\0\1\1\0\0\1\0\1\0\1\0\0\1\0\1\0\1\0\0\1\0\0\1\1\1\0\1\0\0\0\0\0\0\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  65 character A */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\1\1\1\0\0\0\0\0\0\1\0\0\0\0\0\1\0\1\0\0\0\0\1\0\1\0\0\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\1\1\1\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  66 character B */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\1\1\1\1\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\1\1\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\1\1\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  67 character C */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\1\1\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  68 character D */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\1\1\1\1\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\1\1\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  69 character E */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\1\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\1\0\0\0\0\1\1\1\0\0\0\0\1\0\1\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\1\0\1\1\1\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  70 character F */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\1\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\1\0\1\0\0\1\1\1\0\0\0\0\1\0\1\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\1\1\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" },
+/*  71 character G */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\1\1\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\0\0\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  72 character H */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\1\1\1\0\1\1\1\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\1\1\1\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  73 character I */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  74 character J */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\1\1\1\1\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\1\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" },
+/*  75 character K */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\1\1\1\0\1\1\0\0\1\0\0\1\0\0\0\1\0\1\0\0\0\0\1\1\0\0\0\0\0\1\1\1\0\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\0\1\1\1\0\0\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  76 character L */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\1\1\1\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\1\1\1\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  77 character M */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\1\1\0\0\0\1\1\0\1\1\0\1\1\0\0\1\1\0\1\1\0\0\1\0\1\0\1\0\0\1\0\1\0\1\0\0\1\0\1\0\1\0\0\1\0\0\0\1\0\1\1\1\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  78 character N */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\1\1\0\0\1\1\1\0\1\1\0\0\1\0\0\1\1\0\0\1\0\0\1\0\1\0\1\0\0\1\0\1\0\1\0\0\1\0\0\1\1\0\0\1\0\0\1\1\0\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" },
+/*  79 character O */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\1\1\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  80 character P */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\1\1\1\1\1\0\0\0\1\0\0\1\1\0\0\1\0\0\0\1\0\0\1\0\0\1\1\0\0\1\1\1\1\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\1\1\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" },
+/*  81 character Q */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\1\1\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  82 character R */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\1\1\1\1\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\1\1\1\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\1\1\1\0\0\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  83 character S */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\1\1\0\0\0\0\0\0\1\1\1\0\0\0\0\0\0\1\0\0\1\0\0\0\1\0\0\1\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  84 character T */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\1\1\1\1\1\1\1\1\0\0\1\0\0\1\1\0\0\1\0\0\1\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  85 character U */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\1\1\1\0\1\1\1\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\1\0\1\1\0\0\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  86 character V */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\1\1\1\0\1\1\1\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\1\0\1\1\0\0\0\1\0\1\0\0\0\0\1\0\1\0\0\0\0\1\1\1\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" },
+/*  87 character W */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\1\1\1\0\1\1\1\0\1\0\0\0\1\0\0\1\0\1\0\1\0\0\1\0\1\0\1\0\0\1\0\1\0\1\0\0\1\1\0\1\1\0\0\0\1\0\1\0\0\0\0\1\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" },
+/*  88 character X */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\1\1\1\0\1\1\1\0\1\0\0\0\1\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\0\0\1\0\1\0\0\0\0\1\0\1\0\0\0\1\0\0\0\1\0\1\1\1\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  89 character Y */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\1\1\1\0\1\1\1\0\1\0\0\0\1\0\0\0\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  90 character Z */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\0\0\1\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  91 character [ */
+{7,12,0,0,7,"\0\0\0\1\1\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\0\0\0" },
+/*  92 character \ */
+{7,12,0,0,7,"\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  93 character ] */
+{7,12,0,0,7,"\0\0\1\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\1\0\0\0\0\0\0\0\0\0\0" },
+/*  94 character ^ */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\1\0\1\0\0\0\1\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\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  95 character _ */
+{7,12,0,0,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\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\1\1\1\1" },
+/*  96 character ` */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\1\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\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" },
+/*  97 character a */
+{7,12,0,0,7,"\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\0\0\0\0\0\1\0\0\0\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\1\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  98 character b */
+{7,12,0,0,7,"\1\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\1\1\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\1\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/*  99 character c */
+{7,12,0,0,7,"\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\0\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/* 100 character d */
+{7,12,0,0,7,"\0\0\0\0\1\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\1\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/* 101 character e */
+{7,12,0,0,7,"\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\0\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/* 102 character f */
+{7,12,0,0,7,"\0\0\0\1\1\1\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\1\1\1\1\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/* 103 character g */
+{7,12,0,0,7,"\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\0\1\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\1\1\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\1\1\1\0\0" },
+/* 104 character h */
+{7,12,0,0,7,"\1\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\1\1\0\0\0\1\1\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\1\1\1\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/* 105 character i */
+{7,12,0,0,7,"\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/* 106 character j */
+{7,12,0,0,7,"\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\1\1\1\0\0\0" },
+/* 107 character k */
+{7,12,0,0,7,"\1\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\1\1\0\0\1\0\1\0\0\0\0\1\1\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\0\0\1\1\0\0\0\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/* 108 character l */
+{7,12,0,0,7,"\0\1\1\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/* 109 character m */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\1\0\0\0\1\0\1\0\1\0\0\1\0\1\0\1\0\0\1\0\1\0\1\0\0\1\0\1\0\1\0\1\1\0\1\0\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/* 110 character n */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\1\1\1\0\0\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/* 111 character o */
+{7,12,0,0,7,"\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\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/* 112 character p */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\1\1\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\1\1\1\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\1\1\0\0\0\0" },
+/* 113 character q */
+{7,12,0,0,7,"\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\0\1\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\1\1\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\1\1" },
+/* 114 character r */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\1\1\1\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/* 115 character s */
+{7,12,0,0,7,"\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\0\0\0\1\0\0\0\1\0\0\0\1\1\1\0\0\0\0\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/* 116 character t */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\1\1\1\1\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\0\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/* 117 character u */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\1\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/* 118 character v */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\1\1\1\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\0\1\0\0\0\0\1\1\1\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" },
+/* 119 character w */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\1\1\1\0\1\0\0\0\1\0\0\1\0\1\0\1\0\0\1\0\1\0\1\0\0\0\1\0\1\0\0\0\0\1\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" },
+/* 120 character x */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\1\1\1\0\0\1\0\1\0\0\0\0\0\1\0\0\0\0\0\1\0\1\0\0\0\1\0\0\0\1\0\1\1\0\0\0\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/* 121 character y */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\1\1\1\0\1\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\0\1\0\0\0\0\1\1\1\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\0\0\0\0" },
+/* 122 character z */
+{7,12,0,0,7,"\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\1\0\0\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\1\0\0\1\1\1\1\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" },
+/* 123 character { */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0" },
+/* 124 character | */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0" },
+/* 125 character } */
+{7,12,0,0,7,"\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0" },
+/* 126 character ~ */
+{7,12,0,0,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\0\0\0\0\0\1\0\0\1\0\0\1\0\1\0\1\0\0\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\0\0\0\0\0\0\0\0\0" },
+/* 127 character (Control character, retained for backward compatibility) */
+{7,12,0,0,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\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" }
+};
+
+
+
+struct font pbm_defaultFixedfont = { 7, 12, 0, 0, {
+NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+glFxd+ 0, glFxd+ 1, glFxd+ 2, glFxd+ 3, glFxd+ 4, glFxd+ 5,
+glFxd+ 6, glFxd+ 7, glFxd+ 8, glFxd+ 9, glFxd+10, glFxd+11,
+glFxd+12, glFxd+13, glFxd+14, glFxd+15, glFxd+16, glFxd+17,
+glFxd+18, glFxd+19, glFxd+20, glFxd+21, glFxd+22, glFxd+23,
+glFxd+24, glFxd+25, glFxd+26, glFxd+27, glFxd+28, glFxd+29,
+glFxd+30, glFxd+31, glFxd+32, glFxd+33, glFxd+34, glFxd+35,
+glFxd+36, glFxd+37, glFxd+38, glFxd+39, glFxd+40, glFxd+41,
+glFxd+42, glFxd+43, glFxd+44, glFxd+45, glFxd+46, glFxd+47,
+glFxd+48, glFxd+49, glFxd+50, glFxd+51, glFxd+52, glFxd+53,
+glFxd+54, glFxd+55, glFxd+56, glFxd+57, glFxd+58, glFxd+59,
+glFxd+60, glFxd+61, glFxd+62, glFxd+63, glFxd+64, glFxd+65,
+glFxd+66, glFxd+67, glFxd+68, glFxd+69, glFxd+70, glFxd+71,
+glFxd+72, glFxd+73, glFxd+74, glFxd+75, glFxd+76, glFxd+77,
+glFxd+78, glFxd+79, glFxd+80, glFxd+81, glFxd+82, glFxd+83,
+glFxd+84, glFxd+85, glFxd+86, glFxd+87, glFxd+88, glFxd+89,
+glFxd+90, glFxd+91, glFxd+92, glFxd+93, glFxd+94,
+NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, NULL, 0, 0
+};
+
+struct font2 const pbm_defaultFixedfont2 = {
+  sizeof(pbm_defaultFixedfont2),         /* len */
+  PBM_FONT2_STRUCT_SIZE(charset_string), /* size */
+  7, 12, 0, 0,                    /* maxwidth, maxheight, x, y */
+  pbm_defaultFixedfont.glyph,     /* glyph table */
+  255, NULL, 255,                 /* maxglyph, selector, maxmaxglyph */
+  NULL, 0, 0,                     /* oldfont, fcols, frows */
+  PBM_FORMAT,                     /* bit_format */
+  96, 96,                         /* total_chars, chars */
+  FIXED_DATA,                     /* load_fn */
+  32, 1,                          /* default_char, default_char_defined */
+  (char *) "builtin fixed",       /* name */
+  ISO646_1991_IRV, (char *)"ASCII"  /* charset, charset_string */
+};
+
+
+
diff --git a/lib/pbmfontdata2.c b/lib/pbmfontdata2.c
new file mode 100644
index 00000000..11dd84e6
--- /dev/null
+++ b/lib/pbmfontdata2.c
@@ -0,0 +1,463 @@
+#include "pbmfont.h"
+#include "pbmfontdata.h"
+
+/* Default proportional font.
+   BDF-style advance value, bounding box dimensions and point of origin.
+
+
+   Note: In the current version default_bdffont is fixed data, which
+   means it is always accessible, without any preparatory action.
+   However, the storage format may change in future versions.
+
+   To ensure future compatibility call pbm_defaultfont() before accessing
+   default_bdffont.
+
+   The Netpbm development tool 'genfontc' generates C source code like this
+   from a libnetpbm font file or builtin font.
+*/
+
+static struct glyph glBdf[191] = {
+/*  32 character    */
+{ 1, 1, 0, 0, 3, "\0" },
+/*  33 character !  */
+{ 1, 9, 1, 0, 3, "\1\1\1\1\1\1\1\0\1" },
+/*  34 character "  */
+{ 3, 3, 1, 6, 5, "\1\0\1\1\0\1\1\0\1" },
+/*  35 character #  */
+{ 5, 8, 0, 0, 6, "\0\1\0\1\0\0\1\0\1\0\1\1\1\1\1\0\1\0\1\0\0\1\0\1\0\1\1\1\1\1\0\1\0\1\0\0\1\0\1\0" },
+/*  36 character $  */
+{ 5, 11, 0, -1, 6, "\0\0\1\0\0\0\1\1\1\0\1\0\1\0\1\1\0\1\0\0\0\1\1\0\0\0\0\1\1\0\0\0\1\0\1\0\0\1\0\1\1\0\1\0\1\0\1\1\1\0\0\0\1\0\0" },
+/*  37 character %  */
+{ 8, 9, 0, 0, 9, "\0\1\1\0\0\0\1\1\1\0\0\1\1\1\1\0\1\0\0\1\0\1\0\0\0\1\1\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\1\0\1\1\0\0\0\1\0\1\0\0\1\0\1\0\0\1\0\0\1\0\1\0\0\0\1\1\0" },
+/*  38 character &  */
+{ 9, 9, 0, 0, 10, "\0\0\0\1\1\0\0\0\0\0\0\1\0\0\1\0\0\0\0\0\1\0\0\1\0\0\0\0\0\0\1\1\0\1\1\1\0\1\1\1\1\0\0\1\0\1\1\0\0\1\1\1\0\0\1\0\0\0\0\1\0\0\0\1\1\0\0\1\1\1\0\1\0\1\1\1\1\0\1\1\0" },
+/*  39 character '  */
+{ 2, 3, 1, 6, 4, "\1\1\0\1\1\0" },
+/*  40 character (  */
+{ 3, 12, 1, -3, 5, "\0\0\1\0\1\0\0\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\0\1" },
+/*  41 character )  */
+{ 3, 12, 0, -3, 5, "\1\0\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\0\0\1\0\1\0\0" },
+/*  42 character *  */
+{ 5, 5, 0, 4, 6, "\0\0\1\0\0\1\0\1\0\1\0\1\1\1\0\1\0\1\0\1\0\0\1\0\0" },
+/*  43 character +  */
+{ 5, 5, 1, 1, 7, "\0\0\1\0\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\0\1\0\0" },
+/*  44 character ,  */
+{ 2, 3, 0, -2, 3, "\0\1\0\1\1\0" },
+/*  45 character -  */
+{ 5, 1, 1, 3, 8, "\1\1\1\1\1" },
+/*  46 character .  */
+{ 1, 1, 1, 0, 3, "\1" },
+/*  47 character /  */
+{ 3, 9, 0, 0, 3, "\0\0\1\0\0\1\0\0\1\0\1\0\0\1\0\0\1\0\1\0\0\1\0\0\1\0\0" },
+/*  48 character 0  */
+{ 5, 9, 0, 0, 6, "\0\1\1\1\0\1\1\0\1\1\1\0\0\0\1\1\0\0\0\1\1\0\0\0\1\1\0\0\0\1\1\0\0\0\1\1\1\0\1\1\0\1\1\1\0" },
+/*  49 character 1  */
+{ 4, 9, 0, 0, 6, "\0\0\1\0\0\1\1\0\1\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\1\1\1" },
+/*  50 character 2  */
+{ 5, 9, 0, 0, 6, "\0\1\1\1\0\1\0\0\0\1\0\0\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\1\1\1\1\1" },
+/*  51 character 3  */
+{ 5, 9, 0, 0, 6, "\0\1\1\1\0\1\0\0\0\1\0\0\0\0\1\0\0\0\1\0\0\1\1\1\0\0\0\0\0\1\0\0\0\0\1\1\0\0\0\1\0\1\1\1\0" },
+/*  52 character 4  */
+{ 5, 9, 0, 0, 6, "\0\0\0\1\0\0\0\1\1\0\0\0\1\1\0\0\1\0\1\0\0\1\0\1\0\1\0\0\1\0\1\1\1\1\1\0\0\0\1\0\0\0\0\1\0" },
+/*  53 character 5  */
+{ 5, 9, 0, 0, 6, "\0\0\1\1\1\0\1\0\0\0\0\1\0\0\0\0\1\1\1\0\0\0\0\1\1\0\0\0\0\1\0\0\0\0\1\1\0\0\1\1\0\1\1\1\0" },
+/*  54 character 6  */
+{ 5, 9, 0, 0, 6, "\0\0\0\1\1\0\1\1\0\0\0\1\0\0\0\1\1\1\1\0\1\0\0\1\1\1\0\0\0\1\1\0\0\0\1\1\1\0\0\1\0\1\1\1\0" },
+/*  55 character 7  */
+{ 5, 9, 0, 0, 6, "\1\1\1\1\1\1\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\0\0\1\0\0\0" },
+/*  56 character 8  */
+{ 5, 9, 0, 0, 6, "\0\1\1\1\0\1\0\0\0\1\1\0\0\0\1\1\1\0\0\1\0\1\1\1\0\1\0\0\1\1\1\0\0\0\1\1\0\0\0\1\0\1\1\1\0" },
+/*  57 character 9  */
+{ 5, 9, 0, 0, 6, "\0\1\1\1\0\1\0\0\1\1\1\0\0\0\1\1\0\0\0\1\1\1\0\0\1\0\1\1\1\1\0\0\0\1\0\0\0\1\1\0\1\1\0\0\0" },
+/*  58 character :  */
+{ 1, 6, 1, 0, 3, "\1\0\0\0\0\1" },
+/*  59 character ;  */
+{ 2, 8, 0, -2, 3, "\0\1\0\0\0\0\0\0\0\0\0\1\0\1\1\0" },
+/*  60 character <  */
+{ 6, 5, 0, 1, 8, "\0\0\0\0\1\1\0\0\1\1\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1" },
+/*  61 character =  */
+{ 5, 3, 1, 2, 7, "\1\1\1\1\1\0\0\0\0\0\1\1\1\1\1" },
+/*  62 character >  */
+{ 6, 5, 1, 1, 8, "\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\1\1\0\0\1\1\0\0\0\0" },
+/*  63 character ?  */
+{ 4, 9, 0, 0, 5, "\0\1\1\0\1\0\0\1\0\0\0\1\0\0\1\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\0\0\1\0\0" },
+/*  64 character @  */
+{ 10, 11, 1, -2, 11, "\0\0\0\0\1\1\1\1\0\0\0\0\1\1\0\0\0\0\1\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\1\1\0\1\0\1\1\0\0\1\0\0\1\0\0\1\1\0\1\0\0\0\1\0\0\1\1\0\1\0\0\1\0\0\1\0\1\0\1\0\0\1\0\0\1\0\1\0\0\1\1\0\1\1\0\0\0\1\0\0\0\0\0\0\0\0\0\0\1\1\1\1\1\0\0\0" },
+/*  65 character A  */
+{ 9, 9, 0, 0, 9, "\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\1" },
+/*  66 character B  */
+{ 7, 9, 0, 0, 8, "\1\1\1\1\1\1\0\0\1\0\0\0\1\1\0\1\0\0\0\0\1\0\1\0\0\0\1\1\0\1\1\1\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\1\0\1\0\0\0\1\1\1\1\1\1\1\1\0" },
+/*  67 character C  */
+{ 7, 9, 0, 0, 8, "\0\0\1\1\1\0\1\0\1\1\0\0\1\1\0\1\0\0\0\0\1\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\1\0\1\1\0\0\1\1\0\0\1\1\1\1\0" },
+/*  68 character D  */
+{ 8, 9, 0, 0, 9, "\1\1\1\1\1\1\0\0\0\1\0\0\0\1\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\0\0\0\1\1\0\1\1\1\1\1\1\0\0" },
+/*  69 character E  */
+{ 7, 9, 0, 0, 8, "\1\1\1\1\1\1\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\1\1\1" },
+/*  70 character F  */
+{ 7, 9, 0, 0, 8, "\1\1\1\1\1\1\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\1\1\1\0\0\0" },
+/*  71 character G  */
+{ 8, 9, 0, 0, 9, "\0\0\1\1\1\0\1\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\0\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
+/*  72 character H  */
+{ 8, 9, 0, 0, 9, "\1\1\1\0\0\1\1\1\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\1\1\1\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\1\1\1\0\0\1\1\1" },
+/*  73 character I  */
+{ 3, 9, 0, 0, 4, "\1\1\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
+/*  74 character J  */
+{ 4, 9, 0, 0, 4, "\0\1\1\1\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\1\0\1\0\1\1\0\0" },
+/*  75 character K  */
+{ 8, 9, 0, 0, 8, "\1\1\1\0\1\1\1\0\0\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\1\1\1\0\0\0\0\0\1\0\1\1\0\0\0\0\1\0\0\1\1\0\0\0\1\0\0\0\1\1\0\1\1\1\0\0\1\1\1" },
+/*  76 character L  */
+{ 6, 9, 0, 0, 7, "\1\1\1\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\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\1\1\1\1\1\1\1" },
+/*  77 character M  */
+{ 11, 9, 0, 0, 11, "\1\1\0\0\0\0\0\0\0\1\1\0\1\1\0\0\0\0\0\1\1\0\0\1\1\0\0\0\0\0\1\1\0\0\1\0\1\0\0\0\1\0\1\0\0\1\0\1\0\0\0\1\0\1\0\0\1\0\0\1\0\1\0\0\1\0\0\1\0\0\1\0\1\0\0\1\0\0\1\0\0\0\1\0\0\0\1\0\1\1\1\0\0\1\0\0\1\1\1" },
+/*  78 character N  */
+{ 9, 9, 0, 0, 9, "\1\1\0\0\0\0\1\1\1\0\1\1\0\0\0\0\1\0\0\1\1\0\0\0\0\1\0\0\1\0\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\0\1\0\0\1\0\0\0\0\1\1\0\1\1\1\0\0\0\0\1\0" },
+/*  79 character O  */
+{ 8, 9, 0, 0, 9, "\0\0\1\1\1\1\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
+/*  80 character P  */
+{ 7, 9, 0, 0, 7, "\1\1\1\1\1\1\0\0\1\0\0\0\1\1\0\1\0\0\0\0\1\0\1\0\0\0\1\1\0\1\1\1\1\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\1\1\0\0\0\0" },
+/*  81 character Q  */
+{ 8, 11, 0, -2, 9, "\0\0\1\1\1\1\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\0\0\1\1" },
+/*  82 character R  */
+{ 8, 9, 0, 0, 8, "\1\1\1\1\1\1\0\0\0\1\0\0\0\1\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\1\1\0\0\1\1\1\1\1\0\0\0\1\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\1\0\1\1\1\0\0\0\1\1" },
+/*  83 character S  */
+{ 6, 9, 0, 0, 7, "\0\1\1\1\0\1\1\0\0\0\1\1\1\0\0\0\0\1\0\1\1\0\0\0\0\0\1\1\1\0\0\0\0\0\1\1\1\0\0\0\0\1\1\1\0\0\1\1\1\0\1\1\1\0" },
+/*  84 character T  */
+{ 7, 9, 0, 0, 7, "\1\1\1\1\1\1\1\1\0\0\1\0\0\1\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\1\1\1\0\0" },
+/*  85 character U  */
+{ 8, 9, 0, 0, 8, "\1\1\1\0\0\1\1\1\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
+/*  86 character V  */
+{ 9, 9, 0, 0, 9, "\1\1\1\0\0\0\1\1\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0" },
+/*  87 character W  */
+{ 12, 9, 0, 0, 12, "\1\1\1\0\1\1\1\0\0\1\1\1\0\1\0\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\0\1\1\0\1\1\1\0\1\0\0\0\0\0\1\0\1\0\1\0\1\0\0\0\0\0\1\1\0\0\1\1\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0" },
+/*  88 character X  */
+{ 8, 9, 0, 0, 8, "\1\1\1\0\0\1\1\1\0\1\0\0\0\0\1\0\0\0\1\0\0\1\0\0\0\0\1\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\1\0\1\1\0\0\0\0\1\0\0\1\0\0\0\1\0\0\0\0\1\0\1\1\1\0\0\1\1\1" },
+/*  89 character Y  */
+{ 9, 9, 0, 0, 9, "\1\1\1\0\0\0\1\1\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\0\1\0\0\0\0\0\1\1\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\1\1\0\0\0" },
+/*  90 character Z  */
+{ 7, 9, 0, 0, 8, "\1\1\1\1\1\1\1\1\0\0\0\0\1\1\0\0\0\0\1\1\0\0\0\0\1\1\0\0\0\0\1\1\0\0\0\0\1\1\0\0\0\0\1\1\0\0\0\0\0\1\0\0\0\0\0\1\1\1\1\1\1\1\1" },
+/*  91 character [  */
+{ 3, 12, 1, -3, 5, "\1\1\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1" },
+/*  92 character \  */
+{ 3, 9, 0, 0, 3, "\1\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\1\0\0\0\1\0\0\1\0\0\1" },
+/*  93 character ]  */
+{ 3, 12, 0, -3, 5, "\1\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1\1" },
+/*  94 character ^  */
+{ 5, 5, 0, 4, 6, "\0\0\1\0\0\0\1\0\1\0\0\1\0\1\0\1\0\0\0\1\1\0\0\0\1" },
+/*  95 character _  */
+{ 6, 1, 0, -3, 6, "\1\1\1\1\1\1" },
+/*  96 character `  */
+{ 2, 3, 1, 6, 4, "\0\1\1\0\1\1" },
+/*  97 character a  */
+{ 5, 6, 1, 0, 6, "\0\1\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" },
+/*  98 character b  */
+{ 5, 9, 0, 0, 6, "\1\1\0\0\0\0\1\0\0\0\0\1\0\0\0\0\1\1\1\0\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\1\1\0" },
+/*  99 character c  */
+{ 4, 6, 1, 0, 5, "\0\1\1\0\1\0\0\1\1\0\0\0\1\0\0\0\1\0\0\1\0\1\1\0" },
+/* 100 character d  */
+{ 5, 9, 1, 0, 6, "\0\0\1\1\0\0\0\0\1\0\0\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" },
+/* 101 character e  */
+{ 5, 6, 1, 0, 6, "\0\1\1\0\0\1\0\0\1\0\1\1\1\1\0\1\0\0\0\0\1\1\0\0\1\0\1\1\1\0" },
+/* 102 character f  */
+{ 3, 9, 0, 0, 3, "\0\0\1\0\1\0\0\1\0\1\1\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0" },
+/* 103 character g  */
+{ 5, 9, 1, -3, 6, "\0\1\1\1\1\1\0\0\1\0\1\0\0\1\0\1\1\1\0\0\0\1\0\0\0\0\1\1\1\0\1\0\0\0\1\1\0\0\0\1\0\1\1\1\0" },
+/* 104 character h  */
+{ 6, 9, 0, 0, 6, "\1\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1\1\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1\0\1\1" },
+/* 105 character i  */
+{ 3, 9, 0, 0, 3, "\0\1\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
+/* 106 character j  */
+{ 2, 12, 0, -3, 3, "\0\1\0\0\0\0\1\1\0\1\0\1\0\1\0\1\0\1\0\1\0\1\1\0" },
+/* 107 character k  */
+{ 6, 9, 0, 0, 6, "\1\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\0\1\0\0\0\1\1\0\0\0\0\1\0\1\0\0\0\1\0\0\1\0\0\1\0\0\1\1" },
+/* 108 character l  */
+{ 3, 9, 0, 0, 3, "\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
+/* 109 character m  */
+{ 9, 6, 0, 0, 9, "\1\0\1\1\0\1\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1\0\1\1\0\1\1" },
+/* 110 character n  */
+{ 6, 6, 0, 0, 6, "\1\0\1\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1\0\1\1" },
+/* 111 character o  */
+{ 4, 6, 1, 0, 6, "\0\1\1\0\1\0\0\1\1\0\0\1\1\0\0\1\1\0\0\1\0\1\1\0" },
+/* 112 character p  */
+{ 5, 9, 0, -3, 6, "\1\1\1\1\0\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\1\1\0\0\1\0\0\0\0\1\0\0\0\1\1\1\0\0" },
+/* 113 character q  */
+{ 5, 9, 1, -3, 6, "\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\1\0\0\0\0\1\0\0\0\0\1\0\0\0\1\1\1" },
+/* 114 character r  */
+{ 4, 6, 0, 0, 4, "\1\0\1\1\0\1\1\0\0\1\0\0\0\1\0\0\0\1\0\0\1\1\1\0" },
+/* 115 character s  */
+{ 4, 6, 1, 0, 6, "\0\1\1\1\1\0\0\1\1\1\0\0\0\0\1\1\1\0\0\1\1\1\1\0" },
+/* 116 character t  */
+{ 4, 7, 0, 0, 4, "\0\1\0\0\1\1\1\1\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\1\1" },
+/* 117 character u  */
+{ 6, 6, 0, 0, 6, "\1\1\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\1\0\1" },
+/* 118 character v  */
+{ 6, 6, 0, 0, 6, "\1\1\0\0\1\1\0\1\0\0\1\0\0\1\0\1\1\0\0\1\0\1\0\0\0\0\1\1\0\0\0\0\1\0\0\0" },
+/* 119 character w  */
+{ 9, 6, 0, 0, 9, "\1\1\1\0\1\1\0\1\1\0\1\0\0\1\0\0\1\0\0\1\1\0\1\0\1\1\0\0\0\1\0\1\0\1\0\0\0\0\1\1\0\1\0\0\0\0\0\1\0\0\1\0\0\0" },
+/* 120 character x  */
+{ 5, 6, 1, 0, 6, "\1\1\0\1\1\0\1\0\1\0\0\0\1\0\0\0\0\1\0\0\0\1\0\1\0\1\1\0\1\1" },
+/* 121 character y  */
+{ 6, 9, 0, -3, 6, "\1\1\0\0\1\1\0\1\0\0\1\0\0\1\0\1\1\0\0\1\0\1\0\0\0\0\1\1\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\1\0\0\0\0\1\1\0\0\0\0" },
+/* 122 character z  */
+{ 4, 6, 1, 0, 6, "\1\1\1\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1\1\1" },
+/* 123 character {  */
+{ 4, 12, 1, -3, 6, "\0\0\1\1\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\1\1" },
+/* 124 character |  */
+{ 1, 9, 1, 0, 3, "\1\1\1\1\1\1\1\1\1" },
+/* 125 character }  */
+{ 4, 12, 0, -3, 6, "\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\1\1\0\0" },
+/* 126 character ~  */
+{ 6, 2, 0, 3, 7, "\0\1\1\0\0\1\1\0\0\1\1\0" },
+/* 160 */
+{ 1, 1, 0, 0, 3, "\0" },
+/* 161 */
+{ 1, 9, 1, -3, 4, "\1\0\1\1\1\1\1\1\1" },
+/* 162 */
+{ 5, 8, 1, -1, 6, "\0\0\0\0\1\0\1\1\1\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\1\0\0\1\0\1\1\1\0\1\0\0\0\0" },
+/* 163 */
+{ 5, 9, 0, 0, 6, "\0\0\1\1\0\0\1\0\0\1\0\1\0\0\0\0\1\0\0\0\1\1\1\1\0\0\1\0\0\0\0\1\0\0\0\1\1\1\0\1\1\1\0\1\1" },
+/* 164 */
+{ 6, 7, 1, 1, 7, "\1\0\0\0\0\1\0\1\1\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1\1\0\1\0\0\0\0\1" },
+/* 165 */
+{ 5, 9, 0, 0, 6, "\1\0\0\0\1\1\0\0\0\1\0\1\0\1\0\0\1\0\1\0\1\1\1\1\1\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\1\1\0" },
+/* 166 */
+{ 1, 9, 1, 0, 3, "\1\1\1\0\0\1\1\1\1" },
+/* 167 */
+{ 4, 12, 1, -3, 6, "\0\1\1\1\1\0\0\1\1\1\0\0\0\1\1\0\1\0\1\1\1\0\0\1\1\0\0\1\1\1\0\1\0\1\1\0\0\0\1\1\1\0\0\1\1\1\1\0" },
+/* 168 */
+{ 3, 1, 0, 7, 3, "\1\0\1" },
+/* 169 */
+{ 9, 9, 1, 0, 11, "\0\0\0\1\1\1\0\0\0\0\1\1\0\0\0\1\1\0\0\1\0\1\1\1\0\1\0\1\0\1\0\0\1\0\0\1\1\0\1\0\0\0\0\0\1\1\0\1\0\0\1\0\0\1\0\1\0\1\1\1\0\1\0\0\1\1\0\0\0\1\1\0\0\0\0\1\1\1\0\0\0" },
+/* 170 */
+{ 3, 6, 1, 3, 5, "\1\1\0\0\0\1\1\1\1\1\0\1\0\0\0\1\1\1" },
+/* 171 */
+{ 5, 5, 1, 0, 7, "\0\0\1\0\1\0\1\0\1\0\1\0\1\0\0\0\1\0\1\0\0\0\1\0\1" },
+/* 172 */
+{ 6, 4, 1, 1, 8, "\1\1\1\1\1\1\0\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\1" },
+/* 173 */
+{ 4, 1, 1, 3, 6, "\1\1\1\1" },
+/* 174 */
+{ 9, 9, 1, 0, 11, "\0\0\0\1\1\1\0\0\0\0\1\1\0\0\0\1\1\0\0\1\0\1\1\1\0\1\0\1\0\0\1\0\0\1\0\1\1\0\0\1\1\1\0\0\1\1\0\0\1\0\1\0\0\1\1\1\0\1\0\1\0\1\0\0\1\1\0\0\0\1\1\0\0\0\1\1\1\1\0\0\0" },
+/* 175 */
+{ 4, 1, 0, 7, 4, "\1\1\1\1" },
+/* 176 */
+{ 4, 4, 0, 5, 5, "\0\1\1\0\1\0\0\1\1\0\0\1\0\1\1\0" },
+/* 177 */
+{ 5, 7, 1, 0, 7, "\0\0\1\0\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\0\1\0\0\0\0\0\0\0\1\1\1\1\1" },
+/* 178 */
+{ 4, 5, 0, 4, 4, "\0\1\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1\1" },
+/* 179 */
+{ 3, 5, 0, 4, 4, "\1\1\1\0\0\1\0\1\0\0\0\1\1\1\0" },
+/* 180 */
+{ 2, 2, 1, 7, 4, "\0\1\1\0" },
+/* 181 */
+{ 6, 9, 0, -3, 6, "\1\1\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\1\0\0\0\0\0\1\0\0\0\0\0\1\1\0\0\0" },
+/* 182 */
+{ 6, 12, 0, -3, 7, "\0\1\1\1\1\1\1\1\1\0\1\0\1\1\1\0\1\0\1\1\1\0\1\0\1\1\1\0\1\0\0\1\1\0\1\0\0\0\1\0\1\0\0\0\1\0\1\0\0\0\1\0\1\0\0\0\1\0\1\0\0\0\1\0\1\0\0\0\1\0\1\0" },
+/* 183 */
+{ 1, 1, 1, 3, 3, "\1" },
+/* 184 */
+{ 3, 3, 0, -3, 3, "\0\1\0\0\0\1\1\1\1" },
+/* 185 */
+{ 3, 5, 0, 4, 4, "\0\1\0\1\1\0\0\1\0\0\1\0\1\1\1" },
+/* 186 */
+{ 3, 6, 1, 3, 5, "\0\1\0\1\0\1\1\0\1\0\1\0\0\0\0\1\1\1" },
+/* 187 */
+{ 5, 5, 0, 0, 7, "\1\0\1\0\0\0\1\0\1\0\0\0\1\0\1\0\1\0\1\0\1\0\1\0\0" },
+/* 188 */
+{ 9, 9, 0, 0, 9, "\0\1\0\0\0\0\1\0\0\1\1\0\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\1\1\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\0\0\1\0\1\0\1\0\0\0\1\0\0\1\1\1\1\0\0\1\0\0\0\0\1\0" },
+/* 189 */
+{ 9, 9, 0, 0, 9, "\0\1\0\0\0\0\1\0\0\1\1\0\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\1\1\0\1\0\1\1\0\0\0\0\1\0\1\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\1\0\0\1\1\1\1" },
+/* 190 */
+{ 9, 9, 0, 0, 9, "\1\1\1\0\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\0\0\0\0\1\1\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\0\0\1\0\1\0\1\0\0\0\1\0\0\1\1\1\1\0\0\1\0\0\0\0\1\0" },
+/* 191 */
+{ 4, 9, 0, -3, 5, "\0\0\1\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\1\0\0\1\0\0\0\1\0\0\1\0\1\1\0" },
+/* 192 */
+{ 9, 12, 0, 0, 9, "\0\0\0\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\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\1" },
+/* 193 */
+{ 9, 12, 0, 0, 9, "\0\0\0\0\0\1\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\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\1" },
+/* 194 */
+{ 9, 12, 0, 0, 9, "\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\1" },
+/* 195 */
+{ 9, 12, 0, 0, 9, "\0\0\0\0\1\0\1\0\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\1" },
+/* 196 */
+{ 9, 11, 0, 0, 9, "\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\1" },
+/* 197 */
+{ 9, 12, 0, 0, 9, "\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\1\1\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\1\1\1\0\0\0\1\1\1" },
+/* 198 */
+{ 10, 9, 0, 0, 11, "\0\0\1\1\1\1\1\1\1\1\0\0\0\1\1\0\0\0\0\1\0\0\1\0\1\0\0\0\0\0\0\0\1\0\1\0\0\0\1\0\0\1\0\0\1\1\1\1\1\0\0\1\1\1\1\0\0\0\1\0\0\1\0\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\0\1\1\1\0\0\1\1\1\1\1\1" },
+/* 199 */
+{ 7, 12, 0, -3, 8, "\0\0\1\1\1\0\1\0\1\1\0\0\1\1\0\1\0\0\0\0\1\1\0\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\1\0\1\1\0\0\1\1\0\0\1\1\1\1\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\1\1\1\0\0" },
+/* 200 */
+{ 7, 12, 0, 0, 8, "\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\1\1\1\1\1\1\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\1\1\1" },
+/* 201 */
+{ 7, 12, 0, 0, 8, "\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\1\1\1\1\1\1\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\1\1\1" },
+/* 202 */
+{ 7, 12, 0, 0, 8, "\0\0\0\1\0\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\1\1\1\1\1\1\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\1\1\1" },
+/* 203 */
+{ 7, 11, 0, 0, 8, "\0\0\1\0\1\0\0\0\0\0\0\0\0\0\1\1\1\1\1\1\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\0\0\0\1\0\0\1\1\1\1\1\0\0\1\0\0\0\1\0\0\1\0\0\0\0\0\0\1\0\0\0\0\1\1\1\1\1\1\1\1" },
+/* 204 */
+{ 3, 12, 0, 0, 4, "\1\0\0\0\1\0\0\0\0\1\1\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
+/* 205 */
+{ 3, 12, 0, 0, 4, "\0\0\1\0\1\0\0\0\0\1\1\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
+/* 206 */
+{ 3, 12, 0, 0, 4, "\0\1\0\1\0\1\0\0\0\1\1\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
+/* 207 */
+{ 3, 11, 0, 0, 4, "\1\0\1\0\0\0\1\1\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
+/* 208 */
+{ 8, 9, 0, 0, 9, "\1\1\1\1\1\1\0\0\0\1\0\0\0\1\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\0\1\1\1\1\0\0\0\0\1\0\1\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\0\0\0\1\1\0\1\1\1\1\1\1\0\0" },
+/* 209 */
+{ 9, 12, 0, 0, 9, "\0\0\0\0\1\0\1\0\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\1\1\0\0\0\0\1\1\1\0\1\1\0\0\0\0\1\0\0\1\1\0\0\0\0\1\0\0\1\0\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\0\1\0\0\1\0\0\0\0\1\1\0\1\1\1\0\0\0\0\1\0" },
+/* 210 */
+{ 8, 12, 0, 0, 9, "\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\1\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
+/* 211 */
+{ 8, 12, 0, 0, 9, "\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\1\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
+/* 212 */
+{ 8, 12, 0, 0, 9, "\0\0\0\1\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\1\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
+/* 213 */
+{ 8, 12, 0, 0, 9, "\0\0\0\1\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\1\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
+/* 214 */
+{ 8, 11, 0, 0, 9, "\0\0\1\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\1\0\0\0\1\1\0\0\1\1\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\1\0\0\0\0\0\0\1\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
+/* 215 */
+{ 5, 5, 1, 1, 7, "\1\0\0\0\1\0\1\0\1\0\0\0\1\0\0\0\1\0\1\0\1\0\0\0\1" },
+/* 216 */
+{ 9, 10, 0, 0, 9, "\0\0\0\0\0\0\0\0\1\0\0\1\1\1\1\0\1\0\0\1\1\0\0\1\1\0\0\0\1\0\0\0\1\1\0\0\1\0\0\0\1\0\0\1\0\1\0\0\0\1\0\0\1\0\1\0\0\1\0\0\0\1\0\0\1\1\0\0\0\1\0\0\0\1\1\0\0\1\1\0\0\1\0\1\1\1\1\0\0\0" },
+/* 217 */
+{ 8, 12, 0, 0, 8, "\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\0\1\1\1\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
+/* 218 */
+{ 8, 12, 0, 0, 8, "\0\0\0\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\0\1\1\1\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
+/* 219 */
+{ 8, 12, 0, 0, 8, "\0\0\0\1\0\0\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\0\1\1\1\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
+/* 220 */
+{ 8, 11, 0, 0, 8, "\0\0\1\0\1\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\0\1\1\1\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\1\0\0\0\1\1\1\1\0\0" },
+/* 221 */
+{ 9, 12, 0, 0, 9, "\0\0\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\1\1\1\0\0\0\1\1\1\0\1\0\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\1\0\0\1\0\0\0\0\0\1\1\1\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\1\1\1\0\0\0" },
+/* 222 */
+{ 7, 9, 0, 0, 7, "\1\1\1\0\0\0\0\0\1\0\0\0\0\0\0\1\1\1\1\1\0\0\1\0\0\0\1\1\0\1\0\0\0\0\1\0\1\0\0\0\1\1\0\1\1\1\1\1\0\0\1\0\0\0\0\0\1\1\1\0\0\0\0" },
+/* 223 */
+{ 6, 9, 0, 0, 6, "\0\0\1\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\0\0\0\1\1\1\0\0\0\1\0\0\1\0\0\1\0\0\0\1\0\1\0\0\0\1\1\1\0\1\1\0" },
+/* 224 */
+{ 5, 9, 1, 0, 6, "\0\1\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" },
+/* 225 */
+{ 5, 9, 1, 0, 6, "\0\0\0\1\0\0\0\1\0\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" },
+/* 226 */
+{ 5, 9, 1, 0, 6, "\0\0\1\0\0\0\1\0\1\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" },
+/* 227 */
+{ 5, 9, 1, 0, 6, "\0\1\0\1\0\1\0\1\0\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" },
+/* 228 */
+{ 5, 8, 1, 0, 6, "\0\1\0\1\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" },
+/* 229 */
+{ 5, 9, 1, 0, 6, "\0\0\1\0\0\0\1\0\1\0\0\0\1\0\0\0\1\1\0\0\1\0\0\1\0\0\1\1\1\0\1\0\0\1\0\1\0\0\1\0\0\1\1\0\1" },
+/* 230 */
+{ 8, 6, 1, 0, 9, "\0\1\1\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1\1\1\1\0\1\0\0\1\0\0\0\0\1\0\0\1\1\0\0\1\0\1\1\0\1\1\1\0" },
+/* 231 */
+{ 4, 9, 1, -3, 5, "\0\1\1\0\1\0\0\1\1\0\0\0\1\0\0\0\1\0\0\1\0\1\1\0\0\1\0\0\0\0\1\0\1\1\1\0" },
+/* 232 */
+{ 5, 9, 1, 0, 6, "\0\1\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\1\1\1\1\0\1\0\0\0\0\1\1\0\0\1\0\1\1\1\0" },
+/* 233 */
+{ 5, 9, 1, 0, 6, "\0\0\1\0\0\0\1\0\0\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\1\1\1\1\0\1\0\0\0\0\1\1\0\0\1\0\1\1\1\0" },
+/* 234 */
+{ 5, 9, 1, 0, 6, "\0\0\1\0\0\0\1\0\1\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\1\1\1\1\0\1\0\0\0\0\1\1\0\0\1\0\1\1\1\0" },
+/* 235 */
+{ 5, 8, 1, 0, 6, "\0\1\0\1\0\0\0\0\0\0\0\1\1\0\0\1\0\0\1\0\1\1\1\1\0\1\0\0\0\0\1\1\0\0\1\0\1\1\1\0" },
+/* 236 */
+{ 3, 9, 0, 0, 3, "\1\0\0\0\1\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
+/* 237 */
+{ 3, 9, 0, 0, 3, "\0\1\0\1\0\0\0\0\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
+/* 238 */
+{ 3, 9, 0, 0, 3, "\0\1\0\1\0\1\0\0\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
+/* 239 */
+{ 3, 8, 0, 0, 3, "\1\0\1\0\0\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1" },
+/* 240 */
+{ 4, 9, 1, 0, 6, "\0\1\0\0\0\1\1\1\1\0\1\0\0\1\1\1\1\0\0\1\1\0\0\1\1\0\0\1\1\0\0\1\0\1\1\0" },
+/* 241 */
+{ 6, 9, 0, 0, 6, "\0\0\1\0\1\0\0\1\0\1\0\0\0\0\0\0\0\0\1\0\1\1\0\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\1\1\1\0\1\1" },
+/* 242 */
+{ 4, 9, 1, 0, 6, "\0\1\0\0\0\0\1\0\0\0\0\0\0\1\1\0\1\0\0\1\1\0\0\1\1\0\0\1\1\0\0\1\0\1\1\0" },
+/* 243 */
+{ 4, 9, 1, 0, 6, "\0\0\1\0\0\1\0\0\0\0\0\0\0\1\1\0\1\0\0\1\1\0\0\1\1\0\0\1\1\0\0\1\0\1\1\0" },
+/* 244 */
+{ 4, 9, 1, 0, 6, "\0\0\1\0\0\1\0\1\0\0\0\0\0\1\1\0\1\0\0\1\1\0\0\1\1\0\0\1\1\0\0\1\0\1\1\0" },
+/* 245 */
+{ 4, 9, 1, 0, 6, "\0\1\0\1\1\0\1\0\0\0\0\0\0\1\1\0\1\0\0\1\1\0\0\1\1\0\0\1\1\0\0\1\0\1\1\0" },
+/* 246 */
+{ 4, 8, 1, 0, 6, "\1\0\1\0\0\0\0\0\0\1\1\0\1\0\0\1\1\0\0\1\1\0\0\1\1\0\0\1\0\1\1\0" },
+/* 247 */
+{ 5, 5, 1, 1, 7, "\0\0\1\0\0\0\0\0\0\0\1\1\1\1\1\0\0\0\0\0\0\0\1\0\0" },
+/* 248 */
+{ 6, 7, 0, -1, 6, "\0\0\1\1\0\1\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\1\1\0\0\1\0\0\0\0\0" },
+/* 249 */
+{ 6, 9, 0, 0, 6, "\0\0\1\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0\1\1\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\1\0\1" },
+/* 250 */
+{ 6, 9, 0, 0, 6, "\0\0\0\1\0\0\0\0\1\0\0\0\0\0\0\0\0\0\1\1\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\1\0\1" },
+/* 251 */
+{ 6, 9, 0, 0, 6, "\0\0\1\0\0\0\0\1\0\1\0\0\0\0\0\0\0\0\1\1\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\1\0\1" },
+/* 252 */
+{ 6, 8, 0, 0, 6, "\0\1\0\1\0\0\0\0\0\0\0\0\1\1\0\1\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\1\0\0\0\1\1\0\1" },
+/* 253 */
+{ 6, 12, 0, -3, 6, "\0\0\0\0\1\0\0\0\0\1\0\0\0\0\0\0\0\0\1\1\0\0\1\1\0\1\0\0\1\0\0\1\0\1\1\0\0\1\0\1\0\0\0\0\1\1\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\1\0\0\0\0\1\1\0\0\0\0" },
+/* 254 */
+{ 5, 12, 0, -3, 6, "\1\1\0\0\0\0\1\0\0\0\0\1\0\0\0\0\1\1\1\0\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\0\0\1\0\1\1\1\0\0\1\0\0\0\0\1\0\0\0\1\1\1\0\0" },
+/* 255 */
+{ 6, 11, 0, -3, 6, "\0\1\0\0\1\0\0\0\0\0\0\0\1\1\0\0\1\1\0\1\0\0\1\0\0\1\0\1\1\0\0\1\0\1\0\0\0\0\1\1\0\0\0\0\1\0\0\0\0\0\1\0\0\0\0\1\0\0\0\0\1\1\0\0\0\0" }
+
+};
+
+struct font pbm_defaultBdffont = { 14, 15, -1, -3, {
+NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+glBdf+ 0, glBdf+ 1, glBdf+ 2, glBdf+ 3, glBdf+ 4, glBdf+ 5,
+glBdf+ 6, glBdf+ 7, glBdf+ 8, glBdf+ 9, glBdf+10, glBdf+11,
+glBdf+12, glBdf+13, glBdf+14, glBdf+15, glBdf+16, glBdf+17,
+glBdf+18, glBdf+19, glBdf+20, glBdf+21, glBdf+22, glBdf+23,
+glBdf+24, glBdf+25, glBdf+26, glBdf+27, glBdf+28, glBdf+29,
+glBdf+30, glBdf+31, glBdf+32, glBdf+33, glBdf+34, glBdf+35,
+glBdf+36, glBdf+37, glBdf+38, glBdf+39, glBdf+40, glBdf+41,
+glBdf+42, glBdf+43, glBdf+44, glBdf+45, glBdf+46, glBdf+47,
+glBdf+48, glBdf+49, glBdf+50, glBdf+51, glBdf+52, glBdf+53,
+glBdf+54, glBdf+55, glBdf+56, glBdf+57, glBdf+58, glBdf+59,
+glBdf+60, glBdf+61, glBdf+62, glBdf+63, glBdf+64, glBdf+65,
+glBdf+66, glBdf+67, glBdf+68, glBdf+69, glBdf+70, glBdf+71,
+glBdf+72, glBdf+73, glBdf+74, glBdf+75, glBdf+76, glBdf+77,
+glBdf+78, glBdf+79, glBdf+80, glBdf+81, glBdf+82, glBdf+83,
+glBdf+84, glBdf+85, glBdf+86, glBdf+87, glBdf+88, glBdf+89,
+glBdf+90, glBdf+91, glBdf+92, glBdf+93, glBdf+94,
+NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+glBdf+95,  glBdf+96,  glBdf+97,  glBdf+98,  glBdf+99,  glBdf+100,
+glBdf+101, glBdf+102, glBdf+103, glBdf+104, glBdf+105, glBdf+106,
+glBdf+107, glBdf+108, glBdf+109, glBdf+110, glBdf+111, glBdf+112,
+glBdf+113, glBdf+114, glBdf+115, glBdf+116, glBdf+117, glBdf+118,
+glBdf+119, glBdf+120, glBdf+121, glBdf+122, glBdf+123, glBdf+124,
+glBdf+125, glBdf+126, glBdf+127, glBdf+128, glBdf+129, glBdf+130,
+glBdf+131, glBdf+132, glBdf+133, glBdf+134, glBdf+135, glBdf+136,
+glBdf+137, glBdf+138, glBdf+139, glBdf+140, glBdf+141, glBdf+142,
+glBdf+143, glBdf+144, glBdf+145, glBdf+146, glBdf+147, glBdf+148,
+glBdf+149, glBdf+150, glBdf+151, glBdf+152, glBdf+153, glBdf+154,
+glBdf+155, glBdf+156, glBdf+157, glBdf+158, glBdf+159, glBdf+160,
+glBdf+161, glBdf+162, glBdf+163, glBdf+164, glBdf+165, glBdf+166,
+glBdf+167, glBdf+168, glBdf+169, glBdf+170, glBdf+171, glBdf+172,
+glBdf+173, glBdf+174, glBdf+175, glBdf+176, glBdf+177, glBdf+178,
+glBdf+179, glBdf+180, glBdf+181, glBdf+182, glBdf+183, glBdf+184,
+glBdf+185, glBdf+186, glBdf+187, glBdf+188, glBdf+189, glBdf+190 },
+NULL, 0, 0
+};
+
+struct font2 const pbm_defaultBdffont2 = {
+  sizeof(pbm_defaultFixedfont2),         /* len */
+  PBM_FONT2_STRUCT_SIZE(charset_string), /* size */
+  14, 15, -1, -3,                 /* maxwidth, maxheight, x, y */
+  pbm_defaultBdffont.glyph,       /* glyph table */
+  255, NULL, 255,                 /* maxglyph, selector, maxmaxglyph */
+  NULL, 0, 0,                     /* oldfont, fcols, frows */
+  PBM_FORMAT,                     /* bit_format */
+  190, 190,                       /* total_chars, chars */
+  FIXED_DATA,                     /* load_fn */
+  32, 1,                          /* default_char, default_char_defined */
+  (char *) "builtin bdf",         /* name */
+  ISO_8859_1, (char *)"ISO8859-1" /* charset, charset_string */
+};
+
+
+
diff --git a/lib/pgm.h b/lib/pgm.h
index 2de8d531..d4655239 100644
--- a/lib/pgm.h
+++ b/lib/pgm.h
@@ -24,7 +24,7 @@ typedef unsigned int gray;
    (because then old Netpbm programs can process them, and they're
    only half as big).
 
-   So we keep PGM_MAXMAXVAL = 255, even though it's kind of a misnomer.  
+   So we keep PGM_MAXMAXVAL = 255, even though it's kind of a misnomer.
 
    Note that one could always write a file with maxval > PGM_MAXMAXVAL and
    it would just go into plain (text) format instead of raw (binary) format.
@@ -42,6 +42,8 @@ typedef unsigned int gray;
 #define PGM_OVERALLMAXVAL 65535
 #define PGM_MAXMAXVAL 255
 
+#define pgm_unnormalize(value, maxval) \
+  ((gray)((value + 1e-6) * (maxval) + 0.5))
 
 /* Magic constants. */
 
@@ -62,7 +64,7 @@ typedef unsigned int gray;
 
 /* Declarations of routines. */
 
-void 
+void
 pgm_init(int *   const argcP,
          char ** const argv);
 
@@ -78,35 +80,35 @@ pgm_allocrow(unsigned int const cols);
 gray **
 pgm_readpgm(FILE * const file,
             int *  const colsP,
-            int *  const rowsP, 
+            int *  const rowsP,
             gray * const maxvalP);
 
 void
 pgm_readpgminit(FILE * const file,
-                int *  const colsP, 
+                int *  const colsP,
                 int *  const rowsP,
                 gray * const maxvalP,
                 int *  const formatP);
 
 void
 pgm_readpgmrow(FILE * const file,
-               gray * const grayrow, 
+               gray * const grayrow,
                int    const cols,
                gray   const maxval,
                int    const format);
 
 void
-pgm_writepgminit(FILE * const fileP, 
-                 int    const cols, 
-                 int    const rows, 
-                 gray   const maxval, 
+pgm_writepgminit(FILE * const fileP,
+                 int    const cols,
+                 int    const rows,
+                 gray   const maxval,
                  int    const forceplain);
 
 void
-pgm_writepgmrow(FILE *       const fileP, 
-                const gray * const grayrow, 
-                int          const cols, 
-                gray         const maxval, 
+pgm_writepgmrow(FILE *       const fileP,
+                const gray * const grayrow,
+                int          const cols,
+                gray         const maxval,
                 int          const forceplain);
 
 void
@@ -121,11 +123,11 @@ void
 pgm_nextimage(FILE * const file, int * const eofP);
 
 void
-pgm_check(FILE *               const file, 
-          enum pm_check_type   const check_type, 
-          int                  const format, 
-          int                  const cols, 
-          int                  const rows, 
+pgm_check(FILE *               const file,
+          enum pm_check_type   const check_type,
+          int                  const format,
+          int                  const cols,
+          int                  const rows,
           gray                 const maxval,
           enum pm_check_code * const retval_p);
 
diff --git a/lib/pm.h b/lib/pm.h
index 47cbfe83..3fc92fb4 100644
--- a/lib/pm.h
+++ b/lib/pm.h
@@ -110,10 +110,10 @@ extern int pm_plain_output;
     */
 extern const char * pm_progname;
 
-void 
+void
 pm_init(const char * const progname, unsigned int const flags);
 
-void 
+void
 pm_proginit(int * const argcP, const char * argv[]);
 
 void
@@ -122,7 +122,7 @@ pm_setMessage(int const newState, int * const oldStateP);
 int
 pm_getMessage(void);
 
-FILE * 
+FILE *
 pm_tmpfile(void);
 
 int
@@ -141,22 +141,22 @@ pm_nextimage(FILE * const file, int * const eofP);
 
 /* Variable-sized arrays definitions. */
 
-char** 
+char**
 pm_allocarray (int const cols, int const rows, int const size );
 
-void * 
+void *
 pm_allocrow(unsigned int const cols,
             unsigned int const size);
 
-void 
+void
 pm_freearray (char** const its, int const rows);
 
-void 
+void
 pm_freerow(void * const row);
 
 
 /* Obsolete -- use shhopt instead */
-int 
+int
 pm_keymatch(const char * const str,
             const char * const keyword,
             int          const minchars);
@@ -169,7 +169,7 @@ int PURE_FN_ATTR
 pm_bitstomaxval(int const bits);
 
 unsigned int PURE_FN_ATTR
-pm_lcm (unsigned int const x, 
+pm_lcm (unsigned int const x,
         unsigned int const y,
         unsigned int const z,
         unsigned int const limit);
@@ -211,37 +211,37 @@ void
 pm_setusererrormsgfn(pm_usererrormsgfn * fn);
 
 void PM_GNU_PRINTF_ATTR(1,2)
-pm_message (const char format[], ...);     
+pm_message (const char format[], ...);
 
 void PM_GNU_PRINTF_ATTR(1,2)
 pm_errormsg(const char format[], ...);
 
 void PM_GNU_PRINTF_ATTR(1,2)
-pm_error (const char reason[], ...);       
+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[]);             
+void
+pm_usage (const char usage[]);
 
-FILE* 
+FILE*
 pm_openr (const char* const name);
-         
-FILE*    
+
+FILE*
 pm_openw (const char* const name);
-         
+
 FILE *
 pm_openr_seekable(const char name[]);
 
-void     
+void
 pm_close (FILE* const f);
 
-void 
+void
 pm_closer (FILE* const f);
-          
-void      
+
+void
 pm_closew (FILE* const f);
 
 
@@ -267,11 +267,11 @@ pm_writecharu(FILE *        const ofP,
 }
 
 int
-pm_readbigshort(FILE *  const ifP, 
+pm_readbigshort(FILE *  const ifP,
                 short * const sP);
 
 static __inline__ int
-pm_readbigshortu(FILE*            const ifP, 
+pm_readbigshortu(FILE*            const ifP,
                  unsigned short * const sP) {
     return pm_readbigshort(ifP, (short *) sP);
 }
@@ -287,7 +287,7 @@ pm_writebigshortu(FILE *          const ofP,
 }
 
 int
-pm_readbiglong(FILE * const ifP, 
+pm_readbiglong(FILE * const ifP,
                long * const lP);
 
 static __inline__ int
@@ -297,7 +297,7 @@ pm_readbiglongu(FILE *          const ifP,
 }
 
 int
-pm_readbiglong2(FILE * const ifP, 
+pm_readbiglong2(FILE * const ifP,
                 int32_t * const lP);
 
 static __inline__ int
@@ -366,13 +366,20 @@ pm_writelittlelongu(FILE *        const ofP,
     return pm_writelittlelong(ofP, (long) l);
 }
 
-int 
+int
 pm_readmagicnumber(FILE * const ifP);
 
-char* 
-pm_read_unknown_size(FILE * const ifP, 
+char*
+pm_read_unknown_size(FILE * const ifP,
                      long * const buf);
 
+void
+pm_getline(FILE *   const ifP,
+           char **  const bufferP,
+           size_t * const bufferSzP,
+           int *    const eofP,
+           size_t * const lineLenP);
+
 short
 pm_bs_short(short const s);
 
@@ -383,12 +390,12 @@ unsigned int
 pm_tell(FILE * const fileP);
 
 void
-pm_tell2(FILE *       const fileP, 
+pm_tell2(FILE *       const fileP,
          void *       const fileposP,
          unsigned int const fileposSize);
 
 void
-pm_seek2(FILE *             const fileP, 
+pm_seek2(FILE *             const fileP,
          const pm_filepos * const fileposP,
          unsigned int       const fileposSize);
 
@@ -408,8 +415,8 @@ enum pm_check_type {
 };
 
 void
-pm_check(FILE *               const file, 
-         enum pm_check_type   const check_type, 
+pm_check(FILE *               const file,
+         enum pm_check_type   const check_type,
          pm_filepos           const need_raster_size,
          enum pm_check_code * const retval_p);
 
diff --git a/lib/pm_gamma.h b/lib/pm_gamma.h
index 6630e05c..00d551fc 100644
--- a/lib/pm_gamma.h
+++ b/lib/pm_gamma.h
@@ -15,18 +15,28 @@ extern "C" {
 static __inline__ float
 pm_gamma709(float const intensity) {
 
-    /* Here are parameters of the gamma transfer function
-       for the Netpbm formats.  This is CIE Rec 709.
-       
-       This transfer function is linear for sample values 0 .. .018 
+    /* Here are parameters of the gamma transfer function for the Netpbm
+       formats.  This is ITU-R Recommendation BT.709, FKA CIE Rec 709.  It is
+       also ITU-R Recommendation BT.601, FKA CCIR 601.
+
+       This transfer function is linear for sample values 0 .. .018
        and an exponential for larger sample values.
        The exponential is slightly stretched and translated, though,
        unlike the popular pure exponential gamma transfer function.
+
+       The standard actually defines the linear expansion as 4.500, which
+       means there is a discontinuity at linear intensity .018.  We instead
+       use ~4.514 to make a continuous function.  This may have been simply
+       a mistake when this code was written or based on an actual benefit
+       to having a continuous function -- The history is not clear.
+
+       Note that the discrepancy is below the precision of a maxval 255
+       image.
     */
     float const gamma = 2.2;
     float const oneOverGamma = 1.0 / gamma;
     float const linearCutoff = 0.018;
-    float const linearExpansion = 
+    float const linearExpansion =
         (1.099 * pow(linearCutoff, oneOverGamma) - 0.099) / linearCutoff;
 
     float brightness;
@@ -49,9 +59,9 @@ pm_ungamma709(float const brightness) {
     float const gamma = 2.2;
     float const oneOverGamma = 1.0 / gamma;
     float const linearCutoff = 0.018;
-    float const linearExpansion = 
+    float const linearExpansion =
         (1.099 * pow(linearCutoff, oneOverGamma) - 0.099) / linearCutoff;
-    
+
     float intensity;
 
     if (brightness < linearCutoff * linearExpansion)
diff --git a/lib/pm_system.h b/lib/pm_system.h
index a7560f48..dfcd5b3f 100644
--- a/lib/pm_system.h
+++ b/lib/pm_system.h
@@ -10,6 +10,32 @@ extern "C" {
 
 
 void
+pm_system2_vp(const char *    const progName,
+              const char **   const argArray,
+              void stdinFeeder(int, void *),
+              void *          const feederParm,
+              void stdoutAccepter(int, void *),
+              void *          const accepterParm,
+              int *           const termStatusP);
+
+void
+pm_system2_lp(const char *    const progName,
+              void stdinFeeder(int, void *),
+              void *          const feederParm,
+              void stdoutAccepter(int, void *),
+              void *          const accepterParm,
+              int *           const termStatusP,
+              ...);
+
+void
+pm_system2(void                  stdinFeeder(int, void *),
+           void *          const feederParm,
+           void                  stdoutAccepter(int, void *),
+           void *          const accepterParm,
+           const char *    const shellCommand,
+           int *           const termStatusP);
+
+void
 pm_system_vp(const char *    const progName,
              const char **   const argArray,
              void stdinFeeder(int, void *),
@@ -32,6 +58,21 @@ pm_system(void                  stdinFeeder(int, void *),
           void *          const accepterParm,
           const char *    const shellCommand);
 
+const char *
+pm_termStatusDesc(int const termStatus);
+
+
+/* The following are Standard Input feeders and Standard Output accepters
+   for pm_system() etc.
+*/
+void
+pm_feed_null(int    const pipeToFeedFd,
+             void * const feederParm);
+
+void
+pm_accept_null(int    const pipetosuckFd,
+               void * const accepterParm);
+
 struct bufferDesc {
     /* This is just a parameter for the routines below */
     unsigned int    size;
@@ -40,9 +81,11 @@ struct bufferDesc {
 };
 
 
-/* The following are a Standard Input feeder and a Standard Output accepter
-   for pm_system().  
+/* The struct name "bufferDesc", without the "pm" namespace, is an unfortunate
+   historical accident.
 */
+typedef struct bufferDesc pm_bufferDesc;
+
 void
 pm_feed_from_memory(int    const pipeToFeedFd,
                     void * const feederParm);
diff --git a/lib/pmfileio.c b/lib/pmfileio.c
index 8176ae6a..33f89110 100644
--- a/lib/pmfileio.c
+++ b/lib/pmfileio.c
@@ -5,6 +5,7 @@
   These are external functions, unlike 'fileio.c', but are not
   particular to any Netpbm format.
 **************************************************************************/
+#define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
 #define _SVID_SOURCE
     /* Make sure P_tmpdir is defined in GNU libc 2.0.7 (_XOPEN_SOURCE 500
        does it in other libc's).  pm_config.h defines TMPDIR as P_tmpdir
@@ -13,10 +14,10 @@
 #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 _LARGEFILE64_SOURCE 1
 #define _FILE_OFFSET_BITS 64
     /* This means ftello() is really ftello64() and returns a 64 bit file
-       position.  Unless the C library doesn't have ftello64(), in which 
+       position.  Unless the C library doesn't have ftello64(), in which
        case ftello() is still just ftello().
 
        Likewise for all the other C library file functions.
@@ -24,7 +25,7 @@
        And off_t and fpos_t are 64 bit types instead of 32.  Consequently,
        pm_filepos_t might be 64 bits instead of 32.
     */
-#define _LARGE_FILES  
+#define _LARGE_FILES
     /* This does for AIX what _FILE_OFFSET_BITS=64 does for GNU */
 #define _LARGE_FILE_API
     /* This makes the the x64() functions available on AIX */
@@ -59,9 +60,9 @@ pm_openr(const char * const name) {
         f = stdin;
     else {
         f = fopen(name, "rb");
-        if (f == NULL) 
+        if (f == NULL)
             pm_error("Unable to open file '%s' for reading.  "
-                     "fopen() returns errno %d (%s)", 
+                     "fopen() returns errno %d (%s)",
                      name, errno, strerror(errno));
     }
     return f;
@@ -77,9 +78,9 @@ pm_openw(const char * const name) {
         f = stdout;
     else {
         f = fopen(name, "wb");
-        if (f == NULL) 
+        if (f == NULL)
             pm_error("Unable to open file '%s' for writing.  "
-                     "fopen() returns errno %d (%s)", 
+                     "fopen() returns errno %d (%s)",
                      name, errno, strerror(errno));
     }
     return f;
@@ -117,7 +118,7 @@ tmpDir(void) {
 static int
 tempFileOpenFlags(void) {
 /*----------------------------------------------------------------------------
-  Open flags (argument to open()) suitable for a new temporary file  
+  Open flags (argument to open()) suitable for a new temporary file
 -----------------------------------------------------------------------------*/
     int retval;
 
@@ -181,11 +182,11 @@ mkstempx(char * const filenameBuffer) {
             } else {
                 if (errno == EEXIST) {
                     /* We'll just have to keep trying */
-                } else 
+                } else
                     error = TRUE;
             }
         }
-    }    
+    }
     if (gotFile)
         retval = fd;
     else
@@ -215,7 +216,7 @@ makeTmpfileWithTemplate(const char *  const filenameTemplate,
                         int *         const fdP,
                         const char ** const filenameP,
                         const char ** const errorP) {
-    
+
     char * filenameBuffer;  /* malloc'ed */
 
     filenameBuffer = strdup(filenameTemplate);
@@ -225,9 +226,9 @@ makeTmpfileWithTemplate(const char *  const filenameTemplate,
                     "file name");
     else {
         int rc;
-        
+
         rc = mkstemp2(filenameBuffer);
-        
+
         if (rc < 0)
             pm_asprintf(errorP,
                         "Unable to create temporary file according to name "
@@ -260,8 +261,8 @@ pm_make_tmpfile_fd(int *         const fdP,
         dirseparator = "";
     else
         dirseparator = "/";
-    
-    pm_asprintf(&filenameTemplate, "%s%s%s%s", 
+
+    pm_asprintf(&filenameTemplate, "%s%s%s%s",
                 tmpdir, dirseparator, pm_progname, "_XXXXXX");
 
     if (filenameTemplate == pm_strsol)
@@ -290,7 +291,7 @@ pm_make_tmpfile(FILE **       const filePP,
     pm_make_tmpfile_fd(&fd, filenameP);
 
     *filePP = fdopen(fd, "w+b");
-    
+
     if (*filePP == NULL) {
         close(fd);
         unlink(*filenameP);
@@ -304,7 +305,7 @@ pm_make_tmpfile(FILE **       const filePP,
 
 
 
-bool const canUnlinkOpen = 
+bool const canUnlinkOpen =
 #if CAN_UNLINK_OPEN
     1
 #else
@@ -393,7 +394,7 @@ scheduleUnlinkAtExit(const char * const fileName,
    code too badly for Unix.
 -----------------------------------------------------------------------------*/
     static bool unlinkListEstablished = false;
-    
+
     if (!unlinkListEstablished) {
         atexit(unlinkTempFiles);
         unlinkListP = NULL;
@@ -417,7 +418,7 @@ arrangeUnlink(const char * const fileName,
 
 
 
-FILE * 
+FILE *
 pm_tmpfile(void) {
 
     FILE * fileP;
@@ -472,7 +473,7 @@ pm_openr_seekable(const char name[]) {
 
     original_file = pm_openr((char *) name);
 
-    /* I would use fseek() to determine if the file is seekable and 
+    /* I would use fseek() to determine if the file is seekable and
        be a little more general than checking the type of file, but I
        don't have reliable information on how to do that.  I have seen
        streams be partially seekable -- you can, for example seek to
@@ -487,16 +488,16 @@ pm_openr_seekable(const char name[]) {
     stat_rc = fstat(fileno(original_file), &statbuf);
     if (stat_rc == 0 && S_ISREG(statbuf.st_mode))
         seekable = TRUE;
-    else 
+    else
         seekable = FALSE;
 
     if (seekable) {
         seekable_file = original_file;
     } else {
         seekable_file = pm_tmpfile();
-        
+
         /* Copy the input into the temporary seekable file */
-        while (!feof(original_file) && !ferror(original_file) 
+        while (!feof(original_file) && !ferror(original_file)
                && !ferror(seekable_file)) {
             char buffer[4096];
             int bytes_read;
@@ -536,7 +537,7 @@ pm_close(FILE * const f) {
 
 
 
-/* The pnmtopng package uses pm_closer() and pm_closew() instead of 
+/* The pnmtopng package uses pm_closer() and pm_closew() instead of
    pm_close(), apparently because the 1999 Pbmplus package has them.
    I don't know what the difference is supposed to be.
 */
@@ -600,7 +601,7 @@ getcNofail(FILE * const ifP) {
 void
 pm_readchar(FILE * const ifP,
             char * const cP) {
-    
+
     *cP = (char)getcNofail(ifP);
 }
 
@@ -616,7 +617,7 @@ pm_writechar(FILE * const ofP,
 
 
 int
-pm_readbigshort(FILE *  const ifP, 
+pm_readbigshort(FILE *  const ifP,
                 short * const sP) {
 
     unsigned short s;
@@ -632,7 +633,7 @@ pm_readbigshort(FILE *  const ifP,
 
 
 int
-pm_writebigshort(FILE * const ofP, 
+pm_writebigshort(FILE * const ofP,
                  short  const s) {
 
     putc((s >> 8) & 0xff, ofP);
@@ -644,7 +645,7 @@ pm_writebigshort(FILE * const ofP,
 
 
 int
-pm_readbiglong(FILE * const ifP, 
+pm_readbiglong(FILE * const ifP,
                long * const lP) {
 
     unsigned long l;
@@ -679,7 +680,7 @@ pm_readbiglong2(FILE *    const ifP,
 
 
 int
-pm_writebiglong(FILE * const ofP, 
+pm_writebiglong(FILE * const ofP,
                 long   const l) {
 
     putc((l >> 24) & 0xff, ofP);
@@ -693,7 +694,7 @@ pm_writebiglong(FILE * const ofP,
 
 
 int
-pm_readlittleshort(FILE *  const ifP, 
+pm_readlittleshort(FILE *  const ifP,
                    short * const sP) {
     unsigned short s;
 
@@ -708,7 +709,7 @@ pm_readlittleshort(FILE *  const ifP,
 
 
 int
-pm_writelittleshort(FILE * const ofP, 
+pm_writelittleshort(FILE * const ofP,
                     short  const s) {
 
     putc((s >> 0) & 0xff, ofP);
@@ -720,7 +721,7 @@ pm_writelittleshort(FILE * const ofP,
 
 
 int
-pm_readlittlelong(FILE * const ifP, 
+pm_readlittlelong(FILE * const ifP,
                   long * const lP) {
     unsigned long l;
 
@@ -754,7 +755,7 @@ pm_readlittlelong2(FILE *    const ifP,
 
 
 int
-pm_writelittlelong(FILE * const ofP, 
+pm_writelittlelong(FILE * const ofP,
                    long   const l) {
 
     putc((l >>  0) & 0xff, ofP);
@@ -767,17 +768,24 @@ pm_writelittlelong(FILE * const ofP,
 
 
 
-int 
+int
 pm_readmagicnumber(FILE * const ifP) {
 
     int ich1, ich2;
 
     ich1 = getc(ifP);
+
+    if (ich1 == EOF)
+        pm_error("Error reading first byte of what is expected to be "
+                 "a Netpbm magic number.  "
+                 "Most often, this means your input file is empty");
+
     ich2 = getc(ifP);
-    if (ich1 == EOF || ich2 == EOF)
-        pm_error( "Error reading magic number from Netpbm image stream.  "
-                  "Most often, this "
-                  "means your input file is empty." );
+
+    if (ich2 == EOF)
+        pm_error("Error reading second byte of what is expected to be "
+                 "a Netpbm magic number (the first byte was successfully "
+                 "read as 0x%02x)", ich1);
 
     return ich1 * 256 + ich2;
 }
@@ -797,7 +805,7 @@ pm_readmagicnumber(FILE * const ifP) {
                                   than this. */
 
 char *
-pm_read_unknown_size(FILE * const file, 
+pm_read_unknown_size(FILE * const file,
                      long * const nread) {
     long nalloc;
     char * buf;
@@ -823,7 +831,7 @@ pm_read_unknown_size(FILE * const file,
         val = getc(file);
         if (val == EOF)
             eof = TRUE;
-        else 
+        else
             buf[(*nread)++] = val;
     }
     return buf;
@@ -831,6 +839,73 @@ pm_read_unknown_size(FILE * const file,
 
 
 
+void
+pm_getline(FILE *   const ifP,
+           char **  const bufferP,
+           size_t * const bufferSzP,
+           int *    const eofP,
+           size_t * const lineLenP) {
+/*----------------------------------------------------------------------------
+   This is like POSIX 'getline'.
+
+   But we don't include the newline in the returned line.
+-----------------------------------------------------------------------------*/
+    char * buffer;
+    size_t bufferSz;
+    bool gotLine;
+    bool eof;
+    size_t nReadSoFar;
+
+    buffer   = *bufferP;    /* initial value */
+    bufferSz = *bufferSzP;  /* initial value */
+
+    for (nReadSoFar = 0, gotLine = false, eof = false;
+         !gotLine && !eof; ) {
+
+        int rc;
+
+        rc = fgetc(ifP);
+
+        if (rc == EOF) {
+            if (ferror(ifP))
+                pm_error("Error reading input file.  fgets() failed with "
+                         "errno %d (%s)", errno, strerror(errno));
+
+            if (nReadSoFar == 0) {
+                /* Didn't get anything before EOF, so return EOF */
+                eof = true;
+            } else {
+                /* End of file ends the line */
+                gotLine = true;
+            }
+        } else {
+            char const c = (char)rc;
+
+            if (c == '\n') {
+                /* Newline ends the line, and is not part of it */
+                gotLine = true;
+            } else {
+                if (nReadSoFar + 2 > bufferSz) {
+                    /* + 2 = 1 for 'c', one for terminating NUL */
+                    bufferSz += 128;
+                    REALLOCARRAY(buffer, bufferSz);
+                }
+                buffer[nReadSoFar++] = c;
+            }
+        }
+    }
+
+    if (gotLine)
+        buffer[nReadSoFar] = '\0';
+
+    *eofP      = eof;
+    *bufferP   = buffer;
+    *bufferSzP = bufferSz;
+    *lineLenP  = nReadSoFar;
+}
+
+
+
 union cheat {
     uint32_t l;
     short s;
@@ -871,7 +946,7 @@ pm_bs_long(long const l) {
 
 
 void
-pm_tell2(FILE *       const fileP, 
+pm_tell2(FILE *       const fileP,
          void *       const fileposP,
          unsigned int const fileposSize) {
 /*----------------------------------------------------------------------------
@@ -903,7 +978,7 @@ pm_tell2(FILE *       const fileP,
         }
     } else
         pm_error("File position size passed to pm_tell() is invalid: %u.  "
-                 "Valid sizes are %u and %u", 
+                 "Valid sizes are %u and %u",
                  fileposSize, (unsigned int)sizeof(pm_filepos),
                  (unsigned int) sizeof(long));
 }
@@ -912,7 +987,7 @@ pm_tell2(FILE *       const fileP,
 
 unsigned int
 pm_tell(FILE * const fileP) {
-    
+
     long filepos;
 
     pm_tell2(fileP, &filepos, sizeof(filepos));
@@ -923,14 +998,14 @@ pm_tell(FILE * const fileP) {
 
 
 void
-pm_seek2(FILE *             const fileP, 
+pm_seek2(FILE *             const fileP,
          const pm_filepos * const fileposP,
          unsigned int       const fileposSize) {
 /*----------------------------------------------------------------------------
    Position file *fileP to position *fileposP.  Abort if error, including
    if *fileP isn't a seekable file.
 -----------------------------------------------------------------------------*/
-    if (fileposSize == sizeof(pm_filepos)) 
+    if (fileposSize == sizeof(pm_filepos))
         /* Note: FSEEKO() is either fseeko() or fseek(), depending on the
            capabilities of the underlying C library.  It is defined in
            pm_config.h.  fseeko(), in turn, may be either fseek() or
@@ -942,7 +1017,7 @@ pm_seek2(FILE *             const fileP,
         fseek(fileP, fileposLong, SEEK_SET);
     } else
         pm_error("File position size passed to pm_seek() is invalid: %u.  "
-                 "Valid sizes are %u and %u", 
+                 "Valid sizes are %u and %u",
                  fileposSize, (unsigned int)sizeof(pm_filepos),
                  (unsigned int) sizeof(long));
 }
@@ -969,7 +1044,7 @@ pm_nextimage(FILE * const file, int * const eofP) {
    Position the file 'file' to the next image in the stream, assuming it is
    now positioned just after the current image.  I.e. read off any white
    space at the end of the current image's raster.  Note that the raw formats
-   don't permit such white space, but this routine tolerates it anyway, 
+   don't permit such white space, but this routine tolerates it anyway,
    because the plain formats do permit white space after the raster.
 
    Iff there is no next image, return *eofP == TRUE.
@@ -994,7 +1069,7 @@ pm_nextimage(FILE * const file, int * const eofP) {
         int c;
         c = getc(file);
         if (c == EOF) {
-            if (feof(file)) 
+            if (feof(file))
                 eof = TRUE;
             else
                 pm_error("File error on getc() to position to image");
@@ -1005,10 +1080,10 @@ pm_nextimage(FILE * const file, int * const eofP) {
                 nonWhitespaceFound = TRUE;
 
                 /* Have to put the non-whitespace character back in
-                   the stream -- it's part of the next image.  
+                   the stream -- it's part of the next image.
                 */
                 rc = ungetc(c, file);
-                if (rc == EOF) 
+                if (rc == EOF)
                     pm_error("File error doing ungetc() "
                              "to position to image.");
             }
@@ -1020,8 +1095,8 @@ pm_nextimage(FILE * const file, int * const eofP) {
 
 
 void
-pm_check(FILE *               const file, 
-         enum pm_check_type   const check_type, 
+pm_check(FILE *               const file,
+         enum pm_check_type   const check_type,
          pm_filepos           const need_raster_size,
          enum pm_check_code * const retval_p) {
 /*----------------------------------------------------------------------------
@@ -1039,9 +1114,9 @@ pm_check(FILE *               const file,
     curpos = FTELLO(file);
     if (curpos >= 0) {
         /* This type of file has a current position */
-            
+
         rc = fstat(fileno(file), &statbuf);
-        if (rc != 0) 
+        if (rc != 0)
             pm_error("fstat() failed to get size of file, though ftello() "
                      "successfully identified\n"
                      "the current position.  Errno=%s (%d)",
@@ -1051,12 +1126,12 @@ pm_check(FILE *               const file,
             if (retval_p) *retval_p = PM_CHECK_UNCHECKABLE;
         } else {
             pm_filepos const have_raster_size = statbuf.st_size - curpos;
-            
+
             if (have_raster_size < need_raster_size)
                 pm_error("File has invalid format.  The raster should "
                          "contain %u bytes, but\n"
                          "the file ends after only %u bytes.",
-                         (unsigned int) need_raster_size, 
+                         (unsigned int) need_raster_size,
                          (unsigned int) have_raster_size);
             else if (have_raster_size > need_raster_size) {
                 if (retval_p) *retval_p = PM_CHECK_TOO_LONG;
diff --git a/lib/pnm.h b/lib/pnm.h
index 3b490552..0625cb5c 100644
--- a/lib/pnm.h
+++ b/lib/pnm.h
@@ -5,6 +5,8 @@
 #define _PNM_H_
 
 #include <netpbm/pm.h>
+#include <netpbm/pbm.h>
+#include <netpbm/pgm.h>
 #include <netpbm/ppm.h>
 
 #ifdef __cplusplus
@@ -19,6 +21,7 @@ typedef pixel xel;
 typedef pixval xelval;
 #define PNM_OVERALLMAXVAL PPM_OVERALLMAXVAL
 #define PNM_MAXMAXVAL PPM_MAXMAXVAL
+#define pnm_unnormalize ppm_unnormalize
 #define PNM_GET1(x) PPM_GETB(x)
 #define PNM_GETR(x) PPM_GETR(x)
 #define PNM_GETG(x) PPM_GETG(x)
@@ -72,7 +75,7 @@ pnm_readpnm(FILE *   const fileP,
 
 void
 pnm_check(FILE *               const fileP,
-          enum pm_check_type   const check_type, 
+          enum pm_check_type   const check_type,
           int                  const format,
           int                  const cols,
           int                  const rows,
@@ -81,19 +84,19 @@ pnm_check(FILE *               const fileP,
 
 
 void
-pnm_writepnminit(FILE * const fileP, 
-                 int    const cols, 
-                 int    const rows, 
-                 xelval const maxval, 
-                 int    const format, 
+pnm_writepnminit(FILE * const fileP,
+                 int    const cols,
+                 int    const rows,
+                 xelval const maxval,
+                 int    const format,
                  int    const forceplain);
 
 void
-pnm_writepnmrow(FILE *      const fileP, 
-                const xel * const xelrow, 
-                int         const cols, 
-                xelval      const maxval, 
-                int         const format, 
+pnm_writepnmrow(FILE *      const fileP,
+                const xel * const xelrow,
+                int         const cols,
+                xelval      const maxval,
+                int         const format,
                 int         const forceplain);
 
 void
@@ -105,28 +108,28 @@ pnm_writepnm(FILE * const fileP,
              int    const format,
              int    const forceplain);
 
-xel 
+xel
 pnm_backgroundxel(xel** xels, int cols, int rows, xelval maxval, int format);
 
-xel 
+xel
 pnm_backgroundxelrow(xel* xelrow, int cols, xelval maxval, int format);
 
-xel 
+xel
 pnm_whitexel(xelval maxval, int format);
 
-xel 
+xel
 pnm_blackxel(xelval maxval, int format);
 
-void 
+void
 pnm_invertxel(xel *  const x,
               xelval const maxval,
               int    const format);
 
-void 
-pnm_promoteformat(xel** xels, int cols, int rows, xelval maxval, int format, 
+void
+pnm_promoteformat(xel** xels, int cols, int rows, xelval maxval, int format,
                   xelval newmaxval, int newformat);
-void 
-pnm_promoteformatrow(xel* xelrow, int cols, xelval maxval, int format, 
+void
+pnm_promoteformatrow(xel* xelrow, int cols, xelval maxval, int format,
                      xelval newmaxval, int newformat);
 
 pixel
@@ -134,6 +137,16 @@ pnm_xeltopixel(xel const inputxel,
                int const format);
 
 xel
+pnm_pixeltoxel(pixel const inputPixel);
+
+xel
+pnm_graytoxel(gray const inputGray);
+
+xel
+pnm_bittoxel(bit    const inputBit,
+             xelval const maxval);
+
+xel
 pnm_parsecolorxel(const char * const colorName,
                   xelval       const maxval,
                   int          const format);
diff --git a/lib/ppm.h b/lib/ppm.h
index 82241b70..9fc90bb3 100644
--- a/lib/ppm.h
+++ b/lib/ppm.h
@@ -23,6 +23,9 @@ typedef gray pixval;
 
 #define PPM_OVERALLMAXVAL PGM_OVERALLMAXVAL
 #define PPM_MAXMAXVAL PGM_MAXMAXVAL
+
+#define ppm_unnormalize pgm_unnormalize
+
 typedef struct {
     pixval r, g, b;
 } pixel;
@@ -90,61 +93,61 @@ ppm_allocrow(unsigned int const cols);
 #define ppm_freerow(pixelrow) pm_freerow(pixelrow);
 
 pixel**
-ppm_readppm(FILE *   const fileP, 
-            int *    const colsP, 
-            int *    const rowsP, 
+ppm_readppm(FILE *   const fileP,
+            int *    const colsP,
+            int *    const rowsP,
             pixval * const maxvalP);
 
 void
-ppm_readppminit(FILE *   const fileP, 
-                int *    const colsP, 
-                int *    const rowsP, 
-                pixval * const maxvalP, 
+ppm_readppminit(FILE *   const fileP,
+                int *    const colsP,
+                int *    const rowsP,
+                pixval * const maxvalP,
                 int *    const formatP);
 
 void
-ppm_readppmrow(FILE*  const fileP, 
-               pixel* const pixelrow, 
-               int    const cols, 
-               pixval const maxval, 
+ppm_readppmrow(FILE*  const fileP,
+               pixel* const pixelrow,
+               int    const cols,
+               pixval const maxval,
                int    const format);
 
 void
-ppm_writeppm(FILE *  const fileP, 
-             pixel** const pixels, 
-             int     const cols, 
-             int     const rows, 
-             pixval  const maxval, 
+ppm_writeppm(FILE *  const fileP,
+             pixel** const pixels,
+             int     const cols,
+             int     const rows,
+             pixval  const maxval,
              int     const forceplain);
 
 void
-ppm_writeppminit(FILE*  const fileP, 
-                 int    const cols, 
-                 int    const rows, 
-                 pixval const maxval, 
+ppm_writeppminit(FILE*  const fileP,
+                 int    const cols,
+                 int    const rows,
+                 pixval const maxval,
                  int    const forceplain);
 
 void
-ppm_writeppmrow(FILE *        const fileP, 
-                const pixel * const pixelrow, 
-                int           const cols, 
-                pixval        const maxval, 
+ppm_writeppmrow(FILE *        const fileP,
+                const pixel * const pixelrow,
+                int           const cols,
+                pixval        const maxval,
                 int           const forceplain);
 
 void
-ppm_check(FILE *               const fileP, 
-          enum pm_check_type   const check_type, 
-          int                  const format, 
-          int                  const cols, 
-          int                  const rows, 
+ppm_check(FILE *               const fileP,
+          enum pm_check_type   const check_type,
+          int                  const format,
+          int                  const cols,
+          int                  const rows,
           pixval               const maxval,
           enum pm_check_code * const retval_p);
 
 void
-ppm_nextimage(FILE * const fileP, 
+ppm_nextimage(FILE * const fileP,
               int *  const eofP);
 
-pixel 
+pixel
 ppm_parsecolor(const char * const colorname,
                pixval       const maxval);
 
@@ -154,8 +157,8 @@ ppm_parsecolor2(const char * const colorname,
                 int          const closeOk);
 
 char*
-ppm_colorname(const pixel* const colorP, 
-              pixval       const maxval, 
+ppm_colorname(const pixel* const colorP,
+              pixval       const maxval,
               int          const hexok);
 
 void
@@ -167,9 +170,9 @@ ppm_readcolordict(const char *      const fileName,
                   colorhash_table * const chtP);
 
 void
-ppm_readcolornamefile(const char *      const fileName, 
+ppm_readcolornamefile(const char *      const fileName,
                       int               const mustOpen,
-                      colorhash_table * const chtP, 
+                      colorhash_table * const chtP,
                       const char ***    const colornamesP);
 
 void
@@ -211,9 +214,7 @@ PPM_DISTANCE(pixel const p1,
    combination of intensities, whereas luma is a linear combination of
    gamma-adjusted intensities, as you would find in a Netpbm image.
 
-   These are from ITU-R BT.601.5.  That means they probably aren't technically
-   right for use with PPM images, because the PPM spec says ITU-R BT.709.
-   The two are similar, though.
+   These are from ITU-R BT.601.5.
 */
 #define PPM_LUMINR (0.2989)
 #define PPM_LUMING (0.5866)
@@ -222,16 +223,20 @@ PPM_DISTANCE(pixel const p1,
 #define PPM_LUMIN(p) ( PPM_LUMINR * PPM_GETR(p) \
                        + PPM_LUMING * PPM_GETG(p) \
                        + PPM_LUMINB * PPM_GETB(p) )
-#define PPM_CHROM_B(p) ( -0.16874 * PPM_GETR(p) \
-                         - 0.33126 * PPM_GETG(p) \
+
+/* The coefficients in the following formulae are functions of
+   PPM_LUMIN{R,G,B} and nothing else.
+*/
+#define PPM_CHROM_B(p) ( -0.168736 * PPM_GETR(p) \
+                         - 0.331264 * PPM_GETG(p) \
                          + 0.5 * PPM_GETB(p) )
 #define PPM_CHROM_R(p) ( 0.5 * PPM_GETR(p) \
-                         - 0.41869 * PPM_GETG(p) \
-                         - 0.08131 * PPM_GETB(p) )
+                         - 0.418688 * PPM_GETG(p) \
+                         - 0.081312 * PPM_GETB(p) )
 
 pixel
-ppm_color_from_ycbcr(unsigned int const y, 
-                     int          const cb, 
+ppm_color_from_ycbcr(unsigned int const y,
+                     int          const cb,
                      int          const cr);
 
 /* Hue/Saturation/Value calculations */
diff --git a/lib/ppmdraw.h b/lib/ppmdraw.h
index df22b44d..5fd4148c 100644
--- a/lib/ppmdraw.h
+++ b/lib/ppmdraw.h
@@ -9,6 +9,7 @@
 */
 
 #include <netpbm/pm_config.h>
+#include <netpbm/ppm.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -59,6 +60,9 @@ typedef struct {
     } u;
 } ppmd_pathleg;
 
+ppmd_pathleg
+ppmd_makeLineLeg(ppmd_point const point);
+
 typedef struct {
 /*----------------------------------------------------------------------------
    A closed path
@@ -75,9 +79,58 @@ typedef struct {
            as the definition of ppmd_pathleg changes.
         */
     ppmd_pathleg * legs;
+        /* An array of the legs of the path, in order, starting at 'begPoint'.
+        */
 } ppmd_path;
 
+typedef struct {
+
+    ppmd_path path;
+        /* The path we are building (or have built).
+           Null for path.legs means we don't have a leg array yet.
+        */
+
+    bool begIsSet;
+        /* User has set path.begPoint.  If this is false, path.begPoint is
+           meaningless.
+        */
+    
+    unsigned int legsAllocSize;
+        /* How many legs of space is allocated in the leg array path.legs */
+
+    bool legsAreAutoAllocated;
+        /* The array 'legs' is allocated or reallocated automatically by
+           ppmd_path_addlineline(), as opposed to being supplied by the
+           user as part of initializing this structure, never to be altered.
+        */
+    
+} ppmd_pathbuilder;
+
+ppmd_pathbuilder *
+ppmd_pathbuilder_create(void);
+
+void
+ppmd_pathbuilder_destroy(ppmd_pathbuilder * const pathBuilderP);
+
+void
+ppmd_pathbuilder_setLegArray(ppmd_pathbuilder * const pathBuilderP,
+                             ppmd_pathleg *     const legs,
+                             unsigned int       const legCount);
+
+void
+ppmd_pathbuilder_preallocLegArray(ppmd_pathbuilder * const pathBuilderP,
+                                  unsigned int       const legCount);
+
+void
+ppmd_pathbuilder_setBegPoint(ppmd_pathbuilder * const pathBuilderP,
+                             ppmd_point         const begPoint);
+
+void
+ppmd_pathbuilder_addLineLeg(ppmd_pathbuilder * const pathBuilderP,
+                            ppmd_pathleg       const leg);
 
+const ppmd_path *
+ppmd_pathbuilder_pathP(ppmd_pathbuilder * const pathBuilderP);
 
 typedef void ppmd_drawprocp(pixel **, unsigned int, unsigned int,
                             pixval, ppmd_point, const void *);
@@ -272,12 +325,12 @@ ppmd_filledrectangle(pixel **      const pixels,
 
 
 void
-ppmd_fill_path(pixel **      const pixels, 
-               int           const cols, 
-               int           const rows, 
-               pixval        const maxval,
-               ppmd_path *   const pathP,
-               pixel         const color);
+ppmd_fill_path(pixel **          const pixels, 
+               int               const cols, 
+               int               const rows, 
+               pixval            const maxval,
+               const ppmd_path * const pathP,
+               pixel             const color);
     /* Fills in a closed path.  Not much different from ppmd_fill(),
        but with a different interface.
     */
diff --git a/lib/util/mallocvar.c b/lib/util/mallocvar.c
index 339d5d61..f75bd94d 100644
--- a/lib/util/mallocvar.c
+++ b/lib/util/mallocvar.c
@@ -30,7 +30,7 @@ allocarrayNoHeap(void **      const rowIndex,
         void * rowSpace;
 
         mallocProduct(&rowSpace, cols, elementSize);
-        
+
         if (rowSpace == NULL) {
             unsigned int row;
 
@@ -123,7 +123,7 @@ pm_mallocarray2(void **      const resultP,
 
             if (rowheap) {
                 unsigned int row;
-                
+
                 for (row = 0; row < rows; ++row)
                     rowIndex[row] = &(rowheap[row * cols * elementSize]);
             }
@@ -133,7 +133,7 @@ pm_mallocarray2(void **      const resultP,
                format.
             */
             rowIndex[rows+1] = NULL;   /* Declare it fragmented format */
-            
+
             allocarrayNoHeap(rowIndex, rows, cols, elementSize, &failed);
         }
         rowIndex[rows+0] = NULL;   /* mark end of rows */
@@ -151,7 +151,7 @@ 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 
+    /* The end of the rows is marked by a null pointer where a row
        pointer otherwise would be.
     */
 
diff --git a/lib/util/mallocvar.h b/lib/util/mallocvar.h
index e92e3fe4..00ba6484 100644
--- a/lib/util/mallocvar.h
+++ b/lib/util/mallocvar.h
@@ -1,5 +1,5 @@
 /* These are some dynamic memory allocation facilities.  They are essentially
-   an extension to C, as they do allocations with a cognizance of C 
+   an extension to C, as they do allocations with a cognizance of C
    variables.  You can use them to make C read more like a high level
    language.
 
@@ -23,7 +23,7 @@ extern "C" {
 #endif
 
 static __inline__ void
-mallocProduct(void **      const resultP, 
+mallocProduct(void **      const resultP,
               unsigned int const factor1,
               unsigned int const factor2) {
 /*----------------------------------------------------------------------------
@@ -44,10 +44,10 @@ mallocProduct(void **      const resultP,
     if (factor1 == 0 || factor2 == 0)
         *resultP = malloc(1);
     else {
-        if (UINT_MAX / factor2 < factor1) 
+        if (UINT_MAX / factor2 < factor1)
             *resultP = NULL;
-        else 
-            *resultP = malloc(factor1 * factor2); 
+        else
+            *resultP = malloc(factor1 * factor2);
     }
 }
 
@@ -61,11 +61,11 @@ reallocProduct(void **      const blockP,
     void * const oldBlockP = *blockP;
 
     void * newBlockP;
-    
-    if (UINT_MAX / factor2 < factor1) 
+
+    if (UINT_MAX / factor2 < factor1)
         newBlockP = NULL;
-    else 
-        newBlockP = realloc(oldBlockP, factor1 * factor2); 
+    else
+        newBlockP = realloc(oldBlockP, factor1 * factor2);
 
     if (newBlockP)
         *blockP = newBlockP;
diff --git a/lib/util/nstring.c b/lib/util/nstring.c
index 711cfca9..7ef9fcfb 100644
--- a/lib/util/nstring.c
+++ b/lib/util/nstring.c
@@ -3,7 +3,7 @@
  *
 
    THIS MODULE WAS ADAPTED FOR NETPBM BY BRYAN HENDERSON ON 2002.03.24.
-   Bryan got the base from 
+   Bryan got the base from
    http://www.ijs.si/software/snprintf/snprintf-2.2.tar.gz, but made
    a lot of changes and additions.
 
@@ -113,6 +113,7 @@
  */
 
 
+#define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE  /* Make sure strdup() is in string.h */
 #define _GNU_SOURCE
@@ -194,6 +195,19 @@ static char credits[] = "\n\
 @(#)snprintf.c, v2.2: http://www.ijs.si/software/snprintf/\n";
 
 
+/* MacOS X before 10.7, for one, does not have strnlen */
+size_t
+pm_strnlen(const char * const s,
+           size_t       const maxlen) {
+
+    unsigned int i;
+
+    for (i = 0; i < maxlen && s[i]; ++i) {}
+
+    return i;
+}
+
+
 
 void
 pm_vsnprintf(char *       const str,
@@ -201,7 +215,7 @@ pm_vsnprintf(char *       const str,
              const char * const fmt,
              va_list            ap,
              size_t *     const sizeP) {
-    
+
     size_t str_l = 0;
     const char *p = fmt;
 
@@ -226,7 +240,6 @@ pm_vsnprintf(char *       const str,
             }
             p += n; str_l += n;
         } else {
-            const char *starting_p;
             size_t min_field_width;
             size_t precision = 0;
             bool precision_specified;
@@ -268,7 +281,6 @@ pm_vsnprintf(char *       const str,
             str_arg = credits;
                 /* just to make compiler happy (defined but not used) */
             str_arg = NULL;
-            starting_p = p;
             ++p;  /* skip '%' */
 
             /* parse flags */
@@ -405,7 +417,7 @@ pm_vsnprintf(char *       const str,
                         str_arg_l = 0;
                     else if (!precision_specified)
                         /* truncate string if necessary as requested by
-                           precision 
+                           precision
                         */
                         str_arg_l = strlen(str_arg);
                     else if (precision == 0)
@@ -457,7 +469,7 @@ pm_vsnprintf(char *       const str,
                       undefined. (Actually %hp converts only 16-bits
                       of address and %llp treats address as 64-bit
                       data which is incompatible with (void *)
-                      argument on a 32-bit system). 
+                      argument on a 32-bit system).
                     */
 
                     length_modifier = '\0';
@@ -590,7 +602,7 @@ pm_vsnprintf(char *       const str,
                         && !(zero_padding_insertion_ind < str_arg_l
                              && tmp[zero_padding_insertion_ind] == '0')) {
                         /* assure leading zero for alternate-form
-                           octal numbers 
+                           octal numbers
                         */
                         if (!precision_specified ||
                             precision < num_of_digits+1) {
@@ -604,7 +616,7 @@ pm_vsnprintf(char *       const str,
                         }
                     }
                     /* zero padding to specified precision? */
-                    if (num_of_digits < precision) 
+                    if (num_of_digits < precision)
                         number_of_zeros_to_pad = precision - num_of_digits;
                 }
                 /* zero padding to specified minimal field width? */
@@ -756,7 +768,7 @@ pm_snprintf(char *       const dest,
     va_list ap;
 
     va_start(ap, fmt);
-    
+
     pm_vsnprintf(dest, str_m, fmt, ap, &size);
 
     va_end(ap);
@@ -795,12 +807,12 @@ pm_strdup(const char * const arg) {
 
 void PM_GNU_PRINTF_ATTR(2,3)
 pm_asprintf(const char ** const resultP,
-            const char *  const fmt, 
+            const char *  const fmt,
             ...) {
 
     const char * result;
     va_list varargs;
-    
+
 #if HAVE_VASPRINTF
     int rc;
     va_start(varargs, fmt);
@@ -810,7 +822,7 @@ pm_asprintf(const char ** const resultP,
         result = pm_strsol;
 #else
     size_t dryRunLen;
-    
+
     va_start(varargs, fmt);
 
     pm_vsnprintf(NULL, 0, fmt, varargs, &dryRunLen);
@@ -831,7 +843,7 @@ pm_asprintf(const char ** const resultP,
             va_start(varargs, fmt);
 
             pm_vsnprintf(buffer, allocSize, fmt, varargs, &realLen);
-                
+
             assert(realLen == dryRunLen);
             va_end(varargs);
 
@@ -859,7 +871,7 @@ pm_strfree(const char * const string) {
 
 const char *
 pm_strsep(char ** const stringP, const char * const delim) {
-    const char * retval;   
+    const char * retval;
 
     if (stringP == NULL || *stringP == NULL)
         retval = NULL;
@@ -869,16 +881,16 @@ pm_strsep(char ** const stringP, const char * const delim) {
         retval = *stringP;
 
         for (p = *stringP; *p && strchr(delim, *p) == NULL; ++p);
- 
+
         if (*p) {
-            /* We hit a delimiter, not end-of-string.  So null out the 
+            /* We hit a delimiter, not end-of-string.  So null out the
                delimiter and advance user's pointer to the next token
             */
             *p++ = '\0';
             *stringP = p;
         } else {
-            /* We ran out of string.  So the end-of-string delimiter is 
-               already there, and we set the user's pointer to NULL to 
+            /* We ran out of string.  So the end-of-string delimiter is
+               already there, and we set the user's pointer to NULL to
                indicate there are no more tokens.
             */
             *stringP = NULL;
@@ -902,7 +914,7 @@ pm_stripeq(const char * const comparand,
     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
        NUL.
@@ -995,39 +1007,50 @@ pm_strishex(const char * const subject) {
 
 
 void
-pm_interpret_uint(const char *   const string,
-                  unsigned int * const valueP,
+pm_string_to_uint(const char *   const string,
+                  unsigned int * const uintP,
                   const char **  const errorP) {
 
-    if (string[0] == '\0')
-        pm_asprintf(errorP, "Null string.");
+    if (strlen(string) == 0)
+        pm_asprintf(errorP, "Value is a null string");
     else {
-        /* strtoul() does a bizarre thing where if the number is out
+        char * tailptr;
+        long longValue;
+
+        /* We can't use 'strtoul'.  Contrary to expectations, though as
+           designed, it returns junk if there is a minus sign.
+        */
+
+        /* strtol() does a bizarre thing where if the number is out
            of range, it returns a clamped value but tells you about it
            by setting errno = ERANGE.  If it is not out of range,
-           strtoul() leaves errno alone.
+           strtol() leaves errno alone.
         */
-        char * tail;
-        unsigned long ulongValue;
-        
         errno = 0;  /* So we can tell if strtoul() overflowed */
 
-        ulongValue = strtoul(string, &tail, 10);
-
-        if (tail[0] != '\0')
-            pm_asprintf(errorP, "Non-digit stuff in string: %s", tail);
-        else if (errno == ERANGE)
-            pm_asprintf(errorP, "Number too large");
-        else if (ulongValue > UINT_MAX)
-            pm_asprintf(errorP, "Number too large");
-        else if (string[0] == '-')
-            pm_asprintf(errorP, "Negative number");
-            /* Sleazy code; string may have leading spaces. */
+        longValue = strtol(string, &tailptr, 10);
+
+        if (*tailptr != '\0')
+            pm_asprintf(errorP, "Non-numeric crap in string: '%s'", tailptr);
         else {
-            *valueP = ulongValue;
-            *errorP = NULL;
+             if (errno == ERANGE)
+                 pm_asprintf(errorP, "Number is too large for computation");
+             else {
+                 if (longValue < 0)
+                     pm_asprintf(errorP, "Number is negative");
+                 else {
+                     if ((unsigned int)longValue != longValue)
+                         pm_asprintf(errorP,
+                                     "Number is too large for computation");
+                     else {
+                         *uintP = (unsigned int)longValue;
+                         *errorP = NULL;
+                     }
+                 }
+             }
         }
     }
 }
 
 
+
diff --git a/lib/util/nstring.h b/lib/util/nstring.h
index 7238a76e..5159277c 100644
--- a/lib/util/nstring.h
+++ b/lib/util/nstring.h
@@ -3,6 +3,7 @@
 
 #include <stdarg.h>
 #include <string.h>
+#include <strings.h>  /* For strncasecmp */
 #include <ctype.h>
 
 #include "pm_c_util.h"
@@ -59,15 +60,21 @@ static __inline__ int
 memeq(const void * const comparand,
       const void * const comparator,
       size_t       const size) {
-    
+
     return memcmp(comparand, comparator, size) == 0;
 }
 
-/* The Standard C Library may not declare strcasecmp() if the including
-   source file doesn't request BSD functions, with _BSD_SOURCE.  So
-   we don't define functions that use strcasecmp() in that case.
+/* The Standard C Library may not declare strcasecmp() if the including source
+   file doesn't request BSD functions, with _BSD_SOURCE or SUSv2 function,
+   with _XOPEN_SOURCE >= 500.  So we don't define functions that use
+   strcasecmp() in that case.
+
+   (Actually, _XOPEN_SOURCE 500 is stronger than you need for strcasecmp -
+   _XOPEN_SOURCE_EXTENDED, which asks for XPG 4, would do, whereas
+   _XOPEN_SOURCE 500 asks for XPG 5, but for simplicity, we don't use
+   _XOPEN_SOURCE_EXTENDED in Netpbm.
 */
-#ifdef _BSD_SOURCE
+#if defined(_BSD_SOURCE) || (defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE - 0) >= 500)
 static __inline__ int
 strcaseeq(const char * const comparand,
           const char * const comparator) {
@@ -85,7 +92,7 @@ strncaseeq(const char * const comparand,
 #endif
 
 
-/* The standard C library routines isdigit(), for some weird 
+/* The standard C library routines isdigit(), for some weird
    historical reason, does not take a character (type 'char') as its
    argument.  Instead it takes an integer.  When the integer is a whole
    number, it represents a character in the obvious way using the local
@@ -125,7 +132,7 @@ strncaseeq(const char * const comparand,
    Netpbm must include them in its own libraries, and because some
    standard C libraries have some of them, Netpbm must use different
    names for them.
-   
+
    The GNU C library has all of them.  All but the oldest standard C libraries
    have snprintf().
 
@@ -149,6 +156,10 @@ strncaseeq(const char * const comparand,
 
 extern const char * const pm_strsol;
 
+size_t
+pm_strnlen(const char * const s,
+           size_t       const maxlen);
+
 int
 pm_snprintf(char *       const dest,
             size_t       const str_m,
@@ -178,7 +189,7 @@ pm_vasprintf(const char ** const resultP,
 bool
 pm_vasprintf_knows_float(void);
 
-void 
+void
 pm_strfree(const char * const string);
 
 const char *
@@ -198,9 +209,9 @@ bool
 pm_strishex(const char * const subject);
 
 void
-pm_interpret_uint(const char *   const string,
-               unsigned int * const valueP,
-               const char **  const errorP);
+pm_string_to_uint(const char *   const string,
+                  unsigned int * const uintP,
+                  const char **  const errorP);
 
 #ifdef __cplusplus
 }
diff --git a/lib/util/pm_c_util.h b/lib/util/pm_c_util.h
index 01a07657..a093adb6 100644
--- a/lib/util/pm_c_util.h
+++ b/lib/util/pm_c_util.h
@@ -20,6 +20,13 @@
 #define ROUND(X) (((X) >= 0) ? (int)((X)+0.5) : (int)((X)-0.5))
 #undef ROUNDU
 #define ROUNDU(X) ((unsigned int)((X)+0.5))
+    /* Note that imprecision in floating point arithmetic can make an exact
+       half fractional part round down instead of up.  What should be
+       1000.5 might actually be 1000.49999999999.
+
+       Use 'pnm_unnormalized_sample' instead of ROUNDU(samplen*maxval) to get
+       consistent rounding up when you are unnormalizing sample values.
+    */
 
 /* ROUNDUP rounds up to a specified multiple.  E.g. ROUNDUP(22, 8) == 24 */
 
@@ -53,9 +60,11 @@
 #else
   /* The test for __STDC__ is paranoid.  It is there just in case some
      nonstandard compiler defines __STDC_VERSION__ in an arbitrary manner.
+
+     We know GCC 2.95.3 has stdbool; not sure about earlier GCC 2.
   */
-  #if ( defined(__GNUC__) && (__GNUC__ >= 3) ) || \
-      ( defined(__STDC__) && (__STDC__ ==1) && \
+  #if ( defined(__GNUC__) && (__GNUC__ >= 2) ) || \
+      ( defined(__STDC__) && (__STDC__ == 1) && \
         defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) ) 
     #include <stdbool.h>
   #else
diff --git a/lib/util/runlength.c b/lib/util/runlength.c
index e5c60db0..62e3bf0c 100644
--- a/lib/util/runlength.c
+++ b/lib/util/runlength.c
@@ -12,8 +12,8 @@
   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).
+  Functions pm_rlenc_compressbyte() and pm_rlenc_compressword() 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
@@ -22,9 +22,9 @@
 
 ===============================================================================
 
-  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.
+  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,
@@ -34,19 +34,20 @@
   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.
+  Variants of Packbits are employed in pnmtosgi, pamtopdbimg, pbmtoppa
+  pbmtogo and pamtotga.
 
   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
+  Indeed, just determining which programs do 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.
+  Today, all Netpbm programs that do Packbits compression with the exception
+  of pamtotiff, pbmtoppa, pbmtogo and pamtotga use the facilities in this
+  file for it.  
 =============================================================================*/
 
 #include <string.h>
@@ -68,11 +69,11 @@ static const char * const errorUndefinedMode =
 
    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).
+   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:
+   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.
@@ -99,7 +100,7 @@ pm_rlenc_compressbyte(const unsigned char * const inbuf,
   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.
+  This ensures that the output is never unnecessarily bloated.
 
   Original algorithm by Robert A. Knop (rknop@mop.caltech.edu)
 -----------------------------------------------------------------------------*/
@@ -107,8 +108,17 @@ pm_rlenc_compressbyte(const unsigned char * const inbuf,
 
     size_t inCurs, outCurs;
 
-    if (mode != PM_RLE_PACKBITS)
-        pm_error(errorUndefinedMode, mode);
+    int packBase;
+    int packSign;
+ 
+    switch (mode) {
+    case PM_RLE_PACKBITS:
+        packBase = 257 ; packSign = -1; break;
+    case PM_RLE_PALMPDB:
+        packBase = 127 ; packSign = +1; break;
+    default:
+         pm_error(errorUndefinedMode, mode);
+    }
 
     for (inCurs = 0, outCurs = 0; inCurs < inSize; ) {
         if ((inCurs < inSize - 1) && (inbuf[inCurs] == inbuf[inCurs+1])) {
@@ -121,8 +131,7 @@ pm_rlenc_compressbyte(const unsigned char * const inbuf,
                      count < maxRun; 
                  ++inCurs, ++count)
                 ;
-
-            outbuf[outCurs++] = (unsigned char)(257 - count);
+            outbuf[outCurs++] = (unsigned char) (packBase + packSign * count);
             outbuf[outCurs++] = inbuf[hold];
         } else {
             /*Do a non-run*/
@@ -176,11 +185,11 @@ pm_rlenc_compressword(const uint16_t   * const inbuf,
                       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
+   Similar to pm_rlenc_compressbyte(), 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.
+   'inSize' is a number of words, but *outputSizeP is a number of bytes.
 -----------------------------------------------------------------------------*/
     size_t inCurs, outCurs;
     size_t flagSz;
@@ -258,6 +267,8 @@ pm_rlenc_compressword(const uint16_t   * const inbuf,
    number of flag bytes.  The worst case happens when there are no repeat
    sequences in the input.
 
+   The encoder in this file is an efficient one.
+
    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.
@@ -267,9 +278,9 @@ pm_rlenc_compressword(const uint16_t   * const inbuf,
    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.
+   (which could be a run or just part of a very long run) as a value and
+   a 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
@@ -288,6 +299,16 @@ pm_rlenc_compressword(const uint16_t   * const inbuf,
 
      - Whenever savings are larger than the added overhead, encode the run
        as a compressed block.
+
+   -----------------------------------------------------------------------
+
+   MS-DOS/Windows BMP format note
+
+   The compression method for BMP is a variant of packbits.
+   We could support the 8-bit version with some modifications to functions
+   in this file.  Determining the worst-case output size of an efficient
+   coder is rather complicated because uncompressed blocks may not be less
+   than three bytes long and are indicated by two-byte flags.
 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
 
 
@@ -310,23 +331,31 @@ pm_rlenc_maxbytes(size_t          const inSize,  /* number of elements */
    /* 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;
+    size_t blockSize;  /* Number of items in data block */
+    size_t flagSize;   /* Size of flag, in bytes */
+    size_t itemSize;   /* Size of item, in bytes */
+    size_t miscSize;   /* Size of other elements such as term code, in bytes */
+    size_t overhead;   /* Worst-case overhead, in bytes */
+    /* return value:      Worst-case output size, in bytes */ 
 
     switch (mode) {
     case PM_RLE_PACKBITS:
-        blockSize = 128; flagSize = 1; itemSize = 1; miscSize = 0; break;
+    case PM_RLE_PALMPDB:
+        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;
+        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;
+        blockSize = 127; flagSize = 2; itemSize = 2; miscSize = 2;
+        break;
     case PM_RLE_PALM16:
-        blockSize = 128; flagSize = 1; itemSize = 2; miscSize = 0; break;
+        blockSize = 128; flagSize = 1; itemSize = 2; miscSize = 0;
+        break;
     default:
         pm_error(errorUndefinedMode, mode);
     }
diff --git a/lib/util/runlength.h b/lib/util/runlength.h
index 4857ae61..de921650 100644
--- a/lib/util/runlength.h
+++ b/lib/util/runlength.h
@@ -18,7 +18,8 @@ enum pm_RleMode { PM_RLE_PACKBITS,          /* most common mode */
                   PM_RLE_PPA,               /* reserved */
                   PM_RLE_SGI8,              /* reserved */
                   PM_RLE_SGI16,
-                  PM_RLE_PALM16
+                  PM_RLE_PALM16,
+                  PM_RLE_PALMPDB  
                 };
 
 size_t
diff --git a/lib/util/shhopt.c b/lib/util/shhopt.c
index ccf2d1eb..ab489fef 100644
--- a/lib/util/shhopt.c
+++ b/lib/util/shhopt.c
@@ -168,7 +168,7 @@ optString(const optEntry opte, int lng)
 }
 
 
-    
+
 static optEntry
 optStructToEntry(const optStruct opt) {
 /*----------------------------------------------------------------------------
@@ -202,7 +202,7 @@ optStructTblToEntryTbl(const optStruct optStructTable[]) {
     int i;
 
     optEntry *optEntryTable;  /* malloc'ed array */
-    
+
     /* Count the entries in optStructTable[] */
     for (i = 0; optStructTable[i].type != OPT_END && i < 500; i++);
     count = i+1;
@@ -210,7 +210,7 @@ optStructTblToEntryTbl(const optStruct optStructTable[]) {
     optEntryTable = (optEntry *) malloc(count * sizeof(optEntry));
     if (optEntryTable) {
         int i;
-        for (i = 0; i < count; i++) 
+        for (i = 0; i < count; i++)
             optEntryTable[i] = optStructToEntry(optStructTable[i]);
     }
     return(optEntryTable);
@@ -284,7 +284,7 @@ getToken(const char *  const tokenStart,
    doesn't necessarily advance.
 -----------------------------------------------------------------------------*/
     const char * error;
-    
+
     pm_gettoken(tokenStart, delimiter, tokenP, nextP, &error);
 
     if (error)
@@ -383,7 +383,7 @@ parseStringList(const char *   const listText,
  |
  |  FUNCTION      Perform the action of an option.
  |
- |  INPUT         opt     element in array of defined options that 
+ |  INPUT         opt     element in array of defined options that
  |                        applies to this option
  |                arg     argument to option, if it applies.
  |                lng     was the option given as a long option?
@@ -411,7 +411,7 @@ optExecute(optEntry  const opt, char *arg, int lng)
     case OPT_LONG: {
         long tmp;
         char *e;
-	  
+
         if (arg == NULL)
             optFatal("internal error: optExecute() called with NULL argument "
                      "'%s'", optString(opt, lng));
@@ -429,12 +429,12 @@ optExecute(optEntry  const opt, char *arg, int lng)
                 *((long *) opt.arg) = tmp;
         }
     } break;
-	
+
     case OPT_UINT:
     case OPT_ULONG: {
         unsigned long tmp;
         char * tailPtr;
-        
+
         if (arg == NULL)
             optFatal("internal error: optExecute() called with NULL argument "
                      "'%s'", optString(opt, lng));
@@ -460,7 +460,7 @@ optExecute(optEntry  const opt, char *arg, int lng)
     case OPT_FLOAT: {
         float tmp;
         char *e;
-	  
+
         if (arg == NULL)
             optFatal("internal error: optExecute() called with NULL argument "
                      "'%s'", optString(opt, lng));
@@ -597,7 +597,7 @@ pm_optParseOptions(int *argc, char *argv[], optStruct opt[], int allowNegNum)
             arg = NULL;
             if ((p = strchr(argv[ai], '=')) != NULL)
                 arg = p + 1;
-	    
+
             /* does this option take an argument? */
             optarg = -1;
             if (optNeedsArgument(opt_table[mi])) {
@@ -666,7 +666,7 @@ pm_optParseOptions(int *argc, char *argv[], optStruct opt[], int allowNegNum)
 
 static void
 parse_short_option_token(char *argv[], const int argc, const int ai,
-                         const optEntry opt_table[], 
+                         const optEntry opt_table[],
                          int * const tokens_consumed_p) {
 /*----------------------------------------------------------------------------
    Parse a cluster of short options, e.g. -walne .
@@ -681,7 +681,7 @@ parse_short_option_token(char *argv[], const int argc, const int ai,
     char *arg;
     int mi;   /* index into option table */
     unsigned char processed_arg;  /* boolean */
-        /* We processed an argument to one of the one-character options. 
+        /* We processed an argument to one of the one-character options.
            This necessarily means there are no more options in this token
            to process.
            */
@@ -707,7 +707,7 @@ parse_short_option_token(char *argv[], const int argc, const int ai,
             (*tokens_consumed_p)++;
 		    }
 		    processed_arg = 1;
-		} else 
+		} else
             arg = NULL;
 		/* perform the action of this option. */
 		optExecute(opt_table[mi], arg, 0);
@@ -761,7 +761,7 @@ parse_long_option(char *   const argv[],
                   int      const argc,
                   int      const ai,
                   int      const namepos,
-                  optEntry const opt_table[], 
+                  optEntry const opt_table[],
                   int *    const tokens_consumed_p) {
 /*----------------------------------------------------------------------------
    Parse a long option, e.g. -verbose or --verbose.
@@ -788,11 +788,11 @@ parse_long_option(char *   const argv[],
         fatalUnrecognizedLongOption(argv[ai], opt_table);
 
     /* possibly locate the argument to this option. */
-    { 
+    {
         char *p;
         if ((p = strchr(argv[ai], '=')) != NULL)
             equals_arg = p + 1;
-        else 
+        else
             equals_arg = NULL;
     }
     /* does this option take an argument? */
@@ -812,7 +812,7 @@ parse_long_option(char *   const argv[],
             optFatal("option `%s' doesn't allow an argument, but you "
                      "have specified it in the form name=value",
                      optString(opt_table[mi], 1));
-        else 
+        else
             arg = NULL;
     }
     /* perform the action of this option. */
@@ -849,7 +849,7 @@ parse_long_option(char *   const argv[],
  |
  |                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 accommodations for 
+ |                any short options.  It also has accommodations for
  |                future expansion.
  |
  |                Options and arguments used are removed from the argv-
@@ -858,11 +858,11 @@ parse_long_option(char *   const argv[],
  |                Any error leads to program abortion.
  */
 void
-pm_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 pm_optParseOptions3(), except that there is no
-   "specified" return value.  
+   "specified" return value.
 
    This function exists for backward compatibility.
 -----------------------------------------------------------------------------*/
@@ -877,7 +877,7 @@ pm_optParseOptions2(int * const argc_p, char *argv[], const optStruct2 opt,
     if (opt3.opt_table == NULL)
         optFatal("Memory allocation failed (trying to allocate space for "
                  "new-format option table)");
-    
+
     pm_optParseOptions3(argc_p, argv, opt3, sizeof(opt3), flags);
 
     free(opt3.opt_table);
@@ -914,13 +914,13 @@ zero_specified(optEntry opt_table[]) {
  |                        Size of "opt" (since the caller may be older
  |                        than this function, it may be using a structure
  |                        with fewer fields than exist today.  We use this
- |                        parameter to handle those older callers). 
+ |                        parameter to handle those older callers).
  |                flags   Result is undefined if not zero.
  |                        For future expansion.
  |
  |  OUTPUT        argc    new argument count.
  |                argv    array with arguments removed.
- |                
+ |
  |                Areas pointed to by pointers in 'opt' get updated with
  |                option values and counts.
  |
@@ -934,7 +934,7 @@ zero_specified(optEntry opt_table[]) {
  |
  |                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 accommodations for 
+ |                any short options.  It also has accommodations for
  |                future expansion.
  |
  |                Options and arguments used are removed from the argv-
@@ -943,7 +943,7 @@ zero_specified(optEntry opt_table[]) {
  |                Any error leads to program abortion.
  */
 void
-pm_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. */
@@ -958,10 +958,10 @@ pm_optParseOptions3(int * const argc_p, char *argv[], const optStruct3 opt,
      */
     no_more_options = 0;  /* initial value */
     for (ai = 0; ai < *argc_p; ) {
-        if (no_more_options) 
+        if (no_more_options)
             /* Can't be an option -- there aren't any more */
             ai++;
-        else if (argv[ai][0] != '-') 
+        else if (argv[ai][0] != '-')
             /* Can't be an option -- doesn't start with a dash */
             ai++;
         else {
@@ -980,7 +980,7 @@ pm_optParseOptions3(int * const argc_p, char *argv[], const optStruct3 opt,
                     /* The entire thing is "--".  That means no more options */
                     tokens_consumed = 1;
                     no_more_options = 1;
-                } else 
+                } else
                     /* It's an option that starts with "--" */
                     parse_long_option(argv, *argc_p, ai, 2,
                                       opt.opt_table, &tokens_consumed);
@@ -994,7 +994,7 @@ pm_optParseOptions3(int * const argc_p, char *argv[], const optStruct3 opt,
                     parse_long_option(argv, *argc_p, ai, 1,
                                       opt.opt_table, &tokens_consumed);
                 }
-            
+
             }
             /* remove option and any argument from the argv-array. */
             {
diff --git a/lib/util/shhopt.h b/lib/util/shhopt.h
index 9a446290..d9304f9f 100644
--- a/lib/util/shhopt.h
+++ b/lib/util/shhopt.h
@@ -4,7 +4,7 @@ HERE IS AN EXAMPLE OF THE USE OF SHHOPT:
 
 
 #include <shhopt.h>
-int 
+int
 main ( int argc, char **argv ) {
 
     /* initial values here are just to demonstrate what gets set and
@@ -18,19 +18,21 @@ main ( int argc, char **argv ) {
     int height=7;
     int verbose_flag=7;
     int debug_flag=7;
+    char ** methodlist;
     struct optNameValue * optlist;
-    
+
     optStruct3 opt;
     unsigned int option_def_index = 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);
-    OPTENT3('n', "name",     OPT_STRING,   &name,         &name_spec,   0);
-    OPTENT3('v', "verbose",  OPT_FLAG,     &verbose_flag, NULL,         0);
-    OPTENT3('g', "debug",    OPT_FLAG,     &debug_flag,   NULL,         0);
-    OPTENT3(0,   "options",  OPT_NAMELIST, &optlist,      NULL,         0);
+    OPTENT3(0,   "help",     OPT_FLAG,       &help_flag,    &help_spec,   0);
+    OPTENT3(0,   "height",   OPT_INT,        &height,       &height_spec, 0);
+    OPTENT3('n', "name",     OPT_STRING,     &name,         &name_spec,   0);
+    OPTENT3('v', "verbose",  OPT_FLAG,       &verbose_flag, NULL,         0);
+    OPTENT3('g', "debug",    OPT_FLAG,       &debug_flag,   NULL,         0);
+    OPTENT3(0,   "methods",  OPT_STRINGLIST, &methodlist,   NULL,         0);
+    OPTENT3(0,   "options",  OPT_NAMELIST,   &optlist,      NULL,         0);
 
     opt.opt_table = option_def;
     opt.short_allowed = 1;
@@ -38,7 +40,7 @@ main ( int argc, char **argv ) {
 
 
     pm_optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
-    
+
 
     printf("argc=%d\n", argc);
     printf("help_flag=%d\n", help_flag);
@@ -50,13 +52,24 @@ main ( int argc, char **argv ) {
     printf("verbose_flag=%d\n", verbose_flag);
     printf("debug_flag=%d\n", verbose_flag);
 
+    if (methodlist) {
+        unsigned int i;
+        printf("methods: ");
+        while (methodlist[i]) {
+            printf("'%s', ", methodlist[i]);
+            ++i;
+        }
+        free(methodlist);
+    } else
+        printf("No -options\n");
+
     if (optlist) {
         unsigned int i;
         while (optlist[i].name) {
             printf("option '%s' = '%s'\n", optlist[i].name, optlist[i].value);
             ++i;
         }
-        optDestroyNameValueList(optlist);
+        pm_optDestroyNameValueList(optlist);
     } else
         printf("No -options\n");
 }
@@ -116,7 +129,7 @@ typedef struct {
                             * or pointer to function if type == OPT_FUNC. */
     int        flags;      /* modifier flags. */
 } optStruct;
-    
+
 typedef struct {
     /* This structure describes a single program option in a form for
      use by the pm_optParseOptions3() function.
@@ -124,10 +137,10 @@ typedef struct {
     char       shortName;  /* short option name. */
     const char *longName;  /* long option name, not including '--' or '-' */
     optArgType type;       /* option type. */
-    void       *arg;       
+    void       *arg;
         /* pointer to variable in which to return option's argument (or TRUE
-           if it's a flag option), or pointer to function if 
-           type == OPT_FUNC.  If the option is specified multiple times, only 
+           if it's a flag option), or pointer to function if
+           type == OPT_FUNC.  If the option is specified multiple times, only
            the rightmost one affects this return value.
         */
     unsigned int *specified;
@@ -136,7 +149,7 @@ typedef struct {
         */
     int        flags;      /* modifier flags. */
 } optEntry;
-    
+
 
 typedef struct {
     /* This structure describes the options of a program in a form for
@@ -146,20 +159,20 @@ typedef struct {
         /* The syntax may include short (i.e. one-character) options.
            These options may be stacked within a single token (e.g.
            -abc = -a -b -c).  If this value is not true, the short option
-           member of the option table entry is meaningless and long 
+           member of the option table entry is meaningless and long
            options may have either one or two dashes.
            */
     unsigned char allowNegNum;  /* boolean */
         /* Anything that starts with - and then a digit is a numeric
-           parameter, not an option 
+           parameter, not an option
            */
     optStruct *opt_table;
 } optStruct2;
 
 typedef struct {
     /* Same as optStruct2, but for pm_optParseOptions3() */
-    unsigned char short_allowed;  
-    unsigned char allowNegNum;    
+    unsigned char short_allowed;
+    unsigned char allowNegNum;
     optEntry *opt_table;
 } optStruct3;
 
@@ -173,9 +186,9 @@ typedef struct {
        optStruct *option_def = malloc(100*sizeof(optStruct));
        OPTENTRY('h', "help",     OPT_FLAG, &help_flag, 0);
        OPTENTRY(0,   "alphaout", OPT_STRING, &alpha_filename, 0);
-*/   
+*/
 
-/* If you name your variables option_def and option_def_index like in the 
+/* If you name your variables option_def and option_def_index like in the
    example above, everything's easy.  If you want to use OPTENTRY with other
    variables, define macros OPTION_DEF and OPTION_DEF_INDEX before calling
    OPTENTRY.
@@ -217,7 +230,10 @@ typedef struct {
     OPTENTRY(shortvalue, longvalue, typevalue, outputvalue, flagvalue) \
     }
 
-#define OPTENTINIT OPTION_DEF[0].type = OPT_END
+#define OPTENTINIT \
+    do {OPTION_DEF_INDEX=0; \
+        OPTION_DEF[OPTION_DEF_INDEX].type = OPT_END; \
+    } while (0)
 
 
 struct optNameValue {
@@ -226,7 +242,7 @@ struct optNameValue {
 };
 
 
-        
+
 void
 pm_optSetFatalFunc(void (*f)(const char *, ...));
 
@@ -234,10 +250,10 @@ void
 pm_optParseOptions(int *argc, char *argv[],
                    optStruct opt[], int allowNegNum);
 void
-pm_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);
 void
-pm_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
diff --git a/lib/util/wordaccess.h b/lib/util/wordaccess.h
deleted file mode 100644
index df0eaf12..00000000
--- a/lib/util/wordaccess.h
+++ /dev/null
@@ -1,75 +0,0 @@
-#ifndef WORDACCESS_H_INCLUDED
-#define WORDACCESS_H_INCLUDED
-
-/* These are facilities for accessing data in C programs in ways that
-   exploit the way the machine defines words in order to squeeze out
-   speed and CPU efficiency.
-
-   In particular, routines in this file exploit the endianness of the
-   machine and use explicit machine instructions to access C
-   variables.
-
-   A word is the amount of data that fits in a register; the amount of
-   data that a single machine instruction can process.  For example,
-   on IA32, a word is 32 bits because a single load or store
-   instruction moves that many bits and a single add instruction
-   operates on that many bits.
-
-
-   These facilities revolve around two data types:  wordInt and
-   wordIntBytes.
-
-   wordint is an unsigned integer with precision (size) of one word.
-   It is just the number -- nothing is implied about how it is
-   represented in memory.
-
-   wordintBytes is an array of bytes that represent a word-sized
-   unsigned integer.  x[0] is the high order 8 digits of the binary
-   coding of the integer, x[1] the next highest 8 digits, etc.
-   Note that it has big-endian form, regardless of what endianness the
-   underlying machine uses.
-
-   The actual size of word differs by machine.  Usually it is 32 or 64
-   bits.  Logically it can be as small as one byte.  Fixed bit sequences
-   in each program impose a lower limit of word width.  For example, the
-   longest bit sequence in pbmtog3 has 13 bits, so an 8-bit word won't
-   work with that.
-
-   We also assume that a char is 8 bits.
-
-   HAVE_GCC_BITCOUNT and HAVE_GCC_BSWAP are set in pm_config.h
-
-   BITS_PER_LONG is the number of bits in long int.
-*/
-
-#include "pm_config.h"
-
-#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 or GCC prior to v.3.4; little-endian  */
-  #include "wordaccess_generic.h"
-#endif
-
-#endif
diff --git a/lib/util/wordaccess_64_le.h b/lib/util/wordaccess_64_le.h
deleted file mode 100644
index 4d148ad2..00000000
--- a/lib/util/wordaccess_64_le.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*=============================================================================
-  This file is the part of wordaccess.h for use under these
-  conditions:
-
-  * GCC (>=3.4)  (__builtin_clz appears in GCC 3.4)
-  * Little-Endian machines (IA64, X86-64, AMD64)
-  * 64 bit long
- 
-=============================================================================*/
-
-#include "intcode.h"
-
-typedef uint64_t wordint;
-typedef unsigned char wordintBytes[sizeof(wordint)];
-
-
-static __inline__ wordint
-bytesToWordint(wordintBytes const bytes) {
-    return (wordint) pm_uintFromBigend64(*(bigend64*)bytes);
-}
-
-
-
-static __inline__ void
-wordintToBytes(wordintBytes * const bytesP,
-               wordint        const wordInt) {
-     *(bigend64*)bytesP = pm_bigendFromUint64(wordInt);
-}
diff --git a/lib/util/wordaccess_be_aligned.h b/lib/util/wordaccess_be_aligned.h
deleted file mode 100644
index f3bbb841..00000000
--- a/lib/util/wordaccess_be_aligned.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*=============================================================================
-  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_be_unaligned.h b/lib/util/wordaccess_be_unaligned.h
deleted file mode 100644
index 95b68ac7..00000000
--- a/lib/util/wordaccess_be_unaligned.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/*=============================================================================
-  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;
-typedef unsigned char wordintBytes[sizeof(wordint)];
-
-static __inline__ wordint
-bytesToWordint(wordintBytes bytes) {
-    return *((wordint *)bytes);
-}
-
-
-
-static __inline__ void
-wordintToBytes(wordintBytes * const bytesP,
-               wordint        const wordInt) {
-    *(wordint *)bytesP = wordInt;
-}
diff --git a/lib/util/wordaccess_generic.h b/lib/util/wordaccess_generic.h
deleted file mode 100644
index 6e0a20ef..00000000
--- a/lib/util/wordaccess_generic.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*=============================================================================
-
-  This file is the part of wordaccess.h for use under any of these
-  conditions:
-
-  * Compiler other than GCC
-  * GCC before version 3.4
-  * Requested by the user with WORDACCESS_GENERIC
-=============================================================================*/
-
-#include "intcode.h"
-
-typedef uint32_t wordint;
-typedef unsigned char wordintBytes[sizeof(wordint)];
-
-    
-static __inline__ wordint
-bytesToWordint(wordintBytes  const bytes) {
-
-    return (wordint)  pm_uintFromBigend32( * (bigend32*) bytes);
-}
-
-
-static __inline__ void
-wordintToBytes(wordintBytes * const bytesP,
-               wordint    const wordInt) {
-
-    * (bigend32*) bytesP = pm_bigendFromUint32((uint32_t)wordInt);
-}
diff --git a/lib/util/wordintclz.h b/lib/util/wordintclz.h
deleted file mode 100644
index 32e6ade8..00000000
--- a/lib/util/wordintclz.h
+++ /dev/null
@@ -1,93 +0,0 @@
-#ifndef WORDINTCLZ_H_INCLUDED
-#define WORDINTCLZ_H_INCLUDED
-
-#if (!defined(WORDACCESS_GENERIC) && HAVE_GCC_BITCOUNT )
-/* 
-   Compiler is GCC and has __builtin_clz()
-   wordint is long 
- 
-   __builtin_clz is available on GCC 3.4 and above
-     
-   Note that the clz scheme does not work and requires adjustment
-   if long type does not make use of all bits for data storage.
-  
-   This is unlikely.  According to GNU MP (http://www.swox.com/gmp/),
-   in rare cases such as Cray, there are smaller data types that take up
-   the same space as long, but leave the higher bits silent.
-   Currently, there are no known such cases for data type long.
- */
-
-static __inline__ unsigned int
-wordintClz(wordint const x){
-
-    assert(sizeof(unsigned long int) == sizeof(wordint));
-
-    if (x == 0)
-        return sizeof(wordint) * 8;
-    else
-        return (__builtin_clzl( (unsigned long int) x ));
-}
-  
-#else
-
-/* wordint is uint32_t: exactly 32 bits wide */
- 
-static unsigned char const clz8[256]= {
-    8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
-    3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
-    2, 2, 2, 2, 2, 2, 2, 2, 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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-    1, 1, 1, 1, 1, 1, 1, 1, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-    0, 0, 0, 0, 0, 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 
-};
-
-
-
-static __inline__ unsigned int
-clz16(wordint const x) {
-
-    if (x >> 8 != 0)
-        return clz8[x >> 8];
-    else
-        return clz8[x] + 8;
-}
-
-
-
-static __inline__  unsigned int
-clz32(wordint const x) {
-
-    if (x >> 16 != 0)
-        return clz16(x >> 16);
-    else
-        return clz16(x) + 16;
-}
-
-
-
-static __inline__  unsigned int
-wordintClz(wordint const x) {
-
-    assert(sizeof(wordint) == 4);
-
-    return clz32(x);
-}
-
-/* Another way to calculate leading zeros:
-   x == 0 ? 32 : 31 - floor(log(x)/log(2))
-   (Beware: insufficient precision will cause errors)
-*/
-
-#endif
-
-#endif
diff --git a/netpbm.c b/netpbm.c
index eeb82acc..a9a0bf81 100644
--- a/netpbm.c
+++ b/netpbm.c
@@ -30,7 +30,7 @@ int
 main(int argc, char *argv[]) {
 
     const char * cp;
-    
+
     if (strcmp(pm_arg0toprogname(argv[0]), "netpbm") == 0) {
         ++argv;
         --argc;
@@ -45,7 +45,7 @@ main(int argc, char *argv[]) {
 	}
 
     cp = pm_arg0toprogname(argv[0]);
-    
+
     /* merge.h is an automatically generated file that generates code to
        match the string 'cp' against the name of every program that is part
        of this merge and, upon finding a match, invoke that program.
@@ -55,48 +55,8 @@ main(int argc, char *argv[]) {
 
 #include "mergetrylist"
 
-    /* Add the obsolete names for some programs */
-    TRY("bmptoppm", main_bmptopnm);
-    TRY("gemtopbm", main_gemtopnm);
-    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);
-
-    /* We don't do the ppmtojpeg alias because if user doesn't have a JPEG
-       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.  Same with TIFF and PNG converters.
-
-    TRY("ppmtojpeg", main_pnmtojpeg); 
-    TRY("pngtopnm", main_pngtopam); 
-    TRY("pnmtotiff", main_pamtotiff);
-    TRY("pamrgbatopng", main_pamtopng);
-    */
-
     fprintf(stderr,"'%s' is an unknown Netpbm program name \n", cp );
+
     exit(1);
 }
 
diff --git a/other/Makefile b/other/Makefile
index bd2c9dc2..aa0d75bb 100644
--- a/other/Makefile
+++ b/other/Makefile
@@ -45,6 +45,8 @@ OBJECTS = $(BINARIES:%=%.o)
 MERGEBINARIES = $(BINARIES)
 MERGE_OBJECTS = $(MERGEBINARIES:%=%.o2)
 
+HAVE_MERGE_COMPAT=YES
+
 .PHONY: all
 all: $(BINARIES) $(SUBDIRS:%=%/all)
 
@@ -52,7 +54,7 @@ include $(SRCDIR)/common.mk
 
 ppmsvgalib: LDFLAGS_TARGET = $(shell $(LIBOPT) $(LINUXSVGALIB))
 
-install.bin: install.bin.local
+install.bin install.merge: install.bin.local
 .PHONY: install.bin.local
 install.bin.local: $(PKGDIR)/bin
 # Remember that $(SYMLINK) might just be a copy command.
@@ -69,4 +71,10 @@ install.bin.local: $(PKGDIR)/bin
 	  rm -f pnmdepth$(EXE) ; \
 	  $(SYMLINK) pamdepth$(EXE) pnmdepth$(EXE)
 
+mergecomptrylist:
+	cat /dev/null >$@
+	echo "TRY(\"pnmarith\", main_pamarith);" >>$@
+	echo "TRY(\"pnmsplit\", main_pamsplit);" >>$@
+	echo "TRY(\"pnmdepth\", main_pamdepth);" >>$@
+
 FORCE:
diff --git a/other/pamexec.c b/other/pamexec.c
index d14d8752..c3a1ee78 100644
--- a/other/pamexec.c
+++ b/other/pamexec.c
@@ -11,6 +11,7 @@
 
 ******************************************************************************/
 
+#define _DEFAULT_SOURCE 1  /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
diff --git a/other/pamfix.c b/other/pamfix.c
index 8db67e08..e40f51f4 100644
--- a/other/pamfix.c
+++ b/other/pamfix.c
@@ -58,7 +58,7 @@ parseCommandLine(int argc, char ** const argv,
         /* 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 "
@@ -119,7 +119,7 @@ analyzeRaster(const struct pam * const pamP,
     unsigned int row;
     jmp_buf jmpbuf;
     int rc;
-    
+
     tuplerow = pnm_allocpamrow(pamP);
 
     pm_setusererrormsgfn(handleRowErrMsg);
@@ -178,13 +178,13 @@ clipPamRow(const struct pam * const pamP,
         unsigned int plane;
         for (plane = 0; plane < pamP->depth; ++plane) {
             if (tuplerow[col][plane] > pamP->maxval) {
-                if (verbose) 
+                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;
+                tuplerow[col][plane] = pamP->maxval;
             }
         }
     }
@@ -211,7 +211,7 @@ copyGoodRows(const struct pam * const inpamP,
         clipPamRow(outpamP, tuplerow, row, verbose);
         pnm_writepamrow(outpamP, tuplerow);
     }
-    
+
     pnm_freepamrow(tuplerow);
 }
 
@@ -283,7 +283,7 @@ main(int argc, char * argv[]) {
     copyGoodRows(&tweakedPam, &outpam, cmdline.verbose);
 
     pm_close(inpam.file);
-    
+
     return 0;
 }
 
diff --git a/other/pamlookup.c b/other/pamlookup.c
index 4ceb047f..d57546d9 100644
--- a/other/pamlookup.c
+++ b/other/pamlookup.c
@@ -99,7 +99,8 @@ fitLookup(tuple **     const inputLookup,
 /*----------------------------------------------------------------------------
   Scale the lookup table image so that it has dimensions 'cols' x 'rows'.
 -----------------------------------------------------------------------------*/
-    const char * pamscaleCommand;
+    const char * widthArg;
+    const char * heightArg;
     struct pamtuples inPamtuples;
     struct pamtuples outPamtuples;
 
@@ -107,18 +108,21 @@ fitLookup(tuple **     const inputLookup,
     fitLookuppamP->width = cols;
     fitLookuppamP->height = rows;
 
-    pm_asprintf(&pamscaleCommand, "pamscale -width=%u -height=%u", cols, rows);
+    pm_asprintf(&widthArg,  "-width=%u", cols);
+    pm_asprintf(&heightArg, "-height=%u", rows);
 
     inPamtuples.pamP = (struct pam *) &inputLookuppam;
     inPamtuples.tuplesP = (tuple ***) &inputLookup;
     outPamtuples.pamP = fitLookuppamP;
     outPamtuples.tuplesP = fitLookupP;
     
-    pm_system(&pm_feed_from_pamtuples, &inPamtuples,
-              &pm_accept_to_pamtuples, &outPamtuples,
-              pamscaleCommand);
+    pm_system_lp("pamscale",
+                 &pm_feed_from_pamtuples, &inPamtuples,
+                 &pm_accept_to_pamtuples, &outPamtuples,
+                 "pamscale", widthArg, heightArg, NULL);
 
-    pm_strfree(pamscaleCommand);
+    pm_strfree(heightArg);
+    pm_strfree(widthArg);
 }
 
 
diff --git a/other/pampick.c b/other/pampick.c
index 61941f06..67d42fc5 100644
--- a/other/pampick.c
+++ b/other/pampick.c
@@ -58,7 +58,7 @@ isMemberOfUintSet(const struct uintSet * const uintSetP,
     unsigned int i;
 
     retval = FALSE;  /* initial assumption */
-    
+
     for (i = 0; i < uintSetP->count; ++i) {
         if (uintSetP->list[i] == searchValue)
             retval = TRUE;
@@ -137,7 +137,7 @@ parseCommandLine(int argc, const char ** argv,
                 if (*endPtr != '\0')
                     pm_error("Garbage in sequence number argument '%s': '%s'",
                              argv[i+1], endPtr);
-                
+
                 if (strtolResult < 0)
                     pm_error("Image sequence number cannot be negative.  "
                              "You specified %d", strtolResult);
@@ -171,7 +171,7 @@ extractOneImage(FILE * const infileP,
     struct pam inpam;
     struct pam outpam;
     enum pm_check_code checkRetval;
-    
+
     unsigned int row;
     tuple * tuplerow;
 
@@ -222,13 +222,13 @@ main(int argc, const char *argv[]) {
     struct cmdlineInfo cmdline;
 
     int eof;  /* No more images in input */
-    unsigned int imageSeq;  
+    unsigned int imageSeq;
         /* Sequence of current image in input file.  First = 0 */
 
     pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
-    
+
     eof = FALSE;
     for (imageSeq = 0; !eof; ++imageSeq) {
         if (isMemberOfUintSet(&cmdline.imageSeqList, imageSeq)) {
@@ -247,6 +247,6 @@ main(int argc, const char *argv[]) {
 
     pm_close(stdin);
     pm_close(stdout);
-    
+
     return 0;
 }
diff --git a/other/pamsplit.c b/other/pamsplit.c
index 26eb0d59..93c1726a 100644
--- a/other/pamsplit.c
+++ b/other/pamsplit.c
@@ -9,6 +9,7 @@
   Contributed to the public domain by its author.
 ******************************************************************************/
 
+#define _DEFAULT_SOURCE 1  /* New name for SVID & BSD source defines */
 #define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
diff --git a/other/pamx/Makefile b/other/pamx/Makefile
index 4e06e0fd..e4892540 100644
--- a/other/pamx/Makefile
+++ b/other/pamx/Makefile
@@ -9,7 +9,7 @@ include $(BUILDDIR)/config.mk
 
 EXTERN_INCLUDE =
 
-ifeq ($(shell pkg-config x11 --modversion --silence-errors),)
+ifeq ($(shell $(PKG_CONFIG) x11 --modversion --silence-errors),)
   # Pkg-config has never heard of X11, or doesn't even exist
 
   ifneq ($(X11LIB),NONE)
@@ -20,8 +20,8 @@ ifeq ($(shell pkg-config x11 --modversion --silence-errors),)
   endif
 else
   HAVE_X11LIB = Y
-  X11LIB = $(shell pkg-config x11 --libs)
-  EXTERN_INCLUDES += $(shell pkg-config x11 --cflags)
+  X11LIB = $(shell $(PKG_CONFIG) x11 --libs)
+  EXTERN_INCLUDES += $(shell $(PKG_CONFIG) x11 --cflags)
 endif
 
 ifeq ($(HAVE_X11LIB),Y)
@@ -46,10 +46,10 @@ all: $(BINARIES)
 
 include $(SRCDIR)/common.mk
 
-ifeq ($(shell pkg-config x11 --libs),)
+ifeq ($(shell $(PKG_CONFIG) x11 --libs),)
   X11_LIBOPTS = $(shell $(LIBOPT) $(LIBOPTR) $(X11LIB))
 else
-  X11_LIBOPTS = $(shell pkg-config x11 --libs)
+  X11_LIBOPTS = $(shell $(PKG_CONFIG) x11 --libs)
 endif
 
 pamx: image.o send.o window.o
diff --git a/other/pamx/pamx.c b/other/pamx/pamx.c
index e22693ea..35900f82 100644
--- a/other/pamx/pamx.c
+++ b/other/pamx/pamx.c
@@ -3,6 +3,7 @@
    Copyright information is in the file COPYRIGHT
 */
 
+#define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE  /* Make sure strdup() is in <string.h> */
 #include <signal.h>
diff --git a/other/pamx/send.c b/other/pamx/send.c
index fd50d5e9..3c3852e2 100644
--- a/other/pamx/send.c
+++ b/other/pamx/send.c
@@ -22,9 +22,10 @@
 #include "send.h"
 
 #define TRUE_TO_15BIT(PIXEL)     \
+    ((unsigned short) \
     ((((PIXEL) & 0xf80000) >> 9) | \
     (((PIXEL) & 0x00f800) >> 6) | \
-    (((PIXEL) & 0x0000f8) >> 3))
+     (((PIXEL) & 0x0000f8) >> 3)))
 
 #define RED_INTENSITY(P)   (((P) & 0x7c00) >> 10)
 #define GREEN_INTENSITY(P) (((P) & 0x03e0) >> 5)
@@ -116,63 +117,97 @@ bitsPerPixelAtDepth(Display *    const disp,
      
 
 
-static Image *
-itrueToRGB(Image *      const imageP,
-           unsigned int const ddepth) {
-
-    int y, x, num_pixels, colors;
-    unsigned long pixel_counts[32786];
-    unsigned long pixel_array[32786];
-    Pixel pixval;
+static void
+findColors(const Image *   const imageP,
+           unsigned int *  const pixelCt) {
+/*----------------------------------------------------------------------------
+   Find the colors in the Itrue raster 'raster'; return the histogram of
+   those colors as 'pixelCt'.
+-----------------------------------------------------------------------------*/
+    unsigned short color;
     unsigned char * pixel;
-    unsigned char * dpixel;
-    Image * newImageP;
-    
-    newImageP = newRGBImage(imageP->width, imageP->height, ddepth);
+    unsigned int y;
 
-    colors = 1 << ddepth;
-  
-    memset(pixel_counts, 0, 32768 * sizeof(unsigned long));
+    for (color = 0; color < 32768; ++color)
+        pixelCt[color] = 0;  /* initial value */
   
-    pixel= imageP->data;
-    for (y= 0; y < imageP->height; y++) {
+    for (y = 0, pixel = imageP->data; y < imageP->height; ++y) {
         unsigned int x;
-        for (x= 0; x < imageP->width; x++) {
-            unsigned int const z = TRUE_TO_15BIT(memToVal(pixel, 3));
-            pixel_counts[z]++;
+        for (x = 0; x < imageP->width; ++x) {
+            unsigned int const color = TRUE_TO_15BIT(memToVal(pixel, 3));
+            ++pixelCt[color];
             pixel += 3;
         }
     }
-    num_pixels = 0;
-    for (x = 0; x < 32768; ++x) {
-        if (pixel_counts[x] > 0) {
-            unsigned long const red = RED_INTENSITY(x);
-            unsigned long const grn = GREEN_INTENSITY(x);
-            unsigned long const blu = BLUE_INTENSITY(x);
-            pixel_counts[x] = num_pixels;
-            *(newImageP->rgb.red + num_pixels) = red<<11;
-            *(newImageP->rgb.grn + num_pixels) = grn<<11; 
-            *(newImageP->rgb.blu + num_pixels) =  blu<<11;
-            pixel_array[num_pixels++] = (short)x;
-            if (num_pixels > colors)
-                break;
+}
+
+
+
+static Image *
+pseudoColorImageFromItrue(Image *      const imageP,
+                          unsigned int const ddepth) {
+/*----------------------------------------------------------------------------
+  A pseudoColor image from the RGB image *imageP.
+
+  We compute the index -- i.e. assign each color in *imageP a number.
+-----------------------------------------------------------------------------*/
+    unsigned int const maxColorCt = (1 << ddepth);
+
+    unsigned int y;
+    unsigned int colorCt;
+        /* Number of colors we've indexed so far */
+    unsigned int pixelCt[32768];
+        /* colorCt[x] is the number of pixels of color 'x' in the image,
+           where 'x' is the 15 bit RGB representation of the color.
+        */
+    unsigned int colorIndex[32768];
+        /* colorIndex[x] is the color index we assigned to the color 'x',
+           where 'x' is the 15 bit RGB representation of the color.
+        */
+    Pixel pixval;
+    unsigned char * dpixel;
+    Image * newImageP;
+    unsigned short color;  /* A color, in 15 bit RGB */
+    unsigned char * pixel;
+
+    newImageP = newRGBImage(imageP->width, imageP->height, ddepth);
+
+    findColors(imageP, pixelCt);
+
+    for (color = 0, colorCt = 0;
+         color < 32768 && colorCt <= maxColorCt;
+         ++color) {
+        if (pixelCt[color] > 0) {
+            unsigned long const red = RED_INTENSITY(color);
+            unsigned long const grn = GREEN_INTENSITY(color);
+            unsigned long const blu = BLUE_INTENSITY(color);
+
+            /* Put the color in the color map */
+            newImageP->rgb.red[colorCt] = red<<11;
+            newImageP->rgb.grn[colorCt] = grn<<11; 
+            newImageP->rgb.blu[colorCt] = blu<<11;
+
+            /* Reverse-index it */
+            colorIndex[color] = colorCt;
+
+            ++colorCt;
         }
     }    
-
-    pixel = imageP->data;
-    dpixel = newImageP->data;
+    newImageP->rgb.used = colorCt;
     
-    for (y = 0; y < imageP->height; ++y) {
+    for (y = 0, pixel  = imageP->data, dpixel = newImageP->data;
+         y < imageP->height;
+         ++y) {
+
         unsigned int x;
         for (x = 0; x < imageP->width; ++x) {
-            unsigned int const z = TRUE_TO_15BIT(memToVal(pixel, 3));
-            pixval = pixel_counts[z];
+            unsigned short const color = TRUE_TO_15BIT(memToVal(pixel, 3));
+            pixval = colorIndex[color];
             valToMem(pixval, dpixel, newImageP->pixlen);
             pixel += 3;
             dpixel += newImageP->pixlen;
         }
     }
-    newImageP->rgb.used = num_pixels;
     newImageP->rgb.compressed = 1;
 
     return newImageP;
@@ -197,7 +232,7 @@ makeUsableVisual(Image *      const origImageP,
             *newImagePP = origImageP;
             break;
         case PseudoColor:
-            *newImagePP = itrueToRGB(origImageP, ddepth);
+            *newImagePP = pseudoColorImageFromItrue(origImageP, ddepth);
             if (*newImagePP == NULL)
                 pm_error("Unable to convert for Pseudocolor.");
             break;
diff --git a/other/pamx/window.c b/other/pamx/window.c
index e2de1577..d907163b 100644
--- a/other/pamx/window.c
+++ b/other/pamx/window.c
@@ -6,6 +6,7 @@
    See COPYRIGHT file for copyright information.
 */
 
+#define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #define _BSD_SOURCE    /* Make sure strcaseeq() is in nstring.h */
 
diff --git a/other/pnmcolormap.c b/other/pnmcolormap.c
index 57db4329..f687f037 100644
--- a/other/pnmcolormap.c
+++ b/other/pnmcolormap.c
@@ -50,9 +50,9 @@ struct cmdlineInfo {
     */
     const char *inputFilespec;  /* Filespec of input file */
     unsigned int allcolors;  /* boolean: select all colors from the input */
-    unsigned int newcolors;    
+    unsigned int newcolors;
         /* Number of colors argument; meaningless if allcolors true */
-    enum methodForLargest methodForLargest; 
+    enum methodForLargest methodForLargest;
         /* -spreadintensity/-spreadluminosity options */
     enum methodForRep methodForRep;
         /* -center/-meancolor/-meanpixel options */
@@ -68,7 +68,7 @@ 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.  
+   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.
@@ -89,21 +89,21 @@ parseCommandLine (int argc, char ** argv,
     MALLOCARRAY_NOFAIL(option_def, 100);
 
     option_def_index = 0;   /* incremented by OPTENT3 */
-    OPTENT3(0,   "spreadbrightness", OPT_FLAG,   
+    OPTENT3(0,   "spreadbrightness", OPT_FLAG,
             NULL,                       &spreadbrightness, 0);
-    OPTENT3(0,   "spreadluminosity", OPT_FLAG,   
+    OPTENT3(0,   "spreadluminosity", OPT_FLAG,
             NULL,                       &spreadluminosity, 0);
-    OPTENT3(0,   "center",           OPT_FLAG,   
+    OPTENT3(0,   "center",           OPT_FLAG,
             NULL,                       &center,           0);
-    OPTENT3(0,   "meancolor",        OPT_FLAG,   
+    OPTENT3(0,   "meancolor",        OPT_FLAG,
             NULL,                       &meancolor,        0);
-    OPTENT3(0,   "meanpixel",        OPT_FLAG,   
+    OPTENT3(0,   "meanpixel",        OPT_FLAG,
             NULL,                       &meanpixel,        0);
-    OPTENT3(0, "sort",     OPT_FLAG,   NULL,                  
+    OPTENT3(0, "sort",     OPT_FLAG,   NULL,
             &cmdlineP->sort,       0 );
-    OPTENT3(0, "square",   OPT_FLAG,   NULL,                  
+    OPTENT3(0, "square",   OPT_FLAG,   NULL,
             &cmdlineP->square,       0 );
-    OPTENT3(0, "verbose",   OPT_FLAG,   NULL,                  
+    OPTENT3(0, "verbose",   OPT_FLAG,   NULL,
             &cmdlineP->verbose,       0 );
 
     opt.opt_table = option_def;
@@ -114,7 +114,7 @@ parseCommandLine (int argc, char ** argv,
         /* Uses and sets argc, argv, and some of *cmdline_p and others. */
 
 
-    if (spreadbrightness && spreadluminosity) 
+    if (spreadbrightness && spreadluminosity)
         pm_error("You cannot specify both -spreadbrightness and "
                  "spreadluminosity.");
     if (spreadluminosity)
@@ -127,7 +127,7 @@ parseCommandLine (int argc, char ** argv,
                  "-meanpixel.");
     if (meancolor)
         cmdlineP->methodForRep = REP_AVERAGE_COLORS;
-    else if (meanpixel) 
+    else if (meanpixel)
         cmdlineP->methodForRep = REP_AVERAGE_PIXELS;
     else
         cmdlineP->methodForRep = REP_CENTER_BOX;
@@ -179,7 +179,7 @@ static unsigned int compareplanePlane;
        tuples.  qsort() doesn't pass any arguments except the two tuples.
     */
 static int
-compareplane(const void * const arg1, 
+compareplane(const void * const arg1,
              const void * const arg2) {
 
     const struct tupleint * const * const comparandPP  = arg1;
@@ -223,7 +223,7 @@ newColorMap(unsigned int const newcolors,
 
     for (i = 0; i < newcolors; ++i) {
         unsigned int plane;
-        for (plane = 0; plane < depth; ++plane) 
+        for (plane = 0; plane < depth; ++plane)
             colormap.table[i]->tuple[plane] = 0;
     }
     colormap.size = newcolors;
@@ -269,14 +269,14 @@ findBoxBoundaries(tupletable2  const colorfreqtable,
         minval[plane] = colorfreqtable.table[boxStart]->tuple[plane];
         maxval[plane] = minval[plane];
     }
-    
+
     for (i = 1; i < boxSize; ++i) {
         unsigned int plane;
         for (plane = 0; plane < depth; ++plane) {
             sample const v = colorfreqtable.table[boxStart + i]->tuple[plane];
             if (v < minval[plane]) minval[plane] = v;
             if (v > maxval[plane]) maxval[plane] = v;
-        } 
+        }
     }
 }
 
@@ -284,11 +284,11 @@ findBoxBoundaries(tupletable2  const colorfreqtable,
 
 static unsigned int
 largestByNorm(sample minval[], sample maxval[], unsigned int const depth) {
-    
+
     unsigned int largestDimension;
     unsigned int plane;
 
-    sample largestSpreadSoFar = 0;  
+    sample largestSpreadSoFar = 0;
     largestDimension = 0;
     for (plane = 0; plane < depth; ++plane) {
         sample const spread = maxval[plane]-minval[plane];
@@ -302,11 +302,11 @@ largestByNorm(sample minval[], sample maxval[], unsigned int const depth) {
 
 
 
-static unsigned int 
-largestByLuminosity(sample minval[], sample maxval[], 
+static unsigned int
+largestByLuminosity(sample minval[], sample maxval[],
                     unsigned int const depth) {
 /*----------------------------------------------------------------------------
-   This subroutine presumes that the tuple type is either 
+   This subroutine presumes that the tuple type is either
    BLACKANDWHITE, GRAYSCALE, or RGB (which implies pamP->depth is 1 or 3).
    To save time, we don't actually check it.
 -----------------------------------------------------------------------------*/
@@ -323,7 +323,7 @@ largestByLuminosity(sample minval[], sample maxval[],
         largestSpreadSoFar = 0.0;
 
         for (plane = 0; plane < 3; ++plane) {
-            double const spread = 
+            double const spread =
                 pnm_lumin_factor[plane] * (maxval[plane]-minval[plane]);
             if (spread > largestSpreadSoFar) {
                 largestDimension = plane;
@@ -340,7 +340,7 @@ largestByLuminosity(sample minval[], sample maxval[],
 static void
 centerBox(int          const boxStart,
           int          const boxSize,
-          tupletable2  const colorfreqtable, 
+          tupletable2  const colorfreqtable,
           unsigned int const depth,
           tuple        const newTuple) {
 
@@ -351,7 +351,7 @@ centerBox(int          const boxStart,
         unsigned int i;
 
         minval = maxval = colorfreqtable.table[boxStart]->tuple[plane];
-        
+
         for (i = 1; i < boxSize; ++i) {
             int const v = colorfreqtable.table[boxStart + i]->tuple[plane];
             minval = MIN( minval, v);
@@ -366,7 +366,7 @@ centerBox(int          const boxStart,
 static void
 averageColors(int          const boxStart,
               int          const boxSize,
-              tupletable2  const colorfreqtable, 
+              tupletable2  const colorfreqtable,
               unsigned int const depth,
               tuple        const newTuple) {
 
@@ -378,7 +378,7 @@ averageColors(int          const boxStart,
 
         sum = 0;
 
-        for (i = 0; i < boxSize; ++i) 
+        for (i = 0; i < boxSize; ++i)
             sum += colorfreqtable.table[boxStart+i]->tuple[plane];
 
         newTuple[plane] = ROUNDDIV(sum, boxSize);
@@ -390,7 +390,7 @@ averageColors(int          const boxStart,
 static void
 averagePixels(int          const boxStart,
               int          const boxSize,
-              tupletable2  const colorfreqtable, 
+              tupletable2  const colorfreqtable,
               unsigned int const depth,
               tuple        const newTuple) {
 
@@ -411,7 +411,7 @@ averagePixels(int          const boxStart,
 
         sum = 0;
 
-        for (i = 0; i < boxSize; ++i) 
+        for (i = 0; i < boxSize; ++i)
             sum += colorfreqtable.table[boxStart+i]->tuple[plane]
                 * colorfreqtable.table[boxStart+i]->value;
 
@@ -423,9 +423,9 @@ averagePixels(int          const boxStart,
 
 static tupletable2
 colormapFromBv(unsigned int      const newcolors,
-               boxVector         const bv, 
+               boxVector         const bv,
                unsigned int      const boxes,
-               tupletable2       const colorfreqtable, 
+               tupletable2       const colorfreqtable,
                unsigned int      const depth,
                enum methodForRep const methodForRep) {
     /*
@@ -434,7 +434,7 @@ colormapFromBv(unsigned int      const newcolors,
     ** One would be to choose the center of the box; this ignores any structure
     ** within the boxes.  Another method would be to average all the colors in
     ** the box - this is the method specified in Heckbert's paper.  A third
-    ** method is to average all the pixels in the box. 
+    ** method is to average all the pixels in the box.
     */
     tupletable2 colormap;
     unsigned int bi;
@@ -443,8 +443,8 @@ colormapFromBv(unsigned int      const newcolors,
 
     for (bi = 0; bi < boxes; ++bi) {
         switch (methodForRep) {
-        case REP_CENTER_BOX: 
-            centerBox(bv[bi].ind, bv[bi].colors, colorfreqtable, depth, 
+        case REP_CENTER_BOX:
+            centerBox(bv[bi].ind, bv[bi].colors, colorfreqtable, depth,
                       colormap.table[bi]->tuple);
             break;
         case REP_AVERAGE_COLORS:
@@ -467,7 +467,7 @@ colormapFromBv(unsigned int      const newcolors,
 
 static unsigned int
 freqTotal(tupletable2 const freqtable) {
-    
+
     unsigned int i;
     unsigned int sum;
 
@@ -481,16 +481,16 @@ freqTotal(tupletable2 const freqtable) {
 
 
 static void
-splitBox(boxVector             const bv, 
-         unsigned int *        const boxesP, 
+splitBox(boxVector             const bv,
+         unsigned int *        const boxesP,
          unsigned int          const bi,
-         tupletable2           const colorfreqtable, 
+         tupletable2           const colorfreqtable,
          unsigned int          const depth,
          enum methodForLargest const methodForLargest) {
 /*----------------------------------------------------------------------------
    Split Box 'bi' in the box vector bv (so that bv contains one more box
    than it did as input).  Split it so that each new box represents about
-   half of the pixels in the distribution given by 'colorfreqtable' for 
+   half of the pixels in the distribution given by 'colorfreqtable' for
    the colors in the original box, but with distinct colors in each of the
    two new boxes.
 
@@ -512,7 +512,7 @@ splitBox(boxVector             const bv,
     MALLOCARRAY_NOFAIL(minval, depth);
     MALLOCARRAY_NOFAIL(maxval, depth);
 
-    findBoxBoundaries(colorfreqtable, depth, boxStart, boxSize, 
+    findBoxBoundaries(colorfreqtable, depth, boxStart, boxSize,
                       minval, maxval);
 
     /* Find the largest dimension, and sort by that component.  I have
@@ -521,28 +521,28 @@ splitBox(boxVector             const bv,
        transforming into luminosities before the comparison.
     */
     switch (methodForLargest) {
-    case LARGE_NORM: 
+    case LARGE_NORM:
         largestDimension = largestByNorm(minval, maxval, depth);
         break;
-    case LARGE_LUM: 
+    case LARGE_LUM:
         largestDimension = largestByLuminosity(minval, maxval, depth);
         break;
     }
-                                                    
+
     /* TODO: I think this sort should go after creating a box,
        not before splitting.  Because you need the sort to use
        the REP_CENTER_BOX method of choosing a color to
-       represent the final boxes 
+       represent the final boxes
     */
 
     /* Set the gross global variable 'compareplanePlane' as a
        parameter to compareplane(), which is called by qsort().
     */
     compareplanePlane = largestDimension;
-    qsort((char*) &colorfreqtable.table[boxStart], boxSize, 
-          sizeof(colorfreqtable.table[boxStart]), 
+    qsort((char*) &colorfreqtable.table[boxStart], boxSize,
+          sizeof(colorfreqtable.table[boxStart]),
           compareplane);
-            
+
     {
         /* Now find the median based on the counts, so that about half
            the pixels (not colors, pixels) are in each subdivision.  */
@@ -571,7 +571,7 @@ splitBox(boxVector             const bv,
 
 
 static void
-mediancut(tupletable2           const colorfreqtable, 
+mediancut(tupletable2           const colorfreqtable,
           unsigned int          const depth,
           int                   const newcolors,
           enum methodForLargest const methodForLargest,
@@ -605,7 +605,7 @@ mediancut(tupletable2           const colorfreqtable,
         for (bi = 0; bi < boxes && bv[bi].colors < 2; ++bi);
         if (bi >= boxes)
             multicolorBoxesExist = FALSE;
-        else 
+        else
             splitBox(bv, &boxes, bi, colorfreqtable, depth, methodForLargest);
     }
     *colormapP = colormapFromBv(newcolors, bv, boxes, colorfreqtable, depth,
@@ -646,7 +646,7 @@ addImageColorsToHash(struct pam *   const pamP,
 
     tuple * tuplerow;
     unsigned int row;
-    
+
     tuplerow = pnm_allocpamrow(pamP);
 
     for (row = 0; row < pamP->height; ++row) {
@@ -676,7 +676,7 @@ computeHistogram(FILE *         const ifP,
                  tupletable2 *  const colorfreqtableP) {
 /*----------------------------------------------------------------------------
   Make a histogram of the colors in the image stream in the file '*ifP'.
-  
+
   Return as *freqPamP a description of the tuple values in the histogram.
   Only the fields of *freqPamP that describe individual tuples are
   meaningful (depth, maxval, tuple type);
@@ -689,7 +689,7 @@ computeHistogram(FILE *         const ifP,
     tuplehash tuplehash;
     unsigned int colorCount;
     int eof;
-    
+
     pm_message("making histogram...");
 
     tuplehash = pnm_createtuplehash();
@@ -699,7 +699,7 @@ computeHistogram(FILE *         const ifP,
 
     for (imageSeq = 0; !eof; ++imageSeq) {
         struct pam inpam;
-        
+
         pm_message("Scanning image %u", imageSeq);
 
         pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
@@ -708,7 +708,7 @@ computeHistogram(FILE *         const ifP,
             firstPam = inpam;
         else
             validateCompatibleImage(&inpam, &firstPam, imageSeq);
-    
+
         addImageColorsToHash(&inpam, tuplehash, &colorCount);
 
         pm_message("%u colors so far", colorCount);
@@ -722,14 +722,14 @@ computeHistogram(FILE *         const ifP,
     pnm_destroytuplehash(tuplehash);
 
     pm_message("%u colors found", colorfreqtableP->size);
-    
+
     freqPamP->size   = sizeof(*freqPamP);
     freqPamP->len    = PAM_STRUCT_SIZE(tuple_type);
     freqPamP->maxval = firstPam.maxval;
     freqPamP->bytes_per_sample = pnm_bytespersample(freqPamP->maxval);
     freqPamP->depth  = firstPam.depth;
     STRSCPY(freqPamP->tuple_type, firstPam.tuple_type);
-    
+
     *formatP = firstPam.format;
 }
 
@@ -737,8 +737,8 @@ computeHistogram(FILE *         const ifP,
 
 static void
 computeColorMapFromInput(FILE *                const ifP,
-                         bool                  const allColors, 
-                         int                   const reqColors, 
+                         bool                  const allColors,
+                         int                   const reqColors,
                          enum methodForLargest const methodForLargest,
                          enum methodForRep     const methodForRep,
                          int *                 const formatP,
@@ -755,7 +755,7 @@ computeColorMapFromInput(FILE *                const ifP,
 
    The colormap has the same maxval as the input.
 
-   Put the colormap in newly allocated storage as a tupletable2 
+   Put the colormap in newly allocated storage as a tupletable2
    and return its address as *colormapP.  Return the number of colors in
    it as *colorsP and its maxval as *colormapMaxvalP.
 
@@ -766,7 +766,7 @@ computeColorMapFromInput(FILE *                const ifP,
     tupletable2 colorfreqtable;
 
     computeHistogram(ifP, formatP, freqPamP, &colorfreqtable);
-    
+
     if (allColors) {
         *colormapP = colorfreqtable;
     } else {
@@ -787,16 +787,16 @@ computeColorMapFromInput(FILE *                const ifP,
 
 
 static void
-sortColormap(tupletable2  const colormap, 
+sortColormap(tupletable2  const colormap,
              sample       const depth) {
 /*----------------------------------------------------------------------------
-   Sort the colormap in place, in order of ascending Plane 0 value, 
+   Sort the colormap in place, in order of ascending Plane 0 value,
    the Plane 1 value, etc.
 
    Use insertion sort.
 -----------------------------------------------------------------------------*/
     int i;
-    
+
     pm_message("Sorting %u colors...", colormap.size);
 
     for (i = 0; i < colormap.size; ++i) {
@@ -806,8 +806,8 @@ sortColormap(tupletable2  const colormap,
             bool iIsGreater, iIsLess;
 
             iIsGreater = FALSE; iIsLess = FALSE;
-            for (plane = 0; 
-                 plane < depth && !iIsGreater && !iIsLess; 
+            for (plane = 0;
+                 plane < depth && !iIsGreater && !iIsLess;
                  ++plane) {
                 if (colormap.table[i]->tuple[plane] >
                     colormap.table[j]->tuple[plane])
@@ -815,7 +815,7 @@ sortColormap(tupletable2  const colormap,
                 else if (colormap.table[i]->tuple[plane] <
                          colormap.table[j]->tuple[plane])
                     iIsLess = TRUE;
-            }            
+            }
             if (iIsGreater) {
                 for (plane = 0; plane < depth; ++plane) {
                     sample const temp = colormap.table[i]->tuple[plane];
@@ -824,21 +824,21 @@ sortColormap(tupletable2  const colormap,
                     colormap.table[j]->tuple[plane] = temp;
                 }
             }
-        }    
+        }
     }
 }
 
 
 
-static void 
+static void
 colormapToSquare(struct pam * const pamP,
                  tupletable2  const colormap,
                  tuple ***    const outputRasterP) {
     {
         unsigned int const intsqrt = (int)sqrt((float)colormap.size);
-        if (intsqrt * intsqrt == colormap.size) 
+        if (SQR(intsqrt) == colormap.size)
             pamP->width = intsqrt;
-        else 
+        else
             pamP->width = intsqrt + 1;
     }
     {
@@ -852,17 +852,17 @@ colormapToSquare(struct pam * const pamP,
         tuple ** outputRaster;
         unsigned int row;
         unsigned int colormapIndex;
-        
+
         outputRaster = pnm_allocpamarray(pamP);
 
         colormapIndex = 0;  /* initial value */
-        
+
         for (row = 0; row < pamP->height; ++row) {
             unsigned int col;
             for (col = 0; col < pamP->width; ++col) {
                 unsigned int plane;
                 for (plane = 0; plane < pamP->depth; ++plane) {
-                    outputRaster[row][col][plane] = 
+                    outputRaster[row][col][plane] =
                         colormap.table[colormapIndex]->tuple[plane];
                 }
                 if (colormapIndex < colormap.size-1)
@@ -870,24 +870,24 @@ colormapToSquare(struct pam * const pamP,
             }
         }
         *outputRasterP = outputRaster;
-    } 
+    }
 }
 
 
 
-static void 
+static void
 colormapToSingleRow(struct pam * const pamP,
                     tupletable2  const colormap,
                     tuple ***    const outputRasterP) {
 
     tuple ** outputRaster;
     unsigned int col;
-    
+
     pamP->width = colormap.size;
     pamP->height = 1;
-    
+
     outputRaster = pnm_allocpamarray(pamP);
-    
+
     for (col = 0; col < pamP->width; ++col) {
         int plane;
         for (plane = 0; plane < pamP->depth; ++plane)
@@ -904,7 +904,7 @@ colormapToImage(int                const format,
                 tupletable2        const colormap,
                 bool               const sort,
                 bool               const square,
-                struct pam *       const outpamP, 
+                struct pam *       const outpamP,
                 tuple ***          const outputRasterP) {
 /*----------------------------------------------------------------------------
    Create a tuple array and pam structure for an image which includes
@@ -924,9 +924,9 @@ colormapToImage(int                const format,
     if (sort)
         sortColormap(colormap, outpamP->depth);
 
-    if (square) 
+    if (square)
         colormapToSquare(outpamP, colormap, outputRasterP);
-    else 
+    else
         colormapToSingleRow(outpamP, colormap, outputRasterP);
 }
 
@@ -950,8 +950,8 @@ main(int argc, char * argv[] ) {
     ifP = pm_openr(cmdline.inputFilespec);
 
     computeColorMapFromInput(ifP,
-                             cmdline.allcolors, cmdline.newcolors, 
-                             cmdline.methodForLargest, 
+                             cmdline.allcolors, cmdline.newcolors,
+                             cmdline.methodForLargest,
                              cmdline.methodForRep,
                              &format, &colormapPam, &colormap);
 
@@ -964,9 +964,9 @@ main(int argc, char * argv[] ) {
         pm_message("Generating %u x %u image", outpam.width, outpam.height);
 
     outpam.file = stdout;
-    
+
     pnm_writepam(&outpam, colormapRaster);
-    
+
     pnm_freetupletable2(&colormapPam, colormap);
 
     pnm_freepamarray(colormapRaster, &outpam);
@@ -975,3 +975,6 @@ main(int argc, char * argv[] ) {
 
     return 0;
 }
+
+
+
diff --git a/pm_config.in.h b/pm_config.in.h
index fe8b8008..b30bccfe 100644
--- a/pm_config.in.h
+++ b/pm_config.in.h
@@ -10,7 +10,7 @@
 
   Wherever possible, Netpbm handles customization via the make files
   instead of via this file.  However, Netpbm's make file philosophy
-  discourages lining up a bunch of -D options on every compile, so a 
+  discourages lining up a bunch of -D options on every compile, so a
   #define here would be preferable to a -D compile option.
 
 **************************************************************************/
@@ -38,12 +38,12 @@
    what feature sets his program relies.
 
    But some C library developers have misunderstood this and think of these
-   macros like the old __ansi__ macro, which tells the C library, "Don't 
+   macros like the old __ansi__ macro, which tells the C library, "Don't
    have any features that aren't in the ANSI standard."  I.e. it's just
    the opposite -- the macro subtracts features instead of adding them.
 
    This means that on some platforms, Netpbm programs must define
-   _POSIX_SOURCE, and on others, it must not.  Netpbm's POSIX_IS_IMPLIED 
+   _POSIX_SOURCE, and on others, it must not.  Netpbm's POSIX_IS_IMPLIED
    macro indicates that we're on a platform where we need not define
    _POSIX_SOURCE (and probably must not).
 
@@ -63,7 +63,7 @@
 
 /* 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
+** what color "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
@@ -123,7 +123,7 @@
 #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.
+   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.
 */
@@ -176,7 +176,7 @@
   #if (!defined(__inline__))
     #if (defined(__sgi) || defined(_AIX))
       #define __inline__ __inline
-    #else   
+    #else
       #define __inline__
     #endif
   #endif
@@ -195,7 +195,7 @@
 #else
   #if (defined(__sgi))
     #define LITERAL_FN_DEF_MATCH 1
-  #else   
+  #else
     #define LITERAL_FN_DEF_MATCH 0
   #endif
 #endif
@@ -333,7 +333,7 @@ typedef int qsort_comparison_fn(const void *, const void *);
 #if MSVCRT
   #define pm_mkdir(dir, perm) _mkdir(dir)
 #else
-  #define pm_mkdir(dir, perm) mkdir(dir, perm) 
+  #define pm_mkdir(dir, perm) mkdir(dir, perm)
 #endif
 
 #if MSVCRT
diff --git a/test/Available-Testprog b/test/Available-Testprog
new file mode 100755
index 00000000..8176b57a
--- /dev/null
+++ b/test/Available-Testprog
@@ -0,0 +1,75 @@
+#! /bin/bash
+
+# If make target is "check-install" assume that all programs are available
+if [ "${CHECK_TYPE}" = "install" ]; then
+  exit 0
+fi
+
+# Special case: no arguments were passed to this program
+# For all-in-place.test and legacy-names.test
+
+if [ $# = 0 ]; then
+  exit 0
+fi
+
+# Normal operation: Walk through the argument list and exit if an
+# unavailable program is encountered.
+
+# See http://netpbm.sourceforge.net/prereq.html and the makefiles in
+# each directory (for example converter/other/Makefile) for library
+# requirements and relevant variables.
+
+for i in $@
+    do
+    case $i in
+      fiascotopnm|\
+      pnmtofiasco)
+        [ "${BUILD_FIASCO}" = "N" ] && exit 1 ;;
+
+      jpeg2ktopam|\
+      pamtojpeg2k)
+        [ "${JASPERLIB}" = "NONE" ] && exit 1 ;;
+
+      jbigtopnm|\
+      pnmtojbig)
+        [ "${JBIGLIB}" = "NONE" ] && exit 1 ;;
+
+      jpegtopnm|\
+      pnmtojpeg|\
+      ppmtojpeg)
+        [ "${JPEGLIB}" = "NONE" ] && exit 1 ;;
+
+      pamtotiff|\
+      pnmtotiff|\
+      pnmtotiffcmyk|\
+      tifftopnm)
+        [ "${TIFFLIB}" = "NONE" -o \
+          "${JPEGLIB}" = "NONE" -o \
+          "${ZLIB}" = "NONE" ] && exit 1 ;;
+
+      pnmtorle|\
+      rletopnm)
+        [ "${URTLIB}" = "NONE" ] && exit 1 ;;
+
+      pamx)
+        [ "${X11LIB}" = "NONE" ] && exit 1 ;;
+
+      svgtopam)
+        [ "${XML2_LIBS}" = "NONE" ] && exit 1 ;;
+
+      thinkjettopbm)
+        [ -z "${LEX}" ] && exit 1 ;;
+
+      zlib)
+        [ "${ZLIB}" = "NONE" ] && exit 1 ;;
+
+    esac
+done
+
+# All checks passed.  Exit with success status.
+exit 0
+
+
+# TODO: We don't have a good method for testing whether PNGLIB is
+# available for linking.
+# Affected programs: pamtopng, pngtopam, pngtopnm, pnmtopng
diff --git a/test/Execute-Tests b/test/Execute-Tests
index 78091e6b..7a65d51f 100755
--- a/test/Execute-Tests
+++ b/test/Execute-Tests
@@ -180,7 +180,8 @@ elif [ $VALGRIND_TESTS = "on" ]
   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
+    head iconv mkdir mktemp perl rm sed seq sh tee testrandom tr uniq \
+    Available-Testprog
 
     # Tell valgrind not to probe execution of the above programs.
 
@@ -236,19 +237,38 @@ if [ $VALGRIND_TESTS = "on" ]
 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
+# But first check if the .ok file exists.  (Some .ok files are
+# dynamically created.)  Then see if target programs and requirements
+# are in place.  If either of these conditions are not met, do
+# not execute the test and report "Not Testable".
+
+if [ ! -s ${srcdir}/${tname%.test}.ok ]
+then
+  let result=4;
+else
+  ${srcdir}/Available-Testprog \
+    `sed -n -e '/^# This script tests: /s/# This script tests: //p' \
+            -e '/^# Also requires: /s/^# Also requires: //p' \
+            -e '/^$/q' ${srcdir}/$tname | tr '\n' ' '`
+  case $? in
+  0)
+    PATH=${testpath} $vg_command ${srcdir}/$tname > ${tname%.test}.out;
+    let retval=$?
+    case $retval 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 ;;
+  
+  1)   let result=4 ;;
+  *)   let result=1 ;;
+  esac
+fi
 
 # Report whether a single test succeeded or failed.
 # Increment counters.
diff --git a/test/Makefile b/test/Makefile
index 9b7ab047..c640dfff 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -10,7 +10,9 @@ MERGE_OBJECTS =
 
 PROGS = testrandom
 
-all: $(PROGS)
+OKSTOGENERATE = $(patsubst %.rand-ok, %.ok, $(wildcard *.rand-ok))
+
+all: $(PROGS) $(OKSTOGENERATE)
 
 testrandom.o: testrandom.c
 	$(CC_FOR_BUILD) -c -o $@ $(CFLAGS_FOR_BUILD) $<
@@ -18,6 +20,10 @@ testrandom.o: testrandom.c
 testrandom: testrandom.o
 	$(LD_FOR_BUILD) -o $@ $(LDFLAGS_FOR_BUILD) $<
 
+RAND_VARIETY ?= $(shell ./testrandom -x)
+
+$(OKSTOGENERATE): %.ok: %.rand-ok testrandom
+	sed -n "/^$(RAND_VARIETY)|/s/^$(RAND_VARIETY)|//p" $< > $@
 
 OMIT_TEST_RULE = 1
 include $(SRCDIR)/common.mk
@@ -25,4 +31,4 @@ include $(SRCDIR)/common.mk
 distclean clean: cleanlocal
 .PHONY: cleanlocal
 cleanlocal:
-	rm -f $(PROGS)
+	rm -f $(PROGS) $(patsubst %.rand-ok,%.ok,$(wildcard *.rand-ok))
diff --git a/test/Test-Order b/test/Test-Order
index 92efca05..49eaeff2 100644
--- a/test/Test-Order
+++ b/test/Test-Order
@@ -1,4 +1,4 @@
-# General test
+# General tests
 
 all-in-place.test
 legacy-names.test
@@ -12,23 +12,29 @@ pamseq.test
 
 pbmpage.test
 pbmtext.test
+pbmtext-bdf.test
+pbmtext-iso88591.test
+pbmtext-utf8.test
 pbmupc.test
 pgmramp.test
-ppmgauss.test
+pamgauss.test
 ppmcie.test
 ppmwheel.test
 pamcrater.test
+ppmpat.test
 
 # Generators with random components
 
 pgmnoise.test
-ppmpat.test
+ppmpat-random.test
 ppmforge.test
 ppmrough.test
 
 # Analyzer tests
 
 pamfile.test
+pamfind.test
+pamtable.test
 pgmhist.test
 ppmhist.test
 pamsumm.test
@@ -53,15 +59,16 @@ pamcut.test
 pnmcat.test
 pamflip1.test
 pamflip2.test
-pamenlarge.test
 pnminvert.test
 pamchannel.test
 ppmchange.test
 pambackground.test
+pnmpaste-pbm.test
 
 pbmpscale.test
 pnmremap1.test
 pnmremap2.test
+pnmquantall.test
 pnmtile.test
 ppmbrighten.test
 ppmdither.test
@@ -71,7 +78,19 @@ ppmdim.test
 pnmshear.test
 pgmbentley.test
 
+pamenlarge.test
+pamenlarge-pbm.test
+
+pamscale-reportonly.test
+pamscale-filters1.test
+pamscale-filters2.test
+pamscale-filters3.test
+pamenlarge-pamscale-point.test
+
+pamstretch.test
+
 ppmmix.test
+pammixmulti-identity.test
 
 # Symmetry test
 
@@ -79,6 +98,7 @@ symmetry.test
 
 # Format converter tests
 
+pamtopdbimg.test
 pbmtog3.test
 411toppm.test
 eyuvtoppm.test
@@ -103,6 +123,8 @@ ppmchange-roundtrip.test
 pamdice-roundtrip.test
 pamslice-roundtrip.test
 lookup-roundtrip.test
+enlarge-reduce-roundtrip.test
+cut-cat-roundtrip.test
 
 # Round-trip tests : lossless converters
 
@@ -111,6 +133,7 @@ atari-roundtrip.test
 atk-roundtrip.test
 avs-roundtrip.test
 bmp-roundtrip.test
+bmp-quant-roundtrip.test
 cis-roundtrip.test
 cmuw-roundtrip.test
 facesaver-roundtrip.test
@@ -122,18 +145,23 @@ gif-quant-roundtrip.test
 hdiff-roundtrip.test
 ilbm-roundtrip.test
 jbig-roundtrip.test
+jpeg2k-roundtrip.test
 leaf-roundtrip.test
 macp-roundtrip.test
 mda-roundtrip.test
 mgr-roundtrip.test
 mrf-roundtrip.test
+palm-roundtrip.test
+palm-roundtrip2.test
 pcx-roundtrip.test
+pdb-roundtrip.test
 pfm-roundtrip.test
 pi3-roundtrip.test
 pict-roundtrip.test
 png-roundtrip.test
 png-roundtrip2.test
 ps-roundtrip.test
+ps-flate-roundtrip.test
 ps-alt-roundtrip.test
 sgi-roundtrip.test
 sbig-roundtrip.test
@@ -142,6 +170,7 @@ sunicon-roundtrip.test
 sunrast-roundtrip.test
 targa-roundtrip.test
 tiff-roundtrip.test
+tiff-flate-lzw-roundtrip.test
 utahrle-roundtrip.test
 wbmp-roundtrip.test
 winicon-roundtrip.test
@@ -150,6 +179,9 @@ xpm-roundtrip.test
 xv-roundtrip.test
 xwd-roundtrip.test
 
-# Round-trip tests : lossy converter
+# Round-trip tests : lossy converters
 
+fiasco-roundtrip.test
+jpeg-roundtrip.test
+tiffcmyk-roundtrip.test
 yuv-roundtrip.test
diff --git a/test/all-in-place.ok b/test/all-in-place.ok
index 6ba75993..2df9f392 100644
--- a/test/all-in-place.ok
+++ b/test/all-in-place.ok
@@ -11,6 +11,7 @@ cmuwmtopbm: ok
 ddbugtopbm: ok
 escp2topbm: ok
 eyuvtoppm: ok
+fiascotopnm: ok
 fitstopnm: ok
 fstopgm: ok
 g3topbm: ok
@@ -35,9 +36,11 @@ mtvtoppm: ok
 neotoppm: ok
 palmtopnm: ok
 pamaddnoise: ok
+pamaltsat: ok
 pamarith: ok
 pambackground: ok
 pambayer: ok
+pambrighten: ok
 pamchannel: ok
 pamcomp: ok
 pamcrater: ok
@@ -51,14 +54,19 @@ pamendian: ok
 pamenlarge: ok
 pamexec: ok
 pamfile: ok
+pamfind: ok
 pamfix: ok
 pamflip: ok
 pamfunc: ok
 pamgauss: ok
+pamgetcolor: ok
 pamgradient: ok
+pamhue: ok
+pamlevels: ok
 pamlookup: ok
 pammasksharpen: ok
 pammixinterlace: ok
+pammixmulti: ok
 pammosaicknit: ok
 pamoil: ok
 pampaintspill: ok
@@ -78,8 +86,10 @@ pamsplit: ok
 pamstack: ok
 pamstereogram: ok
 pamstretch: ok
+pamstretch-gen: ok
 pamsumm: ok
 pamsummcol: ok
+pamtable: ok
 pamthreshold: ok
 pamtilt: ok
 pamtoavs: ok
@@ -103,6 +113,7 @@ pamtotiff: ok
 pamtouil: ok
 pamtowinicon: ok
 pamtoxvmini: ok
+pamtris: ok
 pamundice: ok
 pamunlookup: ok
 pamvalidate: ok
@@ -195,6 +206,7 @@ pnmhisteq: ok
 pnmhistmap: ok
 pnmindex: ok
 pnminvert: ok
+pnmmargin: ok
 pnmmercator: ok
 pnmmontage: ok
 pnmnlfilt: ok
@@ -202,6 +214,8 @@ pnmnorm: ok
 pnmpad: ok
 pnmpaste: ok
 pnmpsnr: ok
+pnmquant: ok
+pnmquantall: ok
 pnmremap: ok
 pnmrotate: ok
 pnmscalefixed: ok
@@ -236,6 +250,7 @@ ppmdist: ok
 ppmdither: ok
 ppmdmkfont: ok
 ppmdraw: ok
+ppmfade: ok
 ppmflash: ok
 ppmforge: ok
 ppmglobe: ok
@@ -245,8 +260,10 @@ ppmmake: ok
 ppmmix: ok
 ppmntsc: ok
 ppmpat: ok
+ppmrainbow: ok
 ppmrelief: ok
 ppmrough: ok
+ppmshadow: ok
 ppmshift: ok
 ppmspread: ok
 ppmtoacad: ok
@@ -315,15 +332,5 @@ 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
+manweb: ok
diff --git a/test/all-in-place.test b/test/all-in-place.test
index 4c8af56b..6cf677ef 100755
--- a/test/all-in-place.test
+++ b/test/all-in-place.test
@@ -4,9 +4,12 @@
 # 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.
+## See Netpbm Library Prerequisites
+## http://netpbm.sourceforge.net/prereq.html
+## if make succeeds but this test fails
+##
+## If you run the "make test-install" on an older version, some executables
+## may be reported as missing because of missing features.
 
 function testExitStatus () {
 
@@ -50,6 +53,7 @@ ordinary_testprogs="\
   ddbugtopbm \
   escp2topbm \
   eyuvtoppm \
+  fiascotopnm \
   fitstopnm \
   fstopgm \
   g3topbm \
@@ -74,9 +78,11 @@ ordinary_testprogs="\
   neotoppm \
   palmtopnm \
   pamaddnoise \
+  pamaltsat \
   pamarith \
   pambackground \
   pambayer \
+  pambrighten \
   pamchannel \
   pamcomp \
   pamcrater \
@@ -90,14 +96,19 @@ ordinary_testprogs="\
   pamenlarge \
   pamexec \
   pamfile \
+  pamfind \
   pamfix \
   pamflip \
   pamfunc \
   pamgauss \
+  pamgetcolor \
   pamgradient \
+  pamhue \
+  pamlevels \
   pamlookup \
   pammasksharpen \
   pammixinterlace \
+  pammixmulti \
   pammosaicknit \
   pamoil \
   pampaintspill \
@@ -117,8 +128,10 @@ ordinary_testprogs="\
   pamstack \
   pamstereogram \
   pamstretch \
+  pamstretch-gen \
   pamsumm \
   pamsummcol \
+  pamtable \
   pamthreshold \
   pamtilt \
   pamtoavs \
@@ -142,6 +155,7 @@ ordinary_testprogs="\
   pamtouil \
   pamtowinicon \
   pamtoxvmini \
+  pamtris \
   pamundice \
   pamunlookup \
   pamvalidate \
@@ -234,6 +248,7 @@ ordinary_testprogs="\
   pnmhistmap \
   pnmindex \
   pnminvert \
+  pnmmargin \
   pnmmercator \
   pnmmontage \
   pnmnlfilt \
@@ -241,6 +256,8 @@ ordinary_testprogs="\
   pnmpad \
   pnmpaste \
   pnmpsnr \
+  pnmquant \
+  pnmquantall \
   pnmremap \
   pnmrotate \
   pnmscalefixed \
@@ -275,6 +292,7 @@ ordinary_testprogs="\
   ppmdither \
   ppmdmkfont \
   ppmdraw \
+  ppmfade \
   ppmflash \
   ppmforge \
   ppmglobe \
@@ -284,8 +302,10 @@ ordinary_testprogs="\
   ppmmix \
   ppmntsc \
   ppmpat \
+  ppmrainbow \
   ppmrelief \
   ppmrough \
+  ppmshadow \
   ppmshift \
   ppmspread \
   ppmtoacad \
@@ -356,74 +376,58 @@ ordinary_testprogs="\
   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 $?
 
+# The string "fiascotopnm" has to be filtered out by fgrep for fiascotopnm
+# has a slightly different version report format.
 
-# Test anytopnm, pnmnoraw, pnmquant, pnmquantall
-# ppmrainbow, ppmshadow, ppmtomap
-# with trivial input.
+# Switch to control output from "program --version"
+#
+grepSwitch=1        # Initial value
+                    # Always 1 for check-install,
+                    # otherwise changes to 0 once a command succeeds.
+grepOption[0]="-v"  # Suppress output
+grepOption[1]=""    # Output --version message
 
-$i ${tmpdir}/test.pbm > /dev/null 2> /dev/null;
-tmpdir=${tmpdir:-/tmp}
-test_pbm=${tmpdir}/test.pbm
+for i in $ordinary_testprogs
+  do
+    # Stub out programs that aren't built.
+    Available-Testprog "$i"
+      if [ $? = 1 ]; then
+      echo "$i: program was not built" 1>&2
+      echo "$i: ok"
+      continue
+      fi
 
-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
+    $i --version < /dev/null 2>&1 | \
+    # Older versions of pamstretch-gen and ppmshadow (encountered when
+    # one does "make check-install") get stuck when there is no input.
+    # Supply /dev/null as input to placate these programs.
+    
+    egrep -v -e "fiascotopnm" -e \
+    ": ((BSD|SYSV|MSDOS|AMIGA) defined|RGB_?ENV=)" | \
+    egrep ${grepOption[$grepSwitch]} -e \
+    ": (Using lib(net)?pbm|(Built (by|at)|Compiled ))" 1>&2;
+    # See showVersion() in lib/libpm.c for the above regular expressions.
+  
+    exitStatus=${PIPESTATUS[0]}
+    testExitStatus $i 0 ${exitStatus}
 
-for i in pnmquant pnmquantall
-  do
-    $i 2 ${tmpdir}/test.pbm > /dev/null 2> /dev/null;
-    testExitStatus $i 0 $?
+    if [ ${CHECK_TYPE} != "install" ]
+      then grepSwitch=$((${grepSwitch} * ${exitStatus}==0 ? 0 : 1)) ;
+    fi
   done
 
-rm ${test_pbm}
-    ppmrainbow rgb:00/00/00 rgb:ff/ff/ff \
-    -tmpdir=${tmpdir} -width=2 -height=2 > /dev/null
-    testExitStatus ppmrainbow 0 $?
 
+# Anytopnm now supports --version but it delegates the report to pamtopnm.
+# It fails if pamtopnm is absent.
+# We consider anytopnm too important to test in an indirect manner.
 
-# 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 $?
+anytopnm testgrid.pbm > /dev/null 2> /dev/null;
+testExitStatus anytopnm 0 $?
 
 
-# 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.
+# Test manweb which has --help.
 
-for i in pamstretch-gen pcdovtoppm
-  do
-     $i > /dev/null
-     testExitStatus $i 1 $?
-  done
+manweb --help > /dev/null
+    testExitStatus manweb 0 $?
diff --git a/test/bmp-quant-roundtrip.ok b/test/bmp-quant-roundtrip.ok
new file mode 100644
index 00000000..5262df53
--- /dev/null
+++ b/test/bmp-quant-roundtrip.ok
@@ -0,0 +1,6 @@
+colors=256, bpp=8
+0 0 0 : 0
+0 0 0 : 0
+colors=16, bpp=4
+0 0 0 : 0
+0 0 0 : 0
diff --git a/test/bmp-quant-roundtrip.test b/test/bmp-quant-roundtrip.test
new file mode 100755
index 00000000..2c223b7c
--- /dev/null
+++ b/test/bmp-quant-roundtrip.test
@@ -0,0 +1,36 @@
+#! /bin/bash
+# This script tests: bmptopnm ppmtobmp pnmquant
+# Also requires: ppmhist
+
+tmpdir=${tmpdir:-/tmp}
+quant_ppm=${tmpdir}/quant.ppm
+
+colors=256      # any value between 2 - 256 works
+bpp=8
+echo colors=${colors}, bpp=${bpp} 
+
+pnmquant ${colors} testimg.ppm > ${quant_ppm}
+
+for mode in "-windows" "-os2"
+  do
+  ppmtobmp $mode -bpp=${bpp} ${quant_ppm} | bmptopnm | \
+    cmp -s - ${quant_ppm} > /dev/null
+  echo ${PIPESTATUS[@]} ":" $?
+  done
+
+rm ${quant_ppm}
+
+colors=16      # any value between 2 - 16 works
+bpp=4
+echo colors=${colors}, bpp=${bpp}
+
+pnmquant ${colors} testimg.ppm > ${quant_ppm}
+
+for mode in "-windows" "-os2"
+  do
+  ppmtobmp -bpp=${bpp} ${quant_ppm} | bmptopnm | \
+    cmp -s - ${quant_ppm} > /dev/null
+  echo ${PIPESTATUS[@]} ":" $?
+  done
+
+rm ${quant_ppm}
diff --git a/test/bmp-roundtrip.ok b/test/bmp-roundtrip.ok
index 67f7a1fe..4f4f8367 100644
--- a/test/bmp-roundtrip.ok
+++ b/test/bmp-roundtrip.ok
@@ -1,2 +1,20 @@
-1926073387 101484
+PBM
+2425386270 41
+2425386270 41
+2425386270 41
 2425386270 41
+PPM
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+PGM
+1571496937 33838
+1571496937 33838
+1571496937 33838
+1571496937 33838
+1571496937 33838
+1571496937 33838
+1571496937 33838
+1571496937 33838
+1571496937 33838
diff --git a/test/bmp-roundtrip.test b/test/bmp-roundtrip.test
index be9a4548..c9ef363a 100755
--- a/test/bmp-roundtrip.test
+++ b/test/bmp-roundtrip.test
@@ -1,7 +1,42 @@
 #! /bin/bash
 # This script tests: bmptopnm ppmtobmp
-# Also requires:
+# Also requires: pamchannel pamtopnm pamseq
 
+tmpdir=${tmpdir:-/tmp}
 
-ppmtobmp testimg.ppm | bmptopnm | cksum
-ppmtobmp testgrid.pbm | bmptopnm | cksum
+# Test 1.  Should print 2425386270 41 four times
+
+echo PBM
+
+for mode in "" "-bpp=1" "-windows" "-os2"
+  do
+  ppmtobmp ${mode} testgrid.pbm | bmptopnm | cksum
+  done
+
+# Test 2.  Should print 1926073387 101484 four times
+
+echo PPM
+
+for mode in "" "-bpp=24" "-windows" "-os2"
+  do
+  ppmtobmp ${mode} testimg.ppm | bmptopnm | cksum
+  done
+
+# Test 3.  Should print 1571496937 33838 nine times
+
+echo PGM
+
+red_pgm=${tmpdir}/red.pgm
+mapfile_pgm=${tmpdir}/mapfile.pgm
+pamseq -tupletype="GRAYSCALE" 1 255 > ${mapfile_pgm}
+
+pamchannel -infile=testimg.ppm -tupletype="GRAYSCALE" 0 | \
+    pamtopnm | tee ${red_pgm} | cksum
+
+for mode in "" "-bpp=8" "-windows" "-os2"
+  do
+  ppmtobmp ${mode} ${red_pgm} | bmptopnm | cksum
+  ppmtobmp ${mode} -mapfile=${mapfile_pgm} ${red_pgm} | bmptopnm | cksum
+  done
+
+rm ${mapfile_pgm} ${red_pgm}
\ No newline at end of file
diff --git a/test/cut-cat-roundtrip.ok b/test/cut-cat-roundtrip.ok
new file mode 100644
index 00000000..bc9b8cb4
--- /dev/null
+++ b/test/cut-cat-roundtrip.ok
@@ -0,0 +1,92 @@
+Test 1.
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+Test 2.
+3891261972 202953
+3891261972 202953
+3891261972 202953
+3891261972 202953
+3891261972 202953
+3891261972 202953
+Test 3.
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+Test 4.
+26789469 202953
+26789469 202953
+26789469 202953
+26789469 202953
+26789469 202953
+Test 5.
+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
+Test 6.
+1887700557 73
+1887700557 73
+1887700557 73
+1887700557 73
+1887700557 73
+1887700557 73
+1887700557 73
+1887700557 73
+1887700557 73
+1887700557 73
+1887700557 73
+1887700557 73
+1887700557 73
+1887700557 73
+1887700557 73
+Test 7.
+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
+Test 8.
+3221289196 73
+3221289196 73
+3221289196 73
+3221289196 73
+3221289196 73
+3221289196 73
+3221289196 73
+3221289196 73
+3221289196 73
+3221289196 73
+3221289196 73
+3221289196 73
+3221289196 73
+3221289196 73
+3221289196 73
+3221289196 73
+3221289196 73
diff --git a/test/cut-cat-roundtrip.test b/test/cut-cat-roundtrip.test
new file mode 100755
index 00000000..95ee5279
--- /dev/null
+++ b/test/cut-cat-roundtrip.test
@@ -0,0 +1,128 @@
+#! /bin/bash
+# This script tests: pamcut
+# Also requires: pnmcat pnmpad
+
+tmpdir=${tmpdir:-/tmp}
+quant_ppm=${tmpdir}/quant.ppm
+
+right_pbm=${tmpdir}/right.pbm
+left_pbm=${tmpdir}/left.pbm
+right_ppm=${tmpdir}/right.ppm
+left_ppm=${tmpdir}/left.ppm
+bottom_pbm=${tmpdir}/bottom.pbm
+top_pbm=${tmpdir}/top.pbm
+bottom_ppm=${tmpdir}/bottom.ppm
+top_ppm=${tmpdir}/top.ppm
+
+
+# Test 1.  Should print 1926073387 101484 six times
+echo Test 1.
+
+cat testimg.ppm | cksum
+for i in 0 1 128 224 225
+  do
+  pamcut -left=$((i+1)) testimg.ppm > ${right_ppm}
+  pamcut -right=$i      testimg.ppm > ${left_ppm}
+  pnmcat -lr ${left_ppm} ${right_ppm} | \
+    pamcut -left=0 -width=227| cksum
+  rm ${left_ppm} ${right_ppm}
+  done
+
+
+# Test 2.  Should print 3891261972 202953 six times
+# Not roundtrip.  Padding added to right.
+echo Test 2.
+
+pnmpad -right=227 -black testimg.ppm | cksum
+for i in  0 1 128 224 225
+  do
+  pamcut -left=$((i+1)) -width=227 -pad testimg.ppm > ${right_ppm}
+  pamcut -right=$i      -width=227 -pad testimg.ppm > ${left_ppm}
+  pnmcat -lr ${left_ppm} ${right_ppm} ${left_ppm} | \
+    pamcut -left=$((227-i-1))  -width=$((227*2)) | cksum
+  rm ${left_ppm} ${right_ppm}
+  done
+
+
+# Test 3.  Should print 1926073387 101484 five times
+echo Test 3.
+
+cat testimg.ppm | cksum
+for i in 0 1 70 147
+  do
+  pamcut -top=$((i+1)) testimg.ppm > ${bottom_ppm}
+  pamcut -bottom=$i    testimg.ppm > ${top_ppm}
+  pnmcat -tb ${top_ppm} ${bottom_ppm} | \
+    pamcut -top=0 -height=149 | cksum
+  rm ${top_ppm} ${bottom_ppm}
+  done
+
+# Test 4.  Should print 26789469 202953 five times
+# Not roundtrip.  Padding added to bottom.
+echo Test 4.
+
+pnmpad -bottom=149 -black testimg.ppm | cksum
+for i in 0 1 70 147
+  do
+  pamcut -top=$((i+1)) -height=149 -pad testimg.ppm > ${bottom_ppm}
+  pamcut -bottom=$i    -height=149 -pad testimg.ppm > ${top_ppm}
+  pnmcat -tb ${top_ppm} ${bottom_ppm} ${top_ppm} | \
+    pamcut -top=$((149-i-1))  -height=$((149*2)) | cksum
+  rm ${top_ppm} ${bottom_ppm}
+  done
+
+# Test 5.  Should print 2425386270 41 fourteen times
+echo Test 5.
+
+cat testgrid.pbm | cksum
+for i in `seq 0 12`
+  do
+  pamcut -left=$((i+1)) testgrid.pbm > ${right_pbm}
+  pamcut -right=$i      testgrid.pbm > ${left_pbm}
+  pnmcat -lr ${left_pbm} ${right_pbm} | \
+    pamcut -left=0 -width=14 | cksum
+  rm ${left_pbm} ${right_pbm}
+  done
+
+
+# Test 6.  Should print 1887700557 73 fifteen times
+# Not roundtrip.  Padding added to right.
+echo Test 6.
+
+pnmpad -right=14 -black testgrid.pbm | cksum
+for i in `seq 0 13`
+  do
+  pamcut -left=$((i+1)) -width=14 -pad testgrid.pbm > ${right_pbm}
+  pamcut -right=$i      -width=14 -pad testgrid.pbm > ${left_pbm}
+  pnmcat -lr ${left_pbm} ${right_pbm} ${left_pbm} | \
+    pamcut -left=$((14-i-1)) -width=28 | cksum
+  rm ${left_pbm} ${right_pbm}
+  done
+
+# Test 7.  Should print 2425386270 41 sixteen times
+echo Test 7.
+
+cat testgrid.pbm | cksum
+for i in `seq 0 14`
+  do
+  pamcut -top=$((i+1)) testgrid.pbm > ${bottom_pbm}
+  pamcut -bottom=$i    testgrid.pbm > ${top_pbm}
+  pnmcat -tb ${top_pbm} ${bottom_pbm} | \
+    pamcut -top=0 -height=16 | cksum
+  rm ${top_pbm} ${bottom_pbm}
+  done
+
+# Test 8.  Should print 3221289196 73 seventeen times
+# Not roundtrip.  Padding added to bottom.
+echo Test 8.
+
+pnmpad -bottom=16 -black testgrid.pbm | cksum
+for i in `seq 0 15`
+  do
+  pamcut -top=$((i+1)) -height=16 -pad testgrid.pbm > ${bottom_pbm}
+  pamcut -bottom=$i    -height=16 -pad testgrid.pbm > ${top_pbm}
+  pnmcat -tb ${top_pbm} ${bottom_pbm} ${top_pbm} | \
+    pamcut -top=$((16-i-1)) -height=32 | cksum
+  rm ${top_pbm} ${bottom_pbm}
+  done
+
diff --git a/test/enlarge-reduce-roundtrip.ok b/test/enlarge-reduce-roundtrip.ok
new file mode 100644
index 00000000..8ee8118a
--- /dev/null
+++ b/test/enlarge-reduce-roundtrip.ok
@@ -0,0 +1,17 @@
+2364818683 590
+2364818683 590
+2364818683 590
+2364818683 590
+2364818683 590
+2364818683 590
+2364818683 590
+2364818683 590
+2364818683 590
+2364818683 590
+2364818683 590
+2364818683 590
+2364818683 590
+2364818683 590
+2364818683 590
+2364818683 590
+2364818683 590
diff --git a/test/enlarge-reduce-roundtrip.test b/test/enlarge-reduce-roundtrip.test
new file mode 100755
index 00000000..54b52c60
--- /dev/null
+++ b/test/enlarge-reduce-roundtrip.test
@@ -0,0 +1,34 @@
+#! /bin/bash
+# This script tests: pamenlarge pbmpscale pbmreduce
+# Also requires: pbmtext
+
+tmpdir=${tmpdir:-/tmp}
+xanadu_pbm=${tmpdir}/xanadu.pbm
+
+echo "In Xanadu did Kubla khan" | pbmtext > ${xanadu_pbm} 
+
+# Should print 2364818683 590
+
+cat ${xanadu_pbm} | cksum
+
+# Should print 2364818683 590 eight times
+
+for scale in 2 4 6 7
+  do
+  for flag in "-fs -randomseed=0" "-threshold -v 0.4"
+    do
+    pamenlarge ${scale} ${xanadu_pbm} | pbmreduce ${flag} ${scale} | cksum
+    done
+  done
+
+# Should print 2364818683 590 eight times
+
+for scale in 5 7 9 11
+  do
+  for flag in "-fs -randomseed=0" "-threshold"
+    do
+    pbmpscale ${scale} ${xanadu_pbm} | pbmreduce ${flag} ${scale} | cksum
+    done
+  done
+
+rm ${xanadu_pbm}
diff --git a/test/fiasco-roundtrip.ok b/test/fiasco-roundtrip.ok
new file mode 100644
index 00000000..544093d3
--- /dev/null
+++ b/test/fiasco-roundtrip.ok
@@ -0,0 +1 @@
+  3 1000.00 1000.00 1000.00
\ No newline at end of file
diff --git a/test/fiasco-roundtrip.test b/test/fiasco-roundtrip.test
new file mode 100755
index 00000000..8325a37d
--- /dev/null
+++ b/test/fiasco-roundtrip.test
@@ -0,0 +1,23 @@
+#! /bin/bash
+# This script tests: pnmtofiasco fiascotopnm
+# Also requires: pnmpad pnmpsnr
+
+# Pnmtofiasco: number of rows, cols in input file must be even
+# Pnmpsnr output: 15.11 22.71 30.09
+# TODO: As in jpeg-rountrip.test the threshold has been determined
+# without much thought.
+
+# Should print 3 1000.00 1000.00 1000.00
+
+tmpdir=${tmpdir:-/tmp}
+padded_ppm=${tmpdir}/padded.ppm
+
+pnmpad --black --bottom 1 --left 1 testimg.ppm > ${padded_ppm} &&
+pnmtofiasco --progress-meter 0 ${padded_ppm} | fiascotopnm | \
+    pnmpsnr -machine - ${padded_ppm} | \
+    awk '{printf("%3d %.2f %.2f %.2f", NF,
+                  $1>14.0 ? 1000.00 : $1,
+                  $2>21.0 ? 1000.00 : $2,
+                  $3>29.0 ? 1000.00 : $3) }'
+
+rm ${padded_ppm}
\ No newline at end of file
diff --git a/test/g3-roundtrip.ok b/test/g3-roundtrip.ok
index bb0b1cf6..603c9457 100644
--- a/test/g3-roundtrip.ok
+++ b/test/g3-roundtrip.ok
@@ -1,3 +1,13 @@
-0
-0
-0
+0 0 0 : 0
+0 0 0 : 0
+0 0 0 0 : 0
+0 0 0 : 0
+0 0 0 : 0
+0 0 0 0 : 0
+0 0 0 0 : 0
+0 0 0 : 0
+0 0 0 : 0
+0 0 0 : 0
+1777627284 265
+2985378006 3135
+3651878552 3135
diff --git a/test/g3-roundtrip.test b/test/g3-roundtrip.test
index 47e20e1d..6e31c587 100755
--- a/test/g3-roundtrip.test
+++ b/test/g3-roundtrip.test
@@ -2,15 +2,60 @@
 # This script tests: g3topbm pbmtog3
 # Also requires: pnmcrop
 
+tmpdir=${tmpdir:-/tmp}
+complete256_pbm=${tmpdir}/complete256.pbm
+widew_pbm=${tmpdir}/widew.pbm
+wideb_pbm=${tmpdir}/wideb.pbm
 
 pbmtog3 -nofixedwidth testgrid.pbm | \
 g3topbm -width=14 | cmp -s - testgrid.pbm
-echo $?
+echo ${PIPESTATUS[@]} ":" $?
 
 pbmtog3 -nofixedwidth -reversebits testgrid.pbm | \
 g3topbm -width=14 -reversebits | cmp -s - testgrid.pbm
-echo $?
+echo ${PIPESTATUS[@]} ":" $?
 
 pbmtog3 testgrid.pbm | \
 g3topbm  | pnmcrop -white -right -bottom | \
- cmp -s - testgrid.pbm ; echo $?
+ cmp -s - testgrid.pbm ; echo ${PIPESTATUS[@]} ":" $?
+
+# works with gawk and mawk
+# produce all possible 8-bit patterns
+
+LC_ALL=C awk 'BEGIN { print "P4";         # header
+                      print "8 256";
+                      for (i=0;i<256;++i) # raster
+                           printf("%c",i) }' > ${complete256_pbm}
+
+pbmtog3 -nofixedwidth  ${complete256_pbm} |  g3topbm -width=8 | \
+ cmp -s - ${complete256_pbm} ; echo ${PIPESTATUS[@]} ":" $?
+
+pbmtog3 -reverse -nofixedwidth ${complete256_pbm} | \
+g3topbm -reversebits -width=8 | \
+ cmp -s - ${complete256_pbm} ; echo ${PIPESTATUS[@]} ":" $?
+
+pbmtog3 -align8 ${complete256_pbm} | \
+g3topbm -width=1728 | pnmcrop -white -right | \
+ cmp -s - ${complete256_pbm} ; echo ${PIPESTATUS[@]} ":" $?
+
+pbmtog3 -align16 ${complete256_pbm} | \
+g3topbm -width=1728 | pnmcrop -white -right | \
+ cmp -s - ${complete256_pbm} ; echo ${PIPESTATUS[@]} ":" $?
+
+pbmmake -w 5000 5 > ${widew_pbm}
+pbmtog3 -nofixedwidth ${widew_pbm} | g3topbm | \
+ cmp -s - ${widew_pbm} ; echo ${PIPESTATUS[@]} ":" $?
+
+pbmtog3 -nofixedwidth ${widew_pbm} | \
+g3topbm -width=5000 | \
+ cmp -s - ${widew_pbm} ; echo ${PIPESTATUS[@]} ":" $?
+
+pbmmake -b 5000 5 > ${wideb_pbm}
+pbmtog3 -nofixedwidth ${wideb_pbm} | g3topbm | \
+ cmp -s - ${wideb_pbm} ; echo ${PIPESTATUS[@]} ":" $?
+
+cat ${complete256_pbm} | cksum
+cat ${wideb_pbm} | cksum
+cat ${widew_pbm} | cksum
+
+rm ${complete256_pbm} ${wideb_pbm} ${widew_pbm}
diff --git a/test/gif-quant-roundtrip.ok b/test/gif-quant-roundtrip.ok
index 573541ac..b813e8db 100644
--- a/test/gif-quant-roundtrip.ok
+++ b/test/gif-quant-roundtrip.ok
@@ -1 +1 @@
-0
+0 0 0 : 0
diff --git a/test/gif-quant-roundtrip.test b/test/gif-quant-roundtrip.test
index 8b911740..910fa369 100755
--- a/test/gif-quant-roundtrip.test
+++ b/test/gif-quant-roundtrip.test
@@ -13,6 +13,6 @@ quant_ppm=${tmpdir}/quant.ppm
 pnmquant ${colors} testimg.ppm > ${quant_ppm} &&
 pamtogif ${quant_ppm} | giftopnm | \
    cmp -s - ${quant_ppm} > /dev/null
-echo $?
+echo ${PIPESTATUS[@]} ":" $?
 
 rm ${quant_ppm}
diff --git a/test/gif-roundtrip.ok b/test/gif-roundtrip.ok
index 552bf90c..607ebd5c 100644
--- a/test/gif-roundtrip.ok
+++ b/test/gif-roundtrip.ok
@@ -3,9 +3,20 @@
 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
+P1 4 1 0101 
+4030 : 0 0 0 0 : 0 , 4030 : 0 0 0 0 : 0
+4031 : 0 0 0 0 : 0 , 4031 : 0 0 0 0 : 0
+4097 : 0 0 0 0 : 0 , 4097 : 0 0 0 0 : 0
+238 : 0 0 0 0 : 0 , 238 : 0 0 0 0 : 0
+239 : 0 0 0 0 : 0 , 239 : 0 0 0 0 : 0
+240 : 0 0 0 0 : 0 , 240 : 0 0 0 0 : 0
+241 : 0 0 0 0 : 0 , 241 : 0 0 0 0 : 0
+255 : 0 0 0 0 : 0 , 255 : 0 0 0 0 : 0
+256 : 0 0 0 0 : 0 , 256 : 0 0 0 0 : 0
+257 : 0 0 0 0 : 0 , 257 : 0 0 0 0 : 0
diff --git a/test/gif-roundtrip.test b/test/gif-roundtrip.test
index a6d0ec4c..1e6c7760 100755
--- a/test/gif-roundtrip.test
+++ b/test/gif-roundtrip.test
@@ -1,7 +1,6 @@
 #! /bin/bash
 # This script tests: giftopnm pamtogif
-# Also requires: ppmtorgb3 rgb3toppm pbmmake pnminvert
-
+# Also requires: ppmtorgb3 rgb3toppm pbmmake pnminvert pamcut pamdepth
 
 tmpdir=${tmpdir:-/tmp}
 
@@ -33,12 +32,13 @@ 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
+# five times
 
 test_gif=${tmpdir}/testimg.gif
 
 pamtogif ${test_red} | giftopnm | cksum
 pamtogif -interlace ${test_red} | giftopnm | cksum
+pamtogif -noclear ${test_red} | giftopnm | cksum
 pamtogif -sort ${test_red} | tee ${test_gif} | \
   giftopnm | cksum
 echo "junk" >> ${test_gif} && \
@@ -48,6 +48,7 @@ rm  ${test_gif} ${test_red}
 
 
 # Test 3. Should produce 2425386270 41 five times.
+# testgrid.pbm is too small for -noclear to take effect 
 
 pamtogif testgrid.pbm | giftopnm | cksum
 pamtogif -nolzw testgrid.pbm | giftopnm | cksum
@@ -56,11 +57,107 @@ 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 | \
+  pamtogif -verbose | giftopnm -plain | \
   tr '\n' ' '
+echo ""
+
+
+# Test 5.
+# Test whether saturated code tables are handled correctly.
+# Test input images which most use up the string code table or
+# come close to doing so.
+
+# Should produce:
+# 4030 0 , 4030 0
+# 4031 0 , 4031 0
+# 4097 0 , 4097 0
+# 238 0 , 238 0
+# 239 0 , 239 0
+# 240 0 , 240 0
+# 241 0 , 241 0
+# 255 0 , 255 0
+# 256 0 , 256 0
+# 257 0 , 257 0
+
+test4097_pgm=${tmpdir}/testimg4097.pgm
+test_pgm=${tmpdir}/testimg.pgm
+
+# The following awk script produces a PGM file with no repeated sequences.
+# Here is a smaller example with the same property:
+# P2
+# 8 8
+# 7
+# 0 0 1 1 0 2 2 0
+# 3 3 0 4 4 0 5 5
+# 0 6 6 0 7 7 1 2
+# 1 3 1 4 1 5 1 6
+# 1 7 2 3 2 4 2 5
+# 2 6 2 7 3 4 3 5
+# 3 6 3 7 4 5 4 6
+# 4 7 5 6 5 7 6 7
+
+maxval=63
+awk -v maxval=${maxval} 'BEGIN \
+{ print "P2"
+         print "1 "(maxval+1) * (maxval+1) +1
+         print maxval
+         print 0 
+         for (j=i+1; j<=maxval; ++j)
+	    {print 0; print j; print j }
+         for (i=1; i<=maxval; ++i)
+             for (j=i+1; j<=maxval; ++j)
+                 {print i; print j }
+         print 0
+}' > ${test4097_pgm}
+
+for size in 4030 4031 4097
+  do
+  pamcut -height ${size} ${test4097_pgm} > ${test_pgm} &&
+  pamtogif -verbose ${test_pgm} | giftopnm | pamdepth ${maxval} | \
+    cmp - ${test_pgm}
+  # pamdepth ${maxval} is necessary because
+  # giftopnm output is maxval 255
+  echo -n ${size} ":" ${PIPESTATUS[@]} ":" $? ", "
+  pamtogif -nolzw ${test_pgm} | giftopnm | pamdepth ${maxval} | \
+    cmp - ${test_pgm}
+  echo ${size} ":" ${PIPESTATUS[@]} ":" $?
+  rm ${test_pgm}
+  done 
+
+rm ${test4097_pgm}
+test257_pgm=${tmpdir}/testimg257.pgm
+
+maxval=15
+awk -v maxval=${maxval} 'BEGIN \
+{ print "P2"
+         print "1 "(maxval+1) * (maxval+1) +1
+         print maxval
+         print 0 
+         for (j=i+1; j<=maxval; ++j)
+	    {print 0; print j; print j }
+         for (i=1; i<=maxval; ++i)
+             for (j=i+1; j<=maxval; ++j)
+                 {print i; print j }
+         print 0
+}' >  ${test257_pgm}
+
+for size in 238 239 240 241 255 256 257
+  do
+  pamcut -height=${size} ${test257_pgm} > ${test_pgm} &&
+  pamtogif -verbose ${test_pgm} | giftopnm | pamdepth ${maxval} | \
+    cmp - ${test_pgm}
+  echo -n ${size} ":" ${PIPESTATUS[@]} ":" $? ", "
+  pamtogif -nolzw -verbose ${test_pgm} | giftopnm | pamdepth ${maxval} | \
+    cmp - ${test_pgm}
+  echo ${size} ":" ${PIPESTATUS[@]} ":" $?
+  rm ${test_pgm}
+  done 
+
+rm ${test257_pgm}
diff --git a/test/jbig-roundtrip.test b/test/jbig-roundtrip.test
index 5e96a001..570d7336 100755
--- a/test/jbig-roundtrip.test
+++ b/test/jbig-roundtrip.test
@@ -2,7 +2,6 @@
 # This script tests: pnmtojbig jbigtopnm
 # Also requires: pamchannel pamtopnm
 
-
 # Test 1.  Should print 2425386270 41
 pnmtojbig testgrid.pbm | jbigtopnm | cksum
 
diff --git a/test/jpeg-roundtrip.ok b/test/jpeg-roundtrip.ok
new file mode 100644
index 00000000..5551f78e
--- /dev/null
+++ b/test/jpeg-roundtrip.ok
@@ -0,0 +1,3 @@
+match
+match
+match
diff --git a/test/jpeg-roundtrip.test b/test/jpeg-roundtrip.test
new file mode 100755
index 00000000..7cc0fc20
--- /dev/null
+++ b/test/jpeg-roundtrip.test
@@ -0,0 +1,19 @@
+#! /bin/bash
+# This script tests: pnmtojpeg jpegtopnm
+# Also requires: pnmpsnr
+
+# TODO: threshold has been determined without much thought.
+# Observed pnmpsnr output: 56.20 58.26 49.38
+# On another system:       54.73 49.41 44.52
+# A small margin has been added to the above numbers.
+
+# Should print "match" three times
+
+pnmtojpeg testimg.ppm | jpegtopnm | \
+  pnmpsnr -target1=54 -target2=49 -target3=44 - testimg.ppm
+
+pnmtojpeg testimg.ppm -opt | jpegtopnm | \
+  pnmpsnr -target1=54 -target2=49 -target3=44 - testimg.ppm
+
+pnmtojpeg testimg.ppm -progressive | jpegtopnm | \
+  pnmpsnr -target1=54 -target2=49 -target3=44 - testimg.ppm
diff --git a/test/jpeg2k-roundtrip.ok b/test/jpeg2k-roundtrip.ok
new file mode 100644
index 00000000..82eac5a8
--- /dev/null
+++ b/test/jpeg2k-roundtrip.ok
@@ -0,0 +1 @@
+1926073387 101484
diff --git a/test/jpeg2k-roundtrip.test b/test/jpeg2k-roundtrip.test
new file mode 100755
index 00000000..0995849d
--- /dev/null
+++ b/test/jpeg2k-roundtrip.test
@@ -0,0 +1,7 @@
+#! /bin/bash
+# This script tests: pamtojpeg2k jpeg2ktopam
+# Also requires:
+
+# Should produce 1926073387 101484
+
+pamtojpeg2k testimg.ppm | jpeg2ktopam | cksum
\ No newline at end of file
diff --git a/test/legacy-names.ok b/test/legacy-names.ok
index 9676639b..94ef875f 100644
--- a/test/legacy-names.ok
+++ b/test/legacy-names.ok
@@ -4,6 +4,8 @@ icontopbm: ok
 pamfixtrunc: ok
 pamrgbatopng: ok
 pbmtoicon: ok
+pbmtox10bm: ok
+pgmcrater: ok
 pgmedge: ok
 pgmnorm: ok
 pgmoil: ok
@@ -17,21 +19,21 @@ pnmenlarge: ok
 pnmfile: ok
 pnmflip: ok
 pnminterp: ok
+pnmnoraw: ok
 pnmscale: ok
 pnmsplit: ok
 pnmtofits: ok
+pnmtoplainpnm: ok
 pnmtopnm: ok
 pnmtotiff: ok
 ppmnorm: ok
+ppmquant: ok
+ppmquantall: ok
 ppmtogif: ok
 ppmtojpeg: ok
+ppmtomap: ok
 ppmtompeg: ok
 ppmtotga: ok
 ppmtouil: ok
-pgmcrater: ok
-pbmtox10bm: ok
-pnmnoraw: ok
-pnmtoplainpnm: ok
-ppmquant: ok
-ppmquantall: ok
 hpcdtoppm: ok
+pcdovtoppm: ok
diff --git a/test/legacy-names.test b/test/legacy-names.test
index df40e62d..5a65615f 100755
--- a/test/legacy-names.test
+++ b/test/legacy-names.test
@@ -10,6 +10,12 @@
 ##
 ## Important: This test checks obsoleted names.
 ## Programs here do not appear in other tests.
+##
+## Programs here are wrapper scripts provided for backward compatibility.
+## The newer replacement program may be the cause of any failure reported.
+##
+## If you run the "make test-install" on an older version, some executables
+## may be reported as missing because of missing features.
 
 
 # Skip this test if CHECK_TYPE = tree
@@ -55,6 +61,8 @@ ordinary_testprogs="\
   pamfixtrunc \
   pamrgbatopng \
   pbmtoicon \
+  pbmtox10bm \
+  pgmcrater \
   pgmedge \
   pgmnorm \
   pgmoil \
@@ -68,65 +76,59 @@ ordinary_testprogs="\
   pnmfile \
   pnmflip \
   pnminterp \
+  pnmnoraw \
   pnmscale \
   pnmsplit \
   pnmtofits \
+  pnmtoplainpnm \
   pnmtopnm \
   pnmtotiff \
   ppmnorm \
+  ppmquant \
+  ppmquantall \
   ppmtogif \
   ppmtojpeg \
+  ppmtomap \
   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 $?
-
+# Switch to control output from "program --version"
+# See comments in all-in-place.test
 
-# 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
+if [ ${CHECK_TYPE} = "install" ]
+  then grepOption[1]="" ;   # Output --version message
+  else grepOption[0]="-v" ; # Suppress output
+fi
 
-for i in ppmquant ppmquantall
+for i in $ordinary_testprogs
   do
-    $i 2 ${tmpdir}/test.pbm > /dev/null 2> /dev/null;
-    testExitStatus $i 0 $?
+    # Stub out programs that aren't built.
+    Available-Testprog "$i"
+      if [ $? = 1 ]; then
+      echo "$i: program was not built" 1>&2
+      echo "$i: ok"
+      continue
+      fi
+
+    $i --version < /dev/null 2>&1 | \
+    egrep -v -e "fiascotopnm" -e \
+    ": ((BSD|SYSV|MSDOS|AMIGA) defined|RGB_?ENV=)" | \
+    egrep ${grepOption[$grepSwitch]} -e \
+    ": (Using lib(net)?pbm|(Built (by|at)|Compiled ))" 1>&2;
+    # See showVersion() in lib/libpm.c for the above regular expressions.
+  
+    exitStatus=${PIPESTATUS[0]}
+    testExitStatus $i 0 ${exitStatus}
   done
 
 
-rm ${test_pbm}
 
-
-# Test hpcdtoppm.  Simply confirm its existence.
+# Test hpcdtoppm and pcdovtoppm.  Simply confirm their existence.
 
 type -p hpcdtoppm > /dev/null
 testExitStatus hpcdtoppm 0 $?
+
+type -p pcdovtoppm > /dev/null
+testExitStatus pcdovtoppm 0 $?
diff --git a/test/palm-roundtrip.ok b/test/palm-roundtrip.ok
new file mode 100644
index 00000000..b9ea3056
--- /dev/null
+++ b/test/palm-roundtrip.ok
@@ -0,0 +1,11 @@
+Test 1
+584219238 236
+584219238 236
+584219238 236
+584219238 236
+584219238 236
+Test 2
+0 0 0 : 0
+0 0 0 : 0
+0 0 0 : 0
+0 0 0 : 0
diff --git a/test/palm-roundtrip.test b/test/palm-roundtrip.test
new file mode 100755
index 00000000..b00454ca
--- /dev/null
+++ b/test/palm-roundtrip.test
@@ -0,0 +1,39 @@
+#! /bin/bash
+# This script tests: pnmtopalm palmtopnm
+# Also requires: pamdepth pnmquant
+
+tmpdir=${tmpdir:-/tmp}
+test4bit_pgm=${tmpdir}/test4bit.pgm
+test256color_ppm=${tmpdir}/test256color.ppm
+
+# Test 1. Should print 584219238 236  5 times
+
+echo "Test 1"
+
+pamdepth 15 testgrid.pbm | tee ${test4bit_pgm} | cksum
+
+for flags in "" \
+             "-scanline_compression" \
+             "-rle_compression" \
+             "-packbits_compression"
+  do pnmtopalm $flags ${test4bit_pgm} | palmtopnm | cksum; done
+
+rm ${test4bit_pgm}
+
+
+# Test 2. Should print 0 4 times
+
+echo "Test 2"
+
+pnmquant 256 testimg.ppm > ${test256color_ppm} || echo "pnmquant failed"
+
+for flags in "" \
+             "-scanline_compression" \
+             "-rle_compression" \
+             "-packbits_compression" 
+  do pnmtopalm -colormap $flags ${test256color_ppm} | palmtopnm | \
+     cmp -s - ${test256color_ppm} > /dev/null
+     echo ${PIPESTATUS[@]} ":" $?
+  done
+
+rm ${test256color_ppm}
diff --git a/test/palm-roundtrip2.ok b/test/palm-roundtrip2.ok
new file mode 100644
index 00000000..0060d7b6
--- /dev/null
+++ b/test/palm-roundtrip2.ok
@@ -0,0 +1,5 @@
+438301914 101484
+438301914 101484
+438301914 101484
+438301914 101484
+438301914 101484
diff --git a/test/palm-roundtrip2.test b/test/palm-roundtrip2.test
new file mode 100755
index 00000000..4c94df8c
--- /dev/null
+++ b/test/palm-roundtrip2.test
@@ -0,0 +1,31 @@
+#! /bin/bash
+# This script tests: pnmtopalm palmtopnm
+# Also requires: pnmremap
+
+tmpdir=${tmpdir:-/tmp}
+test256color_ppm=${tmpdir}/test256color.ppm
+
+if [ ${CHECK_TYPE} = install ]
+  then mapfile=$(netpbm-config --datadir)/palmcolor8.map
+  else mapfile=${PALMMAPDIR}/palmcolor8.map
+fi
+
+if [ ! -f ${mapfile} ]
+  then
+  echo "Cannot find palmcolor8.map.  Skipping." 1>&2
+  exit 80;
+fi
+
+
+# Test. Should print 438301914 101484
+# 5 times
+
+pnmremap -mapfile=${mapfile} testimg.ppm | tee ${test256color_ppm} | cksum
+
+for flags in "" \
+             "-scanline_compression" \
+             "-rle_compression" \
+             "-packbits_compression" 
+  do pnmtopalm -colormap $flags ${test256color_ppm} | palmtopnm | cksum; done
+
+rm ${test256color_ppm}
diff --git a/test/pamcut.ok b/test/pamcut.ok
index 61ef99bc..b08bc531 100644
--- a/test/pamcut.ok
+++ b/test/pamcut.ok
@@ -1,4 +1,19 @@
+Test 1.
 2958909756 124815
+Test 2.
 1550940962 10933
+Test 3.
 708474423 14
+708474423 14
+Test 4.
+659346598 80124
+659346598 80124
+659346598 80124
+659346598 80124
+Test 5.
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
+Test 6.
 3412257956 129
diff --git a/test/pamcut.test b/test/pamcut.test
index fd9185a3..9971b1a5 100755
--- a/test/pamcut.test
+++ b/test/pamcut.test
@@ -2,17 +2,46 @@
 # This script tests: pamcut pbmmake
 # Also requires:
 
-
 # Test 1.  Should print 2958909756 124815
+echo Test 1.
+
 pamcut -top 0 -left 0 -width 260 -height 160 \
   -pad testimg.ppm | cksum
 
 # Test 2.  Should print 1550940962 10933
+echo Test 2.
+
 pamcut -top 200 -left 120 -width 40 -height 40 \
   -pad testimg.ppm | cksum
 
-# Test 3.  Should print 708474423 14
+# Test 3.  Should print 708474423 14 twice
+echo Test 3.
+
 pamcut -top 5 -left 5 -bottom 5 -right 5 testimg.ppm | cksum
+pamcut -width 1 -height 1 -bottom 5 -right 5 testimg.ppm | cksum
+
+
+# Test 4.  Should print 659346598 80124 four times
+echo Test 4.
+
+pamcut -croptop 10 -cropleft 10 -cropbottom 10 -cropright 10 testimg.ppm | \
+  cksum
+pamcut -top 10 -left 10 -bottom 138 -right 216 testimg.ppm | cksum
+pamcut -top 10 -left 10 -bottom -11 -right -11 testimg.ppm | cksum
+pamcut -top 10 -left 10 -width 207 -height 129 testimg.ppm | cksum
+
+
+# Test 5. Should print 2425386270 41 four times
+echo Test 5.
+
+pamcut -croptop 0 -cropleft 0 -cropbottom 0 -cropright 0 testgrid.pbm | \
+  cksum
+pamcut -top 0 -left 0 -bottom 15 -right 13 testgrid.pbm | cksum
+pamcut -top 0 -left 0 -bottom -1 -right -1 testgrid.pbm | cksum
+pamcut -top 0 -left 0 -width 14 -height 16 testgrid.pbm | cksum
+
+
+# Test 6.  Should print 3412257956 129
+echo Test 6.
 
-# Test 4.  Should print 3412257956 129
 pbmmake -g 50 50 | pamcut 5 5 30 30 | cksum
diff --git a/test/pamenlarge-pamscale-point.ok b/test/pamenlarge-pamscale-point.ok
new file mode 100644
index 00000000..dea0a3a9
--- /dev/null
+++ b/test/pamenlarge-pamscale-point.ok
@@ -0,0 +1,9 @@
+1 0 0 : 0
+2 -filter=point 0 0 : 0
+3 -linear 0 0 : 0
+4 -nomix 0 0 : 0
+5 0 0 : 0
+7 0 0 : 0
+6 -nomix 0 0 : 0
+15 -nomix -linear 0 0 : 0
+24 -nomix 0 0 : 0
diff --git a/test/pamenlarge-pamscale-point.test b/test/pamenlarge-pamscale-point.test
new file mode 100755
index 00000000..08b34bb7
--- /dev/null
+++ b/test/pamenlarge-pamscale-point.test
@@ -0,0 +1,36 @@
+#! /bin/bash
+# This script tests: pamenlarge pamscale
+# Also requires:
+
+tmpdir=${tmpdir:-/tmp}
+enlarge_ppm=${tmpdir}/enlarge.ppm
+
+# When scale factor is an integer and the pamscale filter is point (default)
+# pamenlarge and pamscale should produce identical output
+
+for option in 1 "2 -filter=point" "3 -linear" "4 -nomix" 5
+  do
+  scale=${option% *}
+  pamenlarge $scale testimg.ppm > ${enlarge_ppm}
+  pamscale   $option testimg.ppm | cmp -s - ${enlarge_ppm}  
+  echo $option ${PIPESTATUS[@]} ":" $?
+  rm  ${enlarge_ppm}  
+  done
+
+
+pamenlarge -xscale=7 -yscale=7 testimg.ppm > ${enlarge_ppm}
+pamscale   -xscale=7 -yscale=7 testimg.ppm | cmp -s - ${enlarge_ppm}  
+echo 7 ${PIPESTATUS[@]} ":" $?
+rm  ${enlarge_ppm}
+
+
+enlarge_pbm=${tmpdir}/enlarge.pbm
+
+for option in "6 -nomix" "15 -nomix -linear" "24 -nomix"
+  do
+  scale=${option%% *}
+  pamenlarge $scale testgrid.pbm > ${enlarge_pbm}
+  pamscale   $option testgrid.pbm | cmp -s - ${enlarge_pbm}  
+  echo $option ${PIPESTATUS[@]} ":" $?
+  rm  ${enlarge_pbm}
+  done
diff --git a/test/pamenlarge-pbm.ok b/test/pamenlarge-pbm.ok
new file mode 100644
index 00000000..dcc2da97
--- /dev/null
+++ b/test/pamenlarge-pbm.ok
@@ -0,0 +1,86 @@
+test 1
+1777627284 265
+2806322261 522
+1951888658 778
+3502911227 1034
+3041987260 1290
+2012524746 1546
+4279863183 1802
+3672763467 2058
+3994368242 2314
+4119105059 2570
+1760702195 2826
+2751077869 3082
+262508159 3339
+743430575 3595
+94950162 3851
+2113456870 4107
+1143977004 4363
+2167141643 4619
+1816864555 4875
+2848735506 5131
+600474914 5387
+3831078134 5643
+1848221383 5899
+test 2
+2525154083 7435
+2525154083 7435
+2525154083 7435
+1983944984 14347
+1983944984 14347
+1983944984 14347
+2319780757 20747
+2319780757 20747
+2319780757 20747
+141252239 26635
+141252239 26635
+141252239 26635
+2074950535 32012
+2074950535 32012
+2074950535 32012
+2906360922 36876
+2906360922 36876
+2906360922 36876
+1320535490 41228
+1320535490 41228
+1320535490 41228
+1972046289 45068
+1972046289 45068
+1972046289 45068
+2203980644 48396
+2203980644 48396
+2203980644 48396
+2060433297 51212
+2060433297 51212
+2060433297 51212
+912377848 53516
+912377848 53516
+912377848 53516
+3346570289 55308
+3346570289 55308
+3346570289 55308
+1851051473 56588
+1851051473 56588
+1851051473 56588
+1940412039 57356
+1940412039 57356
+1940412039 57356
+2001859943 57612
+2001859943 57612
+test 3
+1  2764166846 191
+2  971905193 244
+3  6339889 299
+4  3973389476 342
+5  530215023 403
+6  2124428491 451
+7  2999665126 507
+8  4073302787 539
+9  3877636246 612
+10  1762263341 663
+11  3034611851 717
+12  3339857159 760
+13  119666911 821
+14  4197425209 869
+15  1169924963 924
+16  1845126791 954
diff --git a/test/pamenlarge-pbm.test b/test/pamenlarge-pbm.test
new file mode 100755
index 00000000..10c00ba0
--- /dev/null
+++ b/test/pamenlarge-pbm.test
@@ -0,0 +1,61 @@
+#! /bin/bash
+# This script tests: pamenlarge
+# Also requires: pbmmake pnmpad
+
+tmpdir=${tmpdir:-/tmp}
+complete256_pbm=${tmpdir}/complete256.pbm
+
+# works with gawk and mawk
+# produce all possible 8-bit patterns
+
+LC_ALL=C awk 'BEGIN { print "P4";         # header
+                      print "8 256";
+                      for (i=0;i<256;++i) # raster
+                           printf("%c",i) }' > ${complete256_pbm}
+
+# xscale factor should be tested to at least 21 (=13+8)
+
+# Test 1.
+echo "test 1"
+ 
+for xs in `seq 23`
+  do
+  pamenlarge -xscale=$xs ${complete256_pbm} | cksum
+  done
+
+# Test 2.
+echo "test 2"
+
+for xs1 in `seq 15`
+  do
+  xs2=$((30-$xs1))
+  pamenlarge -xscale=$xs1 ${complete256_pbm} | pamenlarge -xscale=$xs2 | cksum
+  if [ $xs1 != $xs2 ]; then
+  pamenlarge -xscale=$xs2 ${complete256_pbm} | pamenlarge -xscale=$xs1 | cksum
+  fi
+  pamenlarge -xscale=$(($xs1 * $xs2)) ${complete256_pbm} | cksum
+  done
+
+rm ${complete256_pbm}
+
+# Test 3.
+echo "test 3"
+
+test3_pbm=${tmpdir}/test3.pbm
+
+for width in `seq 16`
+  do
+  pbmmake -g ${width} 1 | pnmpad -top=1 -white | \
+      pnmpad -bottom=1 -black > ${test3_pbm}.${width}
+  done
+ 
+  for xscale in `seq 16`
+    do echo -n ${xscale} " "
+    for width in `seq 16`
+      do pamenlarge -xscale=${xscale} ${test3_pbm}.${width} ; done | cksum
+      #
+      # unlike most other tests we take the cksum of a composite PBM file
+      #
+    done
+
+rm ${test3_pbm}.[1-9]  ${test3_pbm}.1[1-6]
\ No newline at end of file
diff --git a/test/pamfile.ok b/test/pamfile.ok
index 57cc8cfd..c0d80c28 100644
--- a/test/pamfile.ok
+++ b/test/pamfile.ok
@@ -3,3 +3,10 @@ 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
+stdin:	3 images
+stdin:	Image 0:	PBM raw, 14 by 16
+stdin:	Image 1:	PBM raw, 14 by 16
+stdin:	Image 2:	PBM raw, 14 by 16
+227 149
+testimg.ppm: PPM RAW 227 149 3 255 RGB
+stdin: PBM RAW 14 16 1 1 BLACKANDWHITE
diff --git a/test/pamfile.test b/test/pamfile.test
index ac927172..545a2289 100755
--- a/test/pamfile.test
+++ b/test/pamfile.test
@@ -7,3 +7,10 @@ pamfile testimg.ppm
 pamfile testgrid.pbm
 pamchannel -infile=testimg.ppm -tupletype="GRAYSCALE" 0 | pamtopnm | pamfile
 pamchannel -tupletype="GRAYSCALE" -infile=testimg.ppm 0 | pamfile
+
+cat testgrid.pbm testgrid.pbm testgrid.pbm | pamfile -count
+cat testgrid.pbm testgrid.pbm testgrid.pbm | pamfile -allimages
+
+pamfile -size testimg.ppm
+pamfile -machine testimg.ppm
+cat testgrid.pbm testimg.ppm testgrid.pbm | pamfile -machine
diff --git a/test/pamfind.ok b/test/pamfind.ok
new file mode 100644
index 00000000..dc0f95ed
--- /dev/null
+++ b/test/pamfind.ok
@@ -0,0 +1,83 @@
+Test 1
+Locations containing tuple (43/43/43)/255:
+(6, 0)
+(6, 1)
+(7, 0)
+(7, 1)
+(19, 184)
+Locations containing tuple (210/57/41)/255:
+(44, 109)
+(96, 74)
+(96, 75)
+(100, 75)
+(100, 76)
+(100, 77)
+(100, 78)
+(101, 73)
+(101, 74)
+(101, 75)
+(101, 76)
+(101, 77)
+(101, 78)
+(102, 72)
+Test 2
+Locations containing tuple (1)/1:
+(0, 1)
+(0, 3)
+(0, 5)
+(0, 7)
+(0, 9)
+(0, 11)
+(0, 13)
+(2, 1)
+(2, 3)
+(2, 5)
+(2, 7)
+(2, 9)
+(2, 11)
+(2, 13)
+(4, 1)
+(4, 3)
+(4, 5)
+(4, 7)
+(4, 9)
+(4, 11)
+(4, 13)
+(6, 1)
+(6, 3)
+(6, 5)
+(6, 7)
+(6, 9)
+(6, 11)
+(6, 13)
+(8, 1)
+(8, 3)
+(8, 5)
+(8, 7)
+(8, 9)
+(8, 11)
+(8, 13)
+(10, 1)
+(10, 3)
+(10, 5)
+(10, 7)
+(10, 9)
+(10, 11)
+(10, 13)
+(12, 1)
+(12, 3)
+(12, 5)
+(12, 7)
+(12, 9)
+(12, 11)
+(12, 13)
+(14, 1)
+(14, 3)
+(14, 5)
+(14, 7)
+(14, 9)
+(14, 11)
+(14, 13)
+Test 3
+okay
+okay
diff --git a/test/pamfind.test b/test/pamfind.test
new file mode 100755
index 00000000..39cb1437
--- /dev/null
+++ b/test/pamfind.test
@@ -0,0 +1,32 @@
+#! /bin/bash
+# This script tests: pamfind
+# Also requires:
+
+tmpdir=${tmpdir:-/tmp}
+sorted0_res=${tmpdir}/pamfind_sorted0.res
+sorted1_res=${tmpdir}/pamfind_sorted1.res
+
+# Test 1
+echo Test 1
+pamfind -color=grey17     testimg.ppm 
+pamfind -target=210,57,41 testimg.ppm
+
+# Test 2
+echo Test 2
+pamfind -target=1 testgrid.pbm
+
+# Test 3
+# The two outputs should be disjoint
+echo Test 3
+pamfind -target=0 testgrid.pbm | sort > ${sorted0_res}
+pamfind -target=1 testgrid.pbm | sort > ${sorted1_res}
+comm -3 ${sorted0_res}  ${sorted1_res}  |
+  awk 'END {if (NR==226) print  "okay";
+            else printf("failure (line count=%d)\n", NR)}'
+comm -12 ${sorted0_res}  ${sorted1_res} | 
+  awk '{print}; END { if(NR == 0) print  "okay";
+            else printf("failure (line count=%d)\n", NR)}'
+
+rm ${sorted0_res} ${sorted1_res}
+
+
diff --git a/test/ppmgauss.ok b/test/pamgauss.ok
index cf708d64..153d4f6e 100644
--- a/test/ppmgauss.ok
+++ b/test/pamgauss.ok
@@ -7,15 +7,15 @@
 171732531 55
 448293386 55
 3030522957 55
-757204806 62
-757204806 62
-2890088558 62
-4276668903 62
-1462902064 62
-4161772187 62
-2741154810 62
-417660035 62
-1026188683 62
+1912396937 62
+1912396937 62
+1912396937 62
+1912396937 62
+924437833 62
+2403224870 62
+1828243243 62
+3189486374 62
+3077695768 62
 1236982144 71
 1236982144 71
 2668915323 71
@@ -25,15 +25,15 @@
 1741784255 71
 2975827721 71
 185899480 71
-1890880170 82
-1890880170 82
-2007282168 82
-2290578407 82
-3295586348 82
-3202728672 82
-3794283497 82
-1136760947 82
-2659761303 82
+685596179 82
+685596179 82
+685596179 82
+685596179 82
+3959781759 82
+3753883586 82
+3706474367 82
+2091709635 82
+651233877 82
 3268058875 95
 3268058875 95
 4112134785 95
@@ -43,15 +43,15 @@
 3042873446 95
 299128630 95
 1852505233 95
-3614879265 110
-3614879265 110
-3727088180 110
-3861671574 110
-952180714 110
-1393452065 110
-1551903756 110
-2198775655 110
-3231547603 110
+2257309933 110
+2257309933 110
+2257309933 110
+2257309933 110
+3861656488 110
+130820705 110
+1706835660 110
+4285051006 110
+285426992 110
 3184597171 127
 3184597171 127
 406519147 127
@@ -61,15 +61,15 @@
 1852779549 127
 1734420920 127
 2400421918 127
-3252995941 148
-3252995941 148
-2022176119 148
-473753498 148
-3178129210 148
-2278608777 148
-3169785704 148
-85136480 148
-2396646133 148
+3247957822 148
+3247957822 148
+3247957822 148
+3247957822 148
+3407897457 148
+1176192530 148
+3551192353 148
+557209968 148
+2437783856 148
 1871459418 169
 1871459418 169
 639002491 169
diff --git a/test/ppmgauss.test b/test/pamgauss.test
index 213810c7..b48517c4 100755
--- a/test/ppmgauss.test
+++ b/test/pamgauss.test
@@ -7,6 +7,6 @@ for i in `seq 3 11`
 do
 for s in `seq 1 9`
 do
-pamgauss $i $i -sigma=.$s | cksum
+pamgauss $i $i -oversample=1 -sigma=.$s | cksum
 done
 done
diff --git a/test/pammixmulti-identity.ok b/test/pammixmulti-identity.ok
new file mode 100644
index 00000000..9e712f7f
--- /dev/null
+++ b/test/pammixmulti-identity.ok
@@ -0,0 +1,22 @@
+Test 1.
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+Test 2.
+1926073387 101484
+1926073387 101484
+1926073387 101484
+Test 3.
+1926073387 101484
+1926073387 101484
+1926073387 101484
+Test 4.
+127
+127
+127
+127
+127
+127
diff --git a/test/pammixmulti-identity.test b/test/pammixmulti-identity.test
new file mode 100755
index 00000000..d205c359
--- /dev/null
+++ b/test/pammixmulti-identity.test
@@ -0,0 +1,77 @@
+#! /bin/bash
+# This script tests: pammixmulti
+# Also requires: pgmnoise pnminvert pamsumm
+
+tmpdir=${tmpdir:-/tmp}
+
+# Test 1.  Should print 1926073387 101484 six times
+echo Test 1.
+
+pammixmulti testimg.ppm | cksum
+pammixmulti testimg.ppm testimg.ppm | cksum
+pammixmulti testimg.ppm testimg.ppm testimg.ppm | cksum
+pammixmulti -blend=average testimg.ppm testimg.ppm | cksum
+pammixmulti -blend=average testimg.ppm testimg.ppm testimg.ppm | cksum
+pammixmulti -blend=random -randomseed=1 \
+    testimg.ppm testimg.ppm testimg.ppm | cksum
+
+
+# Test 2.  Should print 1926073387 101484 three times
+echo Test 2.
+
+mask_pgm=${tmpdir}/mask.pgm
+pgmnoise -maxval=2 -randomseed=1 227 149 > ${mask_pgm}
+
+for sd in 0.5 1.2 3.0 
+  do
+  pammixmulti -blend=mask -maskfile=${mask_pgm} -stdev=${sd} \
+      testimg.ppm testimg.ppm testimg.ppm | cksum
+  done
+rm ${mask_pgm}
+
+# Test 3.  Should print
+echo Test 3.
+
+noise_pgm=${tmpdir}/noise.pgm
+pgmnoise --randomseed=1 227 149 > ${noise_pgm}
+
+pammixmulti -blend=mask -maskfile=${noise_pgm} \
+    testimg.ppm testimg.ppm | cksum
+pammixmulti -blend=mask -maskfile=${noise_pgm} \
+    testimg.ppm testimg.ppm testimg.ppm | cksum
+pammixmulti -blend=mask -maskfile=${noise_pgm} \
+    testimg.ppm testimg.ppm testimg.ppm testimg.ppm | cksum
+rm ${noise_pgm}
+
+
+# Test 4.
+
+# Mix image with its own inverse.
+# Output should be a monotone gray sheet.
+#  Should print 127 six times
+echo Test 4.
+
+invert_ppm=${tmpdir}/invert.ppm
+monotone_ppm=${tmpdir}/monotone.ppm
+
+pnminvert testimg.ppm > ${invert_ppm}
+
+pammixmulti -blend=average testimg.ppm ${invert_ppm} | tee ${monotone_ppm} | \
+  pamsumm -brief -max &&
+  pamsumm -brief -min ${monotone_ppm}
+rm ${monotone_ppm}
+
+pammixmulti -blend=average \
+    testimg.ppm ${invert_ppm} testimg.ppm ${invert_ppm} | \
+  tee ${monotone_ppm} | \
+  pamsumm -brief -max &&
+  pamsumm -brief -min ${monotone_ppm}
+rm ${monotone_ppm}
+
+pammixmulti -blend=average \
+    testimg.ppm testimg.ppm ${invert_ppm} ${invert_ppm} | \
+  tee ${monotone_ppm} | \
+  pamsumm -brief -max &&
+  pamsumm -brief -min ${monotone_ppm}
+
+rm ${monotone_ppm} ${invert_ppm}
diff --git a/test/pamscale-filters1.ok b/test/pamscale-filters1.ok
new file mode 100644
index 00000000..083505ee
--- /dev/null
+++ b/test/pamscale-filters1.ok
@@ -0,0 +1,15 @@
+4 box:
+match
+match
+9 triangle:
+match
+match
+4 quadratic:
+match
+match
+10 cubic:
+match
+match
+4 catrom:
+match
+match
diff --git a/test/pamscale-filters1.test b/test/pamscale-filters1.test
new file mode 100755
index 00000000..63e0d012
--- /dev/null
+++ b/test/pamscale-filters1.test
@@ -0,0 +1,64 @@
+#! /bin/bash
+# This script tests: pamscale pamenlarge
+# Also requires: pamvalidate pnmpsnr
+
+tmpdir=${tmpdir:-/tmp}
+enlarge_ppm=${tmpdir}/enlarge.ppm
+
+width_height=`pamfile -size testimg.ppm | \
+                awk '{print "-width="$1, "-height="$2}'`
+
+# The target values here were determined by running the test on a
+# 32-bit GNU/Linux system and subtracting 2.5 (dB) from observed
+# values.
+
+pamenlarge  4 testimg.ppm | pamvalidate > ${enlarge_ppm}
+echo  4 box:  
+pamscale  4 -filter=box testimg.ppm | \
+  pnmpsnr -target1=35.67 -target2=49.25 -target3=43.28 - ${enlarge_ppm} || \
+  echo failure ${PIPESTATUS[@]} ":" $?
+ 
+pamscale ${width_height} -filter=point ${enlarge_ppm} | \
+  pnmpsnr -target1=1000 -target2=1000 -target3=1000 testimg.ppm - || \
+  echo failure ${PIPESTATUS[@]} ":" $?
+rm ${enlarge_ppm}
+
+pamenlarge  9 testimg.ppm | pamvalidate > ${enlarge_ppm}
+echo  9 triangle:  
+pamscale  9 -filter=triangle testimg.ppm | \
+  pnmpsnr -target1=35.27 -target2=49.06 -target3=43.11 - ${enlarge_ppm} || \
+  echo failure ${PIPESTATUS[@]} ":" $?
+pamscale ${width_height} -filter=triangle ${enlarge_ppm} | \
+  pnmpsnr -target1=41.52 -target2=56.96 -target3=52.68 testimg.ppm - || \
+  echo failure ${PIPESTATUS[@]} ":" $?
+rm ${enlarge_ppm}
+
+pamenlarge  4 testimg.ppm | pamvalidate > ${enlarge_ppm}
+echo  4 quadratic:  
+pamscale  4 -filter=quadratic testimg.ppm | \
+  pnmpsnr -target1=35.39 -target2=49.36 -target3=43.46 - ${enlarge_ppm} || \
+  echo failure ${PIPESTATUS[@]} ":" $?
+pamscale ${width_height} -filter=quadratic ${enlarge_ppm} | \
+  pnmpsnr -target1=39.34 -target2=55.24 -target3=50.65 testimg.ppm - || \
+  echo failure ${PIPESTATUS[@]} ":" $?
+rm ${enlarge_ppm}
+
+pamenlarge 10 testimg.ppm | pamvalidate > ${enlarge_ppm}
+echo 10 cubic:  
+pamscale 10 -filter=cubic testimg.ppm | \
+  pnmpsnr -target1=34.39 -target2=48.45 -target3=42.52 - ${enlarge_ppm} || \
+  echo failure ${PIPESTATUS[@]} ":" $?
+pamscale ${width_height} -filter=cubic ${enlarge_ppm} | \
+  pnmpsnr -target1=37.7 -target2=53.84 -target3=48.96 testimg.ppm - || \
+  echo failure ${PIPESTATUS[@]} ":" $?
+rm ${enlarge_ppm}
+
+pamenlarge  4 testimg.ppm | pamvalidate > ${enlarge_ppm}
+echo  4 catrom:  
+pamscale  4 -filter=catrom testimg.ppm | \
+  pnmpsnr -target1=36.05 -target2=49.51 -target3=43.49 - ${enlarge_ppm} || \
+  echo failure ${PIPESTATUS[@]} ":" $?
+pamscale ${width_height} -filter=catrom ${enlarge_ppm} | \
+  pnmpsnr -target1=46.5 -target2=60.68 -target3=57.8 testimg.ppm - || \
+  echo failure ${PIPESTATUS[@]} ":" $?
+rm ${enlarge_ppm}
diff --git a/test/pamscale-filters2.ok b/test/pamscale-filters2.ok
new file mode 100644
index 00000000..b8d51d37
--- /dev/null
+++ b/test/pamscale-filters2.ok
@@ -0,0 +1,24 @@
+-xscale=4 -yscale=3 mitchell:
+match
+match
+-xscale=3 -yscale=4 gauss:
+match
+match
+-xscale=2 -yscale=2 sinc:
+match
+match
+-xscale=2 -yscale=4 bessel:
+match
+match
+-xscale=3 -yscale=3 hanning:
+match
+match
+-xscale=5 -yscale=5 hamming:
+match
+match
+5.85 blackman:
+match
+match
+5.10 kaiser:
+match
+match
diff --git a/test/pamscale-filters2.test b/test/pamscale-filters2.test
new file mode 100755
index 00000000..f7370020
--- /dev/null
+++ b/test/pamscale-filters2.test
@@ -0,0 +1,97 @@
+#! /bin/bash
+# This script tests: pamscale pamstretch pamstretch-gen
+# Also requires: pamvalidate pnmpsnr
+
+tmpdir=${tmpdir:-/tmp}
+stretch_ppm=${tmpdir}/stretch.ppm
+
+width_height=`pamfile -size testimg.ppm | \
+                awk '{print "-width="$1, "-height="$2}'`
+
+pamstretch -xscale=4 -yscale=3 testimg.ppm | pamvalidate > ${stretch_ppm}
+echo -xscale=4 -yscale=3 mitchell:  
+pamscale -xscale=4 -yscale=3 -filter=mitchell testimg.ppm | \
+  pnmpsnr -target1=34.15 -target2=46.95 -target3=41.02 - ${stretch_ppm} || \
+  echo failure ${PIPESTATUS[@]} ":" $?
+
+pamscale ${width_height} -filter=mitchell ${stretch_ppm} | \
+  pnmpsnr -target1=33.36 -target2=46.74 -target3=40.79 testimg.ppm - || \
+  echo failure ${PIPESTATUS[@]} ":" $?
+rm ${stretch_ppm}
+
+pamstretch -xscale=3 -yscale=4 testimg.ppm | pamvalidate > ${stretch_ppm}
+echo -xscale=3 -yscale=4 gauss:  
+pamscale -xscale=3 -yscale=4 -filter=gauss testimg.ppm | \
+  pnmpsnr -target1=34.4 -target2=46.98 -target3=41.07 - ${stretch_ppm} || \
+  echo failure ${PIPESTATUS[@]} ":" $?
+
+pamscale ${width_height} -filter=gauss ${stretch_ppm} | \
+  pnmpsnr -target1=33.1 -target2=46.61 -target3=40.64 testimg.ppm - || \
+  echo failure ${PIPESTATUS[@]} ":" $?
+rm ${stretch_ppm}
+
+pamstretch -xscale=2 -yscale=2 testimg.ppm | pamvalidate > ${stretch_ppm}
+echo -xscale=2 -yscale=2 sinc:  
+pamscale -xscale=2 -yscale=2 -filter=sinc testimg.ppm | \
+  pnmpsnr -target1=36.27 -target2=49.39 -target3=43.62 - ${stretch_ppm} || \
+  echo failure ${PIPESTATUS[@]} ":" $?
+
+pamscale ${width_height} -filter=sinc ${stretch_ppm} | \
+  pnmpsnr -target1=36.23 -target2=49.47 -target3=43.69 testimg.ppm - || \
+  echo failure ${PIPESTATUS[@]} ":" $?
+rm ${stretch_ppm}
+
+pamstretch -xscale=2 -yscale=4 testimg.ppm | pamvalidate > ${stretch_ppm}
+echo -xscale=2 -yscale=4 bessel:  
+pamscale -xscale=2 -yscale=4 -filter=bessel testimg.ppm | \
+  pnmpsnr -target1=35.09 -target2=47.77 -target3=41.88 - ${stretch_ppm} || \
+  echo failure ${PIPESTATUS[@]} ":" $?
+
+pamscale ${width_height} -filter=bessel ${stretch_ppm} | \
+  pnmpsnr -target1=33.99 -target2=47.47 -target3=41.55 testimg.ppm - || \
+  echo failure ${PIPESTATUS[@]} ":" $?
+rm ${stretch_ppm}
+
+pamstretch -xscale=3 -yscale=3 testimg.ppm | pamvalidate > ${stretch_ppm}
+echo -xscale=3 -yscale=3 hanning:  
+pamscale -xscale=3 -yscale=3 -filter=hanning testimg.ppm | \
+  pnmpsnr -target1=34.73 -target2=47.42 -target3=41.54 - ${stretch_ppm} || \
+  echo failure ${PIPESTATUS[@]} ":" $?
+
+pamscale ${width_height} -filter=hanning ${stretch_ppm} | \
+  pnmpsnr -target1=33.86 -target2=47.24 -target3=41.29 testimg.ppm - || \
+  echo failure ${PIPESTATUS[@]} ":" $?
+rm ${stretch_ppm}
+
+pamstretch -xscale=5 -yscale=5 testimg.ppm | pamvalidate > ${stretch_ppm}
+echo -xscale=5 -yscale=5 hamming:  
+pamscale -xscale=5 -yscale=5 -filter=hamming testimg.ppm | \
+  pnmpsnr -target1=33.4 -target2=46.02 -target3=40.07 - ${stretch_ppm} || \
+  echo failure ${PIPESTATUS[@]} ":" $?
+
+pamscale ${width_height} -filter=hamming ${stretch_ppm} | \
+  pnmpsnr -target1=32.49 -target2=45.81 -target3=39.8 testimg.ppm - || \
+  echo failure ${PIPESTATUS[@]} ":" $?
+rm ${stretch_ppm}
+
+pamstretch-gen 5.85 testimg.ppm | pamvalidate > ${stretch_ppm}
+echo 5.85 blackman:  
+pamscale 5.85 -filter=blackman testimg.ppm | \
+  pnmpsnr -target1=34.29 -target2=48.78 -target3=42.75 - ${stretch_ppm} || \
+  echo failure ${PIPESTATUS[@]} ":" $?
+
+pamscale ${width_height} -filter=blackman ${stretch_ppm} | \
+  pnmpsnr -target1=33.69 -target2=48.83 -target3=42.72 testimg.ppm - || \
+  echo failure ${PIPESTATUS[@]} ":" $?
+rm ${stretch_ppm}
+
+pamstretch-gen 5.10 testimg.ppm | pamvalidate > ${stretch_ppm}
+echo 5.10 kaiser:  
+pamscale 5.10 -filter=kaiser testimg.ppm | \
+  pnmpsnr -target1=34.58 -target2=49.03 -target3=43.01 - ${stretch_ppm} || \
+  echo failure ${PIPESTATUS[@]} ":" $?
+
+pamscale ${width_height} -filter=blackman ${stretch_ppm} | \
+  pnmpsnr -target1=33.69 -target2=48.83 -target3=42.72 testimg.ppm - || \
+  echo failure ${PIPESTATUS[@]} ":" $?
+rm ${stretch_ppm}
diff --git a/test/pamscale-filters3.ok b/test/pamscale-filters3.ok
new file mode 100644
index 00000000..94d4ae96
--- /dev/null
+++ b/test/pamscale-filters3.ok
@@ -0,0 +1,7 @@
+failure : 0
+3.96 hermite:
+match
+match
+2.75 lanczos:
+match
+match
diff --git a/test/pamscale-filters3.test b/test/pamscale-filters3.test
new file mode 100755
index 00000000..83198e2b
--- /dev/null
+++ b/test/pamscale-filters3.test
@@ -0,0 +1,32 @@
+#! /bin/bash
+# This script tests: pamscale pamstretch pamstretch-gen 
+ # Also requires: pamvalidate pnmpsnr || \
+  echo failure ${PIPESTATUS[@]} ":" $?
+
+tmpdir=${tmpdir:-/tmp}
+stretch_ppm=${tmpdir}/stretch.ppm
+
+width_height=`pamfile -size testimg.ppm | \
+                awk '{print "-width="$1, "-height="$2}'`
+
+pamstretch-gen 3.96 testimg.ppm | pamvalidate > ${stretch_ppm}
+echo 3.96 hermite:  
+pamscale 3.96 -filter=hermite testimg.ppm | \
+  pnmpsnr -target1=34.13 -target2=48.95 -target3=42.73 - ${stretch_ppm} || \
+  echo failure ${PIPESTATUS[@]} ":" $?
+
+pamscale ${width_height} -filter=hermite ${stretch_ppm} | \
+  pnmpsnr -target1=33.12 -target2=48.59 -target3=42.3 testimg.ppm - || \
+  echo failure ${PIPESTATUS[@]} ":" $?
+rm ${stretch_ppm}
+
+pamstretch-gen 2.75 testimg.ppm | pamvalidate > ${stretch_ppm}
+echo 2.75 lanczos:  
+pamscale 2.75 -filter=lanczos testimg.ppm | \
+  pnmpsnr -target1=32.93 -target2=48.28 -target3=41.89 - ${stretch_ppm} || \
+  echo failure ${PIPESTATUS[@]} ":" $?
+
+pamscale ${width_height} -filter=lanczos ${stretch_ppm} | \
+  pnmpsnr -target1=32.81 -target2=48.43 -target3=41.94 testimg.ppm - || \
+  echo failure ${PIPESTATUS[@]} ":" $?
+rm ${stretch_ppm}
diff --git a/test/pamscale-reportonly.ok b/test/pamscale-reportonly.ok
new file mode 100644
index 00000000..00152411
--- /dev/null
+++ b/test/pamscale-reportonly.ok
@@ -0,0 +1,18 @@
+227 149 3.000000 3.000000 681 447
+227 149 5.000000 5.000000 1135 745
+227 149 2.682819 2.684564 609 400
+227 149 2.819383 2.818792 640 420
+227 149 0.440529 0.442953 100 66
+227 149 0.167401 0.167785 38 25
+227 149 0.101322 0.100671 23 15
+227 149 2.819383 2.684564 640 400
+227 149 2.819383 2.684564 640 400
+227 149 2.819383 2.000000 640 298
+227 149 1.400881 2.684564 318 400
+227 149 1.000000 1.000000 227 149
+expected error
+expected error
+expected error
+expected error
+expected error
+expected error
diff --git a/test/pamscale-reportonly.test b/test/pamscale-reportonly.test
new file mode 100755
index 00000000..7205be2f
--- /dev/null
+++ b/test/pamscale-reportonly.test
@@ -0,0 +1,35 @@
+#! /bin/bash
+# This script tests: pamscale
+# Also requires:
+
+pamscale -reportonly 3 testimg.ppm
+pamscale -reportonly 5 testimg.ppm
+pamscale -reportonly -xysize 640 400 testimg.ppm
+pamscale -reportonly -xyfill 640 400 testimg.ppm
+pamscale -reportonly -xyfit  100 100 testimg.ppm
+pamscale -reportonly -reduce 6 testimg.ppm
+pamscale -reportonly -reduce 10 testimg.ppm
+pamscale -reportonly -xsize=640 -ysize=400 testimg.ppm
+pamscale -reportonly -width=640 -height=400 testimg.ppm
+pamscale -reportonly -width=640 -yscale=2 testimg.ppm
+pamscale -reportonly -xscale=1.4 -height=400 testimg.ppm
+pamscale -reportonly -pixels=45000 testimg.ppm
+
+# expected error cases
+echo 1>&2
+echo "Invalid command-line argument combinations." 1>&2
+echo "Error messages should appear below the line." 1>&2
+echo "-----------------------------------------------------------" 1>&2
+pamscale -reportonly -xsize=640 -ysize=400 -xscale=2 testimg.ppm || \
+  echo expected error
+pamscale -reportonly -xsize=640 -xscale=2 -yscale=3 testimg.ppm || \
+  echo expected error
+pamscale -reportonly -xsize=640 -ysize=400 -pixels=200000 testimg.ppm || \
+  echo expected error
+pamscale -reportonly -xsize=640 -ysize=400 -xysize 640 400 testimg.ppm || \
+  echo expected error
+pamscale -reportonly -xsize=640 -ysize=400 -xyfit  640 400 testimg.ppm || \
+  echo expected error
+pamscale -reportonly -xsize=640 -ysize=400 -xyfill 640 400 testimg.ppm || \
+  echo expected error
+
diff --git a/test/pamstretch.ok b/test/pamstretch.ok
new file mode 100644
index 00000000..3253cfd6
--- /dev/null
+++ b/test/pamstretch.ok
@@ -0,0 +1,20 @@
+test 1
+211995824 12277766
+test 2
+1361899 202953
+test 3
+2735552884 302379
+test 4
+3681010585 802767
+test 5
+1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+test 6
+3638965616 913236
+598302847 913236
+220708621 903111
+3948746482 903111
+3948746482 903111
+220708621 903111
diff --git a/test/pamstretch.test b/test/pamstretch.test
new file mode 100755
index 00000000..55dd4e9c
--- /dev/null
+++ b/test/pamstretch.test
@@ -0,0 +1,48 @@
+#! /bin/bash
+# This script tests: pamstretch
+# Also requires: pamcut pamfile
+
+# Test 1.  Should print 211995824 12277766
+echo test 1
+pamstretch 11 testimg.ppm | cksum
+
+# Test 2.  Should print 1361899 202953
+echo test 2
+
+pamstretch -xscale=2 -blackedge testimg.ppm | cksum
+
+# Test 3.  Should print 3427416462 301047
+echo test 3
+dropedge1_ppm=${tmpdir}/drop1.ppm
+pamstretch -yscale=3 -dropedge testimg.ppm | tee ${dropedge1_ppm} | cksum
+
+# Test 4. Should print 3681010585 802767
+echo test 4
+pamstretch -xscale=2 -yscale=4 -dropedge testimg.ppm | cksum
+
+# Test 5.  Should print 1926073387 101484 four times
+echo test 5
+pamstretch 1 testimg.ppm | cksum
+pamstretch -xscale=1 -yscale=1 testimg.ppm | cksum
+pamstretch 1 -dropedge testimg.ppm | cksum
+pamstretch 1 -blackedge testimg.ppm | cksum
+
+# Test 6.
+# Should print 3638965616 913236 , 598302847 913236 
+# followed by 3948746482 903111 four times and finally
+# -width=678 -height=444"
+
+echo test 6
+stretch_ppm=${tmpdir}/stretch.ppm
+dropedge_ppm=${tmpdir}/drop.ppm
+blackedge_ppm=${tmpdir}/black.ppm
+pamstretch 3 testimg.ppm | tee ${stretch_ppm} | cksum
+pamstretch 3 -blackedge testimg.ppm | tee ${blackedge_ppm} | cksum
+pamstretch -xscale=3 -dropedge ${dropedge1_ppm} | tee ${dropedge_ppm} | cksum
+width_height=`pamfile -size ${dropedge_ppm} | \
+              awk '{print "-width="$1, "-height="$2}'`
+pamcut -left=0 -top=0 ${width_height} ${stretch_ppm} | cksum
+pamcut -left=0 -top=0 ${width_height} ${blackedge_ppm} | cksum
+pamcut -left=0 -top=0 ${width_height} ${dropedge_ppm} | cksum
+
+rm ${stretch_ppm} ${dropedge_ppm} ${dropedge1_ppm} ${blackedge_ppm}
\ No newline at end of file
diff --git a/test/pamtable.ok b/test/pamtable.ok
new file mode 100644
index 00000000..c8db6002
--- /dev/null
+++ b/test/pamtable.ok
@@ -0,0 +1,66 @@
+0 1 0 1 0 1 0 1 0 1 0 1 0 1
+0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 1 0 1 0 1 0 1 0 1 0 1 0 1
+0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 1 0 1 0 1 0 1 0 1 0 1 0 1
+0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 1 0 1 0 1 0 1 0 1 0 1 0 1
+0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 1 0 1 0 1 0 1 0 1 0 1 0 1
+0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 1 0 1 0 1 0 1 0 1 0 1 0 1
+0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 1 0 1 0 1 0 1 0 1 0 1 0 1
+0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 1 0 1 0 1 0 1 0 1 0 1 0 1
+0 0 0 0 0 0 0 0 0 0 0 0 0 0
+
+0 0 0|0 0 1|0 0 2|0 1 0|0 1 1|0 1 2|0 2 0|0 2 1|0 2 2|1 0 0|1 0 1|1 0 2|1 1 0|1 1 1|1 1 2|1 2 0|1 2 1|1 2 2|2 0 0|2 0 1|2 0 2|2 1 0|2 1 1|2 1 2|2 2 0|2 2 1|2 2 2
+
+0 0 0 0|0 0 0 1|0 0 1 0|0 0 1 1|0 1 0 0|0 1 0 1|0 1 1 0|0 1 1 1|1 0 0 0|1 0 0 1|1 0 1 0|1 0 1 1|1 1 0 0|1 1 0 1|1 1 1 0|1 1 1 1
+
+9 0
+0 9
+
+0 0 0|4 4 4|9 9 9
+0 0 0|4 4 4|9 9 9
+
+9 0 0|0 0 9|0 0 9|0 9 0
+9 0 0|0 0 9|0 0 9|0 9 0
+
+10  0
+ 0 10
+
+ 0  0  0| 5  5  5|10 10 10
+ 0  0  0| 5  5  5|10 10 10
+
+10  0  0| 0  0 10| 0  0 10| 0 10  0
+10  0  0| 0  0 10| 0  0 10| 0 10  0
+
+9999    0
+   0 9999
+
+   0    0    0|4980 4980 4980|9999 9999 9999
+   0    0    0|4980 4980 4980|9999 9999 9999
+
+9999    0    0|   0    0 9999|   0    0 9999|   0 9999    0
+9999    0    0|   0    0 9999|   0    0 9999|   0 9999    0
+
+10000     0
+    0 10000
+
+    0     0     0| 4980  4980  4980|10000 10000 10000
+    0     0     0| 4980  4980  4980|10000 10000 10000
+
+10000     0     0|    0     0 10000|    0     0 10000|    0 10000     0
+10000     0     0|    0     0 10000|    0     0 10000|    0 10000     0
+
+65535     0
+    0 65535
+
+    0     0     0|32639 32639 32639|65535 65535 65535
+    0     0     0|32639 32639 32639|65535 65535 65535
+
+65535     0     0|    0     0 65535|    0     0 65535|    0 65535     0
+65535     0     0|    0     0 65535|    0     0 65535|    0 65535     0
+
diff --git a/test/pamtable.test b/test/pamtable.test
new file mode 100755
index 00000000..5c66b7ae
--- /dev/null
+++ b/test/pamtable.test
@@ -0,0 +1,20 @@
+#! /bin/bash
+# This script tests: pamtable
+# Also requires: pamgradient pamseq pamdepth
+
+pamtable testgrid.pbm
+echo
+pamseq 3 2 -tupletype=RGB | pamtable
+echo
+pamseq 4 1 -tupletype=RGBA | pamtable
+echo
+for maxval in 9 10 9999 10000 65535
+  do pbmmake -g 2 2 | pamdepth $maxval | pamtable
+     echo
+     ppmrainbow -width=3 -height=2 -norepeat black white | \
+     pamdepth $maxval | pamtable
+     echo
+     ppmrainbow -width=4 -height=2 -norepeat red blue green | \
+     pamdepth $maxval | pamtable
+     echo
+  done
diff --git a/test/pamtopdbimg.ok b/test/pamtopdbimg.ok
new file mode 100644
index 00000000..57258940
--- /dev/null
+++ b/test/pamtopdbimg.ok
@@ -0,0 +1,20 @@
+high compression
+2
+1
+poor compression
+1
+2
+long titles
+268
+0
+268
+0
+0
+1
+large notefile
+3344
+0
+68885
+0
+0
+1
diff --git a/test/pamtopdbimg.test b/test/pamtopdbimg.test
new file mode 100755
index 00000000..8de78a70
--- /dev/null
+++ b/test/pamtopdbimg.test
@@ -0,0 +1,95 @@
+#! /bin/bash
+# This script tests: pamtopdbimg
+# Also requires: pgmmake pgmnoise
+
+tmpdir=${tmpdir:-/tmp}
+mono_pgm=${tmpdir}/graymono.pgm
+noise_pgm=${tmpdir}/graynoise.pgm
+text65533=${tmpdir}/ascii65533.txt
+text65534=${tmpdir}/ascii65534.txt
+text65597=${tmpdir}/ascii65597.txt
+
+# The PDB image format has a time stamp.  This causes pamtopdbimg
+# to produce slightly different output files with each invocation.
+
+# Test 1. High compression
+# -maybecompressed should produce a compressed file
+# Should print:
+#  2
+#  1
+echo high compression
+pgmmake 0.5 -maxval=15 160 160 > ${mono_pgm}
+for flag in "-compressed" "-maybecompressed" "-uncompressed"
+   do
+   pamtopdbimg $flag ${mono_pgm} | wc -c
+   done  | uniq -c | awk '{print $1}'
+rm ${mono_pgm}
+
+
+# Test 2. Poor compression
+#  -maybecompressed should produce a uncompressed file
+# Should print:
+#  1
+#  2
+echo poor compression
+pgmnoise -maxval=15 -randomseed=0 160 160 > ${noise_pgm}
+for flag in "-compressed" "-maybecompressed" "-uncompressed"
+   do
+   pamtopdbimg $flag ${noise_pgm} | wc -c
+   done  | uniq -c | awk '{print $1}'
+rm ${noise_pgm}
+
+
+# Test 3. long titles
+# Should succeed twice and fail once, producing:
+# 268
+# 0
+# 268
+# 0
+# 1
+# 0
+
+# Some versions of wc have extra whitespace in the output (e.g. MAC OS)
+# Delete with tr -d
+
+echo long titles 
+for flag in "" "-title=0123456789012345678901234567890" \
+               "-title=01234567890123456789012345678901"
+   do
+   pamtopdbimg $flag testgrid.pbm | wc -c | tr -d ' '
+   echo ${PIPESTATUS[0]}
+   done
+
+
+
+# Test 4. large notefile
+# Should succeed twice and fail once, producing:
+# 3344
+# 0
+# 68886
+# 0
+# 1
+# 0
+echo large notefile 
+awk 'BEGIN { ABC="ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+             abc="abcdefghijklmnopqrstuvwxyz";
+             digits="0123456789"; string ="";
+
+             while ( length(string) < 65597 ) 
+                string = string ABC abc digits;
+
+             print string }' > ${text65597}
+
+head -c 65533 ${text65597} > ${text65533}
+head -c 65534 ${text65597} > ${text65534}
+pamtopdbimg -uncompressed testgrid.pbm | \
+  wc -c | tr -d ' '
+  echo ${PIPESTATUS[0]}
+pamtopdbimg -uncompressed -notefile=${text65533} testgrid.pbm | \
+  wc -c | tr -d ' '
+  echo ${PIPESTATUS[0]}
+pamtopdbimg -uncompressed -notefile=${text65534} testgrid.pbm | \
+  wc -c | tr -d ' '
+  echo ${PIPESTATUS[0]}
+
+rm ${text65533} ${text65534} ${text65597}
\ No newline at end of file
diff --git a/test/pbmtext-bdf.ok b/test/pbmtext-bdf.ok
new file mode 100644
index 00000000..b1486493
--- /dev/null
+++ b/test/pbmtext-bdf.ok
@@ -0,0 +1,21 @@
+386826492 35
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+1
+0
+0
diff --git a/test/pbmtext-bdf.test b/test/pbmtext-bdf.test
new file mode 100755
index 00000000..50df7b75
--- /dev/null
+++ b/test/pbmtext-bdf.test
@@ -0,0 +1,169 @@
+#! /bin/bash
+# This script tests: pbmtext
+# Also requires:
+
+tmpdir=${tmpdir:-/tmp}
+
+font_bdf=${tmpdir}/font.bdf
+font_corrupt=${tmpdir}/fontcorrupt
+
+# Though this BDF font file defines only three letters, it is valid.
+
+cat > ${font_bdf} << EOF
+STARTFONT 2.1
+COMMENT simple font for pbmtext test
+COMMENT derived from: $XFree86: xc/fonts/bdf/misc/micro.bdf,v 1.1 1999/09/25 14:36:34 dawes Exp $
+FONT test
+SIZE 4 75 75
+FONTBOUNDINGBOX 4 5 0 0
+STARTPROPERTIES 5
+FONT_DESCENT 0
+FONT_ASCENT 5
+CHARSET_REGISTRY "ISO88591"
+CHARSET_ENCODING "1"
+COPYRIGHT "Public domain font.  Share and enjoy."
+ENDPROPERTIES
+CHARS 3
+STARTCHAR A
+ENCODING 65
+SWIDTH 1000 0
+DWIDTH 4 0
+BBX 4 5 0 0
+BITMAP
+e0
+a0
+e0
+a0
+a0
+ENDCHAR
+STARTCHAR B
+ENCODING 66
+SWIDTH 1000 0
+DWIDTH 4 0
+BBX 4 5 0 0
+BITMAP
+e0
+a0
+c0
+a0
+e0
+ENDCHAR
+STARTCHAR C
+ENCODING 67
+SWIDTH 1000 0
+DWIDTH 4 0
+BBX 4 5 0 0
+BITMAP
+e0
+80
+80
+80
+e0
+ENDCHAR
+ENDFONT
+EOF
+
+# Test 1
+# This should succeed and produce 386826492 35
+pbmtext -font ${font_bdf} ABC | cksum
+
+
+# Test 2
+# These should all fail.  Writes 1 eightteen times.
+
+echo "Test whether corrupted BDF font files are properly handled." 1>&2
+echo "Error messages should appear below the line." 1>&2
+echo "-----------------------------------------------------------" 1>&2
+
+pbmtext -font ${font_bdf} BCD
+echo $?
+
+for token in "STARTPROPERTIES" "CHARS" "STARTCHAR" "ENCODING" "DWIDTH"
+do
+  font_corrupt_bdf=${font_corrupt}.naked_${token}.bdf
+  sed 's/^'${token}' .*$/'${token}'/' \
+    ${font_bdf} >  ${font_corrupt_bdf}
+  pbmtext -font ${font_corrupt_bdf} ABC > /dev/null
+  echo $?
+  rm ${font_corrupt_bdf}
+done
+
+font_corrupt_bdf=${font_corrupt}.fbbx_narrow.bdf
+sed 's/FONTBOUNDINGBOX 4 5 0 0/FONTBOUNDINGBOX 4 4 0 0/' \
+  ${font_bdf} > ${font_corrupt_bdf}
+pbmtext -font ${font_corrupt_bdf} ABC > /dev/null
+echo $?
+rm ${font_corrupt_bdf}
+
+font_corrupt_bdf=${font_corrupt}.fbbx_low.bdf
+sed 's/FONTBOUNDINGBOX 4 5 0 0/FONTBOUNDINGBOX 3 5 0 0/' \
+  ${font_bdf} >  ${font_corrupt_bdf}
+pbmtext -font ${font_corrupt_bdf} ABC > /dev/null
+echo $?
+rm ${font_corrupt_bdf}
+
+font_corrupt_bdf=${font_corrupt}.bbx_only3fields.bdf
+sed 's/BBX 4 5 0 0/BBX 4 5 0/' \
+  ${font_bdf} >  ${font_corrupt_bdf}
+pbmtext -font ${font_corrupt_bdf} ABC > /dev/null
+echo $?
+rm ${font_corrupt_bdf}
+
+font_corrupt_bdf=${font_corrupt}.bbx_wide.bdf
+sed 's/BBX 4 5 0 0/BBX 9 5 0 0/' \
+  ${font_bdf} >  ${font_corrupt_bdf}
+pbmtext -font ${font_corrupt_bdf} ABC > /dev/null
+echo $?
+rm ${font_corrupt_bdf}
+
+font_corrupt_bdf=${font_corrupt}.bbx_zerowidth.bdf
+sed 's/BBX 4 5 0 0/BBX 0 5 0 0/' \
+  ${font_bdf} >  ${font_corrupt_bdf}
+pbmtext -font ${font_corrupt_bdf} ABC > /dev/null
+echo $?
+rm ${font_corrupt_bdf}
+
+font_corrupt_bdf=${font_corrupt}.bbx_tall.bdf
+sed 's/BBX 4 5 0 0/BBX 4 6 0 0/' \
+  ${font_bdf} >  ${font_corrupt_bdf}
+pbmtext -font ${font_corrupt_bdf} ABC > /dev/null
+echo $?
+rm ${font_corrupt_bdf}
+
+font_corrupt_bdf=${font_corrupt}.bbx_low.bdf
+sed 's/BBX 4 5 0 0/BBX 4 1 0 0/' \
+  ${font_bdf} >  ${font_corrupt_bdf}
+pbmtext -font ${font_corrupt_bdf} ABC > /dev/null
+echo $?
+rm ${font_corrupt_bdf}
+
+font_corrupt_bdf=${font_corrupt}.bbx_zeroheight.bdf
+sed 's/BBX 4 5 0 0/BBX 4 0 0 0/' \
+  ${font_bdf} >  ${font_corrupt_bdf}
+pbmtext -font ${font_corrupt_bdf} ABC > /dev/null
+echo $?
+rm ${font_corrupt_bdf}
+
+for delete_line in 14 16 18 20
+  do
+  font_corrupt_bdf=${font_corrupt}.del${delete_line}.pdf
+  sed "${delete_line}"d ${font_bdf} >  ${font_corrupt_bdf}
+  pbmtext -font ${font_corrupt_bdf} ABC > /dev/null
+  echo $?
+  rm ${font_corrupt_bdf}
+  done
+
+
+# Test 2
+# These should succeed.  Warning messages will be displayed.
+# Writes 1 two times.
+
+for token in "CHARSET_ENCODING" "CHARSET_REGISTRY"
+do
+  font_corrupt_bdf=${font_corrupt}.naked_${token}.bdf
+  sed 's/^'${token}' .*$/'${token}'/' \
+    ${font_bdf} >  ${font_corrupt_bdf}
+  pbmtext -font ${font_corrupt_bdf} ABC > /dev/null
+  echo $?
+  rm ${font_corrupt_bdf}
+done
diff --git a/test/pbmtext-iso88591.ok b/test/pbmtext-iso88591.ok
new file mode 100644
index 00000000..6cc1a856
--- /dev/null
+++ b/test/pbmtext-iso88591.ok
@@ -0,0 +1,4 @@
+3806607098 5110
+3806607098 5110
+2858870527 192
+2858870527 192
diff --git a/test/pbmtext-iso88591.test b/test/pbmtext-iso88591.test
new file mode 100755
index 00000000..bc5e83ab
--- /dev/null
+++ b/test/pbmtext-iso88591.test
@@ -0,0 +1,46 @@
+#! /bin/bash
+# This script tests: pbmtext
+# Also requires:
+
+# This test requires the following locale:
+#   LC_ALL en_US.iso88591
+# Skip this test if it is not available
+
+iconv /dev/null
+if [ $? -ne 0  ]
+  then echo "iconv command not available." 1>&2
+       echo "Skipping." 1>&2
+  exit 80;
+fi
+
+echo "A" | LC_ALL=en_US.iso88591 pbmtext -wchar > /dev/null
+if [ $? -ne 0  ]
+  then echo "LC_ALL could not be set to en_US.iso88591" 1>&2
+       echo "Skipping." 1>&2
+  exit 80;
+fi
+
+# Two rows
+# Should print 3806607098 5110 twice
+LC_ALL=C \
+awk 'BEGIN { for (i=32; i<=126;++i) printf("%c",i); print ""; \
+             for (i=160;i<=255;++i) printf("%c",i); }' | \
+    pbmtext -builtin bdf | cksum
+
+
+LC_ALL=C \
+awk 'BEGIN { for (i=32; i<=126;++i) printf("%c",i); print ""; \
+             for (i=160;i<=255;++i) printf("%c",i); }' | \
+    LC_ALL=en_US.iso88591 pbmtext -builtin bdf -wchar | cksum
+
+
+# Two rows
+# Should print 2858870527 192 twice
+LC_ALL=C \
+awk 'BEGIN { for (i=32; i<=126;++i) printf("%c",i); print ""; \
+             for (i=161;i<=255;++i) printf("%c",i); print "" }' | cksum
+
+LC_ALL=C \
+awk 'BEGIN { for (i=32; i<=126;++i) printf("%c",i); print ""; \
+             for (i=161;i<=255;++i) printf("%c",i); print ""}' | \
+    LC_ALL=en_US.iso88591 pbmtext -builtin bdf -wchar -text-dump | cksum
\ No newline at end of file
diff --git a/test/pbmtext-utf8.ok b/test/pbmtext-utf8.ok
new file mode 100644
index 00000000..588bf617
--- /dev/null
+++ b/test/pbmtext-utf8.ok
@@ -0,0 +1,8 @@
+2066913605 5110
+2066913605 5110
+2920616515 2301
+2920616515 2301
+0 0 0 0 : 0
+ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
+ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
+0 1 : 1
diff --git a/test/pbmtext-utf8.test b/test/pbmtext-utf8.test
new file mode 100755
index 00000000..cf495b7c
--- /dev/null
+++ b/test/pbmtext-utf8.test
@@ -0,0 +1,95 @@
+#! /bin/bash
+# This script tests: pbmtext
+# Also requires:
+
+# This test requires the en_US.utf8 locale
+# Skip this test if it is not available
+
+iconv /dev/null
+if [ $? -ne 0  ]
+  then echo "iconv command not available." 1>&2
+       echo "Skipping." 1>&2
+  exit 80;
+fi
+
+echo "A" | LC_ALL=en_US.utf8 pbmtext -wchar > /dev/null
+if [ $? -ne 0  ]
+  then echo "LC_ALL could not be set to en_US.utf8." 1>&2
+       echo "Skipping." 1>&2
+  exit 80;
+fi
+
+# Test 1.
+# Two rows
+# Should print 2066913605 5110 twice
+LC_ALL=C \
+awk 'BEGIN { for (i=32; i<=126;++i) printf("%c",i); print ""; \
+             for (i=161;i<=255;++i) printf("%c",i); }' | \
+    pbmtext -builtin bdf | cksum
+
+LC_ALL=C \
+awk 'BEGIN { for (i=32; i<=126;++i) printf("%c",i); print ""; \
+             for (i=161;i<=255;++i) printf("%c",i);  }' | \
+    iconv -f iso8859-1 -t utf-8 | \
+    LC_ALL=en_US.utf8 pbmtext -builtin bdf -wchar | cksum
+
+
+# Test 2.
+# One row
+# Should print 2920616515 2301 twice
+LC_ALL=C \
+awk 'BEGIN { for (i=32; i<=126;++i) printf("%c",i); print "" }' | \
+    pbmtext -builtin bdf | cksum
+
+LC_ALL=C \
+awk 'BEGIN { for (i=32; i<=126;++i) printf("%c",i);  print ""}' | \
+    LC_ALL=en_US.utf8 pbmtext -builtin bdf -wchar | cksum
+
+
+tmpdir=${tmpdir:-/tmp}
+output=${tmpdir}/output
+
+
+# Test 3.
+# Two rows
+# Output may be affected by locale.  Compare with cmp.
+# Should print 0
+LC_ALL=C \
+awk 'BEGIN { for (i=32; i<=126;++i) printf("%c",i); print ""; \
+             for (i=161;i<=255;++i) printf("%c",i); print "" }' | \
+    iconv -f iso8859-1 -t utf-8 > ${output}
+
+LC_ALL=C \
+awk 'BEGIN { for (i=32; i<=126;++i) printf("%c",i); print ""; \
+             for (i=161;i<=255;++i) printf("%c",i); print "" }' | \
+    iconv -f iso8859-1 -t utf-8 | \
+    LC_ALL=en_US.utf8 pbmtext -builtin bdf -wchar -text-dump | \
+    cmp --quiet - ${output}
+
+echo ${PIPESTATUS[@]} ":" $?
+rm ${output}
+
+
+# Test 4.
+# One row
+# Should print the following twice:
+# !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
+LC_ALL=C \
+awk 'BEGIN { for (i=32; i<=126;++i) printf("%c",i); print "" } '
+
+LC_ALL=C \
+awk 'BEGIN { for (i=32; i<=126;++i) printf("%c",i); print ""}' | \
+        LC_ALL=en_US.utf8 pbmtext -builtin bdf -wchar -text-dump
+
+
+echo "Invalid utf-8 sequence as input." 1>&2
+echo "An error message should appear below the line." 1>&2
+echo "-----------------------------------------------------------" 1>&2
+
+# Test 5.
+# Invalid utf-8 sequence
+# Should print 1
+LC_ALL=C \
+awk 'BEGIN { for (i=128; i<=129;++i) printf("%c",i);  print ""}' | \
+        LC_ALL=en_US.utf8 pbmtext -builtin bdf -wchar -text-dump
+echo ${PIPESTATUS[@]} ":" $?
diff --git a/test/pbmtext.ok b/test/pbmtext.ok
index 438fe7b8..96e351f9 100644
--- a/test/pbmtext.ok
+++ b/test/pbmtext.ok
@@ -1,21 +1,16 @@
-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
+3898818212 967
+3898818212 967
+2506052117 1354
+2506052117 1354
+1028079028 967
+1888680721 445
+1305436978 1018
+1028079028 967
+1028079028 967
+1305436978 1018
+1305436978 1018
+1647614653 2027
+1647614653 2027
+1647614653 2027
+2547645687 4564
+1174281741 5741
diff --git a/test/pbmtext.test b/test/pbmtext.test
index dffb2cb4..38578636 100755
--- a/test/pbmtext.test
+++ b/test/pbmtext.test
@@ -1,12 +1,94 @@
 #! /bin/bash
 # This script tests: pbmtext
-# Also requires:
+# Also requires: pamfile
 
+tmpdir=${tmpdir:-/tmp}
 
-for i in 0123456789 abcdefghijk lmnopqrst uzwxyz ABCDEFGHIJK LMNOPQRST UVWXYZ
-do
-for flags in "" "-nom" "-builtin fixed"
+# Test 1:
+
+# Should print 3898818212 967 twice
+
+pbmtext UNIX Philosophy: Do one thing and do it well. | cksum
+echo -n "UNIX Philosophy: Do one thing and do it well." | pbmtext | cksum
+
+# Should print 2506052117 1354 twice
+
+pbmtext -builtin fixed \
+    For truth is always strange. Stranger than fiction. Lord Byron | cksum
+echo -n "For truth is always strange. Stranger than fiction. Lord Byron" | \
+    pbmtext -builtin fixed | cksum
+
+
+text="The quick brown fog jumps over the lazy docs."
+
+# Test 2:
+
+for flags in "" "-nomargins" "-builtin fixed"
 do
-echo $i | pbmtext $flags | cksum
+echo ${text} | pbmtext ${flags} | cksum
 done
+
+temp_pbm=${tmpdir}/temp.pbm
+
+
+# Test 3: Check if image is produced unaltered when -width is specified
+# Should print 1028079028 967 twice, then 1305436978 1018 twice
+
+for flags in "" "-builtin fixed"
+do
+pbmtext ${flags} ${text} | tee ${temp_pbm} | cksum
+width1=`pamfile ${temp_pbm} | awk '$2=="PBM" && NR==1 {w=$4}; END {print w}' `
+width2=`pbmtext ${flags} ${text} --dry-run | awk '{print $1}' `
+
+if [ ${width1} -eq ${width2} ]; then
+    pbmtext ${flags} -width=${width1} ${text} | cksum
+else
+    echo ${width1} ${width2}
+fi
+rm ${temp_pbm}
 done
+
+
+# Test 4: Should print 1647614653 2027 three times
+# Note: backslashes inserted in 3 locations in the rectange to make
+# possible input as a here document.
+
+fontRectangle_txt=${tmpdir}/fontRectangle.txt
+font_pbm=${tmpdir}/font.pbm
+
+cat > ${fontRectangle_txt} << EOF
+M ",/^_[\`jpqy| M
+
+/  !"#$%&'()*+ /
+< ,-./01234567 <
+> 89:;<=>?@ABC >
+@ DEFGHIJKLMNO @
+_ PQRSTUVWXYZ[ _
+{ \\]^_\`abcdefg {
+} hijklmnopqrs }
+~ tuvwxyz{|}~  ~
+
+M ",/^_[\`jpqy| M
+EOF
+
+pbmtext -dump-sheet -builtin fixed | tee ${font_pbm}      | cksum
+cat ${fontRectangle_txt} | pbmtext -nom -builtin fixed    | cksum
+cat ${fontRectangle_txt} | pbmtext -nom -font ${font_pbm} | cksum
+rm ${fontRectangle_txt} ${font_pbm}
+
+
+# Test 5: Print all characters defined in the built-in bdf font
+
+# One long row
+# Should print 3233136020 4535
+LC_ALL=C \
+awk 'BEGIN { for (i=32; i<=126;++i) printf("%c",i);
+             for (i=160;i<=255;++i) printf("%c",i); }' | \
+    pbmtext -builtin bdf | cksum
+
+# One tall column
+# Should print 1216262214 5711
+LC_ALL=C \
+awk 'BEGIN { for (i=32; i<=126;++i) printf("%c\n",i);
+             for (i=160;i<=255;++i) printf("%c\n",i); }' | \
+    pbmtext -nomargins -builtin bdf | cksum
diff --git a/test/pbmtog3.ok b/test/pbmtog3.ok
index a0768f95..cef9e3f9 100644
--- a/test/pbmtog3.ok
+++ b/test/pbmtog3.ok
@@ -5,7 +5,6 @@
 28792587 47
 277456854 32
 28792587 47
-871281767 162
 3736247115 62
 2820255307 2191856
 4159089282 2226575
diff --git a/test/pbmtog3.test b/test/pbmtog3.test
index 15c0cdf9..c5dc3357 100755
--- a/test/pbmtog3.test
+++ b/test/pbmtog3.test
@@ -2,7 +2,6 @@
 # This script tests: pbmtog3
 # Also requires: pbmmake
 
-
 # Test 1.  Should print 3697098186 144
 pbmtog3 testgrid.pbm | cksum
 # Test 2.  Should print 1248301383 122
@@ -18,12 +17,9 @@ 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
+# Test 8.  Should print 3736247115 62
 pbmmake -b 10 10 | pbmtog3 | cksum
-# Test 10.  Should print 2820255307 2191856
+# Test 9.  Should print 2820255307 2191856
 pbmmake -g 1700 2286 | pbmtog3 | cksum
-# Test 11.  Should print 4159089282 2226575
+# Test 10.  Should print 4159089282 2226575
 pbmmake -g 1800 2286 | pbmtog3 | cksum
diff --git a/test/pdb-roundtrip.ok b/test/pdb-roundtrip.ok
new file mode 100644
index 00000000..b903da8d
--- /dev/null
+++ b/test/pdb-roundtrip.ok
@@ -0,0 +1,18 @@
+pbm grid
+2224198737 25671
+2224198737 25671
+2224198737 25671
+pbm tiled
+0 0 0 0 : 0
+0 0 0 0 : 0
+0 0 0 0 : 0
+0 0 0 0 : 0
+pgm ellipse
+0 0 0 0 0 : 0
+0 0 0 0 0 : 0
+0 0 0 0 0 : 0
+0 0 0 0 0 : 0
+pgm ellipse -4depth
+0 0 0 0 0 : 0
+0 0 0 0 0 : 0
+0 0 0 0 0 : 0
diff --git a/test/pdb-roundtrip.test b/test/pdb-roundtrip.test
new file mode 100755
index 00000000..6b4e152c
--- /dev/null
+++ b/test/pdb-roundtrip.test
@@ -0,0 +1,55 @@
+#! /bin/bash
+# This script tests: pamtopdbimg pdbimgtopam
+# Also requires: pnmtile pgmramp pamtopnm
+
+tmpdir=${tmpdir:-/tmp}
+tiled_pbm=${tmpdir}/tiled.pbm
+ellipse_pgm=${tmpdir}/ellipse.pgm
+
+
+# Test 1. Should produce 2224198737 25671
+# 3 times 
+echo pbm grid
+for flag in "-compressed" "-maybecompressed" "-uncompressed"
+   do
+   pamtopdbimg ${flag} testgrid.pbm | pdbimgtopam | cksum
+   done
+
+
+# Test 2. Should produce 0 0 0
+# 4 times 
+echo pbm tiled
+pnmtile 160 160 testgrid.pbm > ${tiled_pbm}
+for flag in "" "-compressed" "-maybecompressed" "-uncompressed"
+  do
+  pamtopdbimg ${flag} ${tiled_pbm} | pdbimgtopam | pamtopnm | \
+    cmp -s - ${tiled_pbm}
+  echo ${PIPESTATUS[@]} ":" $?
+  done
+rm ${tiled_pbm}
+
+
+# Test 3. Should produce 0 0 0
+# 4 times
+echo pgm ellipse
+pgmramp -ellipse 160 160 -maxval=3 > ${ellipse_pgm}
+for flag in "" "-compressed" "-maybecompressed" "-uncompressed"
+    do
+    pamtopdbimg ${flag} ${ellipse_pgm} | pdbimgtopam | \
+      pamtopnm | pamdepth 3 | cmp -s - ${ellipse_pgm}
+    echo ${PIPESTATUS[@]} ":" $?
+    done
+rm ${ellipse_pgm}
+
+
+# Test 3. Should produce 0 0 0
+# 3 times 
+echo pgm ellipse -4depth
+pgmramp -ellipse 160 160 -maxval=15 > ${ellipse_pgm}
+for flag in "-compressed" "-maybecompressed" "-uncompressed"
+    do
+    pamtopdbimg -4depth ${flag} ${ellipse_pgm} | pdbimgtopam | \
+      pamtopnm | pamdepth 15 | cmp -s - ${ellipse_pgm}
+    echo ${PIPESTATUS[@]} ":" $?
+    done
+rm ${ellipse_pgm}
\ No newline at end of file
diff --git a/test/pgmnoise.ok b/test/pgmnoise.ok
deleted file mode 100644
index 138218c2..00000000
--- a/test/pgmnoise.ok
+++ /dev/null
@@ -1 +0,0 @@
-2005134911 10015
diff --git a/test/pgmnoise.rand-ok b/test/pgmnoise.rand-ok
new file mode 100644
index 00000000..b69f48e5
--- /dev/null
+++ b/test/pgmnoise.rand-ok
@@ -0,0 +1,3 @@
+000|0
+081|2005134911 10015
+082|3516404574 10015
diff --git a/test/pgmnoise.test b/test/pgmnoise.test
index 0b38553b..03301ce6 100755
--- a/test/pgmnoise.test
+++ b/test/pgmnoise.test
@@ -2,22 +2,6 @@
 # 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
+# Should print: 1663614689 10015 (Glibc)
+#               3516404574 10015 (MAC OS)
+pgmnoise --randomseed=0 100 100 | cksum
diff --git a/test/pnmpaste-pbm.ok b/test/pnmpaste-pbm.ok
new file mode 100644
index 00000000..d3b0a7e8
--- /dev/null
+++ b/test/pnmpaste-pbm.ok
@@ -0,0 +1,22 @@
+Test 1.
+-replace
+P1 18 1 001010101010101011 
+-and
+P1 18 1 001111101111101111 
+-or
+P1 18 1 000010000010000011 
+-xor
+P1 18 1 010010010010010011 
+-nand
+P1 18 1 010000010000010011 
+-nor
+P1 18 1 011101111101111111 
+-nxor
+P1 18 1 001101101101101111 
+Test 2.
+-and  -nand
+P1 18 1 110000000000000001 
+-or  -nor
+P1 18 1 110000000000000001 
+-xor  -nxor
+P1 18 1 110000000000000001 
diff --git a/test/pnmpaste-pbm.test b/test/pnmpaste-pbm.test
new file mode 100755
index 00000000..b75797af
--- /dev/null
+++ b/test/pnmpaste-pbm.test
@@ -0,0 +1,47 @@
+#! /bin/bash
+# This script tests: pnmpaste
+# Also requires: pbmmake
+
+tmpdir=${tmpdir:-/tmp}
+base_pbm=${tmpdir}/font.pbm
+insert_pbm=${tmpdir}/insert.pbm
+
+cat > ${base_pbm} << EOF
+P1
+18 1
+000111000111000111
+EOF
+
+pbmmake -g 15 1 > ${insert_pbm}
+
+# Test 1.
+echo "Test 1."
+
+for operation in "-replace" "-and" "-or" "-xor" "-nand" "-nor" "-nxor"
+  do
+  echo ${operation} 
+  pnmpaste ${operation} -plain ${insert_pbm} 1 0 ${base_pbm} | tr '\n' ' '
+  echo
+  done
+
+
+# Test 2.
+# Perform an operation and its negative counterpart, combine the
+# resulting images with "-xor".
+# The untouched area of the base image should be 1; inserted area 0.
+
+echo "Test 2."
+
+positive_pbm=${tmpdir}/positive.pbm
+negative_pbm=${tmpdir}/negative.pbm
+
+for operation in "and" "or" "xor"
+  do
+  echo "-"$operation " -n"$operation 
+  pnmpaste "-"${operation} ${insert_pbm} 2 0 ${base_pbm} > ${positive_pbm}
+  pnmpaste "-n"${operation} ${insert_pbm} 2 0 ${base_pbm} > ${negative_pbm}
+  pnmpaste -xor -plain ${positive_pbm} 0 0 ${negative_pbm} | tr '\n ' ' '
+  echo
+  done
+
+rm ${base_pbm} ${insert_pbm} ${positive_pbm} ${negative_pbm}
diff --git a/test/pnmpsnr.ok b/test/pnmpsnr.ok
index b303cc9d..3469f836 100644
--- a/test/pnmpsnr.ok
+++ b/test/pnmpsnr.ok
@@ -1,2 +1,7 @@
-0.00 dB
-no difference
+0.00
+inf
+1000.00
+match
+300.00 300.00 300.00
+match
+match
diff --git a/test/pnmpsnr.test b/test/pnmpsnr.test
index 3e7408ec..f24c08aa 100755
--- a/test/pnmpsnr.test
+++ b/test/pnmpsnr.test
@@ -12,9 +12,12 @@ 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}'
+pnmpsnr  ${w_pbm}  ${b_pbm} -machine
+pnmpsnr  ${w_pbm}  ${w_pbm} -machine
+pnmpsnr  ${w_pbm}  ${w_pbm} -machine -max=1000
+pnmpsnr  ${w_pbm}  ${w_pbm} -target=1000
+pnmpsnr  testimg.ppm  testimg.ppm -machine -max=300
+pnmpsnr  testimg.ppm  testimg.ppm -target=1000
+pnmpsnr  testimg.ppm  testimg.ppm -target1=1000 -target2=1000 -target3=1000
 
 rm ${b_pbm} ${w_pbm}
diff --git a/test/pnmquantall.ok b/test/pnmquantall.ok
new file mode 100644
index 00000000..172eb0aa
--- /dev/null
+++ b/test/pnmquantall.ok
@@ -0,0 +1,5 @@
+got color map
+2373957371 33838
+3892560659 33838
+1383839923 33838
+1
diff --git a/test/pnmquantall.test b/test/pnmquantall.test
new file mode 100755
index 00000000..cd01bb8d
--- /dev/null
+++ b/test/pnmquantall.test
@@ -0,0 +1,27 @@
+#! /bin/bash
+# This script tests: pnmquantall
+# Also requires: ppmtorgb3 pgmhist
+
+tmpdir=${tmpdir:-/tmp}
+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
+
+pnmquantall 20 ${test_red} ${test_grn} ${test_blu}
+
+for i in ${test_red} ${test_grn} ${test_blu}
+do
+cat $i | cksum
+done
+
+# Should print 1
+
+pnmcat ${test_red} ${test_grn} ${test_blu} -tb | \
+    pgmhist -m | awk '$2>0 {s++}; END { print (s<=20) }'
+
+rm ${test_red} ${test_grn} ${test_blu} ${test_ppm}
\ No newline at end of file
diff --git a/test/pnmtile.ok b/test/pnmtile.ok
index 559d0f65..4a29e0dc 100644
--- a/test/pnmtile.ok
+++ b/test/pnmtile.ok
@@ -1,2 +1,2 @@
 4228632379 259
-0
+0 0 : 0
diff --git a/test/pnmtile.test b/test/pnmtile.test
index 409c0e50..e297da66 100755
--- a/test/pnmtile.test
+++ b/test/pnmtile.test
@@ -17,6 +17,6 @@ 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 $?
+echo ${PIPESTATUS[@]} ":" $?
 
 rm ${testimg2_ppm} ${testimg4_ppm}
diff --git a/test/ppmbrighten.ok b/test/ppmbrighten.ok
index 65feb812..376e71d3 100644
--- a/test/ppmbrighten.ok
+++ b/test/ppmbrighten.ok
@@ -1,3 +1,3 @@
-2737989845 101484
-1059247992 101484
-32344911 101484
+1969633344 101484
+3688219243 101484
+295150171 101484
diff --git a/test/ppmbrighten.test b/test/ppmbrighten.test
index fa7702d8..46c5cab5 100755
--- a/test/ppmbrighten.test
+++ b/test/ppmbrighten.test
@@ -2,6 +2,10 @@
 # This script tests: ppmbrighten
 # Also requires:
 
+# Failure message
+## Ppmbrighten is sensitive to subtle differences in floating point math.
+## If this test fails, please run the program and visually examine
+## the output.
 
 ppmbrighten -v 100 testimg.ppm | cksum
 ppmbrighten -v 100 -normalize testimg.ppm | cksum
diff --git a/test/ppmchange.ok b/test/ppmchange.ok
index 130c3c45..aba3a7a8 100644
--- a/test/ppmchange.ok
+++ b/test/ppmchange.ok
@@ -1,4 +1,9 @@
 22488533 203
 1008787190 613
-3885709071 613
-2101746192 613
+1983174784 613
+2146447222 613
+1216791938 613
+     0     0     0	    0	     78 
+     0     0   255	   29	     40 
+     0    50     2	   30	     41 
+   100     0     1	   30	     41 
diff --git a/test/ppmchange.test b/test/ppmchange.test
index d29a699f..a749a5d2 100755
--- a/test/ppmchange.test
+++ b/test/ppmchange.test
@@ -1,18 +1,19 @@
 #! /bin/bash
 # This script tests: ppmchange
-# Also requires: ppmrainbow pgmramp
+# Also requires: ppmrainbow pgmramp ppmhist
 
 
 #  Failure message
 ## If this test fails and ppmchange-roundtrip.test succeeds,
-## the probably cause is a problem with one of the options of
+## the probable cause is a problem with one of the options of
 ## ppmchange: -closeness or -remainder.
 
 tmpdir=${tmpdir:-/tmp}
 rainbow_ppm=${tmpdir}/rainbow.ppm
+changed_ppm=${tmpdir}/changed.ppm
 
-# Explicit values for intermediate colors: rgb.txt may be defining them
-# in unusual ways.
+# Explicit values for intermediate colors: rgb.txt may not be the one
+# Netpbm provides; they may be defined in unusual ways.
 
 brown=rgb:67/43/00
 cyan=rgb:00/ff/ff
@@ -20,7 +21,7 @@ yellow=rgb:ff/ff/00
 gray=rgb:7f/7f/7f
 
 
-# Test 1. Should print 811868957 60
+# Test 1. Should print 22488533 203
 pgmramp -lr 8 8 | ppmchange black black  white white  $gray $gray \
   -close=10 -remainder=blue | cksum
 
@@ -31,29 +32,31 @@ ppmrainbow -tmpdir=$tmpdir -width=200 -height=1 red green blue | \
   tee ${rainbow_ppm} | \
   ppmchange red $brown   green $brown   blue $brown | cksum
 
+# Validate ${rainbow_ppm}
+# Should print 1983174784 613
 
-# Test 3. Should print 3885709071 613
+cat ${rainbow_ppm} | cksum
+
+
+# Test 3. Should print 2146447222 613
 
 ppmchange red $brown   green $cyan   blue $yellow \
   -closeness=25 ${rainbow_ppm} | cksum
 
 
-# Test 4. Should print 2101746192 613
+# Test 4. Should print 1216791938 613
 
 ppmchange red rgb:64/00/01 rgb:00/ff/00 rgb:00/32/02 blue blue \
-  -remainder=black -closeness=25 ${rainbow_ppm} | cksum
+  -remainder=black -closeness=25 ${rainbow_ppm} | tee ${changed_ppm} | cksum
 
-rm ${rainbow_ppm}
+# Produce a histogram of the colors in the output image
+# Should produce
+#     0     0     0	    0	     78 
+#     0     0   255	   29	     40 
+#     0    50     2	   30	     41 
+#   100     0     1	   30	     41 
 
+ppmhist -sort=rgb -noheader ${changed_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
+rm ${rainbow_ppm} ${changed_ppm}
diff --git a/test/ppmforge.ok b/test/ppmforge.ok
deleted file mode 100644
index e4a4c9e2..00000000
--- a/test/ppmforge.ok
+++ /dev/null
@@ -1 +0,0 @@
-3634219838 196623
diff --git a/test/ppmforge.rand-ok b/test/ppmforge.rand-ok
new file mode 100644
index 00000000..c8b3ac9f
--- /dev/null
+++ b/test/ppmforge.rand-ok
@@ -0,0 +1,3 @@
+000|0
+081|3634219838 196623
+082|3262664440 196623
diff --git a/test/ppmforge.test b/test/ppmforge.test
index 65280d14..3ebea88c 100755
--- a/test/ppmforge.test
+++ b/test/ppmforge.test
@@ -4,17 +4,6 @@
 
 # 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
+# Should print: 3634219838 196623 (Glibc)
+#               3262664440 196623 (MAC OS)
+ppmforge -night -seed 1 | cksum
diff --git a/test/ppmhist.ok b/test/ppmhist.ok
index d7ecf07e..f2ba637b 100644
--- a/test/ppmhist.ok
+++ b/test/ppmhist.ok
@@ -1,5 +1,3 @@
-   r     g     b   	 lum 	 count  
- ----- ----- ----- 	-----	------- 
      0     0     0	    0	      2 
      1     1     1	    1	      2 
      2     2     2	    2	      2 
@@ -8,4 +6,14 @@
      5     5     5	    5	      2 
      6     6     6	    6	      2 
      8     8     8	    8	      2 
-3438989921 711087
+3081591280 60957
+ Summary: 1 colors: 1 black, 0 white, 0 gray, 0 color
+ Summary: 1 colors: 0 black, 1 white, 0 gray, 0 color
+ Summary: 2 colors: 1 black, 1 white, 0 gray, 0 color
+ Summary: 16 colors: 1 black, 1 white, 14 gray, 0 color
+ Summary: 8 colors: 1 black, 1 white, 6 gray, 0 color
+ Summary: 8 colors: 1 black, 1 white, 0 gray, 6 color
+ Summary: 27 colors: 1 black, 1 white, 1 gray, 24 color
+ Summary: 6 colors: 0 black, 0 white, 0 gray, 6 color
+ Summary: 6 colors: 1 black, 0 white, 0 gray, 5 color
+ Summary: 6 colors: 1 black, 1 white, 1 gray, 3 color
diff --git a/test/ppmhist.test b/test/ppmhist.test
index 97fbd79d..27d31562 100755
--- a/test/ppmhist.test
+++ b/test/ppmhist.test
@@ -1,7 +1,22 @@
 #! /bin/bash
 # This script tests: ppmhist
-# Also requires: pgmramp
+# Also requires: pgmramp pamtopnm pbmmake pamseq ppmpat
 
+pgmramp -maxval=8 -lr 8 2 | ppmhist -sort=rgb -noheader
+ppmhist -map -sort=rgb -noheader testimg.ppm | pamtopnm | cksum
 
-pgmramp -maxval=8 -lr 8 2 | ppmhist -sort=rgb
-ppmhist -map -sort=rgb testimg.ppm | cksum
+# Test summary header
+
+pbmmake -b 2 1 | ppmhist   | head -n1
+pbmmake -w 2 1 | ppmhist   | head -n1
+pbmmake -g 2 1 | ppmhist   | head -n1
+pgmramp -lr 16 1 | ppmhist | head -n1
+pgmramp -lr -maxval=7 16 1 | ppmhist | head -n1
+pamseq  3 1 | ppmhist | head -n1
+pamseq  3 2 | ppmhist | head -n1
+ppmpat -madras --color=rgb:32/0d/b7,rgb:31/58/a3,rgb:e9/5e/d4 25 25 | \
+  ppmhist | head -n1
+ppmpat -madras --color=rgb:00/00/00,rgb:31/58/a3,rgb:e9/5e/d4 25 25 | \
+  ppmhist | head -n1
+ppmpat -madras --color=rgb:00/00/00,rgb:31/58/a3,rgb:ff/ff/ff 25 25 | \
+  ppmhist | head -n1
\ No newline at end of file
diff --git a/test/ppmpat-random.rand-ok b/test/ppmpat-random.rand-ok
new file mode 100644
index 00000000..eb8779ab
--- /dev/null
+++ b/test/ppmpat-random.rand-ok
@@ -0,0 +1,7 @@
+000|0
+081|2219119109 36015
+081|3436846137 16813
+081|908097729 16813
+082|3606254242 36015
+082|3615722579 16813
+082|1756684515 16813
diff --git a/test/ppmpat-random.test b/test/ppmpat-random.test
new file mode 100755
index 00000000..a6daa982
--- /dev/null
+++ b/test/ppmpat-random.test
@@ -0,0 +1,22 @@
+#! /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.
+
+# These tests require random numbers.
+
+# Test 1. Should print: 2219119109 36015 (glibc)
+#                       3606254242 36015 (MAC OS)
+ppmpat --randomseed=0 -camo 100 120 | cksum
+
+# Test 2. Should print: 3436846137 16813 (glibc)
+#                       3615722579 16813 (MAC OS)
+ppmpat --randomseed=0 -anticamo 80 70 | cksum
+
+# Test 3. Should print: 908097729 16813 (glibc)
+#                       1756684515 16813 (MAC OS)
+ppmpat --randomseed=0 --color \
+  rgb:55/c0/34,rgb:0/ff/0,rgb:0/ee/0,rgb:0/cd/0,rgb:0/8b/0,rgb:4f/4f/2f \
+  -camo 80 70 | cksum
diff --git a/test/ppmpat.ok b/test/ppmpat.ok
index c5b71909..bb940aee 100644
--- a/test/ppmpat.ok
+++ b/test/ppmpat.ok
@@ -1,6 +1,6 @@
 4008533639 781
-2448908863 9613
+3805937800 9613
 2698433077 1549
 3705929501 781
-2219119109 36015
-3436846137 16813
+3057513592 661
+1861389287 661
diff --git a/test/ppmpat.test b/test/ppmpat.test
index 89f86f67..cd00c0f1 100755
--- a/test/ppmpat.test
+++ b/test/ppmpat.test
@@ -2,34 +2,20 @@
 # 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.
+# Test 1. Should print: 4008533639 781
+ppmpat -g2 --color=rgb:32/0d/b7,rgb:31/58/a3 16 16 | cksum
 
-testrandom -q
-case $? in
-   81)
-       # Test 1. Should print: 4008533639 781
-       ppmpat --randomseed=0 -g2 16 16 | cksum
+# Test 2. Should print: 3805937800 9613
+ppmpat -g3 --color=rgb:32/0d/b7,rgb:31/58/a3,rgb:e9/5e/d4 64 50 | cksum
 
-       # Test 2. Should print: 2448908863 9613
-       ppmpat --randomseed=0 -g2 64 50 | cksum
+# Test 3. Should print: 2698433077 1549
+ppmpat -madras --color=rgb:32/0d/b7,rgb:31/58/a3,rgb:e9/5e/d4 32 16 | cksum
 
-       # Test 3. Should print: 2698433077 1549
-       ppmpat --randomseed=0 -madras 32 16 | cksum
+# Test 4. Should print: 3705929501 781
+ppmpat -tartan --color=rgb:32/0d/b7,rgb:31/58/a3,rgb:e9/5e/d4 16 16 | cksum
 
-       # Test 4. Should print: 3705929501 781
-       ppmpat --randomseed=0 -tartan 16 16 | cksum
+# Test 5. Should print: 3057513592 661
+ppmpat -argyle1 --color=rgb:ff/ff/ff,rgb:ff/0/0 12 18 | 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
+# Test 6. Should print: 1861389287 661
+ppmpat -argyle2 --color=rgb:00/00/00,rgb:ff/80/80,rgb:e0/e0/e0 12 18 | cksum
diff --git a/test/ppmrough.ok b/test/ppmrough.ok
deleted file mode 100644
index 83643849..00000000
--- a/test/ppmrough.ok
+++ /dev/null
@@ -1 +0,0 @@
-378403602 30015
diff --git a/test/ppmrough.rand-ok b/test/ppmrough.rand-ok
new file mode 100644
index 00000000..216545c7
--- /dev/null
+++ b/test/ppmrough.rand-ok
@@ -0,0 +1,3 @@
+000|0
+081|378403602 30015
+082|378403602 30015
diff --git a/test/ppmrough.test b/test/ppmrough.test
index cf948f9f..bd4211a3 100755
--- a/test/ppmrough.test
+++ b/test/ppmrough.test
@@ -2,16 +2,5 @@
 # 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
+# Should print: 378403602 30015 (Glibc and MAC OS)
+ppmrough  -randomseed 1 | cksum
diff --git a/test/ps-flate-roundtrip.ok b/test/ps-flate-roundtrip.ok
new file mode 100644
index 00000000..57fb124f
--- /dev/null
+++ b/test/ps-flate-roundtrip.ok
@@ -0,0 +1,3 @@
+1926073387 101484
+1926073387 101484
+1386192571 507420
diff --git a/test/ps-flate-roundtrip.test b/test/ps-flate-roundtrip.test
new file mode 100755
index 00000000..de1105f0
--- /dev/null
+++ b/test/ps-flate-roundtrip.test
@@ -0,0 +1,48 @@
+#! /bin/bash
+# This script tests: pnmtops pstopnm
+# Also requires: pamtopnm gs zlib
+
+# This script tests the optional flate (zlib) compression feature of
+# pstopnm.
+# Flate compression allows you to make smaller output (.ps) files:
+# it is useful, but not essential.  Flate compression is not neccessary for
+# printing pages with postscript printers - which is why many people need
+# pnmtops on their systems.
+
+# Failure message
+## This test fails when 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 twice
+test1_ps=${tmpdir}/testimg1.ps
+
+for flag in "-ps -flate" "-ps -rle -ascii -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: 1386192571 507420
+# See comments in ps-roundtrip.test
+
+test3_ps=${tmpdir}/testimg3.ps
+flag="-ps -bitspersample=12 -flate -rle -vmreclaim"
+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
+
+rm ${test3_ps}
diff --git a/test/ps-roundtrip.ok b/test/ps-roundtrip.ok
index 0ebfb94a..5ef66cc4 100644
--- a/test/ps-roundtrip.ok
+++ b/test/ps-roundtrip.ok
@@ -2,7 +2,6 @@
 1926073387 101484
 1926073387 101484
 1926073387 101484
-1926073387 101484
 2918318199 62
 2918318199 62
 2918318199 62
diff --git a/test/ps-roundtrip.test b/test/ps-roundtrip.test
index 873bbdef..207646cc 100755
--- a/test/ps-roundtrip.test
+++ b/test/ps-roundtrip.test
@@ -4,21 +4,17 @@
 
 
 # Failure message
-## This test fails when:
-## (1) zlib was not linked.
-## (2) ghostscript is not available.
+## This test fails when 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.
+# Test 1.  Should print: 1926073387 101484 four times
 test1_ps=${tmpdir}/testimg1.ps
 
-for flag in "" "-ps" "-rle" "-ps -ascii" "-ps -flate"
+for flag in "" "-ps" "-rle" "-ps -ascii"
   do
   pnmtops -nocenter -equalpixels -dpi 72 -noturn ${flag} testimg.ppm \
     > ${test1_ps} && \
@@ -57,8 +53,6 @@ for flag in "" "-rle" "-ps -rle -ascii" \
 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.
@@ -72,7 +66,7 @@ rm ${grid_ps} ${g_pbm} ${t_pbm}
 test3_ps=${tmpdir}/testimg3.ps
 
 for flag in "" "-ps" \
-            "-ps -bitspersample=12 -flate -rle -vmreclaim"
+            "-ps -bitspersample=12 -rle -vmreclaim"
   do
 cat testimg.ppm testimg.ppm testimg.ppm testgrid.pbm testgrid.pbm | \
 pnmtops -nocenter -equalpixels -dpi 72 -noturn -setpage ${flag} \
diff --git a/test/rgb3-roundtrip.ok b/test/rgb3-roundtrip.ok
index 64da849d..367e5429 100644
--- a/test/rgb3-roundtrip.ok
+++ b/test/rgb3-roundtrip.ok
@@ -3,4 +3,4 @@
 2425386270 41
 0
 0
-0
+0 0 : 0
diff --git a/test/rgb3-roundtrip.test b/test/rgb3-roundtrip.test
index cac52220..42fd822f 100755
--- a/test/rgb3-roundtrip.test
+++ b/test/rgb3-roundtrip.test
@@ -41,6 +41,6 @@ rgb3toppm ${testgrid_red} ${testgrid_grn} ${testgrid_blu} | \
 cmp -s ${testgrid_red} ${testgrid_grn} ; echo $?
 cmp -s ${testgrid_grn} ${testgrid_blu} ; echo $?
 pgmtopgm < testgrid.pbm | cmp -s - ${testgrid_red}
-  echo $?
+  echo ${PIPESTATUS[@]} ":" $?
 
 rm ${testgrid_pbm} ${testgrid_red} ${testgrid_grn} ${testgrid_blu}
diff --git a/test/symmetry.ok b/test/symmetry.ok
index 23129684..a5945014 100644
--- a/test/symmetry.ok
+++ b/test/symmetry.ok
@@ -10,3 +10,9 @@ ok
 ok
 ok
 ok
+ok
+ok
+ok
+ok
+ok
+ok
diff --git a/test/symmetry.test b/test/symmetry.test
index e6a6b654..52ba6388 100755
--- a/test/symmetry.test
+++ b/test/symmetry.test
@@ -48,9 +48,11 @@ pgmramp -ell 63 63 > ${circle_pgm}
 
 rm ${circle_pgm}
 
-# Test 3.
+# Test 3.  Should print "ok" 7 times
 gauss_pgm=${tmpdir}/gauss.pgm
-pamgauss -sigma=0.1 -tupletype=GRAYSCALE 25 25 > ${gauss_pgm}
+for size in 3 4 5 8 13 21 25
+do
+pamgauss -sigma=0.1 -tupletype=GRAYSCALE $size $size > ${gauss_pgm}
 
 ( for op in -null -tb -lr -r90
     do pamflip $op ${gauss_pgm} | cksum
@@ -58,6 +60,8 @@ pamgauss -sigma=0.1 -tupletype=GRAYSCALE 25 25 > ${gauss_pgm}
   awk '$1==4 && $3>0 { print "ok"; exit }; { print }'
 
 rm ${gauss_pgm}
+done
+
 
 # Test 4.
 kernel_pgm=${tmpdir}/kernel.pgm
diff --git a/test/targa-roundtrip.ok b/test/targa-roundtrip.ok
index 9a428195..514f970f 100644
--- a/test/targa-roundtrip.ok
+++ b/test/targa-roundtrip.ok
@@ -1,3 +1,43 @@
+Test 1
 2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
+2425386270 41
+Test 2
+1571496937 33838
+1571496937 33838
 1571496937 33838
+1571496937 33838
+1571496937 33838
+1571496937 33838
+1571496937 33838
+1571496937 33838
+1571496937 33838
+1571496937 33838
+Test 3
+1926073387 101484
+1926073387 101484
+1926073387 101484
 1926073387 101484
+Test 4
+0 0 0 : 0
+0 0 0 : 0
+0 0 0 : 0
+0 0 0 : 0
+0 0 0 : 0
+0 0 0 : 0
+Test 5
+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/targa-roundtrip.test b/test/targa-roundtrip.test
index f646b8c1..79b01b61 100755
--- a/test/targa-roundtrip.test
+++ b/test/targa-roundtrip.test
@@ -1,18 +1,80 @@
 #! /bin/bash
 # This script tests: pamtotga tgatoppm
-# Also requires: ppmtopgm pgmtopbm pamchannel
+# Also requires: ppmtopgm pgmtopbm pamchannel pnmquant pamdepth
 
+tmpdir=${tmpdir:-/tmp}
 
-#Test 1: Should print 2425386270 41, cksum of testgrid.pbm
+#Test 1: Should print 2425386270 41, cksum of testgrid.pbm, ten times
 
-pamtotga -mono testgrid.pbm | \
-  tgatoppm | ppmtopgm | \
-  pgmtopbm -threshold -val 0.5 | cksum
+echo "Test 1"
 
-#Test 2:  Should produce 1571496937 33838, cksum of testimg.red
-pamchannel -infile=testimg.ppm -tupletype="GRAYSCALE" 0 | \
-  pamtotga -cmap | tgatoppm | ppmtopgm | cksum
+for mode in "-mono" "-mono -norle" \
+            "-cmap" "-cmap -norle" "-cmap16" "-cmap16 -norle" \
+            "-rgb"  "-rgb  -norle" "" "-norle"
+  do
+  pamtotga ${mode} testgrid.pbm | \
+    tgatoppm | ppmtopgm | \
+    pgmtopbm -threshold -val 0.5 | cksum
+  done
 
-#Test 3: Should print 1926073387 101484, cksum of testimg.ppm
+#Test 2:  Should produce 1571496937 33838, cksum of testimg.red, ten times
 
-pamtotga -rgb testimg.ppm | tgatoppm | cksum
+echo "Test 2"
+
+test_pam=${tmpdir}/testimg.pgm
+pamchannel -infile=testimg.ppm -tupletype="GRAYSCALE" 0 > ${test_pam}
+
+for mode in "-mono" "-mono -norle" \
+            "-cmap" "-cmap -norle" "-cmap16" "-cmap16 -norle" \
+            "-rgb"  "-rgb  -norle" "" "-norle"
+  do
+  pamtotga ${mode} ${test_pam} | tgatoppm | ppmtopgm | cksum
+  done
+
+rm ${test_pam}
+
+
+#Test 3: Should print 1926073387 101484, cksum of testimg.ppm, four times
+
+echo "Test 3"
+
+for mode in "-rgb" "-rgb -norle" "" "-norle"
+  do
+  pamtotga ${mode} testimg.ppm | tgatoppm | cksum
+  done
+
+#Test 4: Should print 0 six times
+
+echo "Test 4"
+
+test256_ppm=${tmpdir}/test256.ppm
+pnmquant 256 testimg.ppm > ${test256_ppm} || echo "pnmquant failed"
+# test image may have less than 256 colors
+
+for mode in "-rgb" "-rgb -norle" "-cmap" "-cmap -norle" "" "-norle"
+  do
+  pamtotga ${mode} ${test256_ppm} | tgatoppm | cmp -s - ${test256_ppm}
+  echo ${PIPESTATUS[@]} ":" $?
+done
+
+
+#Test 5: Should print 0 eight times
+
+echo "Test 5"
+
+test256_31_ppm=${tmpdir}/test256-31.ppm
+pamdepth 31 ${test256_ppm} > ${test256_31_ppm} || echo "pamdepth failed"
+rm ${test256_ppm}
+
+for mode in "-cmap16" "-cmap16 -norle"
+  do pamtotga ${mode} ${test256_31_ppm} | tgatoppm | cmp -s - ${test256_31_ppm}
+  echo ${PIPESTATUS[@]} ":" $?
+  done
+
+for mode in  "-rgb" "-rgb -norle" "-cmap" "-cmap -norle" "" "-norle"
+  do pamtotga ${mode} ${test256_31_ppm} | tgatoppm | \
+     pamdepth 31 | cmp -s - ${test256_31_ppm}
+  echo ${PIPESTATUS[@]} ":" $?
+  done
+
+rm ${test256_31_ppm}
diff --git a/test/testrandom.c b/test/testrandom.c
index 43414926..5b85a6e8 100644
--- a/test/testrandom.c
+++ b/test/testrandom.c
@@ -8,6 +8,7 @@
 
   Options:
     -q : quiet mode
+    -x : print exit code to stdout, otherwise equivalent to quite mode
     -v : verbose mode : Use to generate values for new table 
 
   This is a self-contained program which does not require any libnetpbm
@@ -16,11 +17,17 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#define  bool  int
+#define  TRUE  1
+#define  FALSE 0
+
+
 /* Exit values */
 #define EXIT_ERROR 1
 #define EXIT_UNKNOWN 80
 #define ISO_GLIBC 81
-/* 82-90: reserved */
+#define MAC_OS    82
+/* 83-90: reserved */
 
 typedef enum {QUIET=0, NORMAL=1, VERBOSE=2} VerbosityLevel;
 
@@ -41,11 +48,15 @@ static struct {
         /* Sample values returned from our tests */
     const char * const name;
         /* Name for this rand() function */
-} rTable[2] = {
+} rTable[3] = {
     { ISO_GLIBC,  /* glibc rand() */ 
       0x7fffffff, /* 31 bits */ 
       { 217313873, 969144303, 1757357552, 1096307597, 818311031 },
       "ISO C glibc rand() or equivalent" },
+    { MAC_OS,
+      0x7fffffff, /* 31 bits */
+      { 63715337, 1416812753, 1073261735, 1594828992, 1547470337 },
+      "MAC OS c library rand() or equivalent" },
     
     /* Insert additional entries here */
     
@@ -59,15 +70,19 @@ static struct {
 static void
 parseCommandLine(int              const argc,
                  const char *     const argv[],
-                 VerbosityLevel * const verbosityP) {
+                 VerbosityLevel * const verbosityP,
+                 bool *           const printExitCodeP) {
 
-    *verbosityP = NORMAL; /* Initial value */
+    *verbosityP = NORMAL;       /* Initial value */
+    *printExitCodeP = FALSE;    /* 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;
+            case 'x' : *verbosityP = QUIET  ;
+                       *printExitCodeP = TRUE ; break;
             default :  fprintf (stderr,
                                 "Error: Unrecognized argument: %s\n", argv[1]);
                 exit (EXIT_ERROR);
@@ -88,8 +103,9 @@ main(int const argc, const char * const argv[]) {
     unsigned int i;
     unsigned int res[5];
     VerbosityLevel verbosity;
+    bool printExitCode;
 
-    parseCommandLine(argc, argv, &verbosity);
+    parseCommandLine(argc, argv, &verbosity, &printExitCode);
 
     if (verbosity == VERBOSE) {
         if (RAND_MAX > 0)
@@ -120,12 +136,16 @@ main(int const argc, const char * const argv[]) {
                 fprintf(stderr,
                         "Random number generator is %s.\n", rTable[i].name);
 
+            if (printExitCode == TRUE)
+                printf("%03u", rTable[i].type);
             exit(rTable[i].type);
         }
     }
     /* No matches */
     if (verbosity != QUIET)   
         fprintf(stderr, "Random number generator is of unknown type.\n");
+    if (printExitCode == TRUE)
+        printf("%03u",EXIT_UNKNOWN);
     exit(EXIT_UNKNOWN);
 }
 
diff --git a/test/tiff-flate-lzw-roundtrip.ok b/test/tiff-flate-lzw-roundtrip.ok
new file mode 100644
index 00000000..4d5597cc
--- /dev/null
+++ b/test/tiff-flate-lzw-roundtrip.ok
@@ -0,0 +1,7 @@
+1079668603 10237
+1079668603 10237
+1079668603 10237
+1079668603 10237
+1079668603 10237
+1079668603 10237
+2425386270 41
diff --git a/test/tiff-flate-lzw-roundtrip.test b/test/tiff-flate-lzw-roundtrip.test
new file mode 100755
index 00000000..59d4c483
--- /dev/null
+++ b/test/tiff-flate-lzw-roundtrip.test
@@ -0,0 +1,34 @@
+#! /bin/bash
+# This script tests: pamtotiff tifftopnm
+# Also requires: ppmpat
+
+# Failure message
+## If tiff-rountrip.test succeeds and this test fails, the likely
+## cause is an old TIFF library which lacks certain compression
+## features.
+
+tmpdir=${tmpdir:-/tmp}
+
+tartan_ppm=${tmpdir}/testimg40.ppm
+
+# Make a test image with reduced colors which compresses better
+# cksum is 1079668603 10237
+
+ppmpat -tartan --color=rgb:32/0d/b7,rgb:31/58/a3,rgb:e9/5e/d4 71 48 | \
+  tee ${tartan_ppm} | cksum
+pamtotiff ${tartan_ppm} | tifftopnm | cksum
+
+# test flate compression
+pamtotiff -flate ${tartan_ppm} | tifftopnm | cksum
+
+# test adobeflate compression
+pamtotiff -adobeflate ${tartan_ppm} | tifftopnm | cksum
+
+# test LZW compression
+pamtotiff -lzw ${tartan_ppm} | tifftopnm | cksum
+pamtotiff -lzw -predictor=1 ${tartan_ppm} | tifftopnm | cksum
+
+# PBM image: test flate compression
+pamtotiff -flate testgrid.pbm | tifftopnm | cksum
+
+rm ${tartan_ppm}
diff --git a/test/tiff-roundtrip.ok b/test/tiff-roundtrip.ok
index 0e712ce7..14258de2 100644
--- a/test/tiff-roundtrip.ok
+++ b/test/tiff-roundtrip.ok
@@ -1,4 +1,38 @@
+PPM-rose
 1926073387 101484
 1926073387 101484
+1926073387 101484
+1926073387 101484
+1926073387 101484
+PPM-6
+1646869394 29498
+1646869394 29498
+1646869394 29498
+1646869394 29498
+PPM-2
+3743909631 14836
+3743909631 14836
+3743909631 14836
+3743909631 14836
+PPM-gray
+603974118 4954
+603974118 4954
+603974118 4954
+603974118 4954
+603974118 4954
+603974118 4954
+PPM-bilevel-black-white
+1001018459 4954
+1001018459 4954
+1001018459 4954
+1001018459 4954
+PBM
 2425386270 41
 2425386270 41
+2425386270 41
+Extract from multi-image TIFF
+1926073387 101484
+1646869394 29498
+3743909631 14836
+603974118 4954
+1001018459 4954
diff --git a/test/tiff-roundtrip.test b/test/tiff-roundtrip.test
index a99425fa..55aba278 100755
--- a/test/tiff-roundtrip.test
+++ b/test/tiff-roundtrip.test
@@ -1,23 +1,99 @@
 #! /bin/bash
 # This script tests: pamtotiff tifftopnm
-# Also requires:
-
+# Also requires: ppmpat pampick ppmtopgm
 
 # Failure message
-## Second test fails if Netpbm was built without the flate library
+## If this test fails, the cause may be a problem in the TIFF library.
+
+tmpdir=${tmpdir:-/tmp}
+tartan_ppm=${tmpdir}/tartan.ppm
+gingham_ppm=${tmpdir}/gingham.ppm
+argylecolor_ppm=${tmpdir}/argyle1.ppm
+argylegray_ppm=${tmpdir}/argyle2.ppm
+multiimage_tiff=${tmpdir}/multiimage.ppm
+
+# PPM image
+# Should print 1926073387 101484 five times
+
+echo PPM-rose
+pamtotiff testimg.ppm | tifftopnm -headerdump | cksum
+pamtotiff -truecolor testimg.ppm -output ${multiimage_tiff} && \
+  tifftopnm ${multiimage_tiff} -byrow | cksum
+pamtotiff -truecolor -packbits testimg.ppm | tifftopnm | cksum
+pamtotiff -truecolor -rowsperstrip=2 testimg.ppm | tifftopnm | cksum
+pamtotiff -truecolor -lsb2msb  testimg.ppm | \
+  tifftopnm -respectfillorder | cksum
+
+# PPM image with six colors
+# Should print 1646869394 29498 four times
+
+echo PPM-6
+ppmpat -tartan -color=rgb:0/8/8,rgb:8/f/8,rgb:f/f/0 108 91 | \
+  tee ${tartan_ppm} | cksum
+pamtotiff -truecolor ${tartan_ppm} | tifftopnm | cksum
+pamtotiff ${tartan_ppm} | tifftopnm -respectfillorder | cksum
+pamtotiff ${tartan_ppm} -append -output ${multiimage_tiff} && \
+  tifftopnm ${multiimage_tiff} | pampick 1 | cksum
+
+
+# PPM image with two colors
+# Should print 3743909631 14836 four times
+
+echo PPM-2
+ppmpat -argyle2 -color=rgb:0/0/0,rgb:f/8/f,rgb:0/0/0 61 81 | \
+  tee ${argylecolor_ppm} | cksum
+pamtotiff -indexbits=2 ${argylecolor_ppm} -append -output ${multiimage_tiff} && \
+  tifftopnm ${multiimage_tiff} | pampick 2 | cksum
+pamtotiff -indexbits=1 ${argylecolor_ppm} | tifftopnm | cksum
+pamtotiff -indexbits=1 -g4 ${argylecolor_ppm} | tifftopnm | cksum
+
+# PPM image with six shades of gray
+# pamtotiff detects the absence of colors and produces a grayscale
+# tiff image unless -color is specified
+# Pipeline output is PGM unless -color is specified
+# Should print 603974118 4954 six times
+
+echo PPM-gray
+ppmpat -gingham3 -color=rgb:0/0/0,rgb:ff/ff/ff,rgb:0/0/0 61 81 | \
+  tee ${gingham_ppm} | ppmtopgm | cksum
+pamtotiff -color ${gingham_ppm} | tifftopnm | cksum
+pamtotiff -indexbits=1,2 -mb -fill ${gingham_ppm} | tifftopnm | cksum
+pamtotiff -indexbits=1,2 -mw ${gingham_ppm} | tifftopnm | cksum
+pamtotiff -indexbits=1,2,4,8 -mb ${gingham_ppm} | tifftopnm | cksum
+pamtotiff -mb ${gingham_ppm} -append -output ${multiimage_tiff} && \
+  tifftopnm ${multiimage_tiff} | pampick 3 | cksum
+
+# PPM image with two shades of gray (bilevel)
+# pamtotiff detects the absence of colors and produces a grayscale
+# tiff image unless -color is specified
+# Pipeline output is PGM unless -color is specified
+# Should print 1001018459 4954 four times
+
+echo PPM-bilevel-black-white
+ppmpat -argyle2 -color=rgb:0/0/0,rgb:ff/ff/ff,rgb:0/0/0 61 81 | \
+  tee ${argylegray_ppm} | ppmtopgm | cksum
+pamtotiff -color -fill ${argylegray_ppm} | tifftopnm | cksum
+pamtotiff -indexbits=1 -mw ${argylegray_ppm} | tifftopnm | cksum
+pamtotiff -mb ${argylegray_ppm} -append -output ${multiimage_tiff} && \
+  tifftopnm ${multiimage_tiff} | pampick 4 | cksum
 
-pamtotiff testimg.ppm 1<>${tmpdir}/test1.tiff &&
-  tifftopnm ${tmpdir}/test1.tiff | cksum
+# PBM image
+# Should print 2425386270 41 three times
 
-# test flate compression
-pamtotiff -flate testimg.ppm 1<>${tmpdir}/test2.tiff &&
-  tifftopnm ${tmpdir}/test2.tiff | cksum
+echo PBM
+pamtotiff testgrid.pbm | tifftopnm | cksum
+pamtotiff -g3 -fill testgrid.pbm | tifftopnm | cksum
+pamtotiff -g4 -mb testgrid.pbm -append -output ${multiimage_tiff} && \
+  tifftopnm ${multiimage_tiff} | pampick 5 | cksum
 
-pamtotiff testgrid.pbm 1<>${tmpdir}/test3.tiff &&
-  tifftopnm ${tmpdir}/test3.tiff | cksum
+# Extract individual images from multi-image tiff file
 
-# test G4 compression
-pamtotiff -g4 testgrid.pbm 1<>${tmpdir}/test4.tiff &&
-  tifftopnm ${tmpdir}/test4.tiff | cksum
+echo "Extract from multi-image TIFF"
+tifftopnm ${multiimage_tiff} | pampick 0 | cksum
+tifftopnm ${multiimage_tiff} | pampick 1 | cksum
+tifftopnm ${multiimage_tiff} | pampick 2 | cksum
+tifftopnm ${multiimage_tiff} | pampick 3 | cksum
+tifftopnm ${multiimage_tiff} | pampick 4 | cksum
 
-rm ${tmpdir}/test[1234].tiff
+rm ${tartan_ppm} ${gingham_ppm} ${argylecolor_ppm} \
+   ${argylegray_ppm} ${multiimage_tiff}
\ No newline at end of file
diff --git a/test/tiffcmyk-roundtrip.ok b/test/tiffcmyk-roundtrip.ok
new file mode 100644
index 00000000..05c0b188
--- /dev/null
+++ b/test/tiffcmyk-roundtrip.ok
@@ -0,0 +1,5 @@
+match
+match
+match
+match
+match
diff --git a/test/tiffcmyk-roundtrip.test b/test/tiffcmyk-roundtrip.test
new file mode 100755
index 00000000..133c81b7
--- /dev/null
+++ b/test/tiffcmyk-roundtrip.test
@@ -0,0 +1,42 @@
+#! /bin/bash
+# This script tests: pnmtotiffcmyk tifftopnm
+# Also requires: pnmpsnr
+
+# Failure message
+## If this test fails, the cause may be a problem in the TIFF library.
+
+tmpdir=${tmpdir:-/tmp}
+tartan_ppm=${tmpdir}/tartan.ppm
+output_ppm=${tmpdir}/output.ppm
+output_tiff=${tmpdir}/output.tiff
+
+# Note that color images cannot be tested by the roundtrip method
+# because of approximations incurred by converting from RGB to CMYK
+
+pnmtotiffcmyk testimg.ppm > ${output_tiff} && \
+  tifftopnm -headerdump -byrow ${output_tiff} | \
+  pnmpsnr -target1=45.0 -target2=59.5 -target3=56.5 - testimg.ppm
+
+
+# Note that "-rowsperstrip=1" does not work
+pnmtotiffcmyk -rowsperstrip 1 -lsb2msb testimg.ppm > ${output_tiff} && \
+  tifftopnm -respectfillorder -byrow  ${output_tiff} | \
+  pnmpsnr -target1=45.0 -target2=59.5 -target3=56.5 - testimg.ppm
+
+
+pnmtotiffcmyk -packbits testimg.ppm > ${output_tiff} && \
+  tifftopnm -byrow ${output_tiff} | \
+  pnmpsnr -target1=45.0 -target2=59.5 -target3=56.5 - testimg.ppm
+
+
+pnmtotiffcmyk -lzw testimg.ppm > ${output_tiff} && \
+  tifftopnm -byrow ${output_tiff} | \
+  pnmpsnr -target1=45.0 -target2=59.5 -target3=56.5 - testimg.ppm
+
+
+pnmtotiffcmyk -lzw -predictor 2 testimg.ppm > ${output_tiff} && \
+  tifftopnm -respectfillorder -byrow ${output_tiff} | \
+  pnmpsnr -target1=45.0 -target2=59.5 -target=56.5 - testimg.ppm
+
+
+rm ${output_tiff}
diff --git a/test/utahrle-roundtrip.test b/test/utahrle-roundtrip.test
index 982b72a9..17f1a6c4 100755
--- a/test/utahrle-roundtrip.test
+++ b/test/utahrle-roundtrip.test
@@ -2,7 +2,6 @@
 # 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
diff --git a/version.mk b/version.mk
index 2dbfb7ff..f1ae314e 100644
--- a/version.mk
+++ b/version.mk
@@ -1,3 +1,3 @@
 NETPBM_MAJOR_RELEASE = 10
-NETPBM_MINOR_RELEASE = 73
-NETPBM_POINT_RELEASE = 28
+NETPBM_MINOR_RELEASE = 86
+NETPBM_POINT_RELEASE = 4