about summary refs log tree commit diff
diff options
context:
space:
mode:
authorgiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2016-03-27 01:38:28 +0000
committergiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2016-03-27 01:38:28 +0000
commit367c9cb514c9da766488b9bdb218a18e31cb7624 (patch)
treef9e343be94161a4837f0f1c1d072a35538ae0f63
parent6e88e3326cb0c7f7975b56189278cab3f84ba1bd (diff)
downloadnetpbm-mirror-367c9cb514c9da766488b9bdb218a18e31cb7624.tar.gz
netpbm-mirror-367c9cb514c9da766488b9bdb218a18e31cb7624.tar.xz
netpbm-mirror-367c9cb514c9da766488b9bdb218a18e31cb7624.zip
Promote Stable (10.47) to Super Stable
git-svn-id: http://svn.code.sf.net/p/netpbm/code/super_stable@2691 9d0c8265-081b-0410-96cb-a4ca84ce46f8
-rw-r--r--GNUmakefile97
-rw-r--r--Makefile3
-rw-r--r--Makefile.srcdir1
-rw-r--r--Makefile.version3
-rw-r--r--README206
-rw-r--r--analyzer/Makefile10
-rw-r--r--analyzer/pamfile.c1
-rw-r--r--analyzer/pamsharpmap.c1
-rw-r--r--analyzer/pamsharpness.c1
-rw-r--r--analyzer/pamslice.c1
-rw-r--r--analyzer/pamsumm.c1
-rw-r--r--analyzer/pamtilt.c1
-rw-r--r--analyzer/pbmminkowski.c199
-rw-r--r--analyzer/pgmhist.c225
-rw-r--r--analyzer/pnmhistmap.c85
-rw-r--r--analyzer/pnmpsnr.c81
-rw-r--r--analyzer/ppmhist.c33
-rw-r--r--buildtools/Makefile12
-rw-r--r--buildtools/README.pkg10
-rwxr-xr-xbuildtools/configure.pl663
-rw-r--r--buildtools/debian/README122
-rwxr-xr-xbuildtools/debian/mkdeb541
-rwxr-xr-xbuildtools/debian/postinst5
-rwxr-xr-xbuildtools/debian/postrm5
-rw-r--r--buildtools/endiangen.c6
-rwxr-xr-xbuildtools/installnetpbm.pl14
-rwxr-xr-xbuildtools/installosf2
-rw-r--r--buildtools/libopt.c10
-rwxr-xr-xbuildtools/makeman50
-rwxr-xr-xbuildtools/makepointerman10
-rw-r--r--buildtools/manpage.mk (renamed from buildtools/Makefile.manpage)100
-rw-r--r--common.mk (renamed from Makefile.common)145
-rw-r--r--config.mk.in (renamed from Makefile.config.in)22
-rw-r--r--converter/Makefile4
-rw-r--r--converter/other/Makefile58
-rwxr-xr-xconverter/other/anytopnm46
-rw-r--r--converter/other/bmepsoe.c217
-rw-r--r--converter/other/bmptopnm.c114
-rw-r--r--converter/other/cameratopam/Makefile7
-rw-r--r--converter/other/cameratopam/cameratopam.c22
-rw-r--r--converter/other/dithers.h91
-rw-r--r--converter/other/fiasco/Makefile6
-rw-r--r--converter/other/fiasco/codec/Makefile6
-rw-r--r--converter/other/fiasco/input/Makefile8
-rw-r--r--converter/other/fiasco/lib/Makefile6
-rw-r--r--converter/other/fiasco/lib/misc.c32
-rw-r--r--converter/other/fiasco/output/Makefile11
-rw-r--r--converter/other/fiasco/params.c3
-rw-r--r--converter/other/fitstopnm.c889
-rw-r--r--converter/other/giftopnm.c731
-rw-r--r--converter/other/hdifftopam.c5
-rw-r--r--converter/other/infotopam.c1
-rw-r--r--converter/other/jbig/Makefile8
-rw-r--r--converter/other/jpeg2000/Makefile8
-rw-r--r--converter/other/jpeg2000/jpeg2ktopam.c1
-rw-r--r--converter/other/jpeg2000/libjasper/Makefile4
-rw-r--r--converter/other/jpeg2000/libjasper/base/Makefile4
-rw-r--r--converter/other/jpeg2000/libjasper/base/jas_stream.c71
-rw-r--r--converter/other/jpeg2000/libjasper/common.mk (renamed from converter/other/jpeg2000/libjasper/Makefile.common)7
-rw-r--r--converter/other/jpeg2000/libjasper/include/jasper/jas_types.h.orig228
-rw-r--r--converter/other/jpeg2000/libjasper/jp2/Makefile4
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/Makefile4
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_math.c128
-rw-r--r--converter/other/jpeg2000/libjasper/jpc/jpc_qmfb.c34
-rw-r--r--converter/other/jpeg2000/pamtojpeg2k.c1
-rw-r--r--converter/other/jpegdatasource.c36
-rw-r--r--converter/other/jpegdatasource.h3
-rw-r--r--converter/other/jpegtopnm.c153
-rw-r--r--converter/other/pamtodjvurle.c3
-rw-r--r--converter/other/pamtofits.c27
-rw-r--r--converter/other/pamtogif.c1977
-rw-r--r--converter/other/pamtohdiff.c1
-rw-r--r--converter/other/pamtohtmltbl.c106
-rw-r--r--converter/other/pamtompfont.c182
-rw-r--r--converter/other/pamtooctaveimg.c241
-rw-r--r--converter/other/pamtopam.c57
-rw-r--r--converter/other/pamtopfm.c1
-rw-r--r--converter/other/pamtopnm.c60
-rw-r--r--converter/other/pamtosvg/Makefile4
-rw-r--r--converter/other/pamtosvg/curve.c197
-rw-r--r--converter/other/pamtosvg/curve.h46
-rw-r--r--converter/other/pamtosvg/fit.c1870
-rw-r--r--converter/other/pamtosvg/pamtosvg.c4
-rw-r--r--converter/other/pamtosvg/pamtosvg.test6
-rw-r--r--converter/other/pamtosvg/pxl-outline.c45
-rw-r--r--converter/other/pamtosvg/spline.c10
-rw-r--r--converter/other/pamtosvg/spline.h1
-rw-r--r--converter/other/pamtosvg/vector.c282
-rw-r--r--converter/other/pamtosvg/vector.h70
-rw-r--r--converter/other/pamtotga.c3
-rw-r--r--converter/other/pamtotiff.c13
-rw-r--r--converter/other/pamtouil.c64
-rw-r--r--converter/other/pamtoxvmini.c3
-rw-r--r--converter/other/pfmtopam.c1
-rw-r--r--converter/other/pgmtopbm.c50
-rw-r--r--converter/other/pgmtoppm.c345
-rw-r--r--converter/other/pngtopam.c1281
-rw-r--r--converter/other/pngtopnm.c965
-rw-r--r--converter/other/pnmtoddif.c268
-rw-r--r--converter/other/pnmtojpeg.c726
-rw-r--r--converter/other/pnmtopalm/Makefile9
-rw-r--r--converter/other/pnmtopalm/gen_palm_colormap.c48
-rw-r--r--converter/other/pnmtopalm/palmcolor8.map458
-rw-r--r--converter/other/pnmtopalm/palmtopnm.c1
-rw-r--r--converter/other/pnmtopalm/pnmtopalm.c1
-rw-r--r--converter/other/pnmtopclxl.c5
-rw-r--r--converter/other/pnmtopng.c132
-rw-r--r--converter/other/pnmtops.c76
-rw-r--r--converter/other/pnmtorast.c139
-rw-r--r--converter/other/pnmtorle.c198
-rw-r--r--converter/other/pnmtotiffcmyk.c6
-rw-r--r--converter/other/pnmtoxwd.c4
-rw-r--r--converter/other/pstopnm.c150
-rw-r--r--converter/other/rast.h30
-rw-r--r--converter/other/rlatopam.c1
-rw-r--r--converter/other/rletopnm.c5
-rw-r--r--converter/other/svgtopam.c34
-rw-r--r--converter/other/tiff.c6
-rw-r--r--converter/other/tifftopnm.c891
-rw-r--r--converter/other/x11wd.h8
-rw-r--r--converter/other/xwdtopnm.c537
-rw-r--r--converter/pbm/Makefile8
-rw-r--r--converter/pbm/atktopbm.c2
-rw-r--r--converter/pbm/cmuwmtopbm.c181
-rw-r--r--converter/pbm/g3topbm.c147
-rw-r--r--converter/pbm/icontopbm.c12
-rw-r--r--converter/pbm/mgrtopbm.c171
-rw-r--r--converter/pbm/mrftopbm.c66
-rw-r--r--converter/pbm/pbmto4425.c2
-rw-r--r--converter/pbm/pbmtocmuwm.c167
-rw-r--r--converter/pbm/pbmtodjvurle.c166
-rw-r--r--converter/pbm/pbmtoepsi.c1
-rw-r--r--converter/pbm/pbmtoepson.c3
-rw-r--r--converter/pbm/pbmtoescp2.c1
-rw-r--r--converter/pbm/pbmtog3.c34
-rw-r--r--converter/pbm/pbmtogem.c6
-rw-r--r--converter/pbm/pbmtogo.c226
-rw-r--r--converter/pbm/pbmtoibm23xx.c1
-rw-r--r--converter/pbm/pbmtoicon.c252
-rw-r--r--converter/pbm/pbmtolj.c1
-rw-r--r--converter/pbm/pbmtoln03.c5
-rw-r--r--converter/pbm/pbmtolps.c2
-rw-r--r--converter/pbm/pbmtomgr.c209
-rw-r--r--converter/pbm/pbmtomrf.c162
-rw-r--r--converter/pbm/pbmtonokia.c686
-rw-r--r--converter/pbm/pbmtopi3.c6
-rw-r--r--converter/pbm/pbmtopk.c2
-rw-r--r--converter/pbm/pbmtoppa/Makefile4
-rw-r--r--converter/pbm/pbmtopsg3.c1
-rw-r--r--converter/pbm/pbmtoptx.c6
-rw-r--r--converter/pbm/pbmtox10bm45
-rw-r--r--converter/pbm/pbmtox10bm.c120
-rw-r--r--converter/pbm/pbmtoxbm.c410
-rw-r--r--converter/pbm/pbmtoybm.c6
-rw-r--r--converter/pbm/pbmtozinc.c2
-rw-r--r--converter/pbm/pktopbm.c2
-rw-r--r--converter/pbm/thinkjettopbm.l1
-rw-r--r--converter/pbm/xbmtopbm.c568
-rw-r--r--converter/pgm/Makefile4
-rw-r--r--converter/pgm/asciitopgm.c2
-rw-r--r--converter/pgm/hipstopgm.c2
-rw-r--r--converter/pgm/lispmtopgm.c2
-rw-r--r--converter/pgm/pgmtofs.c6
-rw-r--r--converter/pgm/pgmtolispm.c6
-rw-r--r--converter/pgm/pgmtopgm.c4
-rw-r--r--converter/pgm/rawtopgm.c31
-rw-r--r--converter/pgm/sbigtopgm.c2
-rw-r--r--converter/ppm/411toppm.c1
-rw-r--r--converter/ppm/Makefile4
-rw-r--r--converter/ppm/eyuvtoppm.c2
-rw-r--r--converter/ppm/hpcdtoppm/Makefile6
-rw-r--r--converter/ppm/ilbmtoppm.c464
-rw-r--r--converter/ppm/mitsu.h1
-rw-r--r--converter/ppm/pcxtoppm.c1
-rw-r--r--converter/ppm/picttoppm.c1436
-rw-r--r--converter/ppm/ppmtoarbtxt.c493
-rw-r--r--converter/ppm/ppmtobmp.c512
-rw-r--r--converter/ppm/ppmtogif.c1571
-rw-r--r--converter/ppm/ppmtoilbm.c66
-rw-r--r--converter/ppm/ppmtoleaf.c259
-rw-r--r--converter/ppm/ppmtomitsu.c897
-rw-r--r--converter/ppm/ppmtomitsu.test12
-rw-r--r--converter/ppm/ppmtompeg/Makefile33
-rw-r--r--converter/ppm/ppmtompeg/bframe.c1
-rw-r--r--converter/ppm/ppmtompeg/bsearch.c1
-rw-r--r--converter/ppm/ppmtompeg/combine.c6
-rw-r--r--converter/ppm/ppmtompeg/file.c351
-rw-r--r--converter/ppm/ppmtompeg/frames.c58
-rw-r--r--converter/ppm/ppmtompeg/gethostname_win32.c404
-rw-r--r--converter/ppm/ppmtompeg/headers/bitio.h4
-rw-r--r--converter/ppm/ppmtompeg/headers/block.h44
-rw-r--r--converter/ppm/ppmtompeg/headers/byteorder.h2
-rw-r--r--converter/ppm/ppmtompeg/headers/frame.h16
-rw-r--r--converter/ppm/ppmtompeg/headers/frames.h170
-rw-r--r--converter/ppm/ppmtompeg/headers/iframe.h118
-rw-r--r--converter/ppm/ppmtompeg/headers/motion_search.h3
-rw-r--r--converter/ppm/ppmtompeg/headers/mpeg.h5
-rw-r--r--converter/ppm/ppmtompeg/headers/mproto.h8
-rw-r--r--converter/ppm/ppmtompeg/headers/param.h97
-rw-r--r--converter/ppm/ppmtompeg/headers/prototypes.h9
-rw-r--r--converter/ppm/ppmtompeg/headers/subsample.h39
-rw-r--r--converter/ppm/ppmtompeg/huff.h34
-rw-r--r--converter/ppm/ppmtompeg/iframe.c225
-rw-r--r--converter/ppm/ppmtompeg/jpeg.c28
-rw-r--r--converter/ppm/ppmtompeg/mheaders.c712
-rw-r--r--converter/ppm/ppmtompeg/mpeg.c120
-rw-r--r--converter/ppm/ppmtompeg/noparallel.c2
-rw-r--r--converter/ppm/ppmtompeg/parallel.c10
-rw-r--r--converter/ppm/ppmtompeg/param.c143
-rw-r--r--converter/ppm/ppmtompeg/pframe.c2
-rw-r--r--converter/ppm/ppmtompeg/ppmtompeg.c160
-rw-r--r--converter/ppm/ppmtompeg/psearch.c2
-rw-r--r--converter/ppm/ppmtompeg/rate.c1
-rw-r--r--converter/ppm/ppmtompeg/specifics.c45
-rw-r--r--converter/ppm/ppmtompeg/subsample.c1
-rw-r--r--converter/ppm/ppmtopcx.c1
-rw-r--r--converter/ppm/ppmtopict.c11
-rw-r--r--converter/ppm/ppmtopj.c6
-rw-r--r--converter/ppm/ppmtopjxl.c570
-rw-r--r--converter/ppm/ppmtoterm.c1
-rw-r--r--converter/ppm/ppmtowinicon.c3
-rw-r--r--converter/ppm/ppmtoxpm.c4
-rw-r--r--converter/ppm/ppmtoyuvsplit.c270
-rw-r--r--converter/ppm/rawtoppm.c418
-rw-r--r--converter/ppm/sldtoppm.c726
-rw-r--r--converter/ppm/spctoppm.c7
-rw-r--r--converter/ppm/tgatoppm.c4
-rw-r--r--converter/ppm/winicontoppm.c5
-rw-r--r--converter/ppm/ximtoppm.c4
-rw-r--r--converter/ppm/xpmtoppm.c15
-rw-r--r--converter/ppm/xvminitoppm.c10
-rw-r--r--converter/ppm/yuvsplittoppm.c2
-rw-r--r--doc/HISTORY1262
-rw-r--r--doc/INSTALL103
-rw-r--r--doc/README.CYGWIN2
-rw-r--r--doc/README.DJGPP65
-rw-r--r--doc/USERDOC35
-rw-r--r--editor/Makefile70
-rw-r--r--editor/pamaddnoise.c4
-rw-r--r--editor/pambackground.c505
-rw-r--r--editor/pamcomp.c125
-rw-r--r--editor/pamcut.c284
-rw-r--r--editor/pamdice.c30
-rw-r--r--editor/pamdither.c1
-rw-r--r--editor/pamditherbw.c239
-rw-r--r--editor/pamedge.c2
-rw-r--r--editor/pamenlarge.c243
-rw-r--r--editor/pamenlarge.test2
-rw-r--r--editor/pamflip.c1168
-rw-r--r--editor/pamfunc.c191
-rw-r--r--editor/pammasksharpen.c6
-rw-r--r--editor/pammixinterlace.c173
-rw-r--r--editor/pamperspective.c715
-rw-r--r--editor/pamscale.c6
-rw-r--r--editor/pamsistoaglyph.c421
-rwxr-xr-xeditor/pamstretch-gen8
-rw-r--r--editor/pamstretch.c2
-rw-r--r--editor/pamthreshold.c88
-rw-r--r--editor/pamundice.c702
-rw-r--r--editor/pbmclean.c2
-rw-r--r--editor/pbmpscale.c230
-rw-r--r--editor/pbmreduce.c15
-rw-r--r--editor/pgmbentley.c68
-rw-r--r--editor/pgmdeshadow.c2
-rw-r--r--editor/pgmmedian.c278
-rw-r--r--editor/pnmcat.c832
-rw-r--r--editor/pnmcomp.c1
-rw-r--r--editor/pnmconvol.c21
-rw-r--r--editor/pnmcrop.c727
-rw-r--r--editor/pnmcut.c427
-rwxr-xr-xeditor/pnmflip1
-rw-r--r--editor/pnmgamma.c20
-rw-r--r--editor/pnmhisteq.c1
-rwxr-xr-xeditor/pnmindex.sh5
-rw-r--r--editor/pnminvert.test2
-rwxr-xr-xeditor/pnmmargin66
-rw-r--r--editor/pnmmontage.c770
-rw-r--r--editor/pnmnlfilt.c122
-rw-r--r--editor/pnmnorm.c185
-rw-r--r--editor/pnmpad.c285
-rw-r--r--editor/pnmpaste.c577
-rw-r--r--editor/pnmremap.c568
-rw-r--r--editor/pnmrotate.c50
-rw-r--r--editor/pnmscale.c748
-rw-r--r--editor/pnmscalefixed.c2
-rw-r--r--editor/pnmshear.c170
-rw-r--r--editor/pnmsmooth.c39
-rw-r--r--editor/pnmstitch.c14
-rw-r--r--editor/pnmtile.c127
-rw-r--r--editor/ppm3d.c138
-rw-r--r--editor/ppmbrighten.c12
-rw-r--r--editor/ppmchange.c17
-rw-r--r--editor/ppmcolormask.c2
-rw-r--r--editor/ppmdraw.c30
-rwxr-xr-xeditor/ppmfade22
-rwxr-xr-xeditor/ppmquantall5
-rw-r--r--editor/ppmshift.c137
-rw-r--r--editor/ppmspread.c127
-rw-r--r--editor/specialty/Makefile55
-rw-r--r--editor/specialty/pamdeinterlace.c (renamed from editor/pamdeinterlace.c)1
-rw-r--r--editor/specialty/pammixinterlace.c345
-rw-r--r--editor/specialty/pamoil.c (renamed from editor/pamoil.c)0
-rw-r--r--editor/specialty/pampop9.c (renamed from editor/pampop9.c)0
-rw-r--r--editor/specialty/pbmlife.c (renamed from editor/pbmlife.c)0
-rw-r--r--editor/specialty/pgmabel.c (renamed from editor/pgmabel.c)0
-rw-r--r--editor/specialty/pgmbentley.c74
-rw-r--r--editor/specialty/pgmmorphconv.c (renamed from editor/pgmmorphconv.c)0
-rw-r--r--editor/specialty/pnmindex.c (renamed from editor/pnmindex.c)6
-rw-r--r--editor/specialty/ppm3d.c308
-rw-r--r--editor/specialty/ppmglobe.c (renamed from editor/ppmglobe.c)1
-rw-r--r--editor/specialty/ppmntsc.c (renamed from editor/ppmntsc.c)2
-rw-r--r--editor/specialty/ppmrelief.c (renamed from editor/ppmrelief.c)0
-rw-r--r--editor/specialty/ppmshift.c132
-rw-r--r--editor/specialty/ppmspread.c117
-rw-r--r--editor/specialty/ppmtv.c (renamed from editor/ppmtv.c)0
-rw-r--r--generator/Makefile4
-rw-r--r--generator/pamgradient.c13
-rw-r--r--generator/pamseq.c1
-rw-r--r--generator/pamstereogram.c10
-rw-r--r--generator/pamstereogram.test8
-rw-r--r--generator/pbmmake.c13
-rw-r--r--generator/pbmtext.c426
-rw-r--r--generator/pbmtextps.c275
-rw-r--r--generator/pbmupc.c17
-rw-r--r--generator/pgmcrater.c8
-rw-r--r--generator/pgmmake.c10
-rw-r--r--generator/pgmnoise.c182
-rw-r--r--generator/pgmramp.c12
-rw-r--r--generator/ppmcolors.c1
-rw-r--r--generator/ppmforge.c6
-rw-r--r--generator/ppmmake.c10
-rw-r--r--generator/ppmpat.c1384
-rwxr-xr-xgenerator/ppmrainbow14
-rw-r--r--generator/ppmrough.c5
-rw-r--r--lib/Makefile41
-rw-r--r--lib/bitio.h4
-rw-r--r--lib/colorname.c66
-rw-r--r--lib/colorname.h2
-rw-r--r--lib/dithers.h (renamed from editor/dithers.h)3
-rw-r--r--lib/fileio.c33
-rw-r--r--lib/fileio.h6
-rw-r--r--lib/libpam.c96
-rw-r--r--lib/libpam.h2
-rw-r--r--lib/libpamcolor.c34
-rw-r--r--lib/libpammap.c75
-rw-r--r--lib/libpamn.c285
-rw-r--r--lib/libpamread.c108
-rw-r--r--lib/libpamwrite.c31
-rw-r--r--lib/libpbm1.c120
-rw-r--r--lib/libpbm2.c146
-rw-r--r--lib/libpbm3.c300
-rw-r--r--lib/libpbmfont.c840
-rw-r--r--lib/libpgm.h9
-rw-r--r--lib/libpgm1.c221
-rw-r--r--lib/libpgm2.c30
-rw-r--r--lib/libpm.c1128
-rw-r--r--lib/libpnm1.c125
-rw-r--r--lib/libpnm2.c107
-rw-r--r--lib/libpnm3.c352
-rw-r--r--lib/libppm1.c295
-rw-r--r--lib/libppm2.c19
-rw-r--r--lib/libppmcmap.c320
-rw-r--r--lib/libppmcolor.c284
-rw-r--r--lib/libppmd.c1260
-rw-r--r--lib/libppmfloyd.c41
-rw-r--r--lib/libppmfuzzy.c91
-rw-r--r--lib/libsystem.c298
-rw-r--r--lib/libsystem_dummy.c18
-rw-r--r--lib/mkstemp.c8
-rw-r--r--lib/pam.h37
-rw-r--r--lib/pammap.h18
-rw-r--r--lib/pbm.h47
-rw-r--r--lib/pbmfont.h38
-rw-r--r--lib/pgm.h5
-rw-r--r--lib/pm.h73
-rw-r--r--lib/pm_gamma.h2
-rw-r--r--lib/pm_system.h17
-rw-r--r--lib/pmfileio.c966
-rw-r--r--lib/pnm.h32
-rw-r--r--lib/ppm.h51
-rw-r--r--lib/ppmcmap.h48
-rw-r--r--lib/ppmdraw.h186
-rw-r--r--lib/ppmfloyd.h18
-rw-r--r--lib/util/Makefile14
-rw-r--r--lib/util/bitarith.h42
-rw-r--r--lib/util/floatcode.h185
-rw-r--r--lib/util/intcode.h228
-rw-r--r--lib/util/mallocvar.h19
-rw-r--r--lib/util/nsleep.c27
-rw-r--r--lib/util/nsleep.h7
-rw-r--r--lib/util/nstring.c79
-rw-r--r--lib/util/nstring.h82
-rw-r--r--lib/util/pm_c_util.h10
-rw-r--r--lib/util/shhopt.c124
-rw-r--r--lib/util/shhopt.h19
-rw-r--r--lib/util/vasprintf.c58
-rw-r--r--lib/util/wordaccess.h31
-rw-r--r--lib/util/wordaccess_64_le.h46
-rw-r--r--lib/util/wordaccess_gcc3_be.h17
-rw-r--r--lib/util/wordaccess_gcc3_le.h54
-rw-r--r--lib/util/wordaccess_generic.h74
-rw-r--r--lib/util/wordintclz.h93
-rw-r--r--other/Makefile21
-rw-r--r--other/pamarith.c433
-rw-r--r--other/pambayer.c1
-rw-r--r--other/pamchannel.c1
-rw-r--r--other/pamdepth.c5
-rw-r--r--other/pamendian.c2
-rw-r--r--other/pamfixtrunc.c176
-rw-r--r--other/pamlookup.c1
-rw-r--r--other/pampick.c1
-rw-r--r--other/pamsplit.c2
-rw-r--r--other/pamstack.c1
-rw-r--r--other/pamsummcol.c1
-rw-r--r--other/pamx/Makefile7
-rw-r--r--other/pamx/Makefile251
-rw-r--r--other/pamx/image.c92
-rw-r--r--other/pamx/image.h5
-rw-r--r--other/pamx/pamx.c53
-rw-r--r--other/pamx/window.c4
-rw-r--r--other/pnmcolormap.c13
-rw-r--r--other/ppmsvgalib.c4
-rw-r--r--pm_config.in.h158
-rw-r--r--srcdir.mk14
-rw-r--r--urt/Makefile7
-rw-r--r--urt/rle_open_f.c418
-rw-r--r--urt/scanargs.c2
-rw-r--r--version.mk3
428 files changed, 36988 insertions, 22414 deletions
diff --git a/GNUmakefile b/GNUmakefile
index a1484786..c1c2d0a3 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -1,6 +1,6 @@
 # Makefile for Netpbm
  
-# Configuration should normally be done in the included file Makefile.config.
+# Configuration should normally be done in the included file config.mk.
 
 # Targets in this file:
 #
@@ -9,7 +9,7 @@
 #   package:      Make a package of Netpbm files ready to install
 #   
 #   The default target is either "merge" or "nonmerge", as determined by
-#   the DEFAULT_TARGET variable set by Makefile.config.
+#   the DEFAULT_TARGET variable set by config.mk.
 
 # About the "merge" target: Normally the Makefiles build separate
 # executables for each program.  However, on some systems (especially
@@ -27,7 +27,7 @@
 # 2949K to 1663K.
 
 # To build a "merge" system, just set DEFAULT_TARGET to "merge" instead
-# of "nomerge" in Makefile.config.  In that case, you should probably also
+# of "nomerge" in config.mk.  In that case, you should probably also
 # set NETPBMLIBTYPE to "unixstatic", since a shared library doesn't do you 
 # much good.
 
@@ -48,16 +48,17 @@ all package install:
 	@echo "You can get a current GNU Make via http://www.gnu.org/software"
 	@echo 
 	@echo "If upgrading is impossible, try modifying GNUMakefile and "
-	@echo "Makefile.common to replace \$(CURDIR) with \$(shell /bin/pwd) "
+	@echo "common.mk to replace \$(CURDIR) with \$(shell /bin/pwd) "
 else
 
 
-include Makefile.srcdir
+# srcdir.mk defines SRCDIR .
+include srcdir.mk
 BUILDDIR = $(CURDIR)
 SUBDIR = 
 VPATH=.:$(SRCDIR)
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
 PROG_SUBDIRS = converter analyzer editor generator other
 PRODUCT_SUBDIRS = lib $(PROG_SUBDIRS)
@@ -103,10 +104,10 @@ $(PROG_SUBDIRS:%=%/all): lib/all $(SUPPORT_SUBDIRS:%=%/all)
 OMIT_CONFIG_RULE = 1
 OMIT_VERSION_H_RULE = 1
 OMIT_INTTYPES_RULE = 1
-include $(SRCDIR)/Makefile.common
+include $(SRCDIR)/common.mk
 
-$(BUILDDIR)/Makefile.config: $(SRCDIR)/Makefile.config.in
-	$(SRCDIR)/configure $(SRCDIR)/Makefile.config.in
+$(BUILDDIR)/config.mk: $(SRCDIR)/config.mk.in
+	$(SRCDIR)/configure $(SRCDIR)/config.mk.in
 
 
 # typegen is a utility program used by the make file below.
@@ -119,10 +120,8 @@ $(TYPEGEN) $(ENDIANGEN): $(BUILDDIR)/buildtools
 	$(MAKE) -C $(dir $@) -f $(SRCDIR)/buildtools/Makefile \
 	    SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@) 
 
-DELETEIT = (rm -f $@ || false)
-
 inttypes_netpbm.h: $(TYPEGEN)
-	$(TYPEGEN) >$@ || $(DELETEIT)
+	$(TYPEGEN) >$@
 
 # We run a couple of programs on the build machine in computing the
 # contents of pm_config.h.  We need to give the user a way not to do
@@ -130,38 +129,41 @@ inttypes_netpbm.h: $(TYPEGEN)
 # cross compiling.
 
 pm_config.h: \
-  $(SRCDIR)/pm_config.in.h Makefile.config inttypes_netpbm.h $(ENDIANGEN)
-	echo '/* pm_config.h GENERATED BY A MAKE RULE */' >$@ || $(DELETEIT)
-	echo '#ifndef PM_CONFIG_H' >>$@ || $(DELETEIT)
-	echo '#define PM_CONFIG_H' >>$@ || $(DELETEIT)
+  $(SRCDIR)/pm_config.in.h config.mk inttypes_netpbm.h \
+  $(ENDIANGEN)
+# Note that this rule depends on the effect of the .DELETE_ON_ERROR
+# target we get from common.mk
+	echo '/* pm_config.h GENERATED BY A MAKE RULE */' >$@
+	echo '#ifndef PM_CONFIG_H' >>$@
+	echo '#define PM_CONFIG_H' >>$@
 ifeq ($(INTTYPES_H)x,x)
 	echo '/* Dont need to #include any inttypes.h-type thing */' >>$@
 else
   ifeq ($(INTTYPES_H),"inttypes_netpbm.h")
-	cat inttypes_netpbm.h >>$@ || $(DELETEIT)
+	cat inttypes_netpbm.h >>$@
   else
-	echo '#include $(INTTYPES_H)' >>$@ || $(DELETEIT)
+	echo '#include $(INTTYPES_H)' >>$@
   endif
 endif
 ifeq ($(HAVE_INT64),Y)
-	echo "#define HAVE_INT64 1" >>$@ || $(DELETEIT)
+	echo "#define HAVE_INT64 1" >>$@
 else
-	echo "#define HAVE_INT64 0" >>$@ || $(DELETEIT)
+	echo "#define HAVE_INT64 0" >>$@
 endif	
-	echo '/* pm_config.h.in FOLLOWS ... */' >>$@ || $(DELETEIT)
-	cat $(SRCDIR)/pm_config.in.h >>$@ || $(DELETEIT)
-	$(ENDIANGEN) >>$@ || $(DELETEIT)
-	echo '#endif' >>$@ || $(DELETEIT)
+	echo '/* pm_config.h.in FOLLOWS ... */' >>$@
+	cat $(SRCDIR)/pm_config.in.h >>$@
+	$(ENDIANGEN) >>$@
+	echo '#endif' >>$@
 
 
 MAJOR := $(NETPBM_MAJOR_RELEASE)
 MINOR := $(NETPBM_MINOR_RELEASE)
 POINT := $(NETPBM_POINT_RELEASE)
-version.h:
+version.h: $(SRCDIR)/version.mk
 	@rm -f $@
-	@echo "/* Generated by make file rule */" >$@
+	@echo "/* Generated by make file rule */" >>$@
 	@echo "#define NETPBM_VERSION" \
-	  \"Netpbm $(MAJOR).$(MINOR).$(POINT)"\"" >$@
+	  \"Netpbm $(MAJOR).$(MINOR).$(POINT)"\"" >>$@
 
 
 .PHONY: install
@@ -186,7 +188,7 @@ package: build_complete package_build advise_installnetpbm
 
 build_complete:
 # The regular build creates this file as its last act, so if it doesn't exist,
-# that means either the user skipping the build step, or the build failed.
+# that means either the user skipped the build step, or the build failed.
 	@echo "You must build Netpbm before you can package Netpbm. "
 	@echo "The usual way to do this is to type 'make' with no arguments."
 	@echo "If you did that, then the build apparently failed.  There "
@@ -265,10 +267,10 @@ ifneq ($(X11LIB),NONE)
   MERGELIBS += $(X11LIB)
 endif
 
-ifeq ($(shell libpng-config --version),)
+ifeq ($(shell libpng$(PNGVER)-config --version),)
   PNGLD = $(shell $(LIBOPT) $(LIBOPTR) $(PNGLIB) $(ZLIB))
 else
-  PNGLD = $(shell libpng-config --ldflags)
+  PNGLD = $(shell libpng$(PNGVER)-config --ldflags)
 endif
 
 ifeq ($(shell xml2-config --version),)
@@ -342,9 +344,9 @@ install.lib:
 endif
 
 .PHONY: install.manweb
-install.manweb: $(PKGDIR)/man/web/netpbm.url $(PKGDIR)/bin/doc.url
+install.manweb: $(PKGDIR)/$(PKGMANDIR)/web/netpbm.url $(PKGDIR)/bin/doc.url
 
-$(PKGDIR)/man/web/netpbm.url: $(PKGDIR)/man/web
+$(PKGDIR)/$(PKGMANDIR)/web/netpbm.url: $(PKGDIR)/$(PKGMANDIR)/web
 	echo "$(NETPBM_DOCURL)" > $@
 	chmod $(INSTALL_PERM_MAN) $@
 
@@ -360,11 +362,14 @@ $(PKGDIR)/bin/doc.url: $(PKGDIR)/bin
 install-dev: install.hdr install.staticlib install.lib install.sharedlibstub
 
 .PHONY: install.hdr
-install.hdr: $(PKGDIR)/include
-	$(MAKE) -C lib -f $(SRCDIR)/lib/Makefile \
-	    SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) install.hdr
+install.hdr: lib/install.hdr $(PKGDIR)/include/netpbm
 	$(INSTALL) -c -m $(INSTALL_PERM_HDR) \
-	    $(BUILDDIR)/pm_config.h $(PKGDIR)/include
+	    $(BUILDDIR)/pm_config.h $(PKGDIR)/include/netpbm
+
+.PHONY: lib/install.hdr
+lib/install.hdr:
+	$(MAKE) -C $(dir $@) -f $(SRCDIR)/lib/Makefile \
+	    SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@)
 
 ifeq ($(STATICLIB_TOO),y)
 BUILD_STATIC = y
@@ -388,27 +393,29 @@ install.sharedlibstub:
 	$(MAKE) -C lib -f $(SRCDIR)/lib/Makefile \
 	    SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) install.sharedlibstub 
 
-# Make the 'deb' target after making 'package'.  It generates a .deb
-# file in the current directory.
-.PHONY: deb
-deb:
-	buildtools/debian/mkdeb --buildtools=buildtools --pkgdir=$(PKGDIR)
-
 clean: localclean
 
 .PHONY: localclean
 localclean:
 	rm -f netpbm build_started build_complete
 	rm -f pm_config.h inttypes_netpbm.h version.h
-	rm -f *.deb
 
-# Note that removing Makefile.config must be the last thing we do,
+# Note that removing config.mk must be the last thing we do,
 # because no other makes will work after that is done.
 distclean: localdistclean
 .PHONY: localdistclean
 localdistclean: localclean
 	-rm -f `find -type l`
-	-rm -f Makefile.config
+	-rm -f TAGS
+	-rm -f config.mk
+
+# 'tags' generates/updates an Emacs tags file, anmed TAGS, in the current
+# directory.  Use with Emacs command 'find-tag'.
+
+.PHONY: tags
+tags:
+	find . -name "*.c" -o -name "*.h" -o -name "*.cpp" -o -name "*.hpp" | \
+	  etags -
 
 # The following endif is for the else block that contains virtually the
 # whole file, for the test of the existence of CURDIR.
diff --git a/Makefile b/Makefile
index 2227eb5c..7f946702 100644
--- a/Makefile
+++ b/Makefile
@@ -7,8 +7,11 @@
 # GNU Make uses a make file named "GNUmakefile" in preference to "Makefile"
 # if it exists.  Netpbm is shipped with a "GNUmakefile".
 
+default: all
+
 all merge install clean dep:
 	@echo "You must use GNU Make to build Netpbm.  You are running some "
 	@echo "other Make.  GNU Make may be installed on your system with "
 	@echo "the name 'gmake'.  If not, see http://www.gnu.org/software ."
 	@echo
+	false
diff --git a/Makefile.srcdir b/Makefile.srcdir
deleted file mode 100644
index 6a21d9db..00000000
--- a/Makefile.srcdir
+++ /dev/null
@@ -1 +0,0 @@
-SRCDIR = $(CURDIR)
diff --git a/Makefile.version b/Makefile.version
deleted file mode 100644
index dd525d6e..00000000
--- a/Makefile.version
+++ /dev/null
@@ -1,3 +0,0 @@
-NETPBM_MAJOR_RELEASE = 10
-NETPBM_MINOR_RELEASE = 35
-NETPBM_POINT_RELEASE = 98
diff --git a/README b/README
index ece3a32d..7209c8e6 100644
--- a/README
+++ b/README
@@ -1,10 +1,7 @@
 
-                              NETPBM
+                                 NETPBM
 
-
-THIS IS THE PRIMARY DOCUMENTATION DISTRIBUTED WITH NETPBM.  SEE THE doc
-DIRECTORY IN THE SOURCE TREE FOR OTHER INFORMATION, SUCH AS INSTALLATION
-INSTRUCTIONS, AND SEE <http://netpbm.sourceforge.net>.
+This file is part of the source tree for Netpbm.
 
 Netpbm is a toolkit for manipulation of graphic images, including
 conversion of images between a variety of different formats.  There
@@ -14,200 +11,25 @@ manipulation we're talking about are: Shrinking an image by 10%;
 Cutting the top half off of an image; Making a mirror image; Creating
 a sequence of images that fade from one image to another;
 
-For more information on what the package does, see
-<http://netpbm.sourceforge.net/doc>.
-
-The package is intended to be portable to many platforms. It has, at
-least at one time, been tested under various Unix-based systems,
-Windows, Mac OS X, VMS and Amiga OS.  The maintainer uses and builds
-it on a platform that consists (in relevant part) mainly of GNU
-software (you probably know this kind of system by the name "Linux").
-
-The goal of Netpbm is to be a single source for all the primitive
-graphics utilities, especially converters, one might need.  So if you
-know of some freely redistributable software in this vein which is not
-in the package yet, you should bring it to the attention of the Netpbm
-maintainer so it can be included in the next release.
-
-Netpbm does not contain interactive tools and doesn't have a graphical
-interface.
-
-Netpbm replaces the widely spread Pbmplus package (last released
-December 10, 1991).  A lot of improvements and additions have been
-made.  After the latest release of Pbmplus, a lot of additional
-filters began circulating on the net, which was a fairly novel state
-of affairs at the time.  The aim of Netpbm was to collect these and to
-turn them into a package, hence the name "Netpbm."  This work has been
-performed by programmers all over the world.  If _you_ have some code
-to add, please contact the Netpbm maintainer.
-
-
-USING NETPBM IN A WEBSITE
--------------------------
-
-Many people use Netpbm to perform graphics functions in a web site.  They
-have CGI scripts that invoke Netpbm programs to process images for display
-on a web page.  Gallery and 4Images are two web site software packages
-that rely on Netpbm for graphics manipulation.
-
-Installing Netpbm requires different skills and system access than 
-installing most other web site software.  You must be able to compile
-C code for the web server machine and have a basic understanding of 
-how files are organized and programs run on the web server.  Diagnosing
-inevitable problems usually requires shell access to the web server.
-
-Netpbm is basic graphics software that ought to be supplied by any
-web hosting service.  If it isn't on your web server already, you should
-request that the system administrator add it.
-
-The Gallery project provides an easy install package for the parts of
-Netpbm that Gallery needs, and provides technical support at:
-
-    http://gallery.sourceforge.net/forums.php
-
-
-DISTRIBUTION
-------------
-
-You'll find the latest release of Netpbm source code at
-<http://prdownloads.sourceforge.net/netpbm/>.
-
-The user manual is not in the source code package.  It is available online
-at <http://netpbm.sourceforge.net/doc> and you can download it from there.
-See the file doc/USERDOC for details.
-
-A list of prebuilt Netpbm distributions is on the Netpbm
-website, <http://netpbm.sourceforge.net>.
-
-
-PREREQUISITES
--------------
-
-Don't sweat the prerequisites too much.  In most cases, if you're
-missing something, the build of the programs that depend on it will
-bomb, but the rest of the Netpbm programs will build just fine.  And
-you may not need the more demanding programs.
-
-If you have trouble getting, building, or installing the
-prerequisites, the Netpbm maintainer wants to know.  Since he uses
-them himself, he can help you.  And if there is a problem with a
-prerequisite package that its own maintainer cannot fix, it may be
-possible to ship a fix with Netpbm.
-
-To build and install Netpbm, you need GNU Make and a Perl interpreter.
-You can get GNU Make from http://www.gnu.org/software and Perl from 
-http://www.cpan.org.  It's possible to get around the Perl requirement
-by running some of the steps on a different machine that has Perl and
-doing others manually.  There is no practical substitute for GNU Make.
-
-To build pnmtotiff or tifftopnm or pnmtotiffcmyk, you need the Tiff
-library.  You can get it from http://www.libtiff.org.
-
-To build ppmtojpeg or jpegtopnm, you need the JPEG/JFIF library from
-the Independent JPEG Group (IJG).  You can get it at
-ftp://ftp.uu.net/graphics/jpeg.  See http://www.ijg.org for more
-information.  You need Release 6b or better.  With earlier releases, Netpbm
-build fails with undefined jpeg symbols.  The basic JPEG library installation
-procedure installs only the runtime part of the package -- you nee the
-development part as well, so run 'make install-lib'.  The JPEG library 
-documentation erroneously calls this installing "the library itself."
-This apparently was written before shared libraries.  With shared libraries,
-"the library itself" is part of the runtime installation, but install-lib
-still installs the compile-time stuff you need.
-
-You may also need the JPEG library to build the Tiff converters.  If
-your Tiff library references a shared JPEG library, then you do.  The
-Tiff library may also include a static copy of the JPEG library, in
-which case you won't need a separate JPEG library.  Or it may have
-been built without any JPEG compression capability, in which case you
-won't need a separate JPEG library, but the Tiff converters won't be
-able to handle Tiff with JPEG compression.
-
-The same goes for Ppmtompeg.  You need the jpeg library if you want to
-create MPEGs from JPEGs (without the loss of quality that comes with
-converting from JPEG to PPM first), and if you don't have the JPEG
-library and don't say so in Makefile.config, you won't be able to
-build Ppmtompeg at all.
-
-To build or use Pnmtopng and Pngtopnm, you need the Zlib compression
-library and the PNG library (libpng).  You can get Zlib from
-ftp://quest.jpl.nasa.gov/pub/zlib or
-ftp://metalab.unc.edu/pub/Linux/libs.  You can get libpng from
-http://www.libpng.org/pub/png/libpng.html or
-http://libpng.sourceforge.net.  Older libpng won't work -- you get
-unresolved external references to png_sig_cmp and png_set_sig_bytes.
-
-You may also need the Zlib library to build the Tiff converters, in
-the same way as the Tiff converters require the JPEG libraries, as
-explained above.
-
-Pstopnm (the Postscript to PNM image converter) requires Ghostscript
-(installed with the name 'gs' in your command search path).  And it 
-requires in particular that Ghostscript be built with the relevant 
-PNM device drivers.  See http://www.ghostscript.com/doc/GPL/ .
-
-The Utah Raster Toolkit is not a prerequisite because Netpbm includes
-a subset of it that meets the needs of Pnmtorle and Rletopnm.
-However, you can also substitute the real package by properly
-configuring Makefile.config.  You can get it from 
-ftp://ftp.cs.utah.edu/pub/dept/OLD/pub/urt-3.1b.tar.Z.  There's a 
-patch at ftp://ptolemy.berkeley.edu/pub/misc/urt/urt-3.1b-3.1b1.patch
-
-You generally need a compiler other than Gcc 2.96.  Gcc 2.96 has a bug
-in its inlining optimization.  It generates incorrect code.  Netpbm
-source code takes advantage of inlining and you normally build Netpbm
-with inlining enabled.  Therefore, if you use Gcc 2.96 you will get
-broken Netpbm programs.  The usual symptom is bogus syntax error
-messages when you run the program.  You can avoid this compiler bug by
-using a -O0 compile option instead of the usual -O3.  The automatic
-configuration program will usually detect that you need this and set
-it up for you.  This will make some programs noticeably slower,
-though.
-
-Netpbm requires about 3.5 MiB of disk space, not including documentation.
-The documentation is 1.1 MiB, but you don't necessarily have to install
-it; you can just access the public copy.
-
-
-INSTALLATION
-------------
-
-See doc/INSTALL.
-
-
-SUPPORT
--------
-
-The maintainer of Netpbm, since September 1999, is Bryan Henderson:
+For more information, see <http://netpbm.sourceforge.net>.  Examples of
+information there:
 
-  bryanh@giraffe-data.com.  
+  - How to use Netpbm
 
-If for some reason that email address doesn't work, you can reach
-Bryan at giraffedata@yahoo.com as a backup.  Please don't mail to 
-both at the same time.  Bryan doesn't need two copies of your email.
+  - Where to get current Netpbm code
 
-Bryan actively maintains the package and wants to know about any bugs
-or problems people have with Netpbm or suggestions for improvement.
+  - What prerequisites there are and how to satisfy them
 
-There is no bug reporting database or mailing list.  These would not
-be very useful with Netpbm because Bryan personally responds to all
-bug reports and requests for help immediately.  All known bugs in the
-"latest" release are listed in its release notes on Sourceforge
-(updated as the bugs are reported) and the "stable" release is
-generally maintained so as not to have known bugs for more than a few
-days.  The doc/HISTORY file in the package may be useful if you want
-to find out whether upgrading to the current release would solve your
-problem.  The information in that file, on a per-release basis, is 
-also in the change histories on Sourceforge.
+  - How to get help using Netpbm
 
+  - How to report a bug or suggest an enhancement
 
+  - How to contribute code
 
-MORE INFORMATION, DOCUMENTATION
--------------------------------
+The doc/ directory of the source tree contains further documentation, mainly
+of use only to someone who already has the source code.  For example:
 
-For more information about Netpbm, see <http://netpbm.sourceforge.net/doc>.
+  - Build and installation instructions
 
-The 'doc' directory in the source tree has more information.
+  - Where to get the manual (it's not in the source tree).
 
-A good place to start for information about the wide world of computer
-graphics is http://www.faqs.org/faqs/graphics/  .
diff --git a/analyzer/Makefile b/analyzer/Makefile
index 6a447ea7..ed180917 100644
--- a/analyzer/Makefile
+++ b/analyzer/Makefile
@@ -5,7 +5,7 @@ endif
 SUBDIR = analyzer
 VPATH=.:$(SRCDIR)/$(SUBDIR)
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
 # 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.
@@ -15,7 +15,7 @@ include $(BUILDDIR)/Makefile.config
 # build.
 
 PORTBINARIES = pamfile pamslice pamsumm pamtilt \
-               pgmhist pnmhistmap ppmhist pgmminkowski
+               pbmminkowski pgmhist pnmhistmap ppmhist pgmminkowski
 MATHBINARIES = pamsharpmap pamsharpness pgmtexture pnmpsnr 
 
 BINARIES = $(PORTBINARIES) $(MATHBINARIES)
@@ -33,16 +33,16 @@ MERGE_OBJECTS = $(MERGEBINARIES:%=%.o2)
 .PHONY: all
 all: $(BINARIES)
 
-include $(SRCDIR)/Makefile.common
+include $(SRCDIR)/common.mk
 
 install.bin: install.bin.local
 .PHONY: install.bin.local
 install.bin.local: $(PKGDIR)/bin
 # Remember that $(SYMLINK) might just be a copy command.
 	cd $(PKGDIR)/bin ; \
-	$(SYMLINK) pamslice$(EXE) pgmslice
+	$(SYMLINK) pamslice$(EXE) pgmslice$(EXE)
 	cd $(PKGDIR)/bin ; \
-	$(SYMLINK) pamfile$(EXE) pnmfile
+	$(SYMLINK) pamfile$(EXE) pnmfile$(EXE)
 
 FORCE:
 
diff --git a/analyzer/pamfile.c b/analyzer/pamfile.c
index efb2cbee..4fa81330 100644
--- a/analyzer/pamfile.c
+++ b/analyzer/pamfile.c
@@ -10,6 +10,7 @@
 ** implied warranty.
 */
 
+#include "pm_c_util.h"
 #include "mallocvar.h"
 #include "nstring.h"
 #include "shhopt.h"
diff --git a/analyzer/pamsharpmap.c b/analyzer/pamsharpmap.c
index 8a08f981..ddeefe14 100644
--- a/analyzer/pamsharpmap.c
+++ b/analyzer/pamsharpmap.c
@@ -20,6 +20,7 @@
 #include <stdio.h>
 #include <math.h>
 
+#include "pm_c_util.h"
 #include "pam.h"
 #include "shhopt.h"
 #include "mallocvar.h"
diff --git a/analyzer/pamsharpness.c b/analyzer/pamsharpness.c
index 3850aa3a..8e2d9a3f 100644
--- a/analyzer/pamsharpness.c
+++ b/analyzer/pamsharpness.c
@@ -20,6 +20,7 @@
 #include <stdio.h>
 #include <math.h>
 
+#include "pm_c_util.h"
 #include "pam.h"
 #include "shhopt.h"
 
diff --git a/analyzer/pamslice.c b/analyzer/pamslice.c
index fc63a2cc..ab372c8a 100644
--- a/analyzer/pamslice.c
+++ b/analyzer/pamslice.c
@@ -22,6 +22,7 @@
 
 enum orientation {ROW, COLUMN};
 
+#include "pm_c_util.h"
 #include "pam.h"
 #include "shhopt.h"
 
diff --git a/analyzer/pamsumm.c b/analyzer/pamsumm.c
index 390b8ebb..c9b8a7bf 100644
--- a/analyzer/pamsumm.c
+++ b/analyzer/pamsumm.c
@@ -10,6 +10,7 @@
 
 ******************************************************************************/
 
+#include "pm_c_util.h"
 #include "pam.h"
 #include "shhopt.h"
 #include "mallocvar.h"
diff --git a/analyzer/pamtilt.c b/analyzer/pamtilt.c
index cc135ab6..d70f491b 100644
--- a/analyzer/pamtilt.c
+++ b/analyzer/pamtilt.c
@@ -15,6 +15,7 @@
 #include <assert.h>
 #include <math.h>
 
+#include "pm_c_util.h"
 #include "pam.h"
 #include "mallocvar.h"
 #include "shhopt.h"
diff --git a/analyzer/pbmminkowski.c b/analyzer/pbmminkowski.c
index 5edce506..0f7b47a9 100644
--- a/analyzer/pbmminkowski.c
+++ b/analyzer/pbmminkowski.c
@@ -15,154 +15,171 @@
 
 #include "pbm.h"
 
-#define ISWHITE(x) ( (x) == PBM_WHITE )
+#define ISWHITE(x) ((x) == PBM_WHITE)
 
 
-int main( int argc, char** argv ){
+int main(int argc, const char ** argv) {
 
-  FILE* ifp;
+    FILE * ifP;
 
-  bit* prevrow;
-  bit* thisrow;
-  bit* tmprow;
+    bit * prevrow;
+    bit * thisrow;
+    bit * tmprow;
   
-  int row;
-  int col; 
+    int row;
+    int col; 
 
-  int countTile=0;
-  int countEdgeX=0;
-  int countEdgeY=0;
-  int countVertex=0;
+    int countTile;
+    int countEdgeX;
+    int countEdgeY;
+    int countVertex;
   
-  int rows;
-  int cols;
-  int format;
+    int rows;
+    int cols;
+    int format;
 
-  int area, perimeter, eulerchi;
+    int area, perimeter, eulerchi;
 
 
-  /*
-   * parse arg and initialize
-   */ 
-
-  pbm_init( &argc, argv );
+    pm_proginit(&argc, argv);
   
-  if ( argc > 2 )
-    pm_usage( "[pbmfile]" );
+    if (argc > 2)
+        pm_usage("[pbmfile]");
   
-  if ( argc == 2 )
-    ifp = pm_openr( argv[1] );
-  else
-    ifp = stdin;
+    if (argc == 2)
+        ifP = pm_openr(argv[1]);
+    else
+        ifP = stdin;
   
-  pbm_readpbminit( ifp, &cols, &rows, &format );
+    pbm_readpbminit(ifP, &cols, &rows, &format);
+
+    prevrow = pbm_allocrow(cols);
+    thisrow = pbm_allocrow(cols);
 
-  prevrow = pbm_allocrow( cols );
-  thisrow = pbm_allocrow( cols );
 
+    /* first row */
 
-  /* first row */
+    pbm_readpbmrow(ifP, thisrow, cols, format);
 
-  pbm_readpbmrow( ifp, thisrow, cols, format );
+    countTile   = 0;
+    countEdgeX  = 0;
+    countEdgeY  = 0;
+    countVertex = 0;
 
-  /* tiles */
+    /* tiles */
 
-  for ( col = 0; col < cols; ++col ) 
-    if( ISWHITE(thisrow[col]) ) ++countTile;
+    for (col = 0; col < cols; ++col) 
+        if (ISWHITE(thisrow[col]))
+            ++countTile;
   
-  /* shortcut: for the first row, edgeY == countTile */
-  countEdgeY = countTile;
+    /* shortcut: for the first row, edgeY == countTile */
+    countEdgeY = countTile;
 
-  /* x-edges */
+    /* x-edges */
 
-  if( ISWHITE(thisrow[0]) ) ++countEdgeX;
+    if (ISWHITE(thisrow[0]))
+        ++countEdgeX;
 
-  for ( col = 0; col < cols-1; ++col ) 
-    if( ISWHITE(thisrow[col]) || ISWHITE(thisrow[col+1]) ) ++countEdgeX;
+    for (col = 0; col < cols-1; ++col) 
+        if (ISWHITE(thisrow[col]) || ISWHITE(thisrow[col+1]))
+            ++countEdgeX;
 
-  if( ISWHITE(thisrow[cols-1]) ) ++countEdgeX;
+    if (ISWHITE(thisrow[cols-1]))
+        ++countEdgeX;
 
-  /* shortcut: for the first row, countVertex == countEdgeX */
+    /* shortcut: for the first row, countVertex == countEdgeX */
   
-  countVertex = countEdgeX;
+    countVertex = countEdgeX;
   
 
-  for ( row = 1; row < rows; ++row ){  
+    for (row = 1; row < rows; ++row) {  
     
-    tmprow = prevrow; 
-    prevrow = thisrow;
-    thisrow = tmprow;
+        tmprow  = prevrow; 
+        prevrow = thisrow;
+        thisrow = tmprow;
  
-    pbm_readpbmrow( ifp, thisrow, cols, format );
+        pbm_readpbmrow(ifP, thisrow, cols, format);
   
-    /* tiles */
+        /* tiles */
 
-    for ( col = 0; col < cols; ++col ) 
-      if( ISWHITE(thisrow[col]) ) ++countTile;
+        for (col = 0; col < cols; ++col) 
+            if (ISWHITE(thisrow[col]))
+                ++countTile;
     
-    /* y-edges */
+        /* y-edges */
 
-    for ( col = 0; col < cols; ++col ) 
-      if( ISWHITE(thisrow[col]) || ISWHITE( prevrow[col] )) ++countEdgeY;
+        for (col = 0; col < cols; ++col) 
+            if (ISWHITE(thisrow[col]) || ISWHITE(prevrow[col]))
+                ++countEdgeY;
     
-    /* x-edges */
+        /* x-edges */
 
-    if( ISWHITE(thisrow[0]) ) ++countEdgeX;
+        if (ISWHITE(thisrow[0]))
+            ++countEdgeX;
 
-    for ( col = 0; col < cols-1; ++col ) 
-      if( ISWHITE(thisrow[col]) || ISWHITE(thisrow[col+1]) ) ++countEdgeX;
+        for (col = 0; col < cols-1; ++col) 
+            if (ISWHITE(thisrow[col]) || ISWHITE(thisrow[col+1]))
+                ++countEdgeX;
     
-    if( ISWHITE(thisrow[cols-1]) ) ++countEdgeX;
+        if (ISWHITE(thisrow[cols-1]))
+            ++countEdgeX;
     
-    /* vertices */
+        /* vertices */
 
-    if( ISWHITE(thisrow[0]) || ISWHITE(prevrow[0]) ) ++countVertex;
+        if (ISWHITE(thisrow[0]) || ISWHITE(prevrow[0]))
+            ++countVertex;
 
-    for ( col = 0; col < cols-1; ++col ) 
-      if(    ISWHITE(thisrow[col]) || ISWHITE(thisrow[col+1]) 
-	  || ISWHITE(prevrow[col]) || ISWHITE(prevrow[col+1]) ) ++countVertex;
+        for (col = 0; col < cols-1; ++col) 
+            if (ISWHITE(thisrow[col]) || ISWHITE(thisrow[col+1]) 
+                || ISWHITE(prevrow[col]) || ISWHITE(prevrow[col+1]))
+                ++countVertex;
 
-    if( ISWHITE(thisrow[cols-1]) || ISWHITE(prevrow[cols-1]) ) ++countVertex;
+        if (ISWHITE(thisrow[cols-1]) || ISWHITE(prevrow[cols-1]))
+            ++countVertex;
 
 	  
-  } /* for row */
+    } /* for row */
 
-  /* now thisrow contains the top row*/
-  /* tiles and x-edges have been counted, now y-edges and top vertices remain */
+    /* now thisrow contains the top row*/
+    /* tiles and x-edges have been counted, now y-edges and top
+       vertices remain
+    */
 
   
-  /* y-edges */
+    /* y-edges */
 
-  for ( col = 0; col < cols; ++col ) 
-    if( ISWHITE(thisrow[col]) ) ++countEdgeY;
+    for (col = 0; col < cols; ++col) 
+        if (ISWHITE(thisrow[col]))
+            ++countEdgeY;
 
-  /* vertices */
+    /* vertices */
   
-  if( ISWHITE(thisrow[0]) ) ++countVertex;
-
-  for ( col = 0; col < cols-1; ++col ) 
-    if( ISWHITE(thisrow[col]) || ISWHITE(thisrow[col+1]) ) ++countVertex;
+    if (ISWHITE(thisrow[0]))
+        ++countVertex;
 
-  if( ISWHITE(thisrow[cols-1]) ) ++countVertex;
+    for (col = 0; col < cols-1; ++col) 
+        if (ISWHITE(thisrow[col]) || ISWHITE(thisrow[col+1]))
+            ++countVertex;
 
+    if (ISWHITE(thisrow[cols-1]))
+        ++countVertex;
 
-  /* cleanup */
+    /* cleanup */
 
-  pm_close( ifp );
+    pm_close(ifP);
 
-  /* print results */
+    /* print results */
 
-  printf( "   tiles:\t%d\n x-edges:\t%d\n y-edges:\t%d\nvertices:\t%d\n",
-	  countTile, countEdgeX, countEdgeY,countVertex );
+    printf("   tiles:\t%d\n x-edges:\t%d\n y-edges:\t%d\nvertices:\t%d\n",
+           countTile, countEdgeX, countEdgeY,countVertex);
 
-  area      = countTile;
-  perimeter = 2*countEdgeX + 2*countEdgeY - 4*countTile;
-  eulerchi  = countTile - countEdgeX - countEdgeY + countVertex;
+    area      = countTile;
+    perimeter = 2*countEdgeX + 2*countEdgeY - 4*countTile;
+    eulerchi  = countTile - countEdgeX - countEdgeY + countVertex;
 
-  printf( "    area:\t%d\nperimeter:\t%d\n eulerchi:\t%d\n",
-	  area, perimeter, eulerchi );
+    printf("    area:\t%d\nperimeter:\t%d\n eulerchi:\t%d\n",
+           area, perimeter, eulerchi );
   
-  exit( 0 );
-
-} /* main */
+    return 0;
+}
 
diff --git a/analyzer/pgmhist.c b/analyzer/pgmhist.c
index 126fe693..4790ecba 100644
--- a/analyzer/pgmhist.c
+++ b/analyzer/pgmhist.c
@@ -1,4 +1,4 @@
-/* pgmhist.c - print a histogram of the values in a portable graymap
+/* pgmhist.c - print a histogram of the values in a PGM image
 **
 ** Copyright (C) 1989 by Jef Poskanzer.
 **
@@ -10,86 +10,189 @@
 ** implied warranty.
 */
 
+#include <assert.h>
 #include <limits.h>
 
-#include "pgm.h"
+#include "pm_c_util.h"
 #include "mallocvar.h"
+#include "shhopt.h"
+#include "pgm.h"
 
-int
-main( argc, argv )
-    int argc;
-    char *argv[];
-{
-    FILE *ifp;
-    gray maxval, *grayrow;
-    register gray *gP;
-    int argn, rows, cols, format, row;
-    int i, *hist, *rcount, count, size;
-    register int col;
-    const char * const usage = "[pgmfile]";
-
-    pgm_init( &argc, argv );
-
-    argn = 1;
-
-    if ( argn < argc )
-	{
-        ifp = pm_openr( argv[argn] );
-        argn++;
-	}
+
+
+struct cmdline_info {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * inputFileName;  /* Filename of input files */
+};
+
+
+
+static void
+parseCommandLine(int argc, const char ** argv,
+                 struct cmdline_info * 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_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+
+    OPTENTINIT;
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (argc-1 == 0) 
+        cmdlineP->inputFileName = "-";
+    else if (argc-1 != 1)
+        pm_error("Program takes zero or one argument (filename).  You "
+                 "specified %d", argc-1);
     else
-        ifp = stdin;
+        cmdlineP->inputFileName = argv[1];
+}
 
-    if ( argn != argc )
-        pm_usage( usage );
 
-    pgm_readpgminit( ifp, &cols, &rows, &maxval, &format );
+
+static void
+buildHistogram(FILE *          const ifP,
+               unsigned int ** const histP,
+               gray *          const maxvalP) {
+
+    gray * grayrow;
+    int rows, cols;
+    int format;
+    unsigned int row;
+    unsigned int i;
+    unsigned int * hist;  /* malloc'ed array */
+    gray maxval;
+
+    pgm_readpgminit(ifP, &cols, &rows, &maxval, &format);
 
     if (UINT_MAX / cols < rows)
         pm_error("Too many pixels (%u x %u) in image.  "
                  "Maximum computable is %u",
                  cols, rows, UINT_MAX);
 
-    grayrow = pgm_allocrow( cols );
+    grayrow = pgm_allocrow(cols);
 
-    /* Build histogram. */
     MALLOCARRAY(hist, maxval + 1);
-    MALLOCARRAY(rcount, maxval + 1);
-    if ( hist == NULL || rcount == NULL )
-        pm_error( "out of memory" );
-    for ( i = 0; i <= maxval; i++ )
+    if (hist == NULL)
+        pm_error("out of memory");
+
+    for (i = 0; i <= maxval; ++i)
         hist[i] = 0;
-    for ( row = 0; row < rows; row++ )
-	{
-        pgm_readpgmrow( ifp, grayrow, cols, maxval, format );
-        for ( col = 0, gP = grayrow; col < cols; col++, gP++ )
-            hist[(int) *gP]++;
-	}
 
-    pm_close( ifp );
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+
+        pgm_readpgmrow(ifP, grayrow, cols, maxval, format);
+
+        for (col = 0; col < cols; ++col) {
+            /* Because total pixels in image is limited: */
+            assert (hist[grayrow[col]] < INT_MAX);
+
+            ++hist[grayrow[col]];
+        }
+    }
+    pgm_freerow(grayrow);
+
+    *histP   = hist;
+    *maxvalP = maxval;
+}
+
+
+
+static void
+countCumulative(unsigned int    const hist[],
+                gray            const maxval,
+                unsigned int ** const rcountP) {
+
+    unsigned int * rcount;
+    unsigned int cumCount;
+    int i;
+    
+    MALLOCARRAY(rcount, maxval + 1);
+    if (rcount == NULL)
+        pm_error("out of memory");
+
+    for (i = maxval, cumCount = 0; i >= 0; --i) {
+        /* Because total pixels in image is limited: */
+        assert(UINT_MAX - hist[i] >= cumCount);
+
+        cumCount += hist[i];
+        rcount[i] = cumCount;
+    }
+
+    *rcountP = rcount;
+}
+
+
+
+static void
+report(unsigned int const hist[],
+       unsigned int const rcount[],
+       gray         const maxval) {
+
+    unsigned int const totalPixels = rcount[0];
+    unsigned int count;
+    unsigned int i;
+
+    printf("value  count  b%%      w%%   \n");
+    printf("-----  -----  ------  ------\n");
 
-    /* Compute count-down */
-    count = 0;
-    for ( i = maxval; i >= 0; i-- )
-	{
-        count += hist[i];
-        rcount[i] = count;
-	}
-
-    /* And print it. */
-    printf( "value\tcount\tb%%\tw%%\n" );
-    printf( "-----\t-----\t--\t--\n" );
     count = 0;
-    size = rows * cols;
-    for ( i = 0; i <= maxval; i++ )
-        if ( hist[i] > 0 )
-	    {
+
+    for (i = 0; i <= maxval; ++i) {
+        if (hist[i] > 0) {
             count += hist[i];
             printf(
-                "%d\t%d\t%5.3g%%\t%5.3g%%\n", i, hist[i],
-                (float) count * 100.0 / size, 
-                (float) rcount[i] * 100.0 / size );
-	    }
+                "%5d  %5d  %5.3g%%  %5.3g%%\n", i, hist[i],
+                (float) count * 100.0 / totalPixels, 
+                (float) rcount[i] * 100.0 / totalPixels);
+        }
+    }
+}
+
 
-    exit( 0 );
+
+int
+main(int argc, const char ** argv) {
+
+    struct cmdline_info cmdline;
+    FILE * ifP;
+    gray maxval;
+    unsigned int * rcount; /* malloc'ed array */
+    unsigned int * hist;   /* malloc'ed array */
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    buildHistogram(ifP, &hist, &maxval);
+
+    countCumulative(hist, maxval, &rcount);
+
+    report(hist, rcount, maxval);
+
+    free(rcount);
+    free(hist);
+    pm_close(ifP);
+
+    return 0;
 }
+
+
+
diff --git a/analyzer/pnmhistmap.c b/analyzer/pnmhistmap.c
index 0e5cf302..7e504734 100644
--- a/analyzer/pnmhistmap.c
+++ b/analyzer/pnmhistmap.c
@@ -19,6 +19,7 @@
 
 #include <string.h>
 
+#include "pm_c_util.h"
 #include "pnm.h"
 #include "shhopt.h"
 #include "mallocvar.h"
@@ -152,16 +153,16 @@ clipHistogram(unsigned int * const hist,
               unsigned int   const hist_width,
               unsigned int   const hmax) {
 
-            unsigned int i;
+    unsigned int i;
 
-            for (i = 0; i < hist_width; ++i)
-                hist[i] = MIN(hmax, hist[i]);
+    for (i = 0; i < hist_width; ++i)
+        hist[i] = MIN(hmax, hist[i]);
 }
 
 
 
 static void
-pgm_hist(FILE *       const fp,
+pgm_hist(FILE *       const ifP,
          int          const cols,
          int          const rows,
          xelval       const maxval,
@@ -186,41 +187,44 @@ pgm_hist(FILE *       const fp,
     unsigned int * ghist;
     double vscale;
     unsigned int hmax;
-
-    if ((ghist = calloc(hist_width, sizeof(int))) == NULL)
-        pm_error ("Not enough memory for histogram array (%d bytes)",
+    
+    MALLOCARRAY(ghist, hist_width);
+    if (ghist == NULL)
+        pm_error("Not enough memory for histogram array (%d bytes)",
                   hist_width * sizeof(int));
-    if ((bits = pbm_allocarray (hist_width, hist_height)) == NULL)
-        pm_error ("no space for output array (%d bits)",
-                  hist_width * hist_height);
-    memset (ghist, 0, sizeof (ghist));
+    bits = pbm_allocarray(hist_width, hist_height);
+    if (bits == NULL)
+        pm_error("no space for output array (%d bits)",
+                 hist_width * hist_height);
+    memset(ghist, 0, hist_width * sizeof(ghist[0]));
 
     /* read the pixel values into the histogram arrays */
-    grayrow = pgm_allocrow (cols);
-    /*XX error-check! */
-    if (verbose) pm_message ("making histogram...");
+    grayrow = pgm_allocrow(cols);
+
+    if (verbose)
+        pm_message("making histogram...");
+
     for (i = rows; i > 0; --i) {
-        pgm_readpgmrow (fp, grayrow, cols, maxval, format);
+        pgm_readpgmrow (ifP, grayrow, cols, maxval, format);
         for (j = cols-1; j >= 0; --j) {
-            int value;
+            int const value = grayrow[j];
 
-            if ((value = grayrow[j]) >= startval && value <= endval)
-                ghist[SCALE_H(value-startval)]++;
+            if (value >= startval && value <= endval)
+                ++ghist[SCALE_H(value-startval)];
         }
     }
-    pgm_freerow (grayrow);
-    fclose (fp);
+    pgm_freerow(grayrow);
 
     /* find the highest-valued slot and set the vertical scale value */
     if (verbose)
-        pm_message ("finding max. slot height...");
+        pm_message("finding max. slot height...");
     if (clipSpec)
         hmax = clipCount;
     else 
         hmax = maxSlotCount(ghist, hist_width, no_white, no_black);
 
     if (verbose)
-        pm_message ("Done: height = %u", hmax);
+        pm_message("Done: height = %u", hmax);
 
     clipHistogram(ghist, hist_width, hmax);
 
@@ -236,7 +240,7 @@ pgm_hist(FILE *       const fp,
             bits[j][i] = dots ? PBM_BLACK : PBM_WHITE;
     }
 
-    pbm_writepbm (stdout, bits, hist_width, hist_height, 0);
+    pbm_writepbm(stdout, bits, hist_width, hist_height, 0);
 }
 
 
@@ -309,7 +313,7 @@ clipHistogramAll(unsigned int * const hist[3],
 
 
 static void
-ppm_hist(FILE *       const fp,
+ppm_hist(FILE *       const ifP,
          int          const cols,
          int          const rows,
          xelval       const maxval,
@@ -329,8 +333,8 @@ ppm_hist(FILE *       const fp,
 
     bool const hscale_unity = hscale - 1 < epsilon;
 
-    pixel *pixrow;
-    pixel **pixels;
+    pixel * pixrow;
+    pixel ** pixels;
     int i, j;
     unsigned int * hist[3];  /* Subscript is enum wantedColor */
     double vscale;
@@ -339,17 +343,19 @@ ppm_hist(FILE *       const fp,
     createHist(colorWanted, hist_width, &hist);
 
     if ((pixels = ppm_allocarray (hist_width, hist_height)) == NULL)
-        pm_error ("no space for output array (%d pixels)",
-                  hist_width * hist_height);
+        pm_error("no space for output array (%d pixels)",
+                 hist_width * hist_height);
     for (i = 0; i < hist_height; ++i)
-        memset (pixels[i], 0, hist_width * sizeof (pixel));
+        memset(pixels[i], 0, hist_width * sizeof(pixels[i][0]));
 
     /* read the pixel values into the histogram arrays */
-    pixrow = ppm_allocrow (cols);
-    /*XX error-check! */
-    if (verbose) pm_message ("making histogram...");
+    pixrow = ppm_allocrow(cols);
+
+    if (verbose)
+        pm_message("making histogram...");
+
     for (i = rows; i > 0; --i) {
-        ppm_readppmrow (fp, pixrow, cols, maxval, format);
+        ppm_readppmrow(ifP, pixrow, cols, maxval, format);
         for (j = cols-1; j >= 0; --j) {
             int value;
 
@@ -367,12 +373,11 @@ ppm_hist(FILE *       const fp,
                 hist[WANT_BLU][SCALE_H(value-startval)]++;
         }
     }
-    ppm_freerow (pixrow);
-    fclose (fp);
+    ppm_freerow(pixrow);
 
     /* find the highest-valued slot and set the vertical scale value */
     if (verbose)
-        pm_message ("finding max. slot height...");
+        pm_message("finding max. slot height...");
     if (clipSpec)
         hmax = clipCount;
     else 
@@ -420,16 +425,16 @@ ppm_hist(FILE *       const fp,
             }
         }
     }
-    ppm_writeppm (stdout, pixels, hist_width, hist_height, maxval, 0);
+    ppm_writeppm(stdout, pixels, hist_width, hist_height, maxval, 0);
 }
 
 
 
 int
-main (int argc, char ** argv) {
+main(int argc, char ** argv) {
 
     struct cmdlineInfo cmdline;
-    FILE* ifP;
+    FILE * ifP;
     int cols, rows;
     xelval maxval;
     int format;
@@ -479,5 +484,7 @@ main (int argc, char ** argv) {
         pm_error("Cannot do a histogram of a a PBM file");
         break;
     }
+    pm_close(ifP);
+
     return 0;
 }
diff --git a/analyzer/pnmpsnr.c b/analyzer/pnmpsnr.c
index a7331d27..1160fff6 100644
--- a/analyzer/pnmpsnr.c
+++ b/analyzer/pnmpsnr.c
@@ -13,6 +13,7 @@
 #include <math.h>
 
 #include "pm_c_util.h"
+#include "nstring.h"
 #include "pam.h"
 
 #define MAXFILES 16
@@ -47,7 +48,7 @@ validate_input(const struct pam pam1, const struct pam pam2) {
         pm_error("images do not have the same maxval.  This programs works "
                  "only on like maxvals.  "
                  "The first image has maxval %u, "
-                 "while the second has %u.  Use Pnmdepth to change the "
+                 "while the second has %u.  Use Pamdepth to change the "
                  "maxval of one of them.",
                  (unsigned int) pam1.maxval, (unsigned int) pam2.maxval);
 
@@ -66,16 +67,18 @@ validate_input(const struct pam pam1, const struct pam pam2) {
 
 
 static void
-psnr_color(tuple const tuple1, tuple const tuple2,
+psnr_color(tuple    const tuple1,
+           tuple    const tuple2,
            double * const ySqDiffP, 
-           double * const cbSqDiffP, double * const crSqDiffP) {
+           double * const cbSqDiffP,
+           double * const crSqDiffP) {
 
     double y1, y2, cb1, cb2, cr1, cr2;
     
     pnm_YCbCrtuple(tuple1, &y1, &cb1, &cr1);
     pnm_YCbCrtuple(tuple2, &y2, &cb2, &cr2);
     
-    *ySqDiffP =  square(y1  - y2);
+    *ySqDiffP  = square(y1  - y2);
     *cbSqDiffP = square(cb1 - cb2);
     *crSqDiffP = square(cr1 - cr2);
 }
@@ -83,45 +86,49 @@ psnr_color(tuple const tuple1, tuple const tuple2,
 
 
 static void
-reportPsnr(struct pam const pam1, struct pam const pam2, 
-           double const ySumSqDiff, 
-           double const crSumSqDiff, double const cbSumSqDiff,
-           const char filespec1[], const char filespec2[]) {
+reportPsnr(struct pam const pam,
+           double     const ySumSqDiff, 
+           double     const crSumSqDiff,
+           double     const cbSumSqDiff,
+           char       const filespec1[],
+           char       const filespec2[]) {
 
-    bool const color = (strcmp(pam1.tuple_type, PAM_PPM_TUPLETYPE) == 0);
+    bool const color = streq(pam.tuple_type, PAM_PPM_TUPLETYPE);
 
-    /* The PSNR is the mean of the sum of squares of the differences,
-       normalized to the range 0..1
+    /* The PSNR is the ratio of the maximum possible mean square difference
+       to the actual mean square difference.
+    */
+    double const yPsnr =
+        square(pam.maxval) / (ySumSqDiff / (pam.width * pam.height));
+
+    /* Note that in the important special case that the images are
+       identical, the sum square differences are identically 0.0.  No
+       precision error; no rounding error.
     */
-    double const yPsnr = ySumSqDiff 
-        / (pam1.width * pam1.height) 
-        / square(pam1.maxval);
 
     if (color) {
-        double const cbPsnr = cbSumSqDiff 
-            / (pam1.width * pam1.height) 
-            / square(pam1.maxval);
-        double const crPsnr = crSumSqDiff 
-            / (pam1.width * pam1.height) 
-            / (pam1.maxval * pam2.maxval);
+        double const cbPsnr =
+            square(pam.maxval) / (cbSumSqDiff / (pam.width * pam.height));
+        double const crPsnr =
+            square(pam.maxval) / (crSumSqDiff / (pam.width * pam.height));
 
         pm_message("PSNR between %s and %s:", filespec1, filespec2);
-        if (yPsnr > 1e-9)
-            pm_message("Y  color component: %.2f dB", 10 * log10(1/yPsnr));
+        if (ySumSqDiff > 0)
+            pm_message("Y  color component: %.2f dB", 10 * log10(yPsnr));
         else
             pm_message("Y color component does not differ.");
-        if (cbPsnr > 1e-9)
-            pm_message("Cb color component: %.2f dB", 10 * log10(1/cbPsnr));
+        if (cbSumSqDiff > 0)
+            pm_message("Cb color component: %.2f dB", 10 * log10(cbPsnr));
         else
-        pm_message("Cb color component does not differ.");
-        if (crPsnr > 1e-9)
-            pm_message("Cr color component: %.2f dB", 10 * log10(1/crPsnr));
+            pm_message("Cb color component does not differ.");
+        if (crSumSqDiff > 0)
+            pm_message("Cr color component: %.2f dB", 10 * log10(crPsnr));
         else
             pm_message("Cr color component does not differ.");
     } else {
-        if (yPsnr > 1e-9)
+        if (ySumSqDiff > 0)
             pm_message("PSNR between %s and %s: %.2f dB",
-                       filespec1, filespec2, 10 * log10(1/yPsnr));
+                       filespec1, filespec2, 10 * log10(yPsnr));
         else
             pm_message("Images %s and %s don't differ.",
                        filespec1, filespec2);
@@ -166,7 +173,7 @@ main (int argc, char **argv) {
     tuplerow1 = pnm_allocpamrow(&pam1);
     tuplerow2 = pnm_allocpamrow(&pam2);
     
-    ySumSqDiff = 0.0;
+    ySumSqDiff  = 0.0;
     cbSumSqDiff = 0.0;
     crSumSqDiff = 0.0;
 
@@ -181,19 +188,19 @@ main (int argc, char **argv) {
                 double ySqDiff, cbSqDiff, crSqDiff;
                 psnr_color(tuplerow1[col], tuplerow2[col], 
                            &ySqDiff, &cbSqDiff, &crSqDiff);
-                ySumSqDiff += ySqDiff;
+                ySumSqDiff  += ySqDiff;
                 cbSumSqDiff += cbSqDiff;
                 crSumSqDiff += crSqDiff;
                 
             } else {
-                unsigned int yDiffSq;
-                yDiffSq = square(udiff(tuplerow1[col][0], tuplerow2[col][0]));
+                unsigned int const yDiffSq =
+                    square(udiff(tuplerow1[col][0], tuplerow2[col][0]));
                 ySumSqDiff += yDiffSq;
             }
         }
     }
 
-    reportPsnr(pam1, pam2, ySumSqDiff, crSumSqDiff, cbSumSqDiff,
+    reportPsnr(pam1, ySumSqDiff, crSumSqDiff, cbSumSqDiff,
                filespec1, filespec2);
 
     pnm_freepamrow(tuplerow1);
@@ -201,9 +208,3 @@ main (int argc, char **argv) {
 
     return 0;
 }
-
-
-
-
-
-
diff --git a/analyzer/ppmhist.c b/analyzer/ppmhist.c
index 4c4d3c55..673c8175 100644
--- a/analyzer/ppmhist.c
+++ b/analyzer/ppmhist.c
@@ -12,9 +12,11 @@
 
 #include <assert.h>
 
-#include "ppm.h"
-#include "shhopt.h"
+#include "pm_c_util.h"
+#include "mallocvar.h"
 #include "nstring.h"
+#include "shhopt.h"
+#include "ppm.h"
 
 enum sort {SORT_BY_FREQUENCY, SORT_BY_RGB};
 
@@ -24,7 +26,7 @@ struct cmdline_info {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    const char *input_filespec;  /* Filespecs of input files */
+    const char * inputFileName;  /* Name of input file */
     unsigned int noheader;    /* -noheader option */
     enum colorFmt colorFmt;
     unsigned int colorname;   /* -colorname option */
@@ -34,19 +36,21 @@ struct cmdline_info {
 
 
 static void
-parse_command_line(int argc, char ** argv,
-                   struct cmdline_info * const cmdlineP) {
+parseCommandLine(int argc, const char ** argv,
+                 struct cmdline_info * 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 = malloc(100*sizeof(optEntry));
+    optEntry * option_def;
     unsigned int option_def_index;
     
     unsigned int hexcolorOpt, floatOpt, mapOpt, nomapOpt;
     const char * sort_type;
 
+    MALLOCARRAY(option_def, 100);
+
     option_def_index = 0;   /* incremented by OPTENTRY */
     OPTENT3(0,   "map",       OPT_FLAG, NULL,  &mapOpt,                0);
     OPTENT3(0,   "nomap",     OPT_FLAG, NULL,  &nomapOpt,              0);
@@ -63,16 +67,16 @@ parse_command_line(int argc, char ** argv,
     /* Set defaults */
     sort_type = "frequency";
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (argc-1 == 0) 
-        cmdlineP->input_filespec = "-";
+        cmdlineP->inputFileName = "-";
     else if (argc-1 != 1)
         pm_error("Program takes zero or one argument (filename).  You "
                  "specified %d", argc-1);
     else
-        cmdlineP->input_filespec = argv[1];
+        cmdlineP->inputFileName = argv[1];
 
     if (hexcolorOpt + floatOpt + mapOpt > 1)
         pm_error("You can specify only one of -hexcolor, -float, and -map");
@@ -218,9 +222,10 @@ printColors(colorhist_vector const chv,
 
 
 int
-main(int argc, char *argv[] ) {
+main(int argc, const char *argv[]) {
+
     struct cmdline_info cmdline;
-    FILE* ifP;
+    FILE * ifP;
     colorhist_vector chv;
     int rows, cols;
     pixval maxval;
@@ -234,11 +239,11 @@ main(int argc, char *argv[] ) {
     const char ** dictColornames;
     pixel * dictColors;
 
-    ppm_init( &argc, argv );
+    pm_proginit(&argc, argv);
 
-    parse_command_line(argc, argv, &cmdline);
+    parseCommandLine(argc, argv, &cmdline);
 
-    ifP = pm_openr(cmdline.input_filespec);
+    ifP = pm_openr(cmdline.inputFileName);
 
     ppm_readppminit(ifP, &cols, &rows, &maxval, &format);
 
diff --git a/buildtools/Makefile b/buildtools/Makefile
index 8ea61abe..9c0ee778 100644
--- a/buildtools/Makefile
+++ b/buildtools/Makefile
@@ -4,7 +4,7 @@ ifeq ($(SRCDIR)x,x)
 endif
 SUBDIR = buildtools
 VPATH = .:$(SRCDIR)/$(SUBDIR)
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
 MERGE_OBJECTS =
 
@@ -17,7 +17,7 @@ BINARIES =
 SCRIPTS =
 
 OMIT_BUILDTOOL_RULE = 1
-include $(SRCDIR)/Makefile.common
+include $(SRCDIR)/common.mk
 
 ifdef DLLVER
 STRIP_DLL_VERSION=-DDLLVERSTR="\"$(DLLVER)\""
@@ -28,16 +28,16 @@ EXPLICIT=-DEXPLICIT
 endif
 
 libopt.o: libopt.c
-	$(CC_FOR_BUILD) -c $(CFLAGS_FOR_BUILD) \
+	$(CC_FOR_BUILD) -c -o $@ $(CFLAGS_FOR_BUILD) \
 	  -DSHLIBPREFIXLIST="\"$(SHLIBPREFIXLIST)\"" \
 	  $(STRIP_DLL_VERSION) $(EXPLICIT) $(CFLAGS_PERSONAL) $(CADD) \
-	  -o $@ $<
+	  $<
 
 typegen.o endiangen.o:%.o:%.c
-	$(CC_FOR_BUILD) -c $(CFLAGS_FOR_BUILD) -o $@ $<
+	$(CC_FOR_BUILD) -c -o $@ $(CFLAGS_FOR_BUILD) $<
 
 $(PROGS):%:%.o
-	$(LD_FOR_BUILD) -o $@ $<
+	$(LD_FOR_BUILD) -o $@ $(LDFLAGS_FOR_BUILD) $<
 
 distclean clean: cleanlocal
 .PHONY: cleanlocal
diff --git a/buildtools/README.pkg b/buildtools/README.pkg
index b2a6b0ef..e544cbb7 100644
--- a/buildtools/README.pkg
+++ b/buildtools/README.pkg
@@ -69,6 +69,16 @@ The parts to be installed are:
     that is in the default search path of your compiler.  Typical
     directories for this are /usr/include and /usr/local/include.
 
+    All of the files are meant to be named like <netpbm/xxx.h> for C
+    #include purposes, which means on an ordinary system, they are
+    installed in a directory named "netpbm".  Thus, they are organized
+    this way in the package.  BUT: until Netpbm 10.41 (December 2007),
+    they were packaged for, and customarily used, just as <xxx.h>.
+    Therefore, for backward compatibility, it is a good idea to make
+    symbolic links from the parent directory into the netpbm/
+    directory.  But as that may pollute your namespace, you may prefer
+    just to make all users migrate to the <netpbm/xxx.h> form.
+
   Data Files
 
     These are files that you can use for various purposes as input to
diff --git a/buildtools/configure.pl b/buildtools/configure.pl
index ed8fce8a..21f11bf3 100755
--- a/buildtools/configure.pl
+++ b/buildtools/configure.pl
@@ -13,26 +13,26 @@ use Config;
 
 my ($TRUE, $FALSE) = (1,0);
 
-# This program generates Makefile.config, which is included by all of the
+# This program generates config.mk, which is included by all of the
 # Netpbm makefiles.  You run this program as the first step in building 
 # Netpbm.  (The second step is 'make').
 
 # This program is only a convenience.  It is supported to create 
-# Makefile.config any way you want.  In fact, an easy way is to copy
-# Makefile.config.in and follow the instructions in the comments therein
+# config.mk any way you want.  In fact, an easy way is to copy
+# config.mk.in and follow the instructions in the comments therein
 # to uncomment certain lines and make other changes.
 
 # Note that if you invoke 'make' without having first run 'configure',
 # the make will call 'configure' itself when it finds
-# 'Makefile.config' missing.  That might look a little messy to the
+# 'config.mk' missing.  That might look a little messy to the
 # user, but it isn't the normal build process.
 
-# The argument to this program is the filepath of the Makefile.config.in
-# file.  If unspecified, the default is 'Makefile.config.in' in the 
+# The argument to this program is the filepath of the config.mk.in
+# file.  If unspecified, the default is 'config.mk.in' in the 
 # Netpbm source directory.
 
 # For explanations of the stuff we put in the make files, see the comments
-# in Makefile.config.in.
+# in config.mk.in.
 
 
 # $testCc is the command we use to do test compiles.  Note that test
@@ -42,6 +42,18 @@ my ($TRUE, $FALSE) = (1,0);
 
 my $testCc;
 
+##############################################################################
+#
+#  Implementation note:
+#
+#  At one time, we thought we had to add /usr/local/lib and /usr/local/include
+#  to the path on some platforms because they needed it and didn't include
+#  it in the default compiler search path.  But then we had reason to doubt
+#  that was really required, so removed the function on 04.03.15 and never
+#  had any complaint in the next 3 years.
+##############################################################################
+
+
 #******************************************************************************
 #
 #  SUBROUTINES
@@ -87,13 +99,17 @@ sub tmpdir() {
 
     my $retval;
     
-    if ($ENV{"TMPDIR"}) {
-        $retval = $ENV{"TMPDIR"};
+    if ($ENV{'TMPDIR'}) {
+        $retval = $ENV{'TMPDIR'};
+    } elsif ($ENV{'TEMP'}) {
+        $retval = $ENV{'TEMP'};
     } else {
-        if ($Config{'osvers'} eq "djgpp") {
-            $retval = "/dev/env/DJDIR/tmp";
+        if ($Config{'osvers'} eq 'djgpp') {
+            $retval = '/dev/env/DJDIR/tmp';
         } else {
-            $retval =  "/tmp";
+            if (-d('/tmp')) {
+                $retval =  '/tmp';
+            }
         }
     }
     return $retval;
@@ -110,14 +126,43 @@ sub tempFile($) {
 #                                       DIR=>File::Spec->tmpdir(),
 #                                       UNLINK=>0);
     my ($suffix) = @_;
+
     my $fileName;
     local *file;  # For some inexplicable reason, must be local, not my
-    my $i;
-    $i = 0;
-    do {
-        $fileName = tmpdir() . "/netpbm" . $i++ . $suffix;
-    } until sysopen(*file, $fileName, O_RDWR|O_CREAT|O_EXCL);
 
+    my $tmpdir = tmpdir();
+
+    if (!defined($tmpdir)) {
+        print STDERR ("Cannot determine what directory to use for " .
+                      "temporary files.  " .
+                      "Set TMPDIR environment variable to fix this.\n");
+        exit(1);
+    } else {
+        if (!-d($tmpdir)) {
+            print STDERR ("Temporary file directory '$tmpdir' does not " .
+                          "exist.  Create it or set TMPDIR environment " .
+                          "variable appropriately\n");
+            exit(1);
+        } else {
+            for (my $i = 0; $i < 50 && !defined($fileName); ++$i) {
+                my $trialFileName = tmpdir() . "/netpbm" . $i . $suffix;
+
+                my $success = sysopen(*file,
+                                      $trialFileName,
+                                      O_RDWR|O_CREAT|O_EXCL);
+
+                if ($success) {
+                    $fileName = $trialFileName;
+                }
+            }
+
+            if (!defined($fileName)) {
+                print STDERR ("Unable to create a temporary file in " .
+                              "directory '$tmpdir'\n");
+                exit(1);
+            }
+        }
+    }
     return(*file, $fileName);
 }
 
@@ -159,8 +204,7 @@ sub chooseTestCompiler($$) {
 
 
 
-sub testCflags($) {
-    my ($needLocal) = @_;
+sub testCflags() {
 
     my $cflags;
 
@@ -176,9 +220,6 @@ sub testCflags($) {
         $cflags .= " " . $ENV{"CFLAGS"};
     }
     
-    if ($needLocal) {
-        $cflags .= " -I/usr/local/include";
-    }
     return $cflags;
 }    
 
@@ -213,10 +254,40 @@ sub testCompile($$$) {
 
 
 
+sub testCompileLink($$$) {
+    my ($flags, $cSourceCodeR, $successR) = @_;
+#-----------------------------------------------------------------------------
+#  Do a test compile of the program in @{$cSourceCodeR}.
+#  
+#  Return $$successR == $TRUE iff the compile succeeds (exit code 0).
+#-----------------------------------------------------------------------------
+    my ($cFile, $cFileName) = tempFile('.c');
+
+    print $cFile @{$cSourceCodeR};
+    
+    my ($oFile, $oFileName) = tempFile('');
+
+    # Note that $flags may contain -l options, which where static linking
+    # is involved have to go _after_ the base object file ($oFileName).
+    
+    my $compileCommand = "$testCc -o $oFileName $cFileName $flags";
+    print ("Doing test compile/link: $compileCommand\n");
+    my $rc = system($compileCommand);
+    
+    unlink($oFileName);
+    close($oFile);
+    unlink($cFileName);
+    close($cFile);
+
+    $$successR = ($rc == 0);
+}
+
+
+
 sub displayIntroduction() {
     print("This is the Netpbm configurator.  It is an interactive dialog " .
           "that\n");
-    print("helps you build the file 'Makefile.config' and prepare to build ");
+    print("helps you build the file 'config.mk' and prepare to build ");
     print("Netpbm.\n");
     print("\n");
 
@@ -414,8 +485,8 @@ sub getPlatform() {
 
 
 
-sub getCompiler($$) {
-    my ($platform, $compilerR) = @_;
+sub getCompiler($$$) {
+    my ($platform, $subplatform, $compilerR) = @_;
 #-----------------------------------------------------------------------------
 #  Here are some of the issues surrounding choosing a compiler:
 #
@@ -460,6 +531,8 @@ sub getCompiler($$) {
 
     if ($gccUsualPlatform{$platform}) {
         $$compilerR = "gcc";
+    } elsif ($platform eq "WINDOWS" && $subplatform eq "cygwin") {
+        $$compilerR = "gcc";
     } elsif ($gccOptionalPlatform{$platform}) {
         print("GNU compiler or native operating system compiler (cc)?\n");
         print("\n");
@@ -701,7 +774,7 @@ sub inttypesDefault() {
         print("(Doing test compiles to choose a default for you -- " .
               "ignore errors)\n");
 
-        my $cflags = testCflags($FALSE);
+        my $cflags = testCflags();
 
         my $works;
 
@@ -791,7 +864,7 @@ sub getInt64($$) {
         print("(Doing test compiles to determine if you have int64 type -- " .
               "ignore errors)\n");
 
-        my $cflags = testCflags($FALSE);
+        my $cflags = testCflags();
 
         my $works;
 
@@ -822,7 +895,7 @@ sub getInt64($$) {
 # warning after user has chosen.  Also test links to test the link library.
 
 # It looks like these should all be in the default search paths and were there
-# just to override defaults in Makefile.config.in.  Since Configure now
+# just to override defaults in config.mk.in.  Since Configure now
 # creates a default of "standard search path," I'm guessing we don't need
 # to set these anymore.
 
@@ -1140,8 +1213,8 @@ sub help() {
     print("It is not GNU Configure.\n");
     print("\n");
     print("There is one optional argument to this program:  The " .
-          "name of the file to use as the basis for the Makefile.config " .
-          "file.  Default is 'Makefile.config.in'\n");
+          "name of the file to use as the basis for the config.mk " .
+          "file.  Default is 'config.mk.in'\n");
     print("\n");
     print("Otherwise, the program is interactive.\n");
 }
@@ -1188,7 +1261,7 @@ sub gnuOptimizeOpt($) {
         print("Therefore, I am configuring the build to not do inline \n");
         print("optimization.  This will make some Netpbm programs \n");
         print("noticeably slower.  If I am wrong about your compiler, just\n");
-        print("edit Makefile.config and change -O0 to -O3 near the bottom.\n");
+        print("edit config.mk and change -O0 to -O3 near the bottom.\n");
         print("\n");
         print("The problem is known to exist in the GNU Compiler \n");
         print("release 2.96.  If you upgrade, you will not have this \n");
@@ -1204,38 +1277,22 @@ sub gnuOptimizeOpt($) {
 
 
 
-sub needLocal($) {
-#-----------------------------------------------------------------------------
-#  Return wether or not /usr/local paths must be added to compiles and
-#  links.  In a properly configured system, those paths should be in
-#  the compiler and linker default search paths, e.g. the compiler
-#  should search /usr/local/include and then /usr/include without any
-#  -I options needed.  But we've seen some systems where it isn't.
-#
-#  Actually, I have doubts now as to whether these misconfigured systems
-#  really exist.  This subroutine was apparently always broken, because
-#  before 04.03.15, it had "netbsd", etc. in lower case.  So it always
-#  returned false.  I never had a complaint.  Plus, we had a bug in 
-#  Makefile.config.in wherein it wiped out the user's setting of the LDFLAGS
-#  environment variable.  This could explain /usr/local/lib not being in
-#  the path when it should have been.
-#
-#  So I've disabled this function; we'll see if we encounter any truly
-#  misconfigured systems.  04.03.15.
-#-----------------------------------------------------------------------------
-    my ($platform) = @_;
+sub gnuCflags($) {
+    my ($gccCommandName) = @_;
 
-    return $FALSE;  # See comments above.
+    return("CFLAGS = " . gnuOptimizeOpt($gccCommandName) . " -ffast-math " .
+           " -pedantic -fno-common " . 
+           "-Wall -Wno-uninitialized -Wmissing-declarations -Wimplicit " .
+           "-Wwrite-strings -Wmissing-prototypes -Wundef\n");
+}
 
-    my $needLocal;
-    
-    if ($platform eq "NETBSD" || $platform eq "OPENBSD" || 
-        $platform eq "FREEBSD") {
-        $needLocal = $TRUE;
-    } else {
-        $needLocal = $FALSE;
-    }
-    return $needLocal;
+
+
+sub makeCompilerGcc($) {
+    my ($config_mkR) = @_;
+    my $compileCommand = 'gcc';
+    push(@{$config_mkR}, "CC = $compileCommand\n");
+    push(@{$config_mkR}, gnuCflags($compileCommand));
 }
 
 
@@ -1246,7 +1303,7 @@ sub findProcessManagement($) {
 #  Return $TRUE iff the system does not have <sys/wait.h> in its default
 #  search path.
 #-----------------------------------------------------------------------------
-    my $cflags = testCflags($FALSE);
+    my $cflags = testCflags();
 
     my @cSourceCode = (
                        "#include <sys/wait.h>\n",
@@ -1383,13 +1440,13 @@ sub printOldJpegWarning() {
 
 
 
-sub testJpegHdr($$) {
+sub testJpegHdr($) {
 
-    my ($needLocal, $jpeghdr_dir) = @_;
+    my ($jpeghdr_dir) = @_;
 
     if (defined($testCc)) {
 
-        my $generalCflags = testCflags($needLocal);
+        my $generalCflags = testCflags();
 
         my $jpegIOpt = $jpeghdr_dir ? "-I$jpeghdr_dir" : "";
 
@@ -1442,15 +1499,15 @@ sub testCompilePngH($$) {
 
 
 
-sub testPngHdr($$$) {
+sub testPngHdr($$) {
 #-----------------------------------------------------------------------------
 #  Issue a warning if the compiler can't find png.h.
 #-----------------------------------------------------------------------------
-    my ($needLocal, $pnghdr_dir, $zhdr_dir) = @_;
+    my ($pnghdr_dir, $zhdr_dir) = @_;
 
     if (defined($testCc)) {
 
-        my $generalCflags = testCflags($needLocal);
+        my $generalCflags = testCflags();
 
         my $zlibIOpt = $zhdr_dir ? "-I$zhdr_dir" : "";
 
@@ -1474,13 +1531,76 @@ sub testPngHdr($$$) {
 
 
 
-sub testLibpngConfig($) {
-    my ($needLocal) = @_;
+sub printBadPngConfigLdflagsWarning($) {
+    my ($pngLdFlags) = @_;
+
+    print << 'EOF';
+WARNING: 'libpng-config' in this environment (a program in your PATH)
+gives instructions that don't work for linking with the PNG library.
+Our test link failed.
+
+This indicates Libpng is installed incorrectly on this system.  If so,
+your Netpbm build, which uses 'libpng-config', will fail.  But it
+might also just be our test that is broken.
+
+EOF
+
+}
+
+
+
+sub printBadPngConfigCflagsWarning($) {
+    my ($pngCFlags) = @_;
+
+    print << 'EOF';
+WARNING: 'libpng-config' in this environment (a program in your PATH)
+gives instructions that don't work for compiling for the PNG library.
+Our test compile failed.
+
+This indicates Libpng is installed incorrectly on this system.  If so,
+your Netpbm build, which uses 'libpng-config', will fail.  But it
+might also just be our test that is broken.
+
+EOF
+
+}
+
+
+
+sub testLinkPnglib($$) {
+    my ($generalCflags, $pngCflags) = @_;
+
+    my @cSourceCode = (
+                       "#include <png.h>\n",
+                       "int main() {\n",
+                       "png_create_write_struct(0, NULL, NULL, NULL);\n",
+                       "}\n",
+                       );
+
+    testCompile("$generalCflags $pngCflags", \@cSourceCode, \my $success);
+    if (!$success) {
+        # Since it won't compile, we can't test the link
+    } else {
+        my $pngLdflags = qx{libpng-config --ldflags};
+        chomp($pngLdflags);
+        
+        testCompileLink("$generalCflags $pngCflags $pngLdflags",
+                        \@cSourceCode, \my $success);
+        
+        if (!$success) {
+            printBadPngConfigLdflagsWarning($pngLdflags);
+        }
+    }
+}
+
+
+
+sub testLibpngConfig() {
 #-----------------------------------------------------------------------------
 #  Issue a warning if the instructions 'libpng-config' give for compiling
 #  with Libpng don't work.
 #-----------------------------------------------------------------------------
-    my $generalCflags = testCflags($needLocal);
+    my $generalCflags = testCflags();
 
     my $pngCflags = qx{libpng-config --cflags};
     chomp($pngCflags);
@@ -1488,33 +1608,136 @@ sub testLibpngConfig($) {
     testCompilePngH("$generalCflags $pngCflags", \my $success);
 
     if (!$success) {
-        print("\n");
-        print("Unable to compile a test PNG program using the compiler " .
-              "flags that libpng-config says to use: '$pngCflags'.\n");
-        print("This indicates that you have Libpng installed incorrectly.\n");
-        print("libpng-config gets installed as part of the Libpng package.\n");
+        printBadPngConfigCflagsWarning($pngCflags);
     } else {
-        
+        testLinkPnglib($generalCflags, $pngCflags);
+    }
+}
+
+
+
+sub testCompileXmlreaderH($$) {
+    my ($cflags, $successR) = @_;
+#-----------------------------------------------------------------------------
+#  Do a test compile to see if we can see xmlreader.h.
+#-----------------------------------------------------------------------------
+    my @cSourceCode = (
+                       "#include <libxml/xmlreader.h>\n",
+                       );
+    
+    testCompile($cflags, \@cSourceCode, $successR);
+}
+
+
+
+sub printBadXml2CFlagsWarning($) {
+    my ($xml2CFlags) = @_;
+
+    print << 'EOF';
+
+WARNING: 'xml2-config' in this environment (a program in your PATH)
+gives instructions that don't work for compiling for the Libxml2 library.
+Our test compile failed.
+
+This indicates Libxml2 is installed incorrectly on this system.  If so,
+your Netpbm build, which uses 'xml2-config', will fail.  But it
+might also just be our test that is broken.
+
+'xml2-config' says to use compiler options '$xml2CFlags'.
+
+EOF
+
+}
+
+
+
+sub testCompileXmlReaderTypes($$) {
+    my ($cflags, $successR) = @_;
+#-----------------------------------------------------------------------------
+#  Do a test compile to see if xmlreader.h defines xmlReaderTypes,
+#  assuming we can compile with xmlreader.h in general.
+#-----------------------------------------------------------------------------
+    my @cSourceCode = (
+                       "#include <libxml/xmlreader.h>\n",
+                       "xmlReaderTypes dummy;\n",
+                       );
+    
+    testCompile($cflags, \@cSourceCode, $successR);
+}
+
+
+
+sub printMissingXmlReaderTypesWarning() {
+
+    print << 'EOF';
+
+WARNING: Your libxml2 interface header file does not define the type
+'xmlReaderTypes', which Netpbm needs.  In order to build Netpbm successfully,
+you must install a more recent version of Libxml2.
 
+EOF
+}
+
+
+
+sub printNoLibxml2Warning() {
+
+    print << 'EOF';
+
+WARNING: You appear not to have Libxml2 installed ('xml2-config' does not
+exist in your program search PATH).  If this is the case at build time,
+the build will skip building 'svgtopam'.
+
+EOF
+}
+
+
+
+sub testLibxml2Hdr() {
+#-----------------------------------------------------------------------------
+#  Issue a warning if the instructions 'xml2-config' give for compiling
+#  with Libxml2 don't work.  In particular, note whether they get us a
+#  modern enough Libxml2 to have the 'xmlReaderTypes' type.
+#-----------------------------------------------------------------------------
+    if (commandExists('xml2-config')) {
+        my $generalCflags = testCflags();
+
+        my $xml2Cflags = qx{xml2-config --cflags};
+        chomp($xml2Cflags);
 
+        testCompileXmlreaderH("$generalCflags $xml2Cflags", \my $success);
+
+        if (!$success) {
+            printBadXml2CflagsWarning($xml2Cflags);
+        } else {
+            testCompileXmlReaderTypes("$generalCflags $xml2Cflags",
+                                      \my $success);
+
+            if (!$success) {
+                printMissingXmlReaderTypesWarning();
+            }
+        }
+    } else {
+        printNoLibxml2Warning();
     }
 }
 
 
 
-sub testConfiguration($$$$$$$) {
+sub testConfiguration($$$$$$) {
 
-    my ($needLocal, $jpeglib, $jpeghdr_dir,
+    my ($jpeglib, $jpeghdr_dir,
         $pnglib, $pnghdr_dir, $zlib, $zhdr_dir) = @_;
 
     if (defined($jpeglib)) {
-        testJpegHdr($needLocal, $jpeghdr_dir);
+        testJpegHdr($jpeghdr_dir);
     }
     if (defined($pnglib) && defined($zlib)) {
-        testPngHdr($needLocal, $pnghdr_dir, $zhdr_dir);
+        testPngHdr($pnghdr_dir, $zhdr_dir);
     } elsif (commandExists('libpng-config')) {
-        testLibpngConfig($needLocal);
+        testLibpngConfig();
     }
+    testLibxml2Hdr();
 
     # TODO: We ought to validate other libraries too.  But it's not
     # that important, because in the vast majority of cases where the
@@ -1546,8 +1769,8 @@ if (@ARGV > 0) {
     $configInPathArg = $ARGV[0];
 }
 
-if (stat("Makefile.config")) {
-    print("Discard existing Makefile.config?\n");
+if (stat("config.mk")) {
+    print("Discard existing config.mk?\n");
     print("Y or N (N) ==> ");
 
     my $answer = <STDIN>;
@@ -1570,21 +1793,22 @@ my ($platform, $subplatform) = getPlatform();
 print("\n");
 
 if ($platform eq "NONE") {
-    print("You will have to construct Makefile.config manually.  To do \n");
-    print("this, copy Makefile.config.in as Makefile.config, and then \n");
+    print("You will have to construct config.mk manually.  To do \n");
+    print("this, copy config.mk.in as config.mk, and then \n");
     print("edit it.  Follow the instructions and examples in the file. \n");
     print("Please report your results to the Netpbm maintainer so he \n");
     print("can improve the configure program. \n");
     exit;
 }
 
-getCompiler($platform, \my $compiler);
+getCompiler($platform, $subplatform, \my $compiler);
+
 getLinker($platform, $compiler, \my $baseLinker, \my $linkViaCompiler);
 
 chooseTestCompiler($compiler, \$testCc);
 
 my $netpbmlib_runtime_path;
-    # Undefined if the default from Makefile.config.in is acceptable.
+    # Undefined if the default from config.mk.in is acceptable.
 
 if ($platform eq "SOLARIS" or $platform eq "IRIX" or
     $platform eq "DARWIN" or $platform eq "NETBSD" or
@@ -1728,8 +1952,7 @@ validateLibraries($jpeglib, $tifflib, $pnglib, $zlib);
 
 warnJpegTiffDependency($jpeglib, $tifflib);
 
-testConfiguration(needLocal($platform), 
-                  $jpeglib, $jpeghdr_dir,
+testConfiguration($jpeglib, $jpeghdr_dir,
                   $pnglib, $pnghdr_dir,
                   $zlib, $zhdr_dir,
                   );
@@ -1745,7 +1968,7 @@ my $defaultConfigInPath;
 if (-f("GNUmakefile")) {
     # He's apparently running us in the source tree or an already set up
     # build directory.
-    $defaultConfigInPath = "Makefile.config.in";
+    $defaultConfigInPath = "config.mk.in";
 } else {
     my $srcdir;
     my $done;
@@ -1770,41 +1993,24 @@ if (-f("GNUmakefile")) {
     unlink("Makefile");
     symlink("$srcdir/Makefile", "Makefile");
 
-    open(SRCDIR, ">Makefile.srcdir");
+    open(SRCDIR, ">srcdir.mk");
     print(SRCDIR "SRCDIR = $srcdir\n");
     close(SRCDIR);
     
-    $defaultConfigInPath = "$srcdir/Makefile.config.in";
-}
-
-sub makeCompilerGcc($) {
-    my ($Makefile_configR) = @_;
-    my $compileCommand = 'gcc';
-    push(@{$Makefile_configR}, "CC = $compileCommand\n");
-    push(@{$Makefile_configR}, gnuCflags($compileCommand));
+    $defaultConfigInPath = "$srcdir/config.mk.in";
 }
 
-
 #******************************************************************************
 #
-#  BUILD Makefile.config
+#  BUILD config.mk
 #
 #*****************************************************************************
 
-sub gnuCflags($) {
-    my ($gccCommandName) = @_;
-
-    return("CFLAGS = " . gnuOptimizeOpt($gccCommandName) . " -ffast-math " .
-           " -pedantic -fno-common " . 
-           "-Wall -Wno-uninitialized -Wmissing-declarations -Wimplicit " .
-           "-Wwrite-strings -Wmissing-prototypes -Wundef\n");
-}
-
-my @Makefile_config;
-    # This is the complete Makefile.config contents.  We construct it here
-    # and ultimately write the whole thing out as Makefile.config.
+my @config_mk;
+    # This is the complete config.mk contents.  We construct it here
+    # and ultimately write the whole thing out as config.mk.
 
-# First, we just read the 'Makefile.config.in' in
+# First, we just read the 'config.mk.in' in
 
 my $configInPath;
 if (defined($configInPathArg)) {
@@ -1815,9 +2021,9 @@ if (defined($configInPathArg)) {
 open (CONFIG_IN,"<$configInPath") or
     die("Unable to open file '$configInPath' for input.");
 
-@Makefile_config = <CONFIG_IN>;
+@config_mk = <CONFIG_IN>;
 
-unshift(@Makefile_config, 
+unshift(@config_mk, 
         "####This file was automatically created by 'configure.'\n",
         "####Many variables are set twice -- a generic setting, then \n",
         "####a system-specific override at the bottom of the file.\n",
@@ -1826,101 +2032,98 @@ unshift(@Makefile_config,
 close(CONFIG_IN);
 
 # Now, add the variable settings that override the default settings that are
-# done by the Makefile.config.in contents.
+# done by the config.mk.in contents.
 
-push(@Makefile_config, "\n\n\n\n");
-push(@Makefile_config, "####Lines above were copied from Makefile.config.in " .
+push(@config_mk, "\n\n\n\n");
+push(@config_mk, "####Lines above were copied from config.mk.in " .
      "by 'configure'.\n");
-push(@Makefile_config, "####Lines below were added by 'configure' based on " .
+push(@config_mk, "####Lines below were added by 'configure' based on " .
      "the $platform platform.\n");
 if (defined($subplatform)) {
-    push(@Makefile_config, "####subplatform '$subplatform'\n");
+    push(@config_mk, "####subplatform '$subplatform'\n");
 }
 
-push(@Makefile_config, "DEFAULT_TARGET = $default_target\n");
+push(@config_mk, "DEFAULT_TARGET = $default_target\n");
 
-push(@Makefile_config, "NETPBMLIBTYPE=$netpbmlibtype\n");
-push(@Makefile_config, "NETPBMLIBSUFFIX=$netpbmlibsuffix\n");
+push(@config_mk, "NETPBMLIBTYPE=$netpbmlibtype\n");
+push(@config_mk, "NETPBMLIBSUFFIX=$netpbmlibsuffix\n");
 if (defined($shlibprefixlist)) {
-    push(@Makefile_config, "SHLIBPREFIXLIST=$shlibprefixlist\n");
+    push(@config_mk, "SHLIBPREFIXLIST=$shlibprefixlist\n");
 }
-push(@Makefile_config, "STATICLIB_TOO=$staticlib_too\n");
+push(@config_mk, "STATICLIB_TOO=$staticlib_too\n");
 
 if (defined($netpbmlib_runtime_path)) {
-    push(@Makefile_config, "NETPBMLIB_RUNTIME_PATH=$netpbmlib_runtime_path\n");
+    push(@config_mk, "NETPBMLIB_RUNTIME_PATH=$netpbmlib_runtime_path\n");
 }
 
 if ($platform eq "GNU") {
-    my $compileCommand;
     if (!commandExists("cc") && commandExists("gcc")) {
-        $compileCommand = "gcc";
-        push(@Makefile_config, "CC = $compileCommand\n");
+        makeCompilerGcc(\@config_mk);
     } else {
-        $compileCommand = "cc";
+        push(@config_mk, gnuCflags('cc'));
     }
-    push(@Makefile_config, gnuCflags($compileCommand));
 # The merged programs have a main_XXX subroutine instead of main(),
 # which would cause a warning with -Wmissing-declarations or 
 # -Wmissing-prototypes.
-    push(@Makefile_config, "CFLAGS_MERGE = " .
+    push(@config_mk, "CFLAGS_MERGE = " .
          "-Wno-missing-declarations -Wno-missing-prototypes\n");
-    push(@Makefile_config, "LDRELOC = ld --reloc\n");
-    push(@Makefile_config, "LINKER_CAN_DO_EXPLICIT_LIBRARY=Y\n");
+    push(@config_mk, "LDRELOC = ld --reloc\n");
+    push(@config_mk, "LINKER_CAN_DO_EXPLICIT_LIBRARY=Y\n");
 } elsif ($platform eq "SOLARIS") {
-    push(@Makefile_config, 'LDSHLIB = -Wl,-Bdynamic,-G,-h,$(SONAME)', "\n");
+    push(@config_mk, 'LDSHLIB = -Wl,-Bdynamic,-G,-h,$(SONAME)', "\n");
 
-    push(@Makefile_config, 'NEED_RUNTIME_PATH = Y', "\n");
+    push(@config_mk, 'NEED_RUNTIME_PATH = Y', "\n");
     if ($compiler eq "cc") {
-        push(@Makefile_config, "CFLAGS = -O\n");
-        push(@Makefile_config, "CFLAGS_SHLIB = -Kpic\n");
+        push(@config_mk, "CFLAGS = -O\n");
+        push(@config_mk, "CFLAGS_SHLIB = -Kpic\n");
     } else {
-        makeCompilerGcc(\@Makefile_config);
+        makeCompilerGcc(\@config_mk);
     }
     # Before Netpbm 10.20 (January 2004), we set this to -R for 
     # $compiler == cc and -rpath otherwise.  But now we know that the GNU
     # compiler can also invoke a linker that needs -R, so we're more flexible.
     if ($baseLinker eq "GNU") {
-        push(@Makefile_config, "RPATHOPTNAME = -rpath\n");
+        push(@config_mk, "RPATHOPTNAME = -rpath\n");
     } else {
-        push(@Makefile_config, "RPATHOPTNAME = -R\n");
+        push(@config_mk, "RPATHOPTNAME = -R\n");
     }
-    push(@Makefile_config, "NETWORKLD = -lsocket -lnsl\n");
+    push(@config_mk, "NETWORKLD = -lsocket -lnsl\n");
 } elsif ($platform eq "HP-UX") {
     if ($compiler eq "gcc") {
-        makeCompilerGcc(\@Makefile_config);
-        push(@Makefile_config, "CFLAGS += -fPIC\n");
-        push(@Makefile_config, "LDSHLIB = -shared -fPIC\n");
-        push(@Makefile_config, 'LDFLAGS += -Wl,+b,/usr/pubsw/lib', "\n");
+        makeCompilerGcc(\@config_mk);
+        push(@config_mk, "CFLAGS += -fPIC\n");
+        push(@config_mk, "LDSHLIB = -shared -fPIC\n");
+        push(@config_mk, 'LDFLAGS += -Wl,+b,/usr/pubsw/lib', "\n");
     } else {
         # We don't know what to do here.  We used to (before 10.20) just
         # just assume the compiler was gcc.  We know that the gcc stuff
         # above does NOT work for HP native compiler.
-        push(@Makefile_config, "LDSHLIB =\n");
+        push(@config_mk, "LDSHLIB =\n");
     }
 } elsif ($platform eq "AIX") {
-    push(@Makefile_config, 'LDFLAGS = -L /usr/pubsw/lib', "\n");
+    push(@config_mk, 'LDFLAGS += -L /usr/pubsw/lib', "\n");
     if ($compiler eq "cc") {
         # Yes, the -L option implies the runtime as well as linktime library
         # search path.  There's no way to specify runtime path independently.
-        push(@Makefile_config, "RPATHOPTNAME = -L\n");
-        push(@Makefile_config, "LDSHLIB = -qmkshrobj\n");
+        push(@config_mk, "RPATHOPTNAME = -L\n");
+        push(@config_mk, "LDSHLIB = -qmkshrobj\n");
     } else {
-        makeCompilerGcc(\@Makefile_config);
-        push(@Makefile_config, "LDSHLIB = -shared\n");
+        makeCompilerGcc(\@config_mk);
+        push(@config_mk, "LDSHLIB = -shared\n");
     }
 } elsif ($platform eq "TRU64") {
-#    push(@Makefile_config, "INSTALL = installbsd\n");
+#    push(@config_mk, "INSTALL = installbsd\n");
     if ($compiler eq "cc") {
-        push(@Makefile_config, 'CFLAGS = -O2 -std1', "\n");
-        push(@Makefile_config, "LDFLAGS = -call_shared -oldstyle_liblookup " .
+        push(@config_mk, 'CFLAGS = -O2 -std1', "\n");
+        push(@config_mk, "LDFLAGS = -call_shared -oldstyle_liblookup " .
              "-L/usr/local/lib\n");
-        push(@Makefile_config, "LDSHLIB = -shared -expect_unresolved \"*\"\n");
+        push(@config_mk, "LDSHLIB = -shared -expect_unresolved \"*\"\n");
     } else {
         # We've never tested this.  This is just here to give a user a 
         # headstart on submitting to us the necessary information.  2002.07.04.
-        push(@Makefile_config, "CC = gcc\n");
-        push(@Makefile_config, 'CFLAGS = -O3', "\n");
-        push(@Makefile_config, "LDSHLIB = -shared\n");
+        push(@config_mk, "CC = gcc\n");
+        push(@config_mk, 'CFLAGS = -O3', "\n");
+        push(@config_mk, "LDSHLIB = -shared\n");
     }
     # Between May 2000 and July 2003, we had -DLONG_32 in these options.
     # We took it out because it generated bad code for a TRU64 user in
@@ -1928,76 +2131,80 @@ if ($platform eq "GNU") {
     # only Ppmtompeg and it isn't clear that using long instead of int is
     # ever right anyway.
 
-    push(@Makefile_config, "OMIT_NETWORK = y\n");
-    push(@Makefile_config, "LINKER_CAN_DO_EXPLICIT_LIBRARY=Y\n");
+    push(@config_mk, "OMIT_NETWORK = y\n");
+    push(@config_mk, "LINKER_CAN_DO_EXPLICIT_LIBRARY=Y\n");
 } elsif ($platform eq "IRIX") {
-#    push(@Makefile_config, "INSTALL = install\n");
-    push(@Makefile_config, "MANPAGE_FORMAT = cat\n");
-    push(@Makefile_config, "RANLIB = true\n");
-    push(@Makefile_config, "CFLAGS = -n32 -O3 -fullwarn\n");
-    push(@Makefile_config, "LDFLAGS = -n32\n");
-    push(@Makefile_config, "LDSHLIB = -shared -n32\n");
+#    push(@config_mk, "INSTALL = install\n");
+    push(@config_mk, "MANPAGE_FORMAT = cat\n");
+    push(@config_mk, "RANLIB = true\n");
+    push(@config_mk, "CFLAGS = -n32 -O3 -fullwarn\n");
+    push(@config_mk, "LDFLAGS = -n32\n");
+    push(@config_mk, "LDSHLIB = -shared -n32\n");
 } elsif ($platform eq "WINDOWS") {
     if ($subplatform eq "cygwin") {
-        makeCompilerGcc(\@Makefile_config);
+        makeCompilerGcc(\@config_mk);
     }
-    push(@Makefile_config, "EXE = .exe\n");
-    push(@Makefile_config, "OMIT_NETWORK = y\n");
+    push(@config_mk, "EXE = .exe\n");
+    push(@config_mk, "OMIT_NETWORK = y\n");
 #    # Though it may not have the link as "ginstall", "install" in a Windows
 #    # Unix environment is usually GNU install.
 #    my $ginstall_result = `ginstall --version 2>/dev/null`;
 #    if (!$ginstall_result) {
 #        # System doesn't have 'ginstall', so use 'install' instead.
-#        push(@Makefile_config, "INSTALL = install\n");
+#        push(@config_mk, "INSTALL = install\n");
 #    }
-    push(@Makefile_config, 'SYMLINK = ', symlink_command(), "\n");
-    push(@Makefile_config, 'DLLVER=$(NETPBM_MAJOR_RELEASE)', "\n");
-    push(@Makefile_config, "LDSHLIB = " . 
+    push(@config_mk, 'SYMLINK = ', symlink_command(), "\n");
+    push(@config_mk, 'DLLVER=$(NETPBM_MAJOR_RELEASE)', "\n");
+    push(@config_mk, "LDSHLIB = " . 
          '-shared -Wl,--image-base=0x10000000 -Wl,--enable-auto-import', "\n");
+    if ($subplatform ne "cygwin") {
+        push(@config_mk, "MSVCRT = Y\n");
+    }
 } elsif ($platform eq "BEOS") {
-    push(@Makefile_config, "LDSHLIB = -nostart\n");
+    push(@config_mk, "LDSHLIB = -nostart\n");
 } elsif ($platform eq "OPENBSD") {
     # vedge@vedge.com.ar says on 2001.04.29 that there are a ton of 
     # undefined symbols in the Fiasco stuff on OpenBSD.  So we'll just
     # cut it out of the build until someone feels like fixing it.
-    push(@Makefile_config, "BUILD_FIASCO = N\n");
+    push(@config_mk, "BUILD_FIASCO = N\n");
 } elsif ($platform eq "FREEBSD") {
 } elsif ($platform eq "AMIGA") {
-    push(@Makefile_config, "CFLAGS = -m68020-60 -ffast-math -mstackextend\n");
+    push(@config_mk, "CFLAGS = -m68020-60 -ffast-math -mstackextend\n");
 } elsif ($platform eq "UNIXWARE") {
     # Nothing to do.
 } elsif ($platform eq "SCO") {
     # Got this from "John H. DuBois III" <spcecdt@armory.com> 2002.09.27:
-    push(@Makefile_config, "RANLIB = true\n");
+    push(@config_mk, "RANLIB = true\n");
     if ($compiler eq "cc") {
-        push(@Makefile_config, "CFLAGS = -O\n");
-        push(@Makefile_config, "CFLAGS_SHLIB = -O -K pic\n");
-        push(@Makefile_config, "LDSHLIB = -G\n");
-        push(@Makefile_config, "SHLIB_CLIB =\n");
+        push(@config_mk, "CFLAGS = -O\n");
+        push(@config_mk, "CFLAGS_SHLIB = -O -K pic\n");
+        push(@config_mk, "LDSHLIB = -G\n");
+        push(@config_mk, "SHLIB_CLIB =\n");
     } else {
-        makeCompilerGcc(\@Makefile_config);
-        push(@Makefile_config, "LDSHLIB = -shared\n"); 
+        makeCompilerGcc(\@config_mk);
+        push(@config_mk, "LDSHLIB = -shared\n"); 
     }
-    push(@Makefile_config, "NETWORKLD = -lsocket -lresolve\n");
+    push(@config_mk, "NETWORKLD = -lsocket -lresolve\n");
 } elsif ($platform eq "DARWIN") {
-    push(@Makefile_config, "CC = cc -no-cpp-precomp\n");
-    push(@Makefile_config, 'CFLAGS_SHLIB = -fno-common', "\n");
-    push(@Makefile_config, "LDSHLIB = ",
-         "-dynamiclib ",
-         '-install_name $(NETPBMLIB_RUNTIME_PATH)/libnetpbm.$(MAJ).dylib', 
-         "\n");
-#    push(@Makefile_config, "INSTALL = install\n");
+    push(@config_mk, "CC = cc -no-cpp-precomp\n");
+    push(@config_mk, gnuCflags('cc'));
+    push(@config_mk, 'CFLAGS_SHLIB = -fno-common', "\n");
+
+    my $installNameOpt;
+    if ($netpbmlib_runtime_path eq '') {
+        $installNameOpt = '';
+    } else {
+        $installNameOpt  =
+            '-install_name $(NETPBMLIB_RUNTIME_PATH)/libnetpbm.$(MAJ).dylib';
+    }
+    push(@config_mk, "LDSHLIB = -dynamiclib $installNameOpt\n");
+#    push(@config_mk, "INSTALL = install\n");
 } else {
     die ("Internal error: invalid value for \$platform: '$platform'\n");
 }
 
-if (needLocal($platform)) {
-    push(@Makefile_config, "CFLAGS += -I/usr/local/include\n");
-    push(@Makefile_config, "LDFLAGS += -L/usr/local/lib\n");
-}
-
 if ($linkViaCompiler) {
-    push(@Makefile_config, "LINKERISCOMPILER = Y\n");
+    push(@config_mk, "LINKERISCOMPILER = Y\n");
 }
 
 my $flex_result = `flex --version`;
@@ -2019,7 +2226,7 @@ if (!$flex_result) {
         my $key = <STDIN>;
         print("\n");
 
-        push(@Makefile_config, "LEX=\n");
+        push(@config_mk, "LEX=\n");
     } else {
         print("\n");
         print("Using 'lex' as the pattern matcher generator, " .
@@ -2027,70 +2234,70 @@ if (!$flex_result) {
         print("find 'flex' on your system.\n");
         print("\n");
 
-        push(@Makefile_config, "LEX = lex\n"); 
+        push(@config_mk, "LEX = lex\n"); 
     }
 }
 
 if ($compiler eq 'gcc') {
-    push(@Makefile_config, "CFLAGS_SHLIB += -fPIC\n");
+    push(@config_mk, "CFLAGS_SHLIB += -fPIC\n");
 }
 
 if (defined($tiffhdr_dir)) {
-    push(@Makefile_config, "TIFFHDR_DIR = $tiffhdr_dir\n");
+    push(@config_mk, "TIFFHDR_DIR = $tiffhdr_dir\n");
 }
 if (defined($tifflib)) {
-    push(@Makefile_config, "TIFFLIB = $tifflib\n");
+    push(@config_mk, "TIFFLIB = $tifflib\n");
 }
 
 if (defined($jpeghdr_dir)) {
-    push(@Makefile_config, "JPEGHDR_DIR = $jpeghdr_dir\n");
+    push(@config_mk, "JPEGHDR_DIR = $jpeghdr_dir\n");
 }
 if (defined($jpeglib)) {
-    push(@Makefile_config, "JPEGLIB = $jpeglib\n");
+    push(@config_mk, "JPEGLIB = $jpeglib\n");
 }
 
 if (defined($pnghdr_dir)) {
-    push(@Makefile_config, "PNGHDR_DIR = $pnghdr_dir\n");
+    push(@config_mk, "PNGHDR_DIR = $pnghdr_dir\n");
 }
 if (defined($pnglib)) {
-    push(@Makefile_config, "PNGLIB = $pnglib\n");
+    push(@config_mk, "PNGLIB = $pnglib\n");
 }
 
 if (defined($zhdr_dir)) {
-    push(@Makefile_config, "ZHDR_DIR = $zhdr_dir\n");
+    push(@config_mk, "ZHDR_DIR = $zhdr_dir\n");
 }
 if (defined($zlib)) {
-    push(@Makefile_config, "ZLIB = $zlib\n");
+    push(@config_mk, "ZLIB = $zlib\n");
 }
 
 if (defined($x11hdr_dir)) {
-    push(@Makefile_config, "X11HDR_DIR = $x11hdr_dir\n");
+    push(@config_mk, "X11HDR_DIR = $x11hdr_dir\n");
 }
 if (defined($x11lib)) {
-    push(@Makefile_config, "X11LIB = $x11lib\n");
+    push(@config_mk, "X11LIB = $x11lib\n");
 }
 
 if (defined($linuxsvgahdr_dir)) {
-    push(@Makefile_config, "LINUXSVGAHDR_DIR = $linuxsvgahdr_dir\n");
+    push(@config_mk, "LINUXSVGAHDR_DIR = $linuxsvgahdr_dir\n");
 }
 if (defined($linuxsvgalib)) {
-    push(@Makefile_config, "LINUXSVGALIB = $linuxsvgalib\n");
+    push(@config_mk, "LINUXSVGALIB = $linuxsvgalib\n");
 }
 
 if (defined($netpbm_docurl)) {
-    push(@Makefile_config, "NETPBM_DOCURL = $netpbm_docurl\n");
+    push(@config_mk, "NETPBM_DOCURL = $netpbm_docurl\n");
 }
 
 if ($inttypesHeaderFile ne '<inttypes.h>') {
-    push(@Makefile_config, "INTTYPES_H = $inttypesHeaderFile\n");
+    push(@config_mk, "INTTYPES_H = $inttypesHeaderFile\n");
 }
 
 if ($haveInt64 ne 'Y') {
-    push(@Makefile_config, "HAVE_INT64 = $haveInt64\n");
+    push(@config_mk, "HAVE_INT64 = $haveInt64\n");
 }
 
 if ($dontHaveProcessMgmt) {
-    push(@Makefile_config, "DONT_HAVE_PROCESS_MGMT = Y\n");
+    push(@config_mk, "DONT_HAVE_PROCESS_MGMT = Y\n");
 }
 
 #******************************************************************************
@@ -2099,23 +2306,21 @@ if ($dontHaveProcessMgmt) {
 #
 #*****************************************************************************
 
-open(MAKEFILE_CONFIG, ">Makefile.config") or
-    die("Unable to open Makefile.config for writing in the current " .
+open(config_mk, ">config.mk") or
+    die("Unable to open config.mk for writing in the current " .
         "directory.");
 
-print MAKEFILE_CONFIG @Makefile_config;
+print config_mk @config_mk;
 
-close(MAKEFILE_CONFIG) or
-    die("Error:  Close of Makefile.config failed.\n");
+close(config_mk) or
+    die("Error:  Close of config.mk failed.\n");
 
 print("\n");
-print("We have created the file 'Makefile.config'.  Note, however, that \n");
-print("we guessed a lot at your configuration and you may want to look \n");
-print("at Makefile.config and edit it to your requirements and taste \n");
-print("before doing the make.\n");
+print("We have created the file 'config.mk'.  You may want to look \n");
+print("at it and edit it to your requirements and taste before doing the \n");
+print("make.\n");
 print("\n");
 
-
 print("Now you may proceed with 'make'\n");
 print("\n");
 
diff --git a/buildtools/debian/README b/buildtools/debian/README
deleted file mode 100644
index 1c600c6b..00000000
--- a/buildtools/debian/README
+++ /dev/null
@@ -1,122 +0,0 @@
-Files in this directory are for creating a Debian package (.deb file),
-which one can use to install Netpbm on a Debian system (or a system running
-a derivative of Debian, such as Ubuntu or Mint).
-
-You can of course install on one of these systems by running 'installnetpbm',
-or otherwise inserting all the files in the proper place in your system file
-tree, but having a Debian package allows you to manage those files using
-Debian's normal package management.  The package management system will know
-where the Netpbm files came from, and you can upgrade or remove Netpbm easily.
-The package management system will also be aware of prerequisites of Netpbm
-and ensure that you don't have Debian's own inferior version of Netpbm
-installed (which would cause conflicts).
-
-The package we create is named 'netpbm-sf' (where the "sf" is from
-"SourceForge"), to be distinct from the package named "netpbm" which is part
-of Debian.
-
-To install Netpbm as a Debian package:
-
-  1) Follow the regular instructions to build and package Netpbm
-     (configure, make, make package).
-
-  2) With the root of the built Netpbm build tree as your current
-     directory:
-
-     $ make deb
-
-     (This defaults to getting Netpbm from /tmp/netpbm, which is also where
-     'make package' defaults to putting it).
-
-  3) $ dpkg --install netpbm-sfXXXX.deb
-
-     (netpbm-sfXXXX.deb is the file created by 'makedeb', in the current
-     directory).
-
-
-PREREQUSISITES
---------------
-
-The following information was taken from the Wheezy version of Debian, in
-January 2014.
-
-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.
-
-
-Building
---------
-
-You need the following Debian packages to build all of Netpbm.
-
-You could omit some of these and, in the Netpbm build configuration dialog,
-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
-  libtiff5-dev
-  libx11-dev
-  libxml2a-dev
-  zlib1g-dev
-
-
-In addition, you need the following build tools:
-
-  make
-  gcc
-  flex
-  perl
-  pkg-config
-
-
-
-Running
--------
-
-The following Debian packages are the known prerequisites for running Netpbm
-(and the package created by 'mkdeb' knows this).
-
-    libc6
-    libjpeg8
-    libpng12-0
-    libsvga1
-    libtiff5
-    libx11-6
-    zlib1g
-    ghostscript
-    perl
-    perl-base
-    bash
-
-Note that many of these are needed only for a few parts of Netpbm, and it will
-be pretty obvious what the problem is when you need the prerequisite package
-and don't have it, so if you don't want to install a prerequisite, it would
-probably be fine to force install Netpbm, ignoring the prerequisites.
-
-
-CONFLICTS WITH DEBIAN'S NETPBM
-------------------------------
-
-Debian has several packages of Netpbm, all based on a slightly modified
-Sourceforge Netpbm 9.25 from 2002 (don't be confused by Debian's numbering
-system, which makes it look like it is Netpbm 10).  If you want to install
-Sourceforge Netpbm on your system, you will want first to remove any of these
-you have installed:
-
-  netpbm
-  netpbm-dev
-  libnetpbm9
-  libnetpbm10
-
-Sourceforge Netpbm should be backward compatible with all of those.  Note that
-'mkdeb' makes only one package.  It contains the programs, the runtime
-libraries, and the development files.
-
-We have not yet worked out what has to be done about the fact that the Debian
-packaging system thinks the Debian Netpbm packages are prerequisites for
-things.  If you install Sourceforge Netpbm via the package created by mkdeb,
-you should tell the Netpbm maintainer whatever you learn about that.
-
diff --git a/buildtools/debian/mkdeb b/buildtools/debian/mkdeb
deleted file mode 100755
index 42a986eb..00000000
--- a/buildtools/debian/mkdeb
+++ /dev/null
@@ -1,541 +0,0 @@
-#!/usr/bin/perl
-###############################################################################
-#                                mkdeb
-###############################################################################
-#
-#  This generates a Debian packge file (.deb) to install Sourceforge
-#  Netpbm on a Debian system.
-#
-#  This is especially useful because Debian does not have a good Debian
-#  package (what Debian contains is derived from Sourceforge Netpbm ca.
-#  2002).
-#
-###############################################################################
-
-use strict;
-use warnings;
-use English;
-use Getopt::Long;
-
-my $TRUE=1; my $FALSE = 0;
-
-
-
-sub parseCommandLine(@) {
-
-    local @ARGV = @_;  # GetOptions takes input from @ARGV only
-
-    my %cmdline;
-
-    my $validOptions = GetOptions(\%cmdline,
-                                  "buildtools=s",
-                                  "arch=s",
-                                  "pkgdir=s",
-        );
-
-    if (!$validOptions) {
-        print(STDERR "Invalid option syntax.\n");
-        exit(1);
-    }
-    if (@ARGV > 0) {
-        print(STDERR "This program takes no non-option arguments.  " .
-              "You specified ",
-              scalar(@ARGV), "\n");
-        exit(1);
-    } 
-
-    return(\%cmdline);
-}
-
-
-
-sub writeFile($$$$) {
-    my ($fileLinesR, $fileName, $executable, $errorR) = @_;
-
-    my $success = open(FILE, ">$fileName");
-    if ($success) {
-        if ($executable eq 'EXECUTABLE') {
-            chmod(0755, $fileName);
-        } else {
-            chmod(0644, $fileName);
-        }
-        foreach (@{$fileLinesR}) { print FILE; }
-        close(FILE);
-    } else {
-        $$errorR = "Unable to open the file " .
-            "'$fileName' for writing.  Errno=$ERRNO\n";
-    }
-}
-
-
-
-sub netpbmVersion($) {
-    my ($pkgdir) = @_;
-
-    my $versionFileName = "$pkgdir/VERSION";
-
-    my $versionOpened = open(VERSION, "<$versionFileName");
-
-    my $retval;
-    my $error;
-
-    if (!$versionOpened) {
-        $error = "Unable to open '$versionFileName' for reading.  " .
-            "Errno=$ERRNO\n";
-    } else {
-        my $version = <VERSION>;
-        chomp($version);
-
-        if ($version =~ m{^Netpbm (\S*)}) {
-            my ($versionNumber) = ($1);
-            $retval = $versionNumber;
-        } else {
-            die("Can't understand format of '$versionFileName': '$version'");
-        }
-        close(VERSION);
-    }
-
-    if ($error) {
-        print("Failed to determine the version of Netpbm from the package, "
-              . "so that will not be correct in netpbm.config and netpbm.pc.  "
-              . $error . "\n");
-        $retval = "???";
-    }
-    return $retval;
-}
-
-
-
-sub control($$) {
-    my ($release, $architecture) = @_;
-
-# 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.
-
-    my %control;
-
-    my $debianNativeNetpbm = 
-        'netpbm, ' .
-        'libnetpbm10, ' .
-        'libnetpbm10-dev, ' .
-        'netpbm-dev, ' .
-        'netpbm-nonfree, ' .
-        'pbmwbmp, ' .
-        'pnmtopng, ' .
-        'ucbmpeg';
-
-    $control{'Package'} = 'netpbm-sf';
-    $control{'Version'} = $release;
-    $control{'Architecture'} = $architecture;
-    $control{'Maintainer'} = 'Bryan Henderson <bryanh@giraffe-data.com>';
-    $control{'Installed-Size'} = '6164';
-    $control{'Depends'} =
-        'libc6, ' .
-        'libjpeg62, ' .
-        'libpng12-0, ' .
-        'libsvga1, ' .
-        'libtiff4, ' .
-        'libx11-6, ' .
-        'libxml2, ' .
-        'zlib1g, ' .
-        'ghostscript, ' .
-        'perl, ' .
-        'perl-base, ' .
-        'bash'
-        ;
-    $control{'Recommends'} = '';
-    $control{'Recommends'} = '';
-    $control{'Conflicts'} = $debianNativeNetpbm;
-    $control{'Replaces'} = $debianNativeNetpbm;
-    $control{'Provides'} = 
-        'netpbm, ' .
-        'pbmwbmp, ' .
-        'pnmtopng, ' .
-        'netpbm-dev, ' .
-        'libnetpbm10'
-        ;
-    $control{'Section'} = 'graphics';
-    $control{'Priority'} = 'optional';
-    $control{'Section'} = 'graphics';
-    $control{'Description'} = 'Graphics conversion tools between image formats
- Netpbm is a toolkit for manipulation of graphic images, including
- conversion of images between a variety of different formats. There
- are over 300 separate tools in the package including converters for
- more than 80 graphics formats.  This is the Super Stable version from
- the Sourceforge Netpbm project, unmodified.';
-
-    return \%control;
-}
-
-
-
-sub writeControlFile($$) {
-    my ($controlR, $fileName) = @_;
-
-    open(CTL, '>', $fileName)
-        or die "Can't open '$fileName': $ERRNO";
-
-    while (my ($key, $value) = each %{$controlR}) {
-        print CTL ("$key: $value\n");
-    }
-    
-    close(CTL);
-}
-
-
-
-sub createScripts($$) {
-    my ($dpkgDirName, $buildToolsDir) = @_;
-
-    my @scriptList = ('postinst', 'postrm');
-
-    my @scriptFileList = map("$buildToolsDir/debian/$_", @scriptList);
-
-    system('cp', @scriptFileList, "$dpkgDirName/DEBIAN/") &&
-        die("Failed to copy postinst, etc. to '$dpkgDirName/DEBIAN'.");
-
-    my @createdFileList = map("$dpkgDirName/DEBIAN/$_", @scriptList);
-
-    chmod(0755, @createdFileList);
-}
-
-
-
-sub createDirOrDie($) {
-    my ($newDirName) = @_;
-
-    mkdir($newDirName)
-        or die("Couldn't create directory '$newDirName'.  $ERRNO");
-
-    chmod(0755, $newDirName);
-}
-
-
-
-sub 
-processTemplate($$$) {
-    my ($templateR, $infoR, $outputR) = @_;
-
-    my @output;
-
-    foreach (@{$templateR}) {
-        if (m{^@}) {
-            # Comment -- ignore it.
-        } else {
-            if (defined($infoR->{VERSION})) {
-                s/\@VERSION\@/$infoR->{VERSION}/;
-            }
-            if (defined($infoR->{BINDIR})) {
-                s/\@BINDIR@/$infoR->{BINDIR}/;
-            }
-            if (defined($infoR->{LIBDIR})) {
-                s/\@LIBDIR@/$infoR-.{LIBDIR}/;
-            }
-            if (defined($infoR->{LINKDIR})) {
-                s/\@LINKDIR@/$infoR->{LINKDIR}/;
-            }
-            if (defined($infoR->{DATADIR})) {
-                s/\@DATADIR@/$infoR->{DATADIR}/;
-            }
-            if (defined($infoR->{INCLUDEDIR})) {
-                s/\@INCLUDEDIR@/$infoR->{INCLUDEDIR}/;
-            }
-            if (defined($infoR->{MANDIR})) {
-                s/\@MANDIR@/$infoR->{MANDIR}/;
-            }
-            push(@output, $_);
-        }
-    }
-    $$outputR = \@output;
-}
-
-
-
-
-
-sub makeConfig($$$) {
-    my ($fileName, $templateSubsR, $netpbmPkgDir) = @_;
-#-----------------------------------------------------------------------------
-# Install 'netpbm-config' -- a program you run to tell you things about
-# how Netpbm is installed.
-#-----------------------------------------------------------------------------
-    my $error;
-
-    my $configTemplateFilename = $netpbmPkgDir . "/config_template";
-
-    my $templateOpened = open(TEMPLATE, "<$configTemplateFilename");
-    if (!$templateOpened) {
-        $error = "Can't open template file '$configTemplateFilename'.\n";
-    } else {
-        my @template = <TEMPLATE>;
-
-        close(TEMPLATE);
-
-        processTemplate(\@template, $templateSubsR, \my $fileContentsR);
-
-        writeFile($fileContentsR, $fileName, 'EXECUTABLE', \$error);
-    }
-    if ($error) {
-        print(STDERR "Failed to create the Netpbm configuration program.  " .
-              "$error\n");
-    }
-}
-
-
-
-sub makePkgConfig($$$) {
-    my ($fileName, $templateSubsR, $netpbmPkgDir) = @_;
-#-----------------------------------------------------------------------------
-# Install a pkg-config file (netpbm.pc) - used by the 'pkg-config' program to
-# find out various things about how Netpbm is installed.
-#-----------------------------------------------------------------------------
-    my $error;
-
-    my $pcTemplateFilename = "$netpbmPkgDir/pkgconfig_template";
-
-    my $templateOpened = open(TEMPLATE, "<$pcTemplateFilename");
-    if (!$templateOpened) {
-        $error = "Can't open template file '$pcTemplateFilename'.\n";
-    } else {
-        my @template = <TEMPLATE>;
-
-        close(TEMPLATE);
-
-        processTemplate(\@template, $templateSubsR,
-                        \my $fileContentsR);
-
-        writeFile($fileContentsR, $fileName, 'NOTEXECUTABLE', \$error);
-    }
-    if ($error) {
-        print(STDERR "Failed to create the Netpbm Pkg-config file.  " .
-              "$error\n");
-    }
-}
-
-
-
-sub makeManweb($$) {
-    my ($dpkgDirName, $netpbmPkgDir) = @_;
-#-----------------------------------------------------------------------------
-#  Set up things so one can read the manual with e.g.
-#
-#    $ manweb pnmtojpeg
-#-----------------------------------------------------------------------------
-    my @manwebConfContents;
-
-    push(@manwebConfContents, "#Configuration file for Manweb\n");
-    push(@manwebConfContents, "webdir=/usr/man/web\n");
-
-    createDirOrDie("$dpkgDirName/etc");
-
-    my $manwebConfFileName = "$dpkgDirName/etc/manweb.conf";
-
-    writeFile(\@manwebConfContents, $manwebConfFileName,
-        'NOTEXECUTABLE', \my $error);
-
-    if ($error) {
-        die("Failed to create Manweb configuration file $manwebConfFileName");
-    }
-    createDirOrDie("$dpkgDirName/usr/man");
-
-    system('cp', '--archive',
-           "$netpbmPkgDir/man/web", "$dpkgDirName/usr/man/web") &&
-               die("Failed to copy executables from '$netpbmPkgDir/bin' " .
-                   "to '$dpkgDirName/usr/bin'");
-}
-
-
-
-sub buildDpkgBuildTree($$$$$) {
-    my ($dpkgDirName, $release, $netpbmPkgDir, $architecture,
-        $buildToolsDir) = @_;
-#-----------------------------------------------------------------------------
-#  Create the directory tree that is the input to Dpkg-deb --build.
-#  This tree contains all the files to be installed, _plus_ the control
-#  subdirectory named DEBIAN.
-#-----------------------------------------------------------------------------
-    print("Creating file tree for input to dpkg-deb --build as " .
-          "'$dpkgDirName'\n");
-
-    createDirOrDie($dpkgDirName);
-
-    createDirOrDie("$dpkgDirName/DEBIAN");
-
-    my $controlR = control($release, $architecture);
-
-    writeControlFile($controlR, "$dpkgDirName/DEBIAN/control");
-
-    createScripts($dpkgDirName, $buildToolsDir);
-
-    createDirOrDie("$dpkgDirName/usr");
-
-    system('cp', '--archive',
-           "$netpbmPkgDir/bin", "$dpkgDirName/usr/bin") &&
-               die("Failed to copy executables from '$netpbmPkgDir/bin' " .
-                   "to '$dpkgDirName/usr/bin'");
-
-    # doc.url is inappropriate with the program installed into the global
-    # /usr/bin .
-    unlink("$dpkgDirName/usr/bin/doc.url");
-
-    system("cp", "--archive", "$netpbmPkgDir/include",
-           "$dpkgDirName/usr/include") &&
-               die("Failed to copy header files from " .
-                   "'$netpbmPkgDir/include' " .
-                   "to '$dpkgDirName/usr/include'");
-
-    system("cp", "--archive",
-           "$netpbmPkgDir/lib", "$dpkgDirName/usr/lib") &&
-               die("Failed to copy libraries from '$netpbmPkgDir/lib' " .
-                   "to '$dpkgDirName/usr/lib'");
-
-    my @linkFileList = glob("$netpbmPkgDir/link/*");
-
-    if (@linkFileList > 0) {
-        system("cp", "--archive",
-               @linkFileList, "$dpkgDirName/usr/lib/") &&
-                   die("Failed to copy libraries from '$netpbmPkgDir/link' " .
-                       "to '$dpkgDirName/usr/lib'");
-    }
-    createDirOrDie("$dpkgDirName/usr/share");
-
-    system("cp", "--archive",
-           "$netpbmPkgDir/misc", "$dpkgDirName/usr/share/netpbm") &&
-               die("Failed to copy files from '$netpbmPkgDir/misc' " .
-                   "to '$dpkgDirName/usr/share/netpbm'");
-
-    # We install Netpbm in the default search path, so most of the values
-    # 'netpbm-config' returns are null strings.
-    my $templateSubsR =
-    {VERSION    => $release,
-     BINDIR     => '',
-     LIBDIR     => '',
-     LINKDIR    => '',
-     DATADIR    => '/usr/share/netpbm',
-     INCLUDEDIR => '',
-     MANDIR     => ''};
-
-    makeConfig("$dpkgDirName/usr/bin/netpbm-config", $templateSubsR,
-               $netpbmPkgDir);
-
-    createDirOrDie("$dpkgDirName/usr/lib/pkgconfig");
-
-    makePkgConfig("$dpkgDirName/usr/lib/pkgconfig/netpbm.pc", $templateSubsR,
-                  $netpbmPkgDir);
-
-    makeManweb($dpkgDirName, $netpbmPkgDir);
-}
-
-
-
-sub debianArchOfThisMachine() {
-
-    # A lazy implementation that handles only the most common cases
-
-    my $retval;
-
-    my $arch = qx{'arch'};
-    chomp($arch);
-
-    if ($arch eq 'x86_64') {
-        $retval = 'amd64';
-    } elsif ($arch =~ m{i.86}) {
-        $retval = 'i386';
-    } else {
-        die("Can't determine the Debian architecture classification of this " .
-            "system.  You'll have to give a -arch option");
-    }
-    return $retval;
-}
-
-
-
-sub buildToolsDir($) {
-    my ($cmdlineR) = @_;
-
-    my $retval;
-
-    if (exists($cmdlineR->{'buildtools'})) {
-        $retval = $cmdlineR->{'buildtools'};
-    } else {
-        if (-f('./debian/mkdeb')) {
-            $retval = '.';
-        } else {
-            die("The current directory does not appear to be 'buildtools' " .
-                "subdirectory of a Netpbm source tree, so you will have " .
-                "to use the -buildtools option to identify it");
-        }
-    }
-    return $retval;
-}
-
-
-
-sub netpbmPkgDir($) {
-    my ($cmdlineR) = @_;
-
-    my $retval;
-
-    if (exists($cmdlineR->{'pkgdir'})) {
-        $retval = $cmdlineR->{'pkgdir'};
-    } else {
-        my $tmpdir = $ENV{TMPDIR} || "/tmp";
-
-        my $defaultPkgDir = "$tmpdir/netpbm";
-
-        if (-d($defaultPkgDir)) {
-            $retval = $defaultPkgDir;
-        } else {
-            die("No directory '$defaultPkgDir' exists.  " .
-                "You can specify the Netpbm package directory " .
-                "(what 'make package' created), with a -pkgdir option");
-        }
-    }
-    return $retval;
-}
-
-
-
-sub arch($) {
-    my ($cmdlineR) = @_;
-
-    my $retval;
-
-    if (exists($cmdlineR->{'arch'})) {
-        $retval = $cmdlineR->{'arch'};
-    } else {
-        $retval = debianArchOfThisMachine();
-    }
-
-    return $retval;
-}
-
-
-
-###############################################################################
-#                               MAIN PROGRAM
-###############################################################################
-
-my $cmdlineR = parseCommandLine(@ARGV);
-
-my $buildTools = buildToolsDir($cmdlineR);
-
-my $netpbmPkgDir = netpbmPkgDir($cmdlineR);
-
-my $arch = arch($cmdlineR);
-
-my $release = netpbmVersion($netpbmPkgDir);
-
-my $dpkgDirName = "/tmp/netpbm-sf-$release";
- 
-buildDpkgBuildTree($dpkgDirName, $release, $netpbmPkgDir, $arch,
-                   $buildTools);
-
-my $debFileName = 'netpbm-sf-' . $release . '_' . $arch . '.deb';
-
-system('dpkg-deb', '--build', $dpkgDirName, $debFileName) &&
-    die("dpgk-deb --build with input '$dpkgDirName' failed.");
-
-system('rm', '--recursive', $dpkgDirName);
diff --git a/buildtools/debian/postinst b/buildtools/debian/postinst
deleted file mode 100755
index 60836d52..00000000
--- a/buildtools/debian/postinst
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/sh
-set -e
-if [ "$1" = "configure" ]; then
-  ldconfig
-fi
diff --git a/buildtools/debian/postrm b/buildtools/debian/postrm
deleted file mode 100755
index 5f0c15e7..00000000
--- a/buildtools/debian/postrm
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/sh
-set -e
-if [ "$1" = "remove" ]; then
-  ldconfig
-fi
diff --git a/buildtools/endiangen.c b/buildtools/endiangen.c
index 07560c10..6b88b896 100644
--- a/buildtools/endiangen.c
+++ b/buildtools/endiangen.c
@@ -59,9 +59,9 @@ byteOrder(void) {
 
 
 static unsigned int
-bitsPerWord(void) {
+bitsPerLong(void) {
 
-    return MAX(sizeof(long), sizeof(int)) * 8;
+    return sizeof(long) * 8;
 }
 
 
@@ -87,7 +87,7 @@ main(int argc, char **argv) {
            byteOrder() == ENDIAN_LITTLE ? "LITTLE_ENDIAN" : "BIG_ENDIAN");
     printf("#endif\n");
     printf("\n");
-    printf("#define BITS_PER_WORD %u\n", bitsPerWord());
+    printf("#define BITS_PER_LONG %u\n", bitsPerLong());
 
     return 0;
 }
diff --git a/buildtools/installnetpbm.pl b/buildtools/installnetpbm.pl
index f6ab7ca0..25376ec3 100755
--- a/buildtools/installnetpbm.pl
+++ b/buildtools/installnetpbm.pl
@@ -554,7 +554,19 @@ sub installHeader($$$) {
               "failed.\n");
         print("cp exit code is $rc\n");
     } else {
-        print("done.\n");
+        # Install symbolic links for backward compatibility (because the
+        # netpbm/ subdirectory wasn't used before Netpbm 10.41 (December
+        # 2007).
+
+        my $rc = system("cd $hdrDir; ln -s netpbm/* .");
+
+        if ($rc != 0) {
+            print("Failed to create backward compatibilty symlinks from " .
+                  "$hdrDir into $hdrDir/netpbm\n");
+            print("ln exit code is $rc\n");
+        } else {
+            print("done.\n");
+        }
     }
     $$includedirR = $hdrDir;
 }
diff --git a/buildtools/installosf b/buildtools/installosf
index a4e5c262..1d1d61a7 100755
--- a/buildtools/installosf
+++ b/buildtools/installosf
@@ -4,7 +4,7 @@
 # its $(INSTALL) program and invokes OSF1 Install with the proper
 # parameters to effect what the make file wants.  If your system has
 # OSF1 Install on it, you can just set the INSTALL variable in
-# Makefile.config to "installosf" and 'make install' will work for
+# config.mk to "installosf" and 'make install' will work for
 # you.
 
 # Of course, you could also just install Ginstall and forget about this
diff --git a/buildtools/libopt.c b/buildtools/libopt.c
index 8e21e1be..a0bf1cda 100644
--- a/buildtools/libopt.c
+++ b/buildtools/libopt.c
@@ -11,8 +11,8 @@
   filename to be searched for in the linker's default search path, and
   generate a -l option, but no -L.
 
-  If an argument doesn't make sense as a library filespec, it is
-  copied verbatim, blank delimited, to the output string.
+  If an argument doesn't make sense as a library filespec, we copy
+  it verbatim, blank delimited, to the output string.
 
   The "lib" part of the library name, which we call the prefix, may be
   other than "lib".  The list of recognized values is compiled in as
@@ -52,11 +52,11 @@
 
      NETPBMLIB=../lib/libnetpbm.so
      ...
-     pbmmake: pbmmake.o $(PBMLIB)
-             ld -o pbmmake pbmmake.o `libopt $(PBMLIB)` --rpath=/lib/netpbm
+     pbmmake: pbmmake.o $(NETPBMLIB)
+             ld -o pbmmake pbmmake.o `libopt $(NETPBMLIB)` --rpath=/lib/netpbm
 
   Caveat: "-L../lib -lnetpbm" is NOT exactly the same as
-  "../pbm/libnetpbm.so" on any system.  All of the -l libraries are
+  "../lib/libnetpbm.so" on any system.  All of the -l libraries are
   searched for in all of the -L directories.  So you just might get a
   different library with the -L/-l version than if you specify the
   library file explicitly.
diff --git a/buildtools/makeman b/buildtools/makeman
index 634a2c79..2e122779 100755
--- a/buildtools/makeman
+++ b/buildtools/makeman
@@ -19,7 +19,8 @@
 #  * Loses summary information from tables.
 #  * Only permits one <HR> in the HTML, right before the index.
 #
-# Use the makeman: passthrough to insert format lines for tables.
+# You can use the <?makeman ?> PI to pass text directly through to the
+# generated manual page,  A major use is to insert format lines for tables.
 #
 # By Eric S. Raymond <esr@thyrsus.com>
 # Version 1.0, July 26 2004
@@ -29,7 +30,7 @@ import os, sys, exceptions, re
 source = "netpbm documentation"
 section = 1
 
-warning = '''\
+warning = r'''\
 .\" This man page was generated by the Netpbm tool 'makeman' from HTML source.
 .\" Do not hand-hack it!  If you have bug fixes or improvements, please find
 .\" the corresponding HTML page on the Netpbm website, generate a patch
@@ -47,11 +48,15 @@ def makeman(name, file, indoc):
     # Dot at left margin confuses troff.
     # This program generates these,
     indoc = indoc.replace("\n.", "\n@%@%@")
+    # Protect escapes before we try generating font changes.
+    indoc = indoc.replace("\\", r"\e")
     # Header-bashing
+    indoc = indoc.replace('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "DTD/xhtml11.dtd">', "")
+    indoc = indoc.replace('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">', "")
     indoc = indoc.replace('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2//EN">\n',"")
     indoc = indoc.replace('<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">', "")
+    indoc = indoc.replace('<meta http-equiv="Content-Type" content="text/html; charset=us-ascii"/>', "")
     indoc = indoc.replace('<?xml version="1.1" encoding="iso-8859-1" ?>\n',"")
-    indoc = indoc.replace('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "DTD/xhtml11.dtd">', "")
     indoc = indoc.replace('<html xmlns="http://www.w3.org/1999/xhtml">', "")
     indoc = indoc.replace("<HEAD>", "").replace("</HEAD>", "")
     indoc = indoc.replace("<head>", "").replace("</head>", "")
@@ -93,8 +98,8 @@ def makeman(name, file, indoc):
     # Literal layout
     indoc = re.sub("(?i)\n *<PRE>", "\n.nf", indoc)
     indoc = re.sub("(?i)\n *</PRE>", "\n.fi", indoc)
-    indoc = re.sub("(?i)\n *<BLOCKQUOTE>", "\n.nf", indoc)
-    indoc = re.sub("(?i)\n *</BLOCKQUOTE>", "\n.fi", indoc)
+    indoc = re.sub("(?i)\n *<BLOCKQUOTE>", "\n.RS", indoc)
+    indoc = re.sub("(?i)\n *</BLOCKQUOTE>", "\n.RE", indoc)
     # Highlight processing
     indoc = re.sub("(?i)<B>", r"\\fB", indoc)
     indoc = re.sub("(?i)</B>", r"\\fP", indoc)
@@ -108,6 +113,8 @@ def makeman(name, file, indoc):
     indoc = re.sub("(?i)</TT>", r"\\fP", indoc)
     indoc = re.sub("(?i)<KBD>", r"\\f(CW", indoc)
     indoc = re.sub("(?i)</KBD>", r"\\fP", indoc)
+    indoc = re.sub("(?i)<CODE>", r"\\f(CW", indoc)
+    indoc = re.sub("(?i)</CODE>", r"\\fP", indoc)
     indoc = re.sub("(?i)<STRONG>", r"\\fB", indoc)
     indoc = re.sub("(?i)</STRONG>", r"\\fP", indoc)
     indoc = re.sub("(?i)<SUP>", r"\\u", indoc)
@@ -150,6 +157,7 @@ def makeman(name, file, indoc):
     indoc = indoc.replace("&lt;", "@#!#@").replace("&gt;", "#@!@#").replace("&amp;", "#!@!@!#")
     indoc = indoc.replace("&#215;", r"\(mu")
     indoc = indoc.replace("&#174;", r"\*R")
+    indoc = indoc.replace("&copy;", r"\(co")
     # Turn anchors into .UN tags
     indoc = re.sub('(?i)<A NAME *= *"#?([a-zA-Z][a-zA-Z0-9.-]+)">(?:&nbsp;)*</A>\s*', ".UN \\1\n", indoc)
     # Strip off the index trailer
@@ -159,17 +167,17 @@ def makeman(name, file, indoc):
     indoc = indoc.replace("</BODY>", "").replace("</HTML>", "")
     indoc = indoc.replace("</body>", "").replace("</html>", "")
     # Recognize sections with IDs
-    indoc = re.sub('(?i)<H2><A (?:ID|NAME)="([a-zA-Z]+)">([^><]*)</A></H2>',
+    indoc = re.sub('(?i)<H2><A (?:ID|NAME)="([a-zA-Z][_a-zA-Z0-9-]+)">([^><]*)</A></H2>',
                    ".UN \\1\n.SH \\2", indoc)
-    indoc = re.sub('(?i)<H3><A (?:ID|NAME)="([a-zA-Z]+)">([^><]*)</A></H3>',
+    indoc = re.sub('(?i)<H3><A (?:ID|NAME)="([a-zA-Z][_a-zA-Z0-9-]+)">([^><]*)</A></H3>',
                    ".UN \\1\n.SS \\2", indoc)
-    indoc = re.sub('(?i)<H4><A (?:ID|NAME)="([a-zA-Z]+)">([^><]*)</A></H4>',
+    indoc = re.sub('(?i)<H4><A (?:ID|NAME)="([a-zA-Z][_a-zA-Z0-9-]+)">([^><]*)</A></H4>',
                    ".UN \\1\n.B \\2", indoc)
-    indoc = re.sub('(?i)<H2 (?:ID|NAME)="([a-zA-Z]+)">([^><]*)</H2>',
+    indoc = re.sub('(?i)<H2 (?:ID|NAME)="([a-zA-Z][_a-zA-Z0-9-]+)">([^><]*)</H2>',
                    ".UN \\1\n.SH \\2", indoc)
-    indoc = re.sub('(?i)<H3 (?:ID|NAME)="([a-zA-Z]+)">([^><]*)</H3>',
+    indoc = re.sub('(?i)<H3 (?:ID|NAME)="([a-zA-Z][_a-zA-Z0-9-]+)">([^><]*)</H3>',
                    ".UN \\1\n.SS \\2", indoc)
-    indoc = re.sub('(?i)<H4 (?:ID|NAME)="([a-zA-Z]+)">([^><]*)</H4>',
+    indoc = re.sub('(?i)<H4 (?:ID|NAME)="([a-zA-Z][_a-zA-Z0-9-]+)">([^><]*)</H4>',
                    ".UN \\1\n.B \\2", indoc)
     # Sections without IDs
     indoc = re.sub('(?i)<H2>([^><]*)</H2>', ".SH \\1", indoc)
@@ -192,7 +200,7 @@ def makeman(name, file, indoc):
     # Passthrough
     indoc = re.sub(r"<\?makeman (.*) \?>", r'\1', indoc)
     # Comments
-    indoc = re.sub("<!--([^-])*-->", r'.\"\1', indoc)
+    indoc = re.sub("<!--([^\n])*-->", r'.\"\1', indoc)
     # Image tags
     indoc = re.sub(' *<img src="([^"]*)" alt="([^"]*)"( *[a-z]*="?[0-9]*"?)*>', ".B \\2\n.IMG -C \\1", indoc)
     # Special characters
@@ -217,7 +225,7 @@ def makeman(name, file, indoc):
     # Time for error checking now
     badlines = []
     for line in indoc.split("\n"):
-        if "<" in line or ">" in line or re.search("&.*;", line):
+        if "<" in line or ">" in line.replace(" >", "") or re.search("&.*;", line):
             badlines.append(line)
     if badlines:
         sys.stderr.write(("Bad lines from %s:\n-----------------\n" % file) + "\n".join(badlines) + "\n-----------------\n")
@@ -236,19 +244,22 @@ def makeman(name, file, indoc):
 def main(args, mainout=sys.stdout, mainerr=sys.stderr):
     global sectmap
     import getopt
-    (options, arguments) = getopt.getopt(args, "v")
+    (options, arguments) = getopt.getopt(args, "vd:")
+    dirprefix = ""
     verbosity = 0
     for (switch, val) in options:
-        if switch == '-v':
+        if switch == '-d':	# Set HTML input directory
+            dirprefix = val
+        elif switch == '-v':	# Enable verbose error reporting
             verbosity += 1
     try:
         # First pass: gather locations for crossreferences:
         sectmap = {}
         for file in arguments:
             try: 
-                infp = open(file)
+                infp = open(os.path.join(dirprefix, file))
             except:
-                sys.stderr.write("can't open %s" % name)
+                sys.stderr.write("makeman: can't open %s\n" % file)
                 continue
             indoc = infp.read()
             infp.close()
@@ -277,9 +288,9 @@ def main(args, mainout=sys.stdout, mainerr=sys.stderr):
         # Second pass: do formatting
         for file in arguments:
             try: 
-                infp = open(file)
+                infp = open(os.path.join(dirprefix, file))
             except:
-                sys.stderr.write("can't open %s" % name)
+                sys.stderr.write("makeman: can't open %s\n" % file)
                 continue
             indoc = infp.read()
             infp.close()
@@ -321,6 +332,7 @@ def main(args, mainout=sys.stdout, mainerr=sys.stderr):
             (exc_type, exc_value, exc_traceback) = sys.exc_info()
             raise exc_type, exc_value, exc_traceback
         else:
+            mainerr.write("makeman: internal error!\n")
             return 5
 
 if __name__ == "__main__":
diff --git a/buildtools/makepointerman b/buildtools/makepointerman
index 8fbb0f49..58d3a64c 100755
--- a/buildtools/makepointerman
+++ b/buildtools/makepointerman
@@ -74,11 +74,11 @@ print(MANPAGE "If that doesn't work, also try " .
 print(MANPAGE "emailing Bryan Henderson, bryanh\@giraffe-data.com.\n");
 
 print(MANPAGE "\n");
-print(MANPAGE "Note that making the documentation available this way was\n");
-print(MANPAGE "a choice of the person who installed Netpbm on this system.\n");
-print(MANPAGE "It is also possible to install Netpbm such that you would\n");
-print(MANPAGE "simply see the documentation instead of the message you are\n");
-print(MANPAGE "reading now.\n");
+print(MANPAGE "Note that it is possible to install Netpbm with the\n");
+print(MANPAGE "documentation available differently.  For example, you\n");
+print(MANPAGE "could simply see the documentation instead of the message\n");
+print(MANPAGE "you are reading now.  The file 'doc/USERDOC' in the Netpbm\n");
+print(MANPAGE "source tree contains details.");
 print(MANPAGE "\n");
 
 if ($format eq "nroff") {
diff --git a/buildtools/Makefile.manpage b/buildtools/manpage.mk
index e8c17972..e1c0bce2 100644
--- a/buildtools/Makefile.manpage
+++ b/buildtools/manpage.mk
@@ -1,10 +1,8 @@
-# -*-makefile-*-    <-- an Emacs control
-
 # Make Unix man pages from Netpbm HTML user manual
 
 MAKEMAN = makeman
 
-MANDIR = /usr/share/man/man1
+MANDIR = /usr/share/man/
 
 # These can convert to man pages cleanly
 MAN1 = \
@@ -16,6 +14,7 @@ MAN1 = \
 	bmptopnm.1 \
 	bmptoppm.1 \
 	brushtopbm.1 \
+	cameratopam.1 \
 	cmuwmtopbm.1 \
 	ddbugtopbm.1 \
 	escp2topbm.1 \
@@ -41,6 +40,7 @@ MAN1 = \
 	leaftoppm.1 \
 	lispmtopgm.1 \
 	macptopbm.1 \
+	manweb.1 \
 	mdatopbm.1 \
 	mgrtopbm.1 \
 	mrf.1 \
@@ -58,6 +58,7 @@ MAN1 = \
 	pamedge.1 \
 	pamendian.1 \
 	pamfile.1 \
+	pamfixtrunc.1 \
 	pamflip.1 \
 	pamfunc.1 \
 	pamgauss.1 \
@@ -74,8 +75,8 @@ MAN1 = \
 	pamstereogram.1 \
 	pamstretch-gen.1 \
 	pamstretch.1 \
-	pamsummcol.1 \
 	pamsumm.1 \
+	pamsummcol.1 \
 	pamtodjvurle.1 \
 	pamtohdiff.1 \
 	pamtohtmltbl.1 \
@@ -186,8 +187,8 @@ MAN1 = \
 	pnmquant.1 \
 	pnmremap.1 \
 	pnmrotate.1 \
-	pnmscalefixed.1 \
 	pnmscale.1 \
+	pnmscalefixed.1 \
 	pnmshear.1 \
 	pnmsmooth.1 \
 	pnmsplit.1 \
@@ -208,8 +209,8 @@ MAN1 = \
 	pnmtorle.1 \
 	pnmtosgi.1 \
 	pnmtosir.1 \
-	pnmtotiffcmyk.1 \
 	pnmtotiff.1 \
+	pnmtotiffcmyk.1 \
 	pnmtoxwd.1 \
 	ppm3d.1 \
 	ppmbrighten.1 \
@@ -230,8 +231,8 @@ MAN1 = \
 	ppmnorm.1 \
 	ppmntsc.1 \
 	ppmpat.1 \
-	ppmquantall.1 \
 	ppmquant.1 \
+	ppmquantall.1 \
 	ppmrainbow.1 \
 	ppmrelief.1 \
 	ppmrough.1 \
@@ -243,12 +244,12 @@ MAN1 = \
 	ppmtoarbtxt.1 \
 	ppmtobmp.1 \
 	ppmtoeyuv.1 \
-	ppmtogif.1 \
 	ppmtoicr.1 \
 	ppmtoilbm.1 \
 	ppmtojpeg.1 \
 	ppmtoleaf.1 \
 	ppmtolj.1 \
+	ppmtomap.1 \
 	ppmtomitsu.1 \
 	ppmtompeg.1 \
 	ppmtoneo.1 \
@@ -301,6 +302,35 @@ MAN1 = \
 	yuvsplittoppm.1 \
 	yuvtoppm.1 \
 	zeisstopnm.1 \
+        pamaddnoise.1 \
+        pambackground.1 \
+        pambayer.1 \
+        pamdepth.1 \
+        pamenlarge.1 \
+        pamgradient.1 \
+        pammasksharpen.1 \
+        pammixinterlace.1 \
+        pampick.1 \
+        pamrgbatopng.1 \
+        pamsplit.1 \
+        pamthreshold.1 \
+        pamtilt.1 \
+        pamtofits.1 \
+        pamtogif.1 \
+        pamtosvg.1 \
+        pamtotiff.1 \
+        pamtoxvmini.1 \
+        pamx.1 \
+        pbmtoibm23xx.1 \
+        pbmtomatrixorbital.1 \
+        pgmdeshadow.1 \
+        pgmmake.1 \
+        pgmmedian.1 \
+        ppmdcfont.1 \
+        ppmddumpfont.1 \
+        ppmdmkfont.1 \
+        ppmdraw.1 \
+        rlatopam.1 \
 
 MAN3 = \
 	libnetpbm.3 \
@@ -318,39 +348,57 @@ 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 = $(MAN1) netpbm.1 $(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
 
-# These things don't get converted to manual pages
-# They're basically link lists, not useful in the man hierarchy.
-EXCEPTIONS = directory.html libnetpbm_dir.html
+# 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
+# Make man pages -- reports bad lines to standard error.
 manpages:
-	@python $(MAKEMAN) index.html $(HTMLMANUALS) 
-	mv index.1 netpbm.1
+	@python $(MAKEMAN) -d $(USERGUIDE) index.html $(HTMLMANUALS) 
+	@mv index.1 netpbm.1
 
 # Make XML pages, and validate them.
 xmlpages:
-	@for x in $(MANPAGES); do doclifter $$x; done
-	@for x in $(XML); do xmllint -xinclude --postvalid $$x >/dev/null; done
+	@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: manpages
-	for f in $(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
+installman:
+	set -x
+	for f in $(MAN1); do if [ -f $$f ]; then gzip <$$f >$(MANDIR)/man1/$$f.gz; fi; done
+	for f in $(MAN3); do if [ -f $$f ]; then gzip <$$f >$(MANDIR)/man3/$$f.gz; fi; done
+	for f in $(MAN5); do if [ -f $$f ]; then gzip <$$f >$(MANDIR)/man5/$$f.gz; fi; done
+
+# This will uninstall them
+uninstallman:
+	for f in $(MAN1); do rm -f $(MANDIR)/man1/$$f.gz; fi; done
+	for f in $(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
diff --git a/Makefile.common b/common.mk
index 21aabb0e..ed4f68bb 100644
--- a/Makefile.common
+++ b/common.mk
@@ -1,4 +1,3 @@
-# -*-makefile-*-    <-- an Emacs control
 # This is a make file inclusion, to be included in all the
 # Netpbm make files.
 
@@ -36,7 +35,6 @@
 # INSTALL: command to use to copy files to where they belong
 # INSTALL_PERM_BIN: file permissions for installed binaries
 # INSTALL_PERM_LIB: ...same for libraries
-# INSTALL_PERM_HDR: ...same for headers
 # INSTALL_PERM_MAN: ...same for man pages
 # MERGE_OBJECTS: list of object files that go into the merged executable
 #   from the current directory (not subdirectories).  All of these are to
@@ -52,32 +50,36 @@
 #   front end, so takes linker options in a different format
 # LDFLAGS: linker options 
 # LIBS or LOADLIBES: names of libraries to be added to all links
-# INCLUDES: Compiler option string to establish the search path for include
-#   files when compiling things or computing dependencies (make dep).
-#   current directory, corresponding source tree directory, and ./importinc
-#   are implied, so should not be in here.
-
-# In addition, there is CADD, which is extra C compilation flags and
+# COMP_INCLUDES: Compiler option string to establish the search path for
+#   component-specific include files when compiling things or computing
+#   dependencies (make dep).  Header files from this part of the search
+#   path take precedence over general Netpbm header files and external
+#   library header files.
+# EXTERN_INCLUDES: Like COMP_INCLUDES, but for external libraries, e.g.
+#   libjpeg.  All header files from the Netpbm source tree take precedence
+#   over these.
+
+# In addition, there is CADD, which is extra C compilation options and
 # is intended to be set on a make command line (e.g. 'make CADD=-g')
-# for flags that apply just to a particular build.
+# for options that apply just to a particular build.
 
 # In addition, there is CFLAGS_PERSONAL, which is extra C
-# compilation flags and is expected to be set via environment variable
-# for flags that are particular to the person doing the build and not
+# compilation options and is expected to be set via environment variable
+# for options that are particular to the person doing the build and not
 # specific to Netpbm.
 
-include $(SRCDIR)/Makefile.version
+include $(SRCDIR)/version.mk
 
-# The following poses a problem when xml2-config or libpng-config indicates
-# (incorrectly) that you need -I /usr/include to compile for them.  In that
-# case, $(INCLUDES) may include -I /usr/include before the -I's for the local
-# directories.  So if there is another Netpbm installed in /usr/include, the
-# build is wrong (it fails).  This is fixed in later Netpbm by changing the
-# order, but it is more complex than just moving around the following line,
-# because $(INCLUDES) also contains -I's for some local directories, which
-# must go before -I importinc.  It is too complex for the Super Stable series.
-#
-INCLUDES2 := $(INCLUDES) -I$(SRCDIR)/$(SUBDIR) -I. -I importinc
+# .DELETE_ON_ERROR is a special predefined Make target that says to delete
+# the target if a command in the rule for it fails.  That's important,
+# because we don't want a half-made target sitting around looking like it's
+# fully made.
+.DELETE_ON_ERROR:
+
+NETPBM_INCLUDES := -Iimportinc -I$(SRCDIR)/$(SUBDIR)
+
+# -I. is needed when builddir != srcdir
+INCLUDES = -I. $(COMP_INCLUDES) $(NETPBM_INCLUDES) $(EXTERN_INCLUDES)
 
 ifeq ($(NETPBMLIBTYPE),unixstatic)
   NETPBMLIBFNAME = libnetpbm.$(STATICLIBSUFFIX)
@@ -118,12 +120,13 @@ IMPORTINC_ROOT_HEADERS := pm_config.h inttypes_netpbm.h version.h
 IMPORTINC_LIB_HEADERS := \
   pm.h pbm.h pgm.h ppm.h pnm.h pam.h bitio.h pbmfont.h ppmcmap.h \
   pammap.h colorname.h ppmfloyd.h ppmdraw.h pm_system.h ppmdfont.h \
-  pm_gamma.h lum.h
+  pm_gamma.h lum.h dithers.h
 
 IMPORTINC_LIB_UTIL_HEADERS := \
-  bitreverse.h mallocvar.h nstring.h filename.h pm_c_util.h shhopt.h \
-  wordaccess.h wordaccess_64_le.h wordaccess_gcc3_be.h wordaccess_gcc3_le.h \
-  wordaccess_generic.h intcode.h \
+  bitarith.h bitreverse.h filename.h intcode.h floatcode.h mallocvar.h\
+  nsleep.h nstring.h pm_c_util.h shhopt.h \
+  wordaccess.h wordaccess_64_le.h wordaccess_gcc3_be.h wordaccess_generic.h \
+  wordintclz.h
 
 IMPORTINC_HEADERS := \
   $(IMPORTINC_ROOT_HEADERS) \
@@ -138,6 +141,12 @@ importinc: \
   $(IMPORTINC_ROOT_FILES) \
   $(IMPORTINC_LIB_FILES) \
   $(IMPORTINC_LIB_UTIL_FILES) \
+  importinc/netpbm \
+
+importinc/netpbm:
+	mkdir -p importinc
+	rm -f $@
+	$(SYMLINK) . $@
 
 $(IMPORTINC_ROOT_FILES):importinc/%:$(BUILDDIR)/%
 	mkdir -p importinc
@@ -169,7 +178,7 @@ $(BUILDDIR)/version.h:
 endif
 
 ifneq ($(OMIT_CONFIG_RULE),1)
-$(BUILDDIR)/Makefile.config: $(SRCDIR)/Makefile.config.in
+$(BUILDDIR)/config.mk: $(SRCDIR)/config.mk.in
 	$(MAKE) -C $(dir $@) $(notdir $@)
 
 $(BUILDDIR)/pm_config.h:
@@ -182,13 +191,13 @@ $(BUILDDIR)/inttypes_netpbm.h:
 endif
 
 # Note that any time you do a make on a fresh Netpbm source tree,
-# Make notices that 'Makefile.config', which the make files include, does not
-# exist and runs the "Makefile.config" target, which runs Configure.
+# Make notices that 'config.mk', which the make files include, does not
+# exist and runs the "config.mk" target, which runs Configure.
 # If the "config" target were to run Configure as well, it would get run
 # twice in a row if you did a 'make config' on a fresh Netpbm source tree.
 # But we don't want to make "config" just a no-op, because someone might
-# try it after Makefile.config already exists, in order to make a new
-# Makefile.config.  Issuing a message as follows seems to make sense in 
+# try it after config.mk already exists, in order to make a new
+# config.mk.  Issuing a message as follows seems to make sense in 
 # both cases.
 .PHONY: config
 config:
@@ -203,7 +212,6 @@ config:
 $(OBJECTS): %.o: %.c importinc
 #############################################################################
 # Note that the user may have configured -I options into CFLAGS or CPPFLAGS.
-# -I. is needed when builddir != srcdir
 # Note about -o: There used to be systems that couldn't handle a space
 # between flag and value.  But we found a Solaris gcc on 2003.09.02 that
 # actually fails _without_ the space (it invokes Solaris 'as' with the
@@ -219,7 +227,7 @@ $(OBJECTS): %.o: %.c importinc
 # assertion and crash the program if it isn't really true.  You can add
 # -UNDEBUG (in any of various ways) to override this.
 #
-	$(CC) -c $(INCLUDES2) -DNDEBUG \
+	$(CC) -c $(INCLUDES) -DNDEBUG \
 	    $(CPPFLAGS) $(CFLAGS) $(CFLAGS_PERSONAL) $(CADD) -o $@ $<
 
 # libopt is a utility program used in the make file below.
@@ -255,36 +263,41 @@ endif
 #   
 #   Notice that SGI and Sun support two such flavors.
 #
-#   Plus, Scott Schwartz observed on March 25, 2003 that while his
-#   compiler understands -Wl, his linker does not understand -rpath.
-#   His compiler is "Sun WorkShop 6 update 2 C 5.3 2001/05/15".
+# Plus, Scott Schwartz observed on March 25, 2003 that while his
+# compiler understands -Wl, his linker does not understand -rpath.
+# His compiler is "Sun WorkShop 6 update 2 C 5.3 2001/05/15".
 #
-#   Plus, Mike Saunders found in December 2003 that his Solaris 8 system
-#   (uname -a says 'SunOS cannonball.method.cx 5.8 Generic_108528-14 
-#   sun4u sparc SUNW,Ultra-1') with Gcc 2.95.3 requires the syntax
+# Plus, Mike Saunders found in December 2003 that his Solaris 8 system
+# (uname -a says 'SunOS cannonball.method.cx 5.8 Generic_108528-14 
+# sun4u sparc SUNW,Ultra-1') with Gcc 2.95.3 requires the syntax
 #
-#         -Wl,-R,/path/to/dir
-#   
-#   This is apparently because Gcc invokes this linker for Saunders:
+#       -Wl,-R,/path/to/dir
+# 
+# This is apparently because Gcc invokes this linker for Saunders:
 #
-#      ld: Software Generation Utilities - Solaris Link Editors: 5.8-1.273
+#    ld: Software Generation Utilities - Solaris Link Editors: 5.8-1.273
 #
-#   I'd say there are also Solaris systems where Gcc invokes the GNU linker
-#   and then the option would be -Wl,-rpath...
+# I'd say there are also Solaris systems where Gcc invokes the GNU linker
+# and then the option would be -Wl,-rpath...
 #
-#   On IA32 Linux, at least, GNU ld takes -rpath.  It also has a -R option,
-#   but it is something else.
+# The Sun Ld fails in a weird way when you pass it -rpath instead of -R:
 #
-#   Alan Fry and Snowcrash demonstrated in 2006.11 that neither -rpath
-#   nor -R are recognized options on Mac OS X 'ld'.
+#   ld: Software Generation Utilities - Solaris Link Editors: 5.9-1.382
+#   ld: fatal: option -dn and -P are incompatible
 #
-#   http://developer.apple.com/releasenotes/DeveloperTools/RN-dyld/index.html
-#   says that on Mac OS X, libraries aren't searched for in directories,
-#   but rather specified by full name, so that rpath doesn't make any
-#   sense.  On Mac OS X, you use -install_name when you linkedit shared
-#   library S to give the complete installed name of S.  This goes into
-#   S so that when something linkedits with S, the complete installed
-#   name of S goes into the object that uses S.
+# On IA32 Linux, at least, GNU ld takes -rpath.  It also has a -R option,
+# but it is something else.
+#
+# Alan Fry and Snowcrash demonstrated in 2006.11 that neither -rpath
+# nor -R are recognized options on Mac OS X 'ld'.
+#
+# http://developer.apple.com/releasenotes/DeveloperTools/RN-dyld/index.html
+# says that on Mac OS X, libraries aren't searched for in directories,
+# but rather specified by full name, so that rpath doesn't make any
+# sense.  On Mac OS X, you use -install_name when you linkedit shared
+# library S to give the complete installed name of S.  This goes into
+# S so that when something linkedits with S, the complete installed
+# name of S goes into the object that uses S.
 
 ifeq ($(NEED_RUNTIME_PATH),Y)
   ifneq ($(NETPBMLIB_RUNTIME_PATH)x,x)
@@ -322,7 +335,7 @@ MATHLIB ?= -lm
 $(PORTBINARIES) $(MATHBINARIES): %: %.o $(NETPBMLIB) $(LIBOPT)
 # Note that LDFLAGS might contain -L options, so order is important.
 # LDFLAGS is commonly set as an environment variable.
-	$(LD) -o $@ $@.o $(shell $(LIBOPT) $(NETPBMLIB)) \
+	$(LD) -o $@ $@.o $(MATHLIB) $(shell $(LIBOPT) $(NETPBMLIB)) \
 	  $(LDFLAGS) $(LDLIBS) $(MATHLIB) $(RPATH) $(LADD)
 
 
@@ -333,7 +346,7 @@ $(PORTBINARIES) $(MATHBINARIES): %: %.o $(NETPBMLIB) $(LIBOPT)
 
 %.o2: %.c importinc
 # Note that the user may have configured -I options into CFLAGS.
-	$(CC) -c $(INCLUDES2) -DNDEBUG $(CFLAGS) \
+	$(CC) -c $(INCLUDES) -DNDEBUG $(CPPFLAGS) $(CFLAGS) \
 	  "-Dmain=main_$*" \
           $(CFLAGS_MERGE) $(CFLAGS_PERSONAL) $(CADD) -o $@ $<
 
@@ -439,8 +452,6 @@ install.man: install.man1 install.man3 install.man5 \
 
 MANUALS1 = $(BINARIES) $(SCRIPTS)
 
-PKGMANDIR = man
-
 install.man1: $(PKGDIR)/$(PKGMANDIR)/man1 $(MANUALS1:%=%_installman1)
 
 install.man3: $(PKGDIR)/$(PKGMANDIR)/man3 $(MANUALS3:%=%_installman3)
@@ -478,7 +489,7 @@ thisdirclean:
 
 .PHONY: distclean
 distclean: $(SUBDIRS:%=%/distclean) thisdirclean
-	rm -f Makefile.depend
+	rm -f depend.mk
 
 DEP_SOURCES = $(wildcard *.c *.cpp *.cc)
 
@@ -488,7 +499,7 @@ dep: $(SUBDIRS:%=%/dep) importinc
 # before the first make after a clean.
 
 ifneq ($(DEP_SOURCES)x,x)
-	$(CC) -MM -MG $(INCLUDES2) $(DEP_SOURCES) >Makefile.depend
+	$(CC) -MM -MG $(INCLUDES) $(DEP_SOURCES) >depend.mk
 endif
 
 # Note: if I stack all these subdirectory targets into one rule, I get
@@ -550,13 +561,13 @@ $(SUBDIRS:%=$(CURDIR)/%) $(DIRS2):
 # The automatic dependency generation is a pain in the butt and
 # totally unnecessary for people just installing the distributed code,
 # so to avoid needless failures in the field and a complex build, the
-# rule to generate Makefile.depend automatically simply creates an
+# rule to generate depend.mk automatically simply creates an
 # empty file.  A developer may do 'make dep' to create a
-# Makefile.depend full of real dependencies.
+# depend.mk full of real dependencies.
 
-Makefile.depend:
-	cat /dev/null >Makefile.depend
+depend.mk:
+	cat /dev/null >$@
 
-include Makefile.depend
+include depend.mk
 
 FORCE:
diff --git a/Makefile.config.in b/config.mk.in
index a983047a..0e061701 100644
--- a/Makefile.config.in
+++ b/config.mk.in
@@ -4,9 +4,9 @@
 # This file is meant to contain variable settings that customize the
 # build for a particular target system configuration.
 
-# The distribution contains the file Makefile.config.in.  You edit
-# Makefile.config.in in ways relevant to your particular environment 
-# to create Makefile.config.  The "configure" program will do this
+# The distribution contains the file config.mk.in.  You edit
+# config.mk.in in ways relevant to your particular environment 
+# to create config.mk.  The "configure" program will do this
 # for you in simple cases.
 
 # Some of the variables that the including make file must set for this
@@ -97,6 +97,7 @@ HAVE_INT64 = Y
 CC_FOR_BUILD = $(CC)
 LD_FOR_BUILD = $(LD)
 CFLAGS_FOR_BUILD = $(CFLAGS)
+LDFLAGS_FOR_BUILD = $(LDFLAGS)
 
 # MAKE is set automatically by Make to what was used to invoke Make.
 
@@ -153,7 +154,7 @@ LEX = flex
 # -pedantic isn't a problem because it causes at worst a warning.
 #CFLAGS = -O3 -ffast-math -pedantic -fno-common \
 #          -Wall -Wno-uninitialized -Wmissing-declarations -Wimplicit \
-#          -Wwrite-string -Wmissing-prototypes -Wundef
+#          -Wwrite-strings -Wmissing-prototypes -Wundef
 # The merged programs have a main_XXX subroutine instead of main(),
 # which would cause a warning with -Wmissing-declarations or 
 # -Wmissing-prototypes.
@@ -248,6 +249,17 @@ LDSHLIB = -shared -Wl,-soname,$(SONAME)
 #LDSHLIB = -shared
 #AIX Visual Age C:
 #LDSHLIB = -qmkshrobj
+#Mac OSX:
+# According to experiments done by Peter A Crowley in May 2007, if
+# libnetpbm goes in a standard place such as /usr/local/lib,
+# programs need not be built with libnetpbm's location included.
+# But if it goes elsewhere, the link-editor must include the
+# location in the executable.  It finds the runtime location by
+# looking inside the library.  The information in the library
+# comes from the install_name option with which the library was
+# built.  It's an alternative to the -rpath option on other systems.
+#LDSHLIB=-dynamiclib
+#LDSHLIB=-dynamiclib -install_name $(NETPBMLIB_RUNTIME_PATH)/libnetpbm.$(MAJ).dylib
 
 # LDRELOC is the command to combine two .o files (relocateable object files)
 # into a single .o file that can later be linked into something else.  NONE
@@ -302,7 +314,7 @@ CFLAGS_SHLIB =
 
 SHLIB_CLIB = -lc
 # SCO:
-SHLIB_CLIB =
+#SHLIB_CLIB =
 
 # On some systems you have to build into an executable the list of
 # directories where its dynamically linked libraries can be found at
diff --git a/converter/Makefile b/converter/Makefile
index 02cab3be..8c488ee2 100644
--- a/converter/Makefile
+++ b/converter/Makefile
@@ -5,7 +5,7 @@ endif
 SUBDIR = converter
 VPATH=.:$(SRCDIR)/$(SUBDIR)
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
 SUBDIRS = pbm pgm ppm other
 
@@ -14,4 +14,4 @@ all: $(SUBDIRS:%=%/all)
 SCRIPTS = 
 BINARIES =
 
-include $(SRCDIR)/Makefile.common
+include $(SRCDIR)/common.mk
diff --git a/converter/other/Makefile b/converter/other/Makefile
index a83eeb21..df084ceb 100644
--- a/converter/other/Makefile
+++ b/converter/other/Makefile
@@ -5,7 +5,7 @@ endif
 SUBDIR = converter/other
 VPATH=.:$(SRCDIR)/$(SUBDIR)
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
 ifeq ($(shell xml2-config --version),)
   XML2_LIBS=NONE
@@ -20,41 +20,40 @@ ifneq ($(BUILD_FIASCO), N)
   SUBDIRS += fiasco
 endif
 
-INCLUDES = -I$(SRCDIR)/util 
 ifneq ($(TIFFLIB),NONE)
   ifneq ($(TIFFHDR_DIR)x,x)
-    INCLUDES += -I$(TIFFHDR_DIR)
+    EXTERN_INCLUDES += -I$(TIFFHDR_DIR)
   endif
 endif
 
-ifeq ($(shell libpng-config --version),)
+ifeq ($(shell libpng$(PNGVER)-config --version),)
   ifneq ($(PNGLIB),NONE)
     HAVE_PNGLIB = Y
     ifneq ($(PNGHDR_DIR)x,x)
-      INCLUDES += -I$(PNGHDR_DIR)
+      EXTERN_INCLUDES += -I$(PNGHDR_DIR)
     endif
     ifneq ($(ZHDR_DIR)x,x)
-      INCLUDES += -I$(ZHDR_DIR)
+      EXTERN_INCLUDES += -I$(ZHDR_DIR)
     endif
   endif
 else
   HAVE_PNGLIB = Y
-  INCLUDES += $(shell libpng-config --cflags)
+  EXTERN_INCLUDES += $(shell libpng$(PNGVER)-config --cflags)
 endif
 
 ifneq ($(JPEGLIB),NONE)
   ifneq ($(JPEGHDR_DIR)x,x)
-    INCLUDES += -I$(JPEGHDR_DIR)
+    EXTERN_INCLUDES += -I$(JPEGHDR_DIR)
   endif
 endif
 ifneq ($(URTLIB),NONE)
   ifneq ($(URTHDR_DIR)x,x)
-    INCLUDES += -I$(URTHDR_DIR)
+    EXTERN_INCLUDES += -I$(URTHDR_DIR)
   endif
 endif
 ifneq ($(XML2_LIBS),NONE)
   ifneq ($(XML2_CFLAGS),NONE)
-    INCLUDES += $(XML2_CFLAGS)
+    EXTERN_INCLUDES += $(XML2_CFLAGS)
   endif
 endif
 
@@ -80,19 +79,24 @@ endif
 
 PORTBINARIES =  bmptopnm fitstopnm \
 		gemtopnm giftopnm hdifftopam infotopam \
-		pamtodjvurle pamtofits \
-		pamtohdiff pamtohtmltbl pamtopfm pamtopnm pamtouil \
+		pamtodjvurle pamtofits pamtogif \
+		pamtohdiff pamtohtmltbl pamtompfont pamtooctaveimg \
+		pamtopam pamtopfm pamtopnm pamtouil \
 		pamtoxvmini \
 		pbmtopgm pfmtopam \
 	        pgmtopbm pgmtoppm ppmtopgm pnmtoddif \
 		pnmtopclxl \
-		pnmtosgi pnmtosir pamtotga pnmtoxwd pstopnm \
+		pnmtosgi pnmtosir pamtotga pnmtoxwd \
 		rlatopam sgitopnm sirtopnm xwdtopnm zeisstopnm
 
+ifneq ($(DONT_HAVE_PROCESS_MGMT),Y)
+  PORTBINARIES += pstopnm
+endif
+
 BINARIES = $(PORTBINARIES) pnmtorast rasttopnm
 
 ifeq ($(HAVE_PNGLIB),Y)
-  BINARIES += pnmtopng pngtopnm pamrgbatopng
+  BINARIES += pnmtopng pngtopnm pngtopam pamrgbatopng
 endif
 ifneq ($(JPEGLIB),NONE)
   BINARIES += jpegtopnm pnmtojpeg
@@ -133,7 +137,7 @@ SCRIPTS = anytopnm pnmtoplainpnm
 .PHONY: all
 all:	$(BINARIES) $(SUBDIRS:%=%/all)
 
-include $(SRCDIR)/Makefile.common
+include $(SRCDIR)/common.mk
 
 ifeq ($(NEED_RUNTIME_PATH),Y)
   LIBOPTR = -runtime
@@ -148,13 +152,13 @@ tifftopnm pamtotiff pnmtotiffcmyk: %: %.o tiff.o $(NETPBMLIB) $(LIBOPT)
 	$(LD) -o $@ $@.o tiff.o \
 	  $(LIBOPTS_TIFF) $(MATHLIB) $(LDFLAGS) $(LDLIBS) $(RPATH) $(LADD)
 
-ifeq ($(shell libpng-config --version),)
+ifeq ($(shell libpng$(PNGVER)-config --version),)
   PNGLIB_LIBOPTS = $(shell $(LIBOPT) $(LIBOPTR) $(PNGLIB) $(ZLIB))
 else
-  PNGLIB_LIBOPTS = $(shell libpng-config --ldflags)
+  PNGLIB_LIBOPTS = $(shell libpng$(PNGVER)-config --ldflags)
 endif
 
-pngtopnm: %: %.o $(NETPBMLIB) $(LIBOPT)
+pngtopnm pngtopam: %: %.o $(NETPBMLIB) $(LIBOPT)
 	$(LD) -o $@ $@.o \
 	  $(shell $(LIBOPT) $(NETPBMLIB)) \
 	  $(PNGLIB_LIBOPTS) $(MATHLIB) $(LDFLAGS) $(LDLIBS) $(RPATH) $(LADD)
@@ -218,27 +222,29 @@ install.bin.local: $(PKGDIR)/bin
 	$(SYMLINK) pnmtoplainpnm pnmnoraw
 # backward compatibility: program used to be gemtopbm
 	cd $(PKGDIR)/bin ; \
-	$(SYMLINK) gemtopnm$(EXE) gemtopbm
+	$(SYMLINK) gemtopnm$(EXE) gemtopbm$(EXE)
 # In October 2001, pnmtojpeg replaced ppmtojpeg
+ifneq ($(JPEGLIB),NONE)
 	cd $(PKGDIR)/bin ; \
-	$(SYMLINK) pnmtojpeg$(EXE) ppmtojpeg
+	$(SYMLINK) pnmtojpeg$(EXE) ppmtojpeg$(EXE)
+endif
 # In March 2002, bmptopnm replaced bmptoppm
 	cd $(PKGDIR)/bin ; \
-	$(SYMLINK) bmptopnm$(EXE) bmptoppm
+	$(SYMLINK) bmptopnm$(EXE) bmptoppm$(EXE)
 # In May 2002, pamtouil replaced ppmtouil
 	cd $(PKGDIR)/bin ; \
-	$(SYMLINK) pamtouil$(EXE) ppmtouil
+	$(SYMLINK) pamtouil$(EXE) ppmtouil$(EXE)
 # In July 2002, pamtotga replaced ppmtotga
 	cd $(PKGDIR)/bin ; \
 	$(SYMLINK) pamtotga$(EXE) ppmtotga$(EXE)
 # In March 2005, we realized that pamtopnm obviates pnmtopnm
 	cd $(PKGDIR)/bin ; \
-	$(SYMLINK) pamtopnm$(EXE) pnmtopnm
+	$(SYMLINK) pamtopnm$(EXE) pnmtopnm$(EXE)
 # In October 2005, pamtofits replaced pnmtofits
 	cd $(PKGDIR)/bin ; \
-	$(SYMLINK) pamtofits$(EXE) pnmtofits
+	$(SYMLINK) pamtofits$(EXE) pnmtofits$(EXE)
 ifneq ($(TIFF_PREREQ_MISSING),Y)
 # In October 2005, pamtotiff replaced pnmtotiff
 	cd $(PKGDIR)/bin ; \
-	$(SYMLINK) pamtotiff$(EXE) pnmtotiff
-endif
\ No newline at end of file
+	$(SYMLINK) pamtotiff$(EXE) pnmtotiff$(EXE)
+endif
diff --git a/converter/other/anytopnm b/converter/other/anytopnm
index b2873772..06f48b4f 100755
--- a/converter/other/anytopnm
+++ b/converter/other/anytopnm
@@ -47,18 +47,39 @@ putInputIntoTempfile() {
 
 
 setMimeType() {
+#------------------------------------------------------------------------------
+# Set variable 'mimeType' to the mime type string (e.g. "application/binary")
+#
+# If we can't tell, set it to "unknown".
+#------------------------------------------------------------------------------
     # $1 is the file name
 
-    # Christos Zoulas's current 'file' (see Freshmeat) has the --mime option.
+    # Old versions of 'file' cannot tell us the mime type; they lack any option
+    # to do so.
+    #
+    # Newer ones have a --mime option for that.
+    #
+    # Still newer ones (ca 2008) have a --mime option, but it does something
+    # different - it prints the mime type plus the mime encoding.  And they
+    # have --mime-type to print just the mime type.
 
-    file --mime /dev/null >/dev/null 2>/dev/null
+    file --mime-type /dev/null >/dev/null 2>/dev/null
     if [ $? -eq 0 ]; then
-        # Now that we know the --mime option exists, use it.
-        mimeType=`file --mime "$1" | cut -d: -f2- | cut -c2-`
+        # Now that we know the --mime-type option exists, use it.
+        mimeType=`file --mime-type "$1" | cut -d: -f2- | cut -c2-`
     else
-        # file --mime failed; we don't know why, but we assume it's because it
-        # is a traditional 'file' program that doesn't have a --mime option.
-    mimeType="unknown"
+        # file --mime-type failed; we don't know why, but we assume it's
+        # because it is an older 'file' program that doesn't have a --mime-type
+        # option.
+        file --mime /dev/null >/dev/null 2>/dev/null
+        if [ $? -eq 0 ]; then
+            # Now that we know the --mime option exists, use it.
+            mimeType=`file --mime "$1" | cut -d: -f2- | cut -c2-`
+        else
+            # file --mime failed; we don't know why, but we assume it's because
+            # it is an older 'file' program that doesn't have a --mime option.
+            mimeType="unknown"
+        fi
     fi
 }
 
@@ -109,7 +130,7 @@ computeTypeFromTypeDescription () {
 
     case "$1" in
     
-        *PBM* | *PGM* | *PPM* )
+        *PBM* | *PGM* | *PPM* | *Netpbm*PAM*)
             filetype=pnm
             ;;
     
@@ -149,6 +170,10 @@ computeTypeFromTypeDescription () {
             filetype=gzip
             ;;
     
+        *XWD*X*Window*Dump* ) 
+            filetype=xwd
+            ;;
+
         *compress* )
             filetype=compress
             ;;
@@ -507,9 +532,8 @@ else
 fi
 
 tempdir="${TMPDIR-/tmp}/anytopnm.$$"
-mkdir $tempdir || { echo "Could not create temporary file. Exiting."; exit 1;}
-chmod 700 $tempdir
-
+mkdir -m 0700 $tempdir || \
+  { echo "Could not create temporary file. Exiting."; exit 1;}
 trap 'rm -rf $tempdir' 0
 
 # Take out all spaces
diff --git a/converter/other/bmepsoe.c b/converter/other/bmepsoe.c
index cdb52779..02bf39aa 100644
--- a/converter/other/bmepsoe.c
+++ b/converter/other/bmepsoe.c
@@ -209,76 +209,85 @@ static void after_flate_add(Output_Encoder *o, int b)
   
 }
 
-static void do_flate_flush(Output_Encoder *o, int final)
-{
-  Bytef *iptr, *optr, *xptr;
-  uLong  is, os, xs;
-  int err, must_continue;
-  
-  iptr = o->fl_i_buffer; optr = o->fl_o_buffer;
-  is = o->fl_i_size; os = o->fl_o_size;
-  
-  if(iptr && optr && is && os) {
-    is = o->fl_i_used;
-    if(is) {
-      (o->flate_stream).next_in = iptr;
-      (o->flate_stream).avail_in = is;
-      if(final) { 
-    must_continue = 1;
-    while(must_continue) {
-      (o->flate_stream).next_out = optr;
-      (o->flate_stream).avail_out = os;
-      must_continue = 0;
-      err = deflate(&(o->flate_stream), Z_FINISH);
-      switch(err) {
-        case Z_STREAM_END: { 
-              xptr = optr;
+
+
+static void
+do_flate_flush(Output_Encoder * const oP,
+               int              const final) {
+
+    Bytef *iptr, *optr, *xptr;
+    unsigned long is;
+    unsigned long os;
+    unsigned long xs;
+    int err;
+    int mustContinue;
+  
+    iptr = oP->fl_i_buffer; optr = oP->fl_o_buffer;
+    is = oP->fl_i_size; os = oP->fl_o_size;
+  
+    if (iptr && optr && is && os) {
+        is = oP->fl_i_used;
+        if (is || final) {
+            oP->flate_stream.next_in = iptr;
+            oP->flate_stream.avail_in = is;
+            if (final) { 
+                mustContinue = 1;
+                while (mustContinue) {
+                    oP->flate_stream.next_out = optr;
+                    oP->flate_stream.avail_out = os;
+                    mustContinue = 0;
+                    err = deflate(&oP->flate_stream, Z_FINISH);
+                    switch (err) {
+                    case Z_STREAM_END: { 
+                        xptr = optr;
           
-          xs = os - ((o->flate_stream).avail_out);
-          while(xs--) {
-        after_flate_add(o, (*(xptr++)));
-          }
-        } break;
-        case Z_OK : {
-          must_continue = 1;
-              xptr = optr;
+                        xs = os - (oP->flate_stream.avail_out);
+                        while (xs--) {
+                            after_flate_add(oP, *(xptr++));
+                        }
+                    } break;
+                    case Z_OK : {
+                        mustContinue = 1;
+                        xptr = optr;
           
-          xs = os - ((o->flate_stream).avail_out);
-          while(xs--) {
-        after_flate_add(o, (*(xptr++)));
-          }
-        } break;
-        default : { 
-        } break;
-      }
-    }
-      } else { 
-    must_continue = 1;
-    while(must_continue) {
-      must_continue = 0;
-      (o->flate_stream).avail_out = os; (o->flate_stream).next_out = optr;
-      err = deflate(&(o->flate_stream), 0);
-      switch(err) {
-        case Z_OK: {
-          if((o->flate_stream).avail_in) {
-        must_continue = 1;
-          }
+                        xs = os - (oP->flate_stream.avail_out);
+                        while (xs--) {
+                            after_flate_add(oP, *(xptr++));
+                        }
+                    } break;
+                    default : { 
+                    } break;
+                    }
+                }
+            } else { 
+                mustContinue = 1;
+                while (mustContinue) {
+                    mustContinue = 0;
+                    oP->flate_stream.avail_out = os;
+                    oP->flate_stream.next_out = optr;
+                    err = deflate(&oP->flate_stream, 0);
+                    switch (err) {
+                    case Z_OK: {
+                        if (oP->flate_stream.avail_in) {
+                            mustContinue = 1;
+                        }
           
-          xptr = optr; xs = os - ((o->flate_stream).avail_out);
-          while(xs--) {
-        after_flate_add(o, (*(xptr++)));
-          }
-        } break;
-        default : { 
-        } break;
-      }
-    }
-      }
+                        xptr = optr; xs = os - (oP->flate_stream.avail_out);
+                        while (xs--) {
+                            after_flate_add(oP, *(xptr++));
+                        }
+                    } break;
+                    default : { 
+                    } break;
+                    }
+                }
+            }
+        }
     }
-  }
-  
 }
 
+
+
 static void flate_add(Output_Encoder *o, int b)
 {
   Byte bt;
@@ -457,17 +466,20 @@ static void rl_flush(Output_Encoder *o)
   
 }
 
-static void internal_byte_add(Output_Encoder *o, int b)
-{
-  
-  if((o->mode) & OE_RL) {
-    rl_add(o,b);
-  } else {
-    after_rl_add(o,b);
-  }
+
+
+static void
+internal_byte_add(Output_Encoder * const oP,
+                  int              const b) {
   
+    if (oP->mode & OE_RL)
+        rl_add(oP, b);
+    else
+        after_rl_add(oP, b);
 }
 
+
+
 static void internal_byte_flush(Output_Encoder *o)
 {
   
@@ -479,19 +491,23 @@ static void internal_byte_flush(Output_Encoder *o)
   
 }
 
-void oe_bit_add(Output_Encoder *o, int b)
-{
-  
-  o->bit_value = 2 * o->bit_value + (b ? 1 : 0);
-  o->bit_consumed = o->bit_consumed + 1;
-  if(o->bit_consumed >= 8) {
-    o->bit_consumed = 0;
-    internal_byte_add(o, (o->bit_value));
-    o->bit_value = 0;
-  }
+
+
+void
+oe_bit_add(Output_Encoder * const oP,
+           int              const b) {
   
+    oP->bit_value = 2 * oP->bit_value + (b ? 1 : 0);
+    oP->bit_consumed = oP->bit_consumed + 1;
+    if (oP->bit_consumed >= 8) {
+        oP->bit_consumed = 0;
+        internal_byte_add(oP, oP->bit_value);
+        oP->bit_value = 0;
+    }
 }
 
+
+
 void oe_bit_flush(Output_Encoder *o)
 {
   
@@ -511,26 +527,31 @@ void oe_bit_flush(Output_Encoder *o)
   
 }
 
-void oe_byte_add(Output_Encoder *o, int b)
-{
+
+
+void
+oe_byte_add(Output_Encoder * const oP,
+            int              const b) {
   
-  if(o->bit_consumed) {
-    int testval,i;
-    testval = 128;
-    for(i = 0; i < 8; i++) {
-      if(b & testval) {
-    oe_bit_add(o,1);
-      } else {
-    oe_bit_add(o,0);
-      }
-      testval = testval / 2;
+    if (oP->bit_consumed) {
+        int testval;
+        int i;
+        testval = 128;
+        for (i = 0; i < 8; ++i) {
+            if (b & testval) {
+                oe_bit_add(oP, 1);
+            } else {
+                oe_bit_add(oP, 0);
+            }
+            testval = testval / 2;
+        }
+    } else {
+        internal_byte_add(oP, b);
     }
-  } else {
-    internal_byte_add(o,b);
-  }
-  
 }
 
+
+
 void oe_byte_flush(Output_Encoder *o)
 {
   
diff --git a/converter/other/bmptopnm.c b/converter/other/bmptopnm.c
index e1bd1403..9a405d70 100644
--- a/converter/other/bmptopnm.c
+++ b/converter/other/bmptopnm.c
@@ -17,11 +17,17 @@
  in supporting documentation.  This software is provided "as is"
  without express or implied warranty.
 
+ Note: From mid-2003 to mid-2007, this program would crash on any 16
+ bit BMP without transparency and no one reported it.  Before that, it
+ refused to even try to read a 16 bit BMP.  I conclude that essentially
+ nobody is using 16 bit BMP.
+
 *****************************************************************************/
 #include <string.h>
 #include <limits.h>
 #include <assert.h>
 
+#include "pm_c_util.h"
 #include "pnm.h"
 #include "shhopt.h"
 #include "nstring.h"
@@ -42,6 +48,10 @@ struct bitPosition {
 
        Example: if 16 bits are laid out as XRRRRRGGGGGBBBBB then the shift
        count for the R component is 10 and the mask is 0000000000011111.
+
+       A 'mask' of zero denotes absence of any bits; e.g. in the example
+       above, the mask for the transparency component is zero because there
+       is no transparency component .  'shift' is arbitrary in that case.
     */
     unsigned int shift;
         /* How many bits right you have to shift the value to get the subject
@@ -306,6 +316,36 @@ readOs2InfoHeader(FILE *                 const ifP,
 
 
 static void
+validateCompression(unsigned long const compression,
+                    enum rowOrder const rowOrder,
+                    unsigned int  const cBitCount) {
+    
+    if (compression != COMP_RGB && compression != COMP_BITFIELDS &&
+        compression != COMP_RLE4 && compression != COMP_RLE8 ) 
+        pm_error("Input has unknown encoding.  "
+                 "Compression type code = %ld.  The only ones we know "
+                 "are RGB (%u), BITFIELDS (%u), "
+                 "RLE4 (%u), and RLE8 (%u)",
+                 compression, COMP_RGB, COMP_BITFIELDS,
+                 COMP_RLE4, COMP_RLE8);
+                     
+    if ((compression == COMP_RLE4 || compression == COMP_RLE8) &&
+        rowOrder == TOPDOWN )                        
+        pm_error("Invalid BMP header.  Claims image is top-down and also "
+                 "compressed, which is an impossible combination.");
+
+    if ( (compression == COMP_RLE4 && cBitCount !=4) ||
+         (compression == COMP_RLE8 && cBitCount !=8) ) 
+        pm_error("Invalid BMP header.  " 
+                 "Compression type (%s) disagrees with "
+                 "number of bits per pixel (%u).",
+                 compression == COMP_RLE4 ? "RLE4" : "RLE8",
+                 cBitCount);
+}
+
+
+
+static void
 readWindowsBasic40ByteInfoHeader(FILE *                 const ifP,
                                  struct bmpInfoHeader * const headerP) {
 /*----------------------------------------------------------------------------
@@ -340,22 +380,9 @@ readWindowsBasic40ByteInfoHeader(FILE *                 const ifP,
         unsigned long int const compression = GetLong(ifP);
 
         headerP->bitFields = (compression == COMP_BITFIELDS);
-    
-        if (compression != COMP_RGB && compression != COMP_BITFIELDS &&
-            compression != COMP_RLE4 && compression != COMP_RLE8 ) 
-            pm_error("Input is compressed.  Unsupported encoding method.\n"
-                     "Compression type code = %ld", compression);
-                     
-        if ( (compression == COMP_RLE4 || compression == COMP_RLE8) &&
-              headerP->rowOrder == TOPDOWN )                        
-            pm_error("Invalid BMP header.  Top-down images cannot be compressed.");
 
-        if ( (compression == COMP_RLE4 && headerP->cBitCount !=4) ||
-             (compression == COMP_RLE8 && headerP->cBitCount !=8) )                        
-            pm_error("Invalid BMP header.\n" 
-                     "Compression type (%s) disagrees with number of bits per pixel (%u).",
-                      compression == COMP_RLE4 ? "RLE4" : "RLE8",
-                      headerP->cBitCount);
+        validateCompression(compression, headerP->rowOrder,
+                            headerP->cBitCount);
 
         headerP->compression = compression;             
     }
@@ -399,7 +426,7 @@ lsbZeroCount(unsigned int const mask)
    Use GCC built-in when available.
 -----------------------------------------------------------------------------*/
 
-#if ( defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 304) )
+#if HAVE_GCC_BITCOUNT 
 {
       return ( mask==0 ? sizeof(mask)*8 : __builtin_ctz(mask) );
 }
@@ -463,6 +490,10 @@ defaultPixelformat(unsigned int const bitCount) {
 
     switch (bitCount) {
     case 16:
+        /* This layout is sometimes called "RGB555".  A document from
+           Microsoft says this is the default (when the "compression"
+           field of the header says COMP_BITFIELDS).
+        */
         retval.conventionalBgr = FALSE;
         retval.red.shift = 10;
         retval.grn.shift = 5;
@@ -500,6 +531,11 @@ readV4InfoHeaderExtension(FILE *                 const ifP,
                           struct bmpInfoHeader * const headerP) {
 
     if (headerP->bitFields) {
+        /* A document from Microsoft says on Windows 95 there is no
+           transparency plane and (red, green, blue) must be either
+           (5,5,5) or (5,6,5) for 16 bit and (8,8,8) for 32 bit.
+           It calls these RGB555, RGB565, RGB888.
+        */
         headerP->pixelformat.red = bitPositionFromMask(GetLong(ifP));
         headerP->pixelformat.grn = bitPositionFromMask(GetLong(ifP));
         headerP->pixelformat.blu = bitPositionFromMask(GetLong(ifP));
@@ -655,16 +691,16 @@ extractBitFields(unsigned int       const rasterval,
         (rasterval >> pixelformat.blu.shift) & pixelformat.blu.mask;
     unsigned int const abits = 
         (rasterval >> pixelformat.trn.shift) & pixelformat.trn.mask;
-    
-    *rP = pixelformat.red.mask ?
-              (unsigned int) rbits * maxval / pixelformat.red.mask : 0;
-    *gP = pixelformat.grn.mask ?
-              (unsigned int) gbits * maxval / pixelformat.grn.mask : 0;
-    *bP = pixelformat.blu.mask ?
-              (unsigned int) bbits * maxval / pixelformat.blu.mask : 0;
-    *aP = pixelformat.trn.mask ?
-              (unsigned int) abits * maxval / pixelformat.trn.mask : 0;
-}
+
+    *rP = pixelformat.red.mask > 0 ?
+        (unsigned int) rbits * maxval / pixelformat.red.mask : 0;
+    *gP = pixelformat.grn.mask > 0 ?
+        (unsigned int) gbits * maxval / pixelformat.grn.mask : 0;
+    *bP = pixelformat.blu.mask > 0 ?
+        (unsigned int) bbits * maxval / pixelformat.blu.mask : 0;
+    *aP = pixelformat.trn.mask > 0 ?
+        (unsigned int) abits * maxval / pixelformat.trn.mask : 0;
+}        
 
 
 
@@ -1220,7 +1256,7 @@ readColorMap(FILE *               const ifP,
                    bytesRead,
                    BMPlencolormap(BMPheader.class, BMPheader.cBitCount, 
                                   BMPheader.cmapsize));
-}
+    }
 }
 
 
@@ -1259,6 +1295,25 @@ warnIfBadFileSize(struct bmpInfoHeader const BMPheader,
 
 
 
+static bool
+isValidBmpBpp(unsigned int const cBitCount) {
+
+    switch (cBitCount) {
+    case 1:
+    case 2:
+    case 4:
+    case 8:
+    case 16:
+    case 24:
+    case 32:
+        return true;
+    default:
+        return false;
+    }
+}
+
+
+
 static void
 readBmp(FILE *               const ifP, 
         unsigned char ***    const BMPrasterP, 
@@ -1316,6 +1371,11 @@ readBmp(FILE *               const ifP,
     if (fgetc(ifP) != EOF)
         pm_message("warning: some image data remains unread.");
     
+    if (!isValidBmpBpp(BMPheader.cBitCount))
+        pm_error("Invalid BMP image: 'cBitCount' field of header "
+                 "(number of bits for each pixel in raster) is %u",
+                 BMPheader.cBitCount);
+
     *colsP        = BMPheader.cols;
     *rowsP        = BMPheader.rows;
     *cBitCountP   = BMPheader.cBitCount;
diff --git a/converter/other/cameratopam/Makefile b/converter/other/cameratopam/Makefile
index b6778c67..20a95aa2 100644
--- a/converter/other/cameratopam/Makefile
+++ b/converter/other/cameratopam/Makefile
@@ -5,14 +5,15 @@ endif
 SUBDIR = converter/other/cameratopam
 VPATH=.:$(SRCDIR)/$(SUBDIR)
 
+EXTERN_INCLUDES =
 ifneq ($(JPEGLIB),NONE)
   ifneq ($(JPEGHDR_DIR)x,x)
-    INCLUDES += -I$(JPEGHDR_DIR)
+    EXTERN_INCLUDES += -I$(JPEGHDR_DIR)
     CFLAGS += -DHAVE_JPEG
   endif
 endif
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
 
 .PHONY: all
@@ -27,7 +28,7 @@ BINARIES = cameratopam
 MERGEBINARIES = 
 SCRIPTS = 
 
-include $(SRCDIR)/Makefile.common
+include $(SRCDIR)/common.mk
 
 cameratopam: $(OBJECTS) $(NETPBMLIB) $(LIBOPT)
 	$(LD) -o $@ \
diff --git a/converter/other/cameratopam/cameratopam.c b/converter/other/cameratopam/cameratopam.c
index 92773c91..b2d6da9b 100644
--- a/converter/other/cameratopam/cameratopam.c
+++ b/converter/other/cameratopam/cameratopam.c
@@ -24,18 +24,13 @@
 #include <string.h>
 
 #ifdef __CYGWIN__
-#include <io.h>
+  #include <io.h>
 #endif
-#ifdef WIN32
-  #include <winsock2.h>
-  #pragma comment(lib, "ws2_32.lib")
-  #define strcasecmp stricmp
-  typedef __int64 INT64;
-  static bool const have64BitArithmetic = true;
-#else
+#if !defined(WIN32) || defined(__CYGWIN__)
   #include <unistd.h>
 #endif
 
+#include "pm_c_util.h"
 #include "mallocvar.h"
 #include "shhopt.h"
 #include "pam.h"
@@ -48,17 +43,6 @@
 #include "foveon.h"
 #include "dng.h"
 
-#if HAVE_INT64
-   typedef int64_t INT64;
-   static bool const have64BitArithmetic = true;
-#else
-   /* We define INT64 to something that lets the code compile, but we
-      should not execute any INT64 code, because it will get the wrong
-      result.  */
-   typedef int INT64;
-   static bool const have64BitArithmetic = false;
-#endif
-
 /*
    All global variables are defined here, and all functions that
    access them are prefixed with "CLASS".  Note that a thread-safe
diff --git a/converter/other/dithers.h b/converter/other/dithers.h
deleted file mode 100644
index 1ced833d..00000000
--- a/converter/other/dithers.h
+++ /dev/null
@@ -1,91 +0,0 @@
-#ifndef DITHERS_H_INCLUDED
-#define DITHERS_H_INCLUDED
-
-/*
-** dithers.h
-**
-** Here are some dithering matrices.  They are all taken from "Digital
-** Halftoning" by Robert Ulichney, MIT Press, ISBN 0-262-21009-6.
-*/
-
-
-#if 0
-/*
-** Order-6 ordered dithering matrix.  Note that smaller ordered dithers
-** have no advantage over larger ones, so use dither8 instead.
-*/
-static int dither6[8][8] = {
-  {  1, 59, 15, 55,  2, 56, 12, 52 },
-  { 33, 17, 47, 31, 34, 18, 44, 28 },
-  {  9, 49,  5, 63, 10, 50,  6, 60 },
-  { 41, 25, 37, 21, 42, 26, 38, 22 },
-  {  3, 57, 13, 53,  0, 58, 14, 54 },
-  { 35, 19, 45, 29, 32, 16, 46, 30 },
-  { 11, 51,  7, 61,  8, 48,  4, 62 },
-  { 43, 27, 39, 23, 40, 24, 36, 20 }
-  };
-#endif
-
-/* Order-8 ordered dithering matrix. */
-static int dither8[16][16] = {
-  {   1,235, 59,219, 15,231, 55,215,  2,232, 56,216, 12,228, 52,212},
-  { 129, 65,187,123,143, 79,183,119,130, 66,184,120,140, 76,180,116},
-  {  33,193, 17,251, 47,207, 31,247, 34,194, 18,248, 44,204, 28,244},
-  { 161, 97,145, 81,175,111,159, 95,162, 98,146, 82,172,108,156, 92},
-  {   9,225, 49,209,  5,239, 63,223, 10,226, 50,210,  6,236, 60,220},
-  { 137, 73,177,113,133, 69,191,127,138, 74,178,114,134, 70,188,124},
-  {  41,201, 25,241, 37,197, 21,255, 42,202, 26,242, 38,198, 22,252},
-  { 169,105,153, 89,165,101,149, 85,170,106,154, 90,166,102,150, 86},
-  {   3,233, 57,217, 13,229, 53,213,  0,234, 58,218, 14,230, 54,214},
-  { 131, 67,185,121,141, 77,181,117,128, 64,186,122,142, 78,182,118},
-  {  35,195, 19,249, 45,205, 29,245, 32,192, 16,250, 46,206, 30,246},
-  { 163, 99,147, 83,173,109,157, 93,160, 96,144, 80,174,110,158, 94},
-  {  11,227, 51,211,  7,237, 61,221,  8,224, 48,208,  4,238, 62,222},
-  { 139, 75,179,115,135, 71,189,125,136, 72,176,112,132, 68,190,126},
-  {  43,203, 27,243, 39,199, 23,253, 40,200, 24,240, 36,196, 20,254},
-  { 171,107,155, 91,167,103,151, 87,168,104,152, 88,164,100,148, 84} 
-};
-
-/* Order-3 clustered dithering matrix. */
-static int cluster3[6][6] = {
-  {  9,11,10, 8, 6, 7},
-  { 12,17,16, 5, 0, 1},
-  { 13,14,15, 4, 3, 2},
-  {  8, 6, 7, 9,11,10},
-  {  5, 0, 1,12,17,16},
-  {  4, 3, 2,13,14,15}
-};
-
-/* Order-4 clustered dithering matrix. */
-static int cluster4[8][8] = {
-  { 18,20,19,16,13,11,12,15},
-  { 27,28,29,22, 4, 3, 2, 9},
-  { 26,31,30,21, 5, 0, 1,10},
-  { 23,25,24,17, 8, 6, 7,14},
-  { 13,11,12,15,18,20,19,16},
-  {  4, 3, 2, 9,27,28,29,22},
-  {  5, 0, 1,10,26,31,30,21},
-  {  8, 6, 7,14,23,25,24,17}
-};
-
-/* Order-8 clustered dithering matrix. */
-static int cluster8[16][16] = {
-   { 64, 69, 77, 87, 86, 76, 68, 67, 63, 58, 50, 40, 41, 51, 59, 60},
-   { 70, 94,100,109,108, 99, 93, 75, 57, 33, 27, 18, 19, 28, 34, 52},
-   { 78,101,114,116,115,112, 98, 83, 49, 26, 13, 11, 12, 15, 29, 44},
-   { 88,110,123,124,125,118,107, 85, 39, 17,  4,  3,  2,  9, 20, 42},
-   { 89,111,122,127,126,117,106, 84, 38, 16,  5,  0,  1, 10, 21, 43},
-   { 79,102,119,121,120,113, 97, 82, 48, 25,  8,  6,  7, 14, 30, 45},
-   { 71, 95,103,104,105, 96, 92, 74, 56, 32, 24, 23, 22, 31, 35, 53},
-   { 65, 72, 80, 90, 91, 81, 73, 66, 62, 55, 47, 37, 36, 46, 54, 61},
-   { 63, 58, 50, 40, 41, 51, 59, 60, 64, 69, 77, 87, 86, 76, 68, 67},
-   { 57, 33, 27, 18, 19, 28, 34, 52, 70, 94,100,109,108, 99, 93, 75},
-   { 49, 26, 13, 11, 12, 15, 29, 44, 78,101,114,116,115,112, 98, 83},
-   { 39, 17,  4,  3,  2,  9, 20, 42, 88,110,123,124,125,118,107, 85},
-   { 38, 16,  5,  0,  1, 10, 21, 43, 89,111,122,127,126,117,106, 84},
-   { 48, 25,  8,  6,  7, 14, 30, 45, 79,102,119,121,120,113, 97, 82},
-   { 56, 32, 24, 23, 22, 31, 35, 53, 71, 95,103,104,105, 96, 92, 74},
-   { 62, 55, 47, 37, 36, 46, 54, 61, 65, 72, 80, 90, 91, 81, 73, 66}
-};
-
-#endif
diff --git a/converter/other/fiasco/Makefile b/converter/other/fiasco/Makefile
index 9f7310bd..16221d77 100644
--- a/converter/other/fiasco/Makefile
+++ b/converter/other/fiasco/Makefile
@@ -5,9 +5,9 @@ endif
 SUBDIR = converter/other/fiasco
 VPATH=.:$(SRCDIR)/$(SUBDIR)
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
-INCLUDES = \
+COMP_INCLUDES = \
 	-I$(SRCDIR)/$(SUBDIR)/codec -I$(SRCDIR)/$(SUBDIR)/input \
 	-I$(SRCDIR)/$(SUBDIR)/output -I$(SRCDIR)/$(SUBDIR)/lib \
 
@@ -32,7 +32,7 @@ MERGE_OBJECTS = $(BINARIES:%=%.o2) $(COMMON_OBJECTS)  $(FIASCOLIBS)
 
 SUBDIRS = codec input output lib
 
-include $(SRCDIR)/Makefile.common
+include $(SRCDIR)/common.mk
 
 $(BINARIES):%:%.o $(COMMON_OBJECTS) $(FIASCOLIBS) $(NETPBMLIB) \
    $(LIBOPT)
diff --git a/converter/other/fiasco/codec/Makefile b/converter/other/fiasco/codec/Makefile
index 9a9d502a..e3b2a112 100644
--- a/converter/other/fiasco/codec/Makefile
+++ b/converter/other/fiasco/codec/Makefile
@@ -6,9 +6,9 @@ FIASCOSUBDIR = converter/other/fiasco
 SUBDIR = $(FIASCOSUBDIR)/codec
 VPATH=.:$(SRCDIR)/$(SUBDIR)
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
-INCLUDES = -I$(SRCDIR)/$(FIASCOSUBDIR) -I$(SRCDIR)/$(FIASCOSUBDIR)/lib \
+COMP_INCLUDES = -I$(SRCDIR)/$(FIASCOSUBDIR) -I$(SRCDIR)/$(FIASCOSUBDIR)/lib \
 	 -I$(SRCDIR)/$(FIASCOSUBDIR)/input -I$(SRCDIR)/$(FIASCOSUBDIR)/output 
 
 OBJECTS =  approx.o bintree.o coder.o coeff.o \
@@ -19,7 +19,7 @@ MERGE_OBJECTS = $(OBJECTS)
 
 all: libfiasco_codec.a
 
-include $(SRCDIR)/Makefile.common
+include $(SRCDIR)/common.mk
 
 libfiasco_codec.a: $(OBJECTS)
 	$(AR) -rc $@ $(OBJECTS)
diff --git a/converter/other/fiasco/input/Makefile b/converter/other/fiasco/input/Makefile
index c01af772..2f8749f6 100644
--- a/converter/other/fiasco/input/Makefile
+++ b/converter/other/fiasco/input/Makefile
@@ -7,18 +7,18 @@ SUBDIR = $(FIASCOSUBDIR)/input
 BUILDDIR = ../../../..
 VPATH=.:$(SRCDIR)/$(SUBDIR)
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
 OBJECTS =  basis.o matrices.o mc.o nd.o read.o tree.o weights.o
 
 MERGE_OBJECTS = $(OBJECTS)
 
-INCLUDES = -I$(SRCDIR)/$(FIASCOSUBDIR) -I$(SRCDIR)/$(FIASCOSUBDIR)/lib \
-	   -I$(SRCDIR)/$(FIASCOSUBDIR)/codec
+COMP_INCLUDES = -I$(SRCDIR)/$(FIASCOSUBDIR) -I$(SRCDIR)/$(FIASCOSUBDIR)/lib \
+	        -I$(SRCDIR)/$(FIASCOSUBDIR)/codec
 
 all: libfiasco_input.a
 
-include $(SRCDIR)/Makefile.common
+include $(SRCDIR)/common.mk
 
 libfiasco_input.a: $(OBJECTS)
 	$(AR) -rc $@ $(OBJECTS)
diff --git a/converter/other/fiasco/lib/Makefile b/converter/other/fiasco/lib/Makefile
index 99d7c1d7..801fd24e 100644
--- a/converter/other/fiasco/lib/Makefile
+++ b/converter/other/fiasco/lib/Makefile
@@ -6,7 +6,7 @@ FIASCOSUBDIR = converter/other/fiasco
 SUBDIR = $(FIASCOSUBDIR)/lib
 VPATH=.:$(SRCDIR)/$(SUBDIR)
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
 OBJECTS = \
   arith.o \
@@ -21,11 +21,11 @@ OBJECTS = \
 
 MERGE_OBJECTS = $(OBJECTS)
 
-INCLUDES = -I$(SRCDIR)/$(FIASCOSUBDIR)
+COMP_INCLUDES = -I$(SRCDIR)/$(FIASCOSUBDIR)
 
 all: libfiasco_lib.a
 
-include $(SRCDIR)/Makefile.common
+include $(SRCDIR)/common.mk
 
 libfiasco_lib.a: $(OBJECTS)
 	$(AR) -rc $@ $(OBJECTS)
diff --git a/converter/other/fiasco/lib/misc.c b/converter/other/fiasco/lib/misc.c
index 02a1314f..12b94e7a 100644
--- a/converter/other/fiasco/lib/misc.c
+++ b/converter/other/fiasco/lib/misc.c
@@ -432,38 +432,6 @@ Log2 (double x)
    return log (x) / 0.69314718;
 }
 
-#ifndef HAVE_STRCASECMP
-bool_t
-strcaseeq (const char *s1, const char *s2)
-/*
- *  Compare strings 's1' and 's2', ignoring  the  case of the characters.
- *
- *  Return value:
- *	TRUE if strings match, else FALSE
- */
-{
-   bool_t  matched;
-   char	  *ls1, *ls2, *ptr;
-
-   assert (s1 && s2);
-   
-   ls1 = strdup (s1);
-   ls2 = strdup (s2);
-   
-   for (ptr = ls1; *ptr; ptr++)
-      *ptr = tolower (*ptr);
-   for (ptr = ls2; *ptr; ptr++)
-      *ptr = tolower (*ptr);
-
-   matched = streq (ls1, ls2) ? YES : NO;
-
-   Free (ls1);
-   Free (ls2);
-   
-   return matched;
-}
-#endif /* not HAVE_STRCASECMP */
-
 real_t
 variance (const word_t *pixels, unsigned x0, unsigned y0,
 	  unsigned width, unsigned height, unsigned cols)
diff --git a/converter/other/fiasco/output/Makefile b/converter/other/fiasco/output/Makefile
index 3bdc4635..26b6d924 100644
--- a/converter/other/fiasco/output/Makefile
+++ b/converter/other/fiasco/output/Makefile
@@ -6,19 +6,20 @@ FIASCOSUBDIR = converter/other/fiasco
 SUBDIR = $(FIASCOSUBDIR)/output
 VPATH=.:$(SRCDIR)/$(SUBDIR)
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
 OBJECTS =  matrices.o mc.o nd.o tree.o weights.o write.o
 
 MERGE_OBJECTS = $(OBJECTS)
 
-INCLUDES = -I$(SRCDIR)/$(FIASCOSUBDIR) \
-	   -I$(SRCDIR)/$(FIASCOSUBDIR)/lib \
-	   -I$(SRCDIR)/$(FIASCOSUBDIR)/codec 
+COMP_INCLUDES = \
+  -I$(SRCDIR)/$(FIASCOSUBDIR) \
+  -I$(SRCDIR)/$(FIASCOSUBDIR)/lib \
+  -I$(SRCDIR)/$(FIASCOSUBDIR)/codec 
 
 all: libfiasco_output.a
 
-include $(SRCDIR)/Makefile.common
+include $(SRCDIR)/common.mk
 
 libfiasco_output.a: $(OBJECTS)
 	$(AR) -rc $@ $(OBJECTS)
diff --git a/converter/other/fiasco/params.c b/converter/other/fiasco/params.c
index 3d0a0252..7a302b82 100644
--- a/converter/other/fiasco/params.c
+++ b/converter/other/fiasco/params.c
@@ -15,7 +15,8 @@
  *  $State: Exp $
  */
 
-#define _BSD_SOURCE 1   /* Make sure strdup() is in string.h */
+#define _BSD_SOURCE 1
+    /* Make sure strdup() is in string.h and strcaseeq() is in nstring.h */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
 #include "config.h"
diff --git a/converter/other/fitstopnm.c b/converter/other/fitstopnm.c
index b4240d63..73564c4b 100644
--- a/converter/other/fitstopnm.c
+++ b/converter/other/fitstopnm.c
@@ -1,4 +1,4 @@
- /* fitstopnm.c - read a FITS file and produce a portable anymap
+ /* fitstopnm.c - read a FITS file and produce a PNM.
  **
  ** Copyright (C) 1989 by Jef Poskanzer.
  **
@@ -30,16 +30,118 @@
  ** disabled min max scanning when reading from stdin.
  */
 
+/*
+  The official specification of FITS format (which is for more than
+  just visual images) is at
+  ftp://legacy.gsfc.nasa.gov/fits_info/fits_office/fits_standard.pdf
+*/
+
 #include <string.h>
+#include <float.h>
 
+#include "pm_config.h"
 #include "pm_c_util.h"
+#include "mallocvar.h"
+#include "floatcode.h"
+#include "shhopt.h"
 #include "pnm.h"
 
-/* Do what you have to, to get a sensible value here.  This may not be so */
-/* portable as the rest of the program.  We want to use MAXFLOAT and */
-/* MAXDOUBLE, so you could define them manually if you have to. */
-/* #include <values.h> */
-#include <float.h>
+
+
+struct cmdlineInfo {
+    const char * inputFileName;
+    unsigned int image;  /* zero if unspecified */
+    float max;
+    unsigned int maxSpec;
+    float min;
+    unsigned int minSpec;
+    unsigned int scanmax;
+    unsigned int printmax;
+    unsigned int noraw;
+        /* This is for backward compatibility only.  Use the common option
+           -plain now.  (pnm_init() processes -plain).
+        */
+    unsigned int verbose;
+    unsigned int omaxval;
+    unsigned int omaxvalSpec;
+};
+
+
+
+static void 
+parseCommandLine(int argc, char ** argv, 
+                 struct cmdlineInfo * const cmdlineP) {
+/* --------------------------------------------------------------------------
+   Parse program command line described in Unix standard form by argc
+   and argv.  Return the information in the options as *cmdlineP.  
+
+   If command line is internally inconsistent (invalid options, etc.),
+   issue error message to stderr and abort program.
+
+   Note that the strings we return are stored in the storage that
+   was passed to us as the argv array.  We also trash *argv.
+--------------------------------------------------------------------------*/
+    optEntry * option_def;
+        /* Instructions to optParseOptions3 on how to parse our options. */
+    optStruct3 opt;
+
+    unsigned int imageSpec;
+    unsigned int option_def_index;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "image",    OPT_UINT,
+            &cmdlineP->image,   &imageSpec,                            0);
+    OPTENT3(0, "min",      OPT_FLOAT,
+            &cmdlineP->min,     &cmdlineP->minSpec,                    0);
+    OPTENT3(0, "max",      OPT_FLOAT,
+            &cmdlineP->max,     &cmdlineP->maxSpec,                    0);
+    OPTENT3(0, "scanmax",  OPT_FLAG,
+            NULL,               &cmdlineP->scanmax,                    0);
+    OPTENT3(0, "printmax", OPT_FLAG,
+            NULL,               &cmdlineP->printmax,                   0);
+    OPTENT3(0, "noraw",    OPT_FLAG,
+            NULL,               &cmdlineP->noraw,                      0);
+    OPTENT3(0, "verbose",  OPT_FLAG,
+            NULL,               &cmdlineP->verbose,                    0);
+    OPTENT3(0, "omaxval",  OPT_UINT,
+            &cmdlineP->omaxval, &cmdlineP->omaxvalSpec,                0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;   /* We have no parms that are negative numbers */
+
+    /* Set some defaults the lazy way (using multiple setting of variables) */
+
+    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (imageSpec) {
+        if (cmdlineP->image == 0)
+            pm_error("You may not specify zero for the image number.  "
+                     "Images are numbered starting at 1.");
+    } else
+        cmdlineP->image = 0;
+
+    if (cmdlineP->maxSpec && cmdlineP->minSpec) {
+        if (cmdlineP->max <= cmdlineP->min)
+            pm_error("-max must be greater than -min.  You specified "
+                     "-max=%f, -min=%f", cmdlineP->max, cmdlineP->min);
+    }
+
+    if (argc-1 < 1)
+        cmdlineP->inputFileName = "-";
+    else {
+        cmdlineP->inputFileName = argv[1];
+        
+        if (argc-1 > 1)
+            pm_error("Too many arguments (%u).  The only non-option argument "
+                     "is the input file name.", argc-1);
+    }
+}
+
+
 
 struct FITS_Header {
   int simple;       /* basic format or not */
@@ -54,10 +156,262 @@ struct FITS_Header {
   double bscale;
 };
 
-static void read_fits_header ARGS(( FILE* fp, struct FITS_Header* hP ));
-static void read_card ARGS(( FILE* fp, char* buf ));
-static void read_val ARGS(( FILE* fp, int bitpix, double* vp ));
-     
+
+/* This code deals properly with integers, no matter what the byte order
+   or integer size of the host machine.  We handle sign extension manually to
+   prevent problems with signed/unsigned characters.  We read floating point
+   values properly only when the host architecture conforms to IEEE-754.  If
+   you need to tweak this code for other machines, you might want to get a
+   copy of the FITS documentation from nssdca.gsfc.nasa.gov
+*/
+
+static void
+readFitsChar(FILE *   const ifP,
+             double * const vP) {
+
+    /* 8 bit FITS integers are unsigned */
+
+    int const ich = getc(ifP);
+
+    if (ich == EOF)
+        pm_error("EOF / read error");
+    else
+        *vP = ich;
+}
+
+
+
+static void
+readFitsShort(FILE *   const ifP,
+              double * const vP) {
+
+    int ich;
+    int ival;
+    unsigned char c[8];
+
+    ich = getc(ifP);
+
+    if (ich == EOF)
+        pm_error("EOF / read error");
+
+    c[0] = ich;
+
+    ich = getc(ifP);
+
+    if (ich == EOF)
+        pm_error("EOF / read error");
+
+    c[1] = ich;
+
+    if (c[0] & 0x80)
+        ival = ~0xFFFF | c[0] << 8 | c[1];
+    else
+        ival = c[0] << 8 | c[1];
+
+    *vP = ival;
+}
+
+
+
+static void
+readFitsLong(FILE *   const ifP,
+             double * const vP) {
+
+    unsigned int i;
+    long int lval;
+    unsigned char c[4];
+
+    for (i = 0; i < 4; ++i) {
+        int const ich = getc(ifP);
+        if (ich == EOF)
+            pm_error("EOF / read error");
+        c[i] = ich;
+    }
+
+    if (c[0] & 0x80)
+        lval = ~0xFFFFFFFF | c[0] << 24 | c[1] << 16 | c[2] << 8 | c[3];
+    else
+        lval = c[0] << 24 | c[1] << 16 | c[2] << 8 | c[3] << 0;
+
+    *vP = lval;
+}
+
+
+
+static void
+readFitsFloat(FILE *   const ifP,
+              double * const vP) {
+
+    unsigned int i;
+    pm_bigendFloat bigend;
+
+    for (i = 0; i < 4; ++i) {
+        int const ich = getc(ifP);
+        if (ich == EOF)
+            pm_error("EOF / read error");
+        bigend.bytes[i] = ich;
+    }
+
+    *vP = pm_floatFromBigendFloat(bigend);
+}
+
+
+
+static void
+readFitsDouble(FILE *   const ifP,
+               double * const vP) {
+
+    unsigned int i;
+    pm_bigendDouble bigend;
+
+    for (i = 0; i < 8; ++i) {
+        int const ich = getc(ifP);
+        if (ich == EOF)
+            pm_error("EOF / read error");
+        bigend.bytes[i] = ich;
+    }
+
+    *vP = pm_doubleFromBigendDouble(bigend);
+}
+
+
+
+static void
+readVal(FILE *   const ifP,
+        int      const bitpix,
+        double * const vP) {
+
+    switch (bitpix) {
+    case 8:
+        readFitsChar(ifP, vP);
+        break;
+
+    case 16:
+        readFitsShort(ifP, vP);
+        break;
+      
+    case 32:
+        readFitsLong(ifP, vP);
+        break;
+      
+    case -32:
+        readFitsFloat(ifP, vP);
+        break;
+      
+    case -64:
+        readFitsDouble(ifP, vP);
+        break;
+      
+    default:
+        pm_error("Strange bitpix value %d in readVal()", bitpix);
+    }
+}
+
+
+
+static void
+readCard(FILE * const ifP,
+         char * const buf) {
+
+    size_t bytesRead;
+
+    bytesRead = fread(buf, 1, 80, ifP);
+    if (bytesRead == 0)
+        pm_error("error reading header");
+}
+
+
+
+static void
+readFitsHeader(FILE *               const ifP,
+               struct FITS_Header * const hP) {
+
+    int seenEnd;
+  
+    seenEnd = 0;
+    /* Set defaults */
+    hP->simple  = 0;
+    hP->bzer    = 0.0;
+    hP->bscale  = 1.0;
+    hP->datamin = - DBL_MAX;
+    hP->datamax = DBL_MAX;
+  
+    while (!seenEnd) {
+        unsigned int i;
+        for (i = 0; i < 36; ++i) {
+            char buf[80];
+            char c;
+
+            readCard(ifP, buf);
+    
+            if (sscanf(buf, "SIMPLE = %c", &c) == 1) {
+                if (c == 'T' || c == 't')
+                    hP->simple = 1;
+            } else if (sscanf(buf, "BITPIX = %d", &(hP->bitpix)) == 1);
+            else if (sscanf(buf, "NAXIS = %d", &(hP->naxis)) == 1);
+            else if (sscanf(buf, "NAXIS1 = %d", &(hP->naxis1)) == 1);
+            else if (sscanf(buf, "NAXIS2 = %d", &(hP->naxis2)) == 1);
+            else if (sscanf(buf, "NAXIS3 = %d", &(hP->naxis3)) == 1);
+            else if (sscanf(buf, "DATAMIN = %lf", &(hP->datamin)) == 1);
+            else if (sscanf(buf, "DATAMAX = %lf", &(hP->datamax)) == 1);
+            else if (sscanf(buf, "BZERO = %lf", &(hP->bzer)) == 1);
+            else if (sscanf(buf, "BSCALE = %lf", &(hP->bscale)) == 1);
+            else if (strncmp(buf, "END ", 4 ) == 0) seenEnd = 1;
+        }
+    }
+}
+
+
+
+static void
+interpretPlanes(struct FITS_Header const fitsHeader,
+                unsigned int       const imageRequest,
+                bool               const verbose,
+                unsigned int *     const imageCountP,
+                bool *             const multiplaneP,
+                unsigned int *     const desiredImageP) {
+
+    if (fitsHeader.naxis == 2) {
+        *imageCountP   = 1;
+        *multiplaneP   = FALSE;
+        *desiredImageP = 1;
+    } else {
+        if (imageRequest) {
+            if (imageRequest > fitsHeader.naxis3)
+                pm_error("Only %u plane%s in this file.  "
+                         "You requested image %u", 
+                         fitsHeader.naxis3, fitsHeader.naxis3 > 1 ? "s" : "",
+                         imageRequest);
+            else {
+                *imageCountP   = fitsHeader.naxis3;
+                *multiplaneP   = FALSE;
+                *desiredImageP = imageRequest;
+            }
+        } else {
+            if (fitsHeader.naxis3 == 3) {
+                *imageCountP   = 1;
+                *multiplaneP   = TRUE;
+                *desiredImageP = 1;
+            } else if (fitsHeader.naxis3 > 1)
+                pm_error("This FITS file contains multiple (%u) images.  "
+                         "You must specify which one you want with a "
+                         "-image option.", fitsHeader.naxis3);
+            else {
+                *imageCountP   = fitsHeader.naxis3;
+                *multiplaneP   = FALSE;
+                *desiredImageP = 1;
+            }
+        }
+    }
+    if (verbose) {
+        
+        pm_message("FITS stream is %smultiplane", *multiplaneP ? "" : "not ");
+        pm_message("We will take image %u (1 is first) of %u "
+                   "in the FITS stream",
+                   *desiredImageP, *imageCountP);
+    }
+}
+
 
 
 static void
@@ -100,7 +454,7 @@ scanImageForMinMax(FILE *       const ifP,
             unsigned int col;
             for (col = 0; col < cols; ++col) {
                 double val;
-                read_val(ifP, bitpix, &val);
+                readVal(ifP, bitpix, &val);
                 if (image == imagenum || multiplane ) {
                     dmax = MAX(dmax, val);
                     dmin = MIN(dmin, val);
@@ -170,344 +524,233 @@ computeMinMax(FILE *             const ifP,
 
 
 
-int
-main(int argc, char * argv[]) {
-
-    FILE* ifp;
-    int argn, imagenum, image, row;
-    register int col;
-    xelval maxval;
-    double val, frmin, frmax, scale, t;
-    double datamin, datamax;
-    int rows, cols, images, format;
-    struct FITS_Header h;
-    xel** pnmarray;
-    xelval tx, txv[4];
-    const char* fits_name;
-    const char* const usage = "[-image N] [-scanmax] [-printmax] [-min f] [-max f] [FITSfile]";
-
-    int doscan = 0;
-    int forceplain = 0;
-    int forcemin = 0;
-    int forcemax = 0;
-    int printmax = 0;
-    bool multiplane;
-  
-    pnm_init( &argc, argv );
-  
-    argn = 1;
-    imagenum = 0;
-  
-    while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
-    {
-        if ( pm_keymatch( argv[argn], "-image", 2 ) )
-        {
-            ++argn;
-            if ( argn == argc || sscanf( argv[argn], "%d", &imagenum ) != 1 )
-                pm_usage( usage );
-        }
-        else if ( pm_keymatch( argv[argn], "-max", 3 ) )
-        {
-            ++argn;
-            forcemax = 1;
-            if ( argn == argc || sscanf( argv[argn], "%lf", &frmax ) != 1 )
-                pm_usage( usage );
-        }
-        else if ( pm_keymatch( argv[argn], "-min", 3 ) )
-        {
-            ++argn;
-            forcemin = 1;
-            if ( argn == argc || sscanf( argv[argn], "%lf", &frmin ) != 1 )
-                pm_usage( usage );
-        }
-        else if ( pm_keymatch( argv[argn], "-scanmax", 2 ) )
-            doscan = 1;
-        else if ( pm_keymatch( argv[argn], "-noraw", 2 ) )
-            /* This is for backward compatibility only.  Use the common option
-               -plain now.  (pnm_init() processes -plain).
+static xelval
+determineMaxval(struct cmdlineInfo const cmdline,
+                struct FITS_Header const fitsHeader,
+                double             const datamax,
+                double             const datamin) {
+
+    xelval retval;
+                
+    if (cmdline.omaxvalSpec)
+        retval = cmdline.omaxval;
+    else {
+        if (fitsHeader.bitpix < 0) {
+            /* samples are floating point, which means the resolution
+               could be anything.  So we just pick a convenient maxval
+               of 255.  Before Netpbm 10.20 (January 2004), we did
+               maxval = max - min for floating point as well as
+               integer samples.
             */
-            forceplain = 1;
-        else if ( pm_keymatch( argv[argn], "-printmax", 2 ) )
-            printmax = 1;
-        else
-            pm_usage( usage );
-        ++argn;
-    }
-  
-    if ( argn < argc )
-    {
-        fits_name = argv[argn];
-        ++argn;
+            retval = 255;
+            if (cmdline.verbose)
+                pm_message("FITS image has floating point samples.  "
+                           "Using maxval = %u.", (unsigned int)retval);
+        } else {
+            retval = MAX(1, MIN(PNM_OVERALLMAXVAL, datamax - datamin));
+            if (cmdline.verbose)
+                pm_message("FITS image has samples in the range %d-%d.  "
+                           "Using maxval %u.",
+                           (int)(datamin+0.5), (int)(datamax+0.5),
+                           (unsigned int)retval);
+        }
     }
-    else
-        fits_name = "-";
-
-    if ( argn != argc )
-        pm_usage( usage );
-
-    ifp = pm_openr_seekable(fits_name);
-  
-    read_fits_header( ifp, &h );
-  
-    if ( ! h.simple )
-        pm_error( "FITS file is not in simple format, can't read" );
-    if ( h.naxis != 2 && h.naxis != 3 )
-        pm_message( "Warning: FITS file has %d axes", h.naxis );
-    cols = h.naxis1;
-    rows = h.naxis2;
-    if ( h.naxis == 2 )
-        images = imagenum = 1;
-    else
-        images = h.naxis3;
-    if ( imagenum > images )
-        pm_error( "only %d plane%s in this file", 
-                  images, images > 1 ? "s" : "" );
-    if ( images != 3 && images > 1 && imagenum == 0 )
-        pm_error( "need to specify a plane using the -imagenum flag" );
-
-    multiplane = ( images == 3 && imagenum == 0 );
+    return retval;
+}
 
-    computeMinMax(ifp, images, cols, rows, h, imagenum, multiplane,
-                  forcemin, forcemax, frmin, frmax,
-                  &datamin, &datamax);
 
-    if (h.bitpix < 0) {
-        /* samples are floating point, which means the resolution could be
-           anything.  So we just pick a convenient maxval of 255.  We should
-           have a program option to choose the maxval.  Before Netpbm 10.20
-           (January 2004), we did maxval = max - min for floating point as
-           well as integer samples.
-        */
-        maxval = 255;
-    } else
-        maxval = MAX(1, MIN(PNM_OVERALLMAXVAL, datamax - datamin));
 
-    if ( datamax - datamin == 0 )
-        scale = 1.0;
-    else
-        scale = maxval / ( datamax - datamin );
-
-    /* If printmax option is true, just print and exit. */
-    /* For use in shellscripts.  Ex:                    */
-    /* eval `fitstopnm -printmax $filename | \          */
-    /*   awk '{min = $1; max = $2}\                     */
-    /*         END {print "min=" min; " max=" max}'`    */
-    if (printmax) {
-        printf( "%f %f\n", datamin, datamax);
-        pm_close( ifp );
-        pm_close( stdout );
-        exit( 0 );
-    }
+static void
+convertPgmRaster(FILE *             const ifP,
+                 unsigned int       const cols,
+                 unsigned int       const rows,
+                 xelval             const maxval,
+                 unsigned int       const desiredImage,
+                 unsigned int       const imageCount,
+                 struct FITS_Header const fitsHdr,
+                 double             const scale,
+                 double             const datamin,
+                 xel **             const xels) {
+        
+    /* Note: the FITS specification does not give the association between
+       file position and image position (i.e. is the first pixel in the
+       file the top left, bottom left, etc.).  We use the common sense,
+       popular order of row major, top to bottom, left to right.  This
+       has been the case and accepted since 1989, but in 2008, we discovered
+       that Gimp and ImageMagick do bottom to top.
+    */
+    unsigned int image;
 
-    if (multiplane)
-        format = PPM_FORMAT;
-    else
-        format = PGM_FORMAT;
+    pm_message("Writing PPM file "
+               "(Probably not what you want - consider an -image option)");
 
-    pnmarray = pnm_allocarray( cols, rows );
-
-    switch( PNM_FORMAT_TYPE( format ))
-    {
-    case PGM_TYPE:
-        pm_message( "writing PGM file" );
-        for ( image = 1; image <= imagenum; ++image )
-        {
-            if ( image != imagenum )
-                pm_message( "skipping image plane %d of %d", image, images );
-            else if ( images > 1 )
-                pm_message( "reading image plane %d of %d", image, images );
-            for ( row = 0; row < rows; ++row)
-                for ( col = 0; col < cols; ++col )
+    for (image = 1; image <= desiredImage; ++image) {
+        unsigned int row;
+        if (image != desiredImage)
+            pm_message("skipping image plane %u of %u", image, imageCount);
+        else if (imageCount > 1)
+            pm_message("reading image plane %u of %u", image, imageCount);
+        for (row = 0; row < rows; ++row) {
+            unsigned int col;
+            for (col = 0; col < cols; ++col) {
+                double val;
+                readVal(ifP, fitsHdr.bitpix, &val);
                 {
-                    read_val (ifp, h.bitpix, &val);
-                    t = scale * ( val * h.bscale + h.bzer - datamin );
-                    tx = MAX( 0, MIN( t, maxval ) );
-                    if ( image == imagenum )
-                        PNM_ASSIGN1( pnmarray[row][col], tx );
+                    double const t = scale *
+                        (val * fitsHdr.bscale + fitsHdr.bzer - datamin);
+                    xelval const tx = MAX(0, MIN(t, maxval));
+                    if (image == desiredImage)
+                        PNM_ASSIGN1(xels[row][col], tx);
                 }
+            }
         }
-        break;
-    case PPM_TYPE:
-        pm_message( "writing PPM file" );
-        for ( image = 1; image <= images; image++ )
-        {
-            pm_message( "reading image plane %d of %d", image, images );
-            for ( row = 0; row < rows; row++ )
-                for ( col = 0; col < cols; col++ )
+    } 
+}
+
+
+
+static void
+convertPpmRaster(FILE *             const ifP,
+                 unsigned int       const cols,
+                 unsigned int       const rows,
+                 xelval             const maxval,
+                 struct FITS_Header const fitsHdr,
+                 double             const scale,
+                 double             const datamin,
+                 xel **             const xels) {
+/*----------------------------------------------------------------------------
+   Read the FITS raster from file *ifP into xels[][].  Image dimensions
+   are 'cols' by 'rows'.  The FITS raster is 3 planes composing one
+   image: a red plane followed by a green plane followed by a blue plane.
+-----------------------------------------------------------------------------*/
+    unsigned int plane;
+
+    pm_message("writing PPM file");
+
+    for (plane = 0; plane < 3; ++plane) {
+        unsigned int row;
+        pm_message("reading image plane %u (%s)",
+                   plane, plane == 0 ? "red" : plane == 1 ? "green" : "blue");
+        for (row = 0; row < rows; ++row) {
+            unsigned int col;
+            for (col = 0; col < cols; ++col) {
+                double val;
+                readVal(ifP, fitsHdr.bitpix, &val);
                 {
-                    read_val (ifp, h.bitpix, &val);
-                    txv[1] = PPM_GETR( pnmarray[row][col] );
-                    txv[2] = PPM_GETG( pnmarray[row][col] );
-                    txv[3] = PPM_GETB( pnmarray[row][col] );
-                    t = scale * ( val * h.bscale + h.bzer - datamin );
-                    txv[image] = MAX( 0, MIN( t, maxval ));
-                    PPM_ASSIGN( pnmarray[row][col], txv[1], txv[2], txv[3] );
+                    double const t = scale *
+                        (val * fitsHdr.bscale + fitsHdr.bzer - datamin);
+                    xelval const sample = MAX(0, MIN(t, maxval));
+
+                    switch (plane) {
+                    case 0: PPM_PUTR(xels[row][col], sample); break;
+                    case 1: PPM_PUTG(xels[row][col], sample); break;
+                    case 2: PPM_PUTB(xels[row][col], sample); break;
+                    }
                 }
+            }
         }
-        break;
-    default:
-        pm_error( "can't happen" );
-        break;
     }
-
-    pnm_writepnm( stdout, pnmarray, cols, rows, maxval, format, forceplain );
-    pnm_freearray( pnmarray, rows );
-
-    pm_close( ifp );
-    pm_close( stdout );
-  
-    exit( 0 );
 }
 
 
+
 static void
-swapbytes(void *       const p,
-          unsigned int const nbytes) {
-#if BYTE_ORDER == LITTLE_ENDIAN
-    unsigned char * const c = p;
-    unsigned int i;
-    for (i = 0; i < nbytes/2; ++i) {
-        unsigned char const orig = c[i];
-        c[i] = c[nbytes-(i+1)];
-        c[nbytes-(i+1)] = orig;
+convertRaster(FILE *             const ifP,
+              unsigned int       const cols,
+              unsigned int       const rows,
+              xelval             const maxval,
+              bool               const forceplain,
+              bool               const multiplane,
+              unsigned int       const desiredImage,
+              unsigned int       const imageCount,
+              struct FITS_Header const fitsHdr,
+              double             const scale,
+              double             const datamin) {
+
+    xel ** xels;
+    int format;
+
+    xels = pnm_allocarray(cols, rows);
+
+    if (multiplane) {
+        format = PPM_FORMAT;
+        convertPpmRaster(ifP, cols, rows, maxval, fitsHdr, scale, datamin,
+                         xels);
+    } else {
+        format = PGM_FORMAT;
+        convertPgmRaster(ifP, cols, rows, maxval,
+                         desiredImage, imageCount, fitsHdr, scale, datamin,
+                         xels);
     }
-#endif
+    pnm_writepnm(stdout, xels, cols, rows, maxval, format, forceplain);
+    pnm_freearray(xels, rows);
 }
 
 
-/*
- ** This code will deal properly with integers, no matter what the byte order
- ** or integer size of the host machine.  Sign extension is handled manually
- ** to prevent problems with signed/unsigned characters.  Floating point
- ** values will only be read properly when the host architecture is IEEE-754
- ** conformant.  If you need to tweak this code for other machines, you might
- ** want to snag a copy of the FITS documentation from nssdca.gsfc.nasa.gov
- */
 
-static void
-read_val (fp, bitpix, vp)
-    FILE *fp;
-    int bitpix;
-    double *vp;
+int
+main(int argc, char * argv[]) {
 
-{
-    int i, ich, ival;
-    long int lval;
-    unsigned char c[8];
-  
-    switch ( bitpix )
-    {
-        /* 8 bit FITS integers are unsigned */
-    case 8:
-        ich = getc( fp );
-        if ( ich == EOF )
-            pm_error( "EOF / read error" );
-        *vp = ich;
-        break;
-      
-    case 16:
-        ich = getc( fp );
-        if ( ich == EOF )
-            pm_error( "EOF / read error" );
-        c[0] = ich;
-        ich = getc( fp );
-        if ( ich == EOF )
-            pm_error( "EOF / read error" );
-        c[1] = ich;
-        if (c[0] & 0x80)
-            ival = ~0xFFFF | c[0]<<8 | c[1];
-        else
-            ival = c[0]<<8 | c[1];
-        *vp = ival;
-        break;
-      
-    case 32:
-        for (i=0; i<4; i++) {
-            ich = getc( fp );
-            if ( ich == EOF )
-                pm_error( "EOF / read error" );
-            c[i] = ich;
-        }
-        if (c[0] & 0x80)
-            lval = ~0xFFFFFFFF |
-                c[0]<<24 | c[1]<<16 | c[2]<<8 | c[3];
-        else
-            lval = c[0]<<24 | c[1]<<16 | c[2]<<8 | c[3];
-        *vp = lval;
-        break;
-      
-    case -32:
-        for (i=0; i<4; i++) {
-            ich = getc( fp );
-            if ( ich == EOF )
-                pm_error( "EOF / read error" );
-            c[i] = ich;
-        }
-        swapbytes(c, 4);
-        *vp = *( (float *) c);
-        break;
-      
-    case -64:
-        for (i=0; i<8; i++) {
-            ich = getc( fp );
-            if ( ich == EOF )
-                pm_error( "EOF / read error" );
-            c[i] = ich;
-        }
-        swapbytes(c, 8);
-        *vp = *( (double *) c);
-        break;
-      
-    default:
-        pm_error( "Strange bitpix in read_value" );
-    }
-}
+    struct cmdlineInfo cmdline;
+    FILE * ifP;
+    unsigned int cols, rows;
+    xelval maxval;
+    double scale;
+    double datamin, datamax;
+    struct FITS_Header fitsHeader;
 
-static void
-read_fits_header( fp, hP )
-    FILE* fp;
-    struct FITS_Header* hP;
-{
-    char buf[80];
-    int seen_end;
-    int i;
-    char c;
+    unsigned int imageCount;
+    unsigned int desiredImage;
+        /* Plane number (starting at one) of plane that contains the image
+           we want.
+        */
+    bool multiplane;
+        /* This is a one-image multiplane stream; 'desiredImage'
+           is undefined
+        */
   
-    seen_end = 0;
-    hP->simple = 0;
-    hP->bzer = 0.0;
-    hP->bscale = 1.0;
-    hP->datamin = - DBL_MAX;
-    hP->datamax = DBL_MAX;
+    pnm_init( &argc, argv );
   
-    while ( ! seen_end )
-        for ( i = 0; i < 36; ++i )
-        {
-            read_card( fp, buf );
-    
-            if ( sscanf( buf, "SIMPLE = %c", &c ) == 1 )
-            {
-                if ( c == 'T' || c == 't' )
-                    hP->simple = 1;
-            }
-            else if ( sscanf( buf, "BITPIX = %d", &(hP->bitpix) ) == 1 );
-            else if ( sscanf( buf, "NAXIS = %d", &(hP->naxis) ) == 1 );
-            else if ( sscanf( buf, "NAXIS1 = %d", &(hP->naxis1) ) == 1 );
-            else if ( sscanf( buf, "NAXIS2 = %d", &(hP->naxis2) ) == 1 );
-            else if ( sscanf( buf, "NAXIS3 = %d", &(hP->naxis3) ) == 1 );
-            else if ( sscanf( buf, "DATAMIN = %lf", &(hP->datamin) ) == 1 );
-            else if ( sscanf( buf, "DATAMAX = %lf", &(hP->datamax) ) == 1 );
-            else if ( sscanf( buf, "BZERO = %lf", &(hP->bzer) ) == 1 );
-            else if ( sscanf( buf, "BSCALE = %lf", &(hP->bscale) ) == 1 );
-            else if ( strncmp( buf, "END ", 4 ) == 0 ) seen_end = 1;
-        }
-}
+    parseCommandLine(argc, argv, &cmdline);
 
-static void
-read_card( fp, buf )
-    FILE* fp;
-    char* buf;
-{
-    if ( fread( buf, 1, 80, fp ) == 0 )
-        pm_error( "error reading header" );
+    ifP = pm_openr(cmdline.inputFileName);
+
+    readFitsHeader(ifP, &fitsHeader);
+  
+    if (!fitsHeader.simple)
+        pm_error("FITS file is not in simple format, can't read");
+
+    if (fitsHeader.naxis != 2 && fitsHeader.naxis != 3)
+        pm_message("Warning: FITS file has %u axes", fitsHeader.naxis);
+
+    cols = fitsHeader.naxis1;
+    rows = fitsHeader.naxis2;
+
+    interpretPlanes(fitsHeader, cmdline.image, cmdline.verbose,
+                    &imageCount, &multiplane, &desiredImage);
+
+    computeMinMax(ifP, imageCount, cols, rows, fitsHeader,
+                  desiredImage, multiplane,
+                  cmdline.minSpec, cmdline.maxSpec,
+                  cmdline.min, cmdline.max,
+                  &datamin, &datamax);
+
+    maxval = determineMaxval(cmdline, fitsHeader, datamax, datamin);
+
+    if (datamax - datamin == 0)
+        scale = 1.0;
+    else
+        scale = maxval / (datamax - datamin);
+
+    if (cmdline.printmax)
+        printf("%f %f\n", datamin, datamax);
+    else
+        convertRaster(ifP, cols, rows, maxval, cmdline.noraw,
+                      multiplane, desiredImage, imageCount,
+                      fitsHeader, scale, datamin);
+
+    pm_close(ifP);
+    pm_close(stdout);
+
+    return 0;
 }
diff --git a/converter/other/giftopnm.c b/converter/other/giftopnm.c
index 7337960c..4b8b0487 100644
--- a/converter/other/giftopnm.c
+++ b/converter/other/giftopnm.c
@@ -23,10 +23,11 @@
 #include <string.h>
 #include <assert.h>
 
-#include "pnm.h"
-#include "shhopt.h"
+#include "pm_c_util.h"
 #include "mallocvar.h"
 #include "nstring.h"
+#include "shhopt.h"
+#include "pnm.h"
 
 #define GIFMAXVAL 255
 #define MAXCOLORMAPSIZE 256
@@ -41,6 +42,13 @@
 #define LOCALCOLORMAP  0x80
 #define BitSet(byte, bit)      (((byte) & (bit)) == (bit))
 
+#if !defined(BYTE_ORDER) || !defined(LITTLE_ENDIAN)
+  /* make sure (BYTE_ORDER == LITTLE_ENDIAN) is FALSE */ 
+  #define BYTE_ORDER    0
+  #define LITTLE_ENDIAN 1
+#endif
+
+
 static __inline__ bool
 ReadOK(FILE *          const fileP,
        unsigned char * const buffer,
@@ -54,6 +62,33 @@ ReadOK(FILE *          const fileP,
 }
 
 
+
+static void
+readFile(FILE *          const ifP,
+         unsigned char * const buffer,
+         size_t          const len,
+         const char **   const errorP) {
+
+    size_t bytesRead;
+
+    bytesRead = fread(buffer, len, 1, ifP);
+
+    if (bytesRead == len)
+        *errorP = NULL;
+    else {
+        if (ferror(ifP))
+            asprintfN(errorP, "Error reading file.  errno=%d (%s)",
+                      errno, strerror(errno));
+        else if (feof(ifP))
+            asprintfN(errorP, "End of file encountered");
+        else
+            asprintfN(errorP, "Short read -- %u bytes of %u",
+                              (unsigned)bytesRead, (unsigned)len);
+    }
+}
+
+
+
 #define LM_to_uint(a,b)                        (((b)<<8)|(a))
 
 static int const maxnum_lzwCode = (1<<MAX_LZW_BITS);
@@ -72,6 +107,7 @@ struct cmdlineInfo {
         */
     const char * alpha_filename;
     unsigned int quitearly;
+    unsigned int repair;
 };
 
 
@@ -101,8 +137,10 @@ parseCommandLine(int argc, char ** argv,
             &cmdlineP->verbose,         0);
     OPTENT3(0, "comments",    OPT_FLAG, NULL,
             &cmdlineP->comments,        0);
-    OPTENT3(0, "quitearly",    OPT_FLAG, NULL,
+    OPTENT3(0, "quitearly",   OPT_FLAG, NULL,
             &cmdlineP->quitearly,       0);
+    OPTENT3(0, "repair",      OPT_FLAG, NULL,
+            &cmdlineP->repair,          0);
     OPTENT3(0, "image",       OPT_STRING, &image,
             &imageSpec,                 0);
     OPTENT3(0, "alphaout",    OPT_STRING, &cmdlineP->alpha_filename, 
@@ -232,13 +270,15 @@ readColorMap(FILE *ifP, const int colormapsize,
 
 static bool zeroDataBlock = FALSE;
     /* the most recently read DataBlock was an EOD marker, i.e. had
-       zero length */
+       zero length
+    */
 
 static void
 getDataBlock(FILE *          const ifP, 
              unsigned char * const buf, 
              bool *          const eofP,
-             unsigned int *  const lengthP) {
+             unsigned int *  const lengthP,
+             const char **   const errorP) {
 /*----------------------------------------------------------------------------
    Read a DataBlock from file 'ifP', return it at 'buf'.
 
@@ -258,10 +298,11 @@ getDataBlock(FILE *          const ifP,
     unsigned char count;
     bool successfulRead;
     
-    long const pos=ftell(ifP);
+    long const pos = ftell(ifP);
     successfulRead = ReadOK(ifP, &count, 1);
     if (!successfulRead) {
         pm_message("EOF or error in reading DataBlock size from file" );
+        *errorP = NULL;
         *eofP = TRUE;
         *lengthP = 0;
     } else {
@@ -270,17 +311,21 @@ getDataBlock(FILE *          const ifP,
         *eofP = FALSE;
         *lengthP = count;
 
-        if (count == 0) 
+        if (count == 0) {
+            *errorP = NULL;
             zeroDataBlock = TRUE;
-        else {
+        } else {
             bool successfulRead;
 
             zeroDataBlock = FALSE;
             successfulRead = ReadOK(ifP, buf, count); 
-            
-            if (!successfulRead) 
-                pm_error("EOF or error reading data portion of %d byte "
-                         "DataBlock from file", count);
+
+            if (successfulRead) 
+                *errorP = NULL;
+            else
+                asprintfN(errorP,
+                          "EOF or error reading data portion of %u byte "
+                          "DataBlock from file", count);
         }
     }
 }
@@ -303,14 +348,15 @@ readThroughEod(FILE * const ifP) {
     while (!eod) {
         bool eof;
         unsigned int count;
+        const char * error;
 
-        getDataBlock(ifP, buf, &eof, &count);
-        if (eof)
+        getDataBlock(ifP, buf, &eof, &count, &error);
+        if (error || eof)
             pm_message("EOF encountered before EOD marker.  The GIF "
                        "file is malformed, but we are proceeding "
                        "anyway as if an EOD marker were at the end "
                        "of the file.");
-        if (eof || count == 0)
+        if (error || eof || count == 0)
             eod = TRUE;
     }
 }
@@ -334,7 +380,11 @@ doCommentExtension(FILE * const ifP) {
     done = FALSE;
     while (!done) {
         bool eof;
-        getDataBlock(ifP, (unsigned char*) buf, &eof, &blocklen); 
+        const char * error;
+        getDataBlock(ifP, (unsigned char*) buf, &eof, &blocklen, &error); 
+        if (error)
+            pm_error("Error reading a data block in a comment extension.  %s",
+                     error);
         if (blocklen == 0 || eof)
             done = TRUE;
         else {
@@ -355,8 +405,11 @@ doGraphicControlExtension(FILE *         const ifP,
     bool eof;
     unsigned int length;
     static unsigned char buf[256];
+    const char * error;
 
-    getDataBlock(ifP, buf, &eof, &length);
+    getDataBlock(ifP, buf, &eof, &length, &error);
+    if (error)
+        pm_error("Error reading 1st data block of Graphic Control Extension");
     if (eof)
         pm_error("EOF/error encountered reading "
                  "1st DataBlock of Graphic Control Extension.");
@@ -451,28 +504,16 @@ struct getCodeState {
         */
     bool streamExhausted;
         /* The last time we read from the input stream, we got an EOD marker
-           or EOF
+           or EOF or an error that prevents further reading of the stream.
         */
 };
 
 
 
 static void
-initGetCode(struct getCodeState * const getCodeStateP) {
-    
-    /* Fake a previous data block */
-    getCodeStateP->buf[0] = 0;
-    getCodeStateP->buf[1] = 0;
-    getCodeStateP->bufCount = 2;
-    getCodeStateP->curbit = getCodeStateP->bufCount * 8;
-    getCodeStateP->streamExhausted = FALSE;
-}
-
-
-
-static void
-getAnotherBlock(FILE * const ifP, 
-                struct getCodeState * const gsP) {
+getAnotherBlock(FILE *                const ifP, 
+                struct getCodeState * const gsP,
+                const char **         const errorP) {
 
     unsigned int count;
     unsigned int assumed_count;
@@ -490,98 +531,145 @@ getAnotherBlock(FILE * const ifP,
     gsP->bufCount = 2;
         
     /* Add the next block to the buffer */
-    getDataBlock(ifP, &gsP->buf[gsP->bufCount], &eof, &count);
-    if (eof) {
-        pm_message("EOF encountered in image "
-                   "before EOD marker.  The GIF "
-                   "file is malformed, but we are proceeding "
-                   "anyway as if an EOD marker were at the end "
-                   "of the file.");
-        assumed_count = 0;
-    } else
-        assumed_count = count;
-
-    gsP->streamExhausted = (assumed_count == 0);
+    getDataBlock(ifP, &gsP->buf[gsP->bufCount], &eof, &count, errorP);
+    if (*errorP)
+        gsP->streamExhausted = TRUE;
+    else {
+        if (eof) {
+            pm_message("EOF encountered in image "
+                       "before EOD marker.  The GIF "
+                       "file is malformed, but we are proceeding "
+                       "anyway as if an EOD marker were at the end "
+                       "of the file.");
+            assumed_count = 0;
+        } else
+            assumed_count = count;
 
-    gsP->bufCount += assumed_count;
+        gsP->streamExhausted = (assumed_count == 0);
+        
+        gsP->bufCount += assumed_count;
+    }
 }
 
 
 
+static struct getCodeState getCodeState;
+
 static void
-doGetCode(FILE *                const ifP, 
-          int                   const codeSize,
-          struct getCodeState * const gsP,
-          int *                 const retvalP) {
+getCode_init(struct getCodeState * const getCodeStateP) {
+    
+    /* Fake a previous data block */
+    getCodeStateP->buf[0] = 0;
+    getCodeStateP->buf[1] = 0;
+    getCodeStateP->bufCount = 2;
+    getCodeStateP->curbit = getCodeStateP->bufCount * 8;
+    getCodeStateP->streamExhausted = FALSE;
+}
 
-    while (gsP->curbit + codeSize > gsP->bufCount * 8 &&
-           !gsP->streamExhausted) 
-        /* Not enough left in buffer to satisfy request.  Get the next
-           data block into the buffer.
 
-           Note that a data block may be as small as one byte, so we may need
-           to do this multiple times to get the full code.  (This probably
-           never happens in practice).
-        */
-        getAnotherBlock(ifP, gsP);
 
-    if ((gsP->curbit+codeSize) > gsP->bufCount*8) {
-        /* If the buffer still doesn't have enough bits in it, that means
-           there were no data blocks left to read.
+static unsigned int
+bitsOfLeBuffer(const unsigned char * const buf,
+               unsigned int          const start,
+               unsigned int          const len) {
+/*----------------------------------------------------------------------------
+   Return a string of 'len' bits (up to 16) starting at bit 'start' of buffer
+   buf[].
+
+   In the buffer, the bits are numbered Intel-style, with the first bit of a
+   byte being the least significant bit.  So bit 3 is the "16" bit of the
+   first byte of buf[].
+
+   We return the string as an integer such that its pure binary encoding with
+   the bits numbered Intel-style is the string.  E.g. the string 0,1,1 
+   yields six.
+-----------------------------------------------------------------------------*/
+    uint32_t codeBlock;
+        /* The 3 whole bytes of the buffer that contain the requested
+           bit string
         */
-        *retvalP = -1;  /* EOF */
-
-        {
-            int const bitsUnused = gsP->bufCount*8 - gsP->curbit;
-            if (bitsUnused > 0)
-                pm_message("Stream ends with a partial code "
-                           "(%d bits left in file; "
-                           "expected a %d bit code).  Ignoring.",
-                           bitsUnused, codeSize);
-        }
-    } else {
-        int i, j;
-        int code;
-        unsigned char * const buf = gsP->buf;
-
-        code = 0;  /* initial value */
-        for (i = gsP->curbit, j = 0; j < codeSize; ++i, ++j)
-            code |= ((buf[ i / 8 ] & (1 << (i % 8))) != 0) << j;
-        gsP->curbit += codeSize;
-        *retvalP = code;
-    }
+
+    assert(len <= 16);
+
+    if (BYTE_ORDER == LITTLE_ENDIAN)
+        /* Fast path */
+        codeBlock = *(uint32_t *) & buf[start/8];
+    else
+        /* logic works for little endian too */
+        codeBlock =
+            (buf[start/8+0] <<  0) |
+            (buf[start/8+1] <<  8) |
+            (buf[start/8+2] << 16);
+            
+    return (unsigned int) 
+        (codeBlock >> (start % 8)) & ((1 << len) - 1);
 }
 
 
 
-static int
-getCode(FILE * const ifP, 
-        int    const codeSize, 
-        bool   const init)
-{
+static void
+getCode_get(struct getCodeState * const gsP,
+            FILE *                const ifP, 
+            int                   const codeSize,
+            bool *                const eofP,
+            unsigned int *        const codeP,
+            const char **         const errorP) {
 /*----------------------------------------------------------------------------
-   If 'init', initialize the code getter.
+  Read and return the next lzw code from the file *ifP.
+
+  'codeSize' is the number of bits in the code we are to get.
 
-   Otherwise, read and return the next lzw code from the file *ifP.
+  Return *eofP == TRUE iff we hit the end of the stream.  That means a legal
+  end of stream, marked by an EOD marker, not just end of file.  An end of
+  file in the middle of the GIF stream is an error.
 
-   'codeSize' is the number of bits in the code we are to get.
+  If there are bits left in the stream, but not 'codeSize' of them, we
+  call that a success with *eofP == TRUE.
 
-   Return -1 instead of a code if we encounter the end of the file.
+  Return the code read (assuming *eofP == FALSE and *errorP == NULL)
+  as *codeP.
 -----------------------------------------------------------------------------*/
-    static struct getCodeState getCodeState;
 
-    int retval;
+    *errorP = NULL;
+
+    while (gsP->curbit + codeSize > gsP->bufCount * 8 &&
+           !gsP->streamExhausted && !*errorP) 
+        /* Not enough left in buffer to satisfy request.  Get the next
+           data block into the buffer.
+
+           Note that a data block may be as small as one byte, so we may need
+           to do this multiple times to get the full code.  (This probably
+           never happens in practice).
+        */
+        getAnotherBlock(ifP, gsP, errorP);
 
-    if (init) {
-        initGetCode(&getCodeState);
-        retval = 0;
-    } else 
-        doGetCode(ifP, codeSize, &getCodeState, &retval);
+    if (!*errorP) {
+        if (gsP->curbit + codeSize > gsP->bufCount * 8) {
+            /* The buffer still doesn't have enough bits in it; that means
+               there were no data blocks left to read.
+            */
+            *eofP = TRUE;
+
+            {
+                int const bitsUnused = gsP->bufCount * 8 - gsP->curbit;
+                if (bitsUnused > 0)
+                    pm_message("Stream ends with a partial code "
+                               "(%d bits left in file; "
+                               "expected a %d bit code).  Ignoring.",
+                               bitsUnused, codeSize);
+            }
+        } else {
+            *codeP = bitsOfLeBuffer(gsP->buf, gsP->curbit, codeSize);
 
-    return retval;
+            gsP->curbit += codeSize;
+            *eofP = FALSE;
+        }
+    }
 }
 
 
+
+
 struct stack {
     /* Stack grows from low addresses to high addresses */
     int * stack;  /* malloc'ed array */
@@ -638,6 +726,7 @@ termStack(struct stack * const stackP) {
     stackP->stack = NULL;
 }
 
+    
 
 /*----------------------------------------------------------------------------
    Some notes on LZW.
@@ -657,7 +746,7 @@ termStack(struct stack * const stackP) {
    max_dataVal.  The first byte in the stream tells you what dataWidth
    is.
 
-   LZW codes 0 - max_dataVal are direct codes.  Each on represents
+   LZW codes 0 - max_dataVal are direct codes.  Each one represents
    the true data element whose value is that of the LZW code itself.
    No decompression is required.
 
@@ -694,7 +783,7 @@ struct decompressor {
         */
     int      next_tableSlot;
         /* Index in the code translation table of the next free entry */
-    int      firstcode;
+    unsigned int firstcode;
         /* This is always a true data element code */
     int      prevcode;
         /* The code just before, in the image, the one we're processing now */
@@ -759,7 +848,7 @@ lzwInit(struct decompressor * const decompP,
     }
     resetDecompressor(decompP);
 
-    getCode(decompP->ifP, 0, TRUE);
+    getCode_init(&getCodeState);
     
     decompP->fresh = TRUE;
     
@@ -779,7 +868,7 @@ lzwTerm(struct decompressor * const decompP) {
 static void
 expandCodeOntoStack(struct decompressor * const decompP,
                     int                   const incode,
-                    bool *                const errorP) {
+                    const char **         const errorP) {
 /*----------------------------------------------------------------------------
    'incode' is an LZW string code.  It represents a string of true data
    elements, as defined by the string translation table in *decompP.
@@ -790,12 +879,13 @@ expandCodeOntoStack(struct decompressor * const decompP,
    Also add to the translation table where appropriate.
 
    Iff the translation table contains a cycle (which means the LZW stream
-   from which it was built is invalid), return *errorP == TRUE.
+   from which it was built is invalid), fail (return text explanation
+   as *errorP).
 -----------------------------------------------------------------------------*/
     int code;
-    bool error;
+    const char * error;
 
-    error = FALSE;
+    error = NULL; /* Initial value */
 
     if (incode < decompP->next_tableSlot) 
         code = incode;
@@ -810,15 +900,15 @@ expandCodeOntoStack(struct decompressor * const decompP,
            represents and push it onto the code stack so the
            leftmost code is on top.  Set decompP->firstcode to the
            first (leftmost) code in that string.
-            */
+        */
 
         unsigned int stringCount;
         stringCount = 0;
 
         while (code > decompP->max_dataVal && !error) {
             if (stringCount > maxnum_lzwCode) {
-                pm_message("Error in GIF image: contains LZW string loop");
-                error = TRUE;
+                asprintfN(&error,
+                          "Error in GIF image: contains LZW string loop");
             } else {
                 ++stringCount;
                 pushStack(&decompP->stack, decompP->table[1][code]);
@@ -847,78 +937,106 @@ expandCodeOntoStack(struct decompressor * const decompP,
         }
     }
 
-    decompP->prevcode = incode;
     *errorP = error;
+
+    decompP->prevcode = incode;
 }
 
 
 
-static int
-lzwReadByte(struct decompressor * const decompP) {
+static void
+lzwReadByteFresh(struct getCodeState * const getCodeStateP,
+                 struct decompressor * const decompP,
+                 bool *                const endOfImageP,
+                 unsigned int *        const dataReadP,
+                 const char **         const errorP) {
+                     
+    /* Read off all initial clear codes, read the first non-clear code,
+       and return it.  There are no strings in the table yet, so the next
+       code must be a direct true data code.
+    */
+    bool eof;
+    do {
+        getCode_get(getCodeStateP, decompP->ifP, decompP->codeSize,
+                    &eof, &decompP->firstcode, errorP);
+        decompP->prevcode = decompP->firstcode;
+    } while (decompP->firstcode == decompP->clear_code && !*errorP && !eof);
+
+    if (!*errorP) {
+        if (eof)
+            *endOfImageP = TRUE;
+        else if (decompP->firstcode == decompP->end_code) {
+            if (!zeroDataBlock)
+                readThroughEod(decompP->ifP);
+            *endOfImageP = TRUE;
+        } else {
+            *endOfImageP = FALSE;
+            *dataReadP = decompP->firstcode;
+        }
+    }
+}
+
+
+
+static void
+lzwReadByte(struct decompressor * const decompP,
+            unsigned int *        const dataReadP,
+            bool *                const endOfImageP,
+            const char **         const errorP) {
 /*----------------------------------------------------------------------------
   Return the next data element of the decompressed image.  In the context
   of a GIF, a data element is the color table index of one pixel.
 
-  We read and return the next byte of the decompressed image, or:
+  We read and return the next byte of the decompressed image.
 
-    Return -1 if we hit EOF prematurely (i.e. before an "end" code.  We
-    forgive the case that the "end" code is followed by EOF instead of
-    an EOD marker (zero length DataBlock)).
+  If we can't, because the stream is too corrupted to make sense out of
+  it or the stream ends, we fail (return text description of why as
+  *errorP).
 
-    Return -2 if there are no more bytes in the image.  In that case,
-    make sure the file is positioned immediately after the image (i.e.
-    after the EOD marker that marks the end of the image or EOF).
+  We forgive the case that the "end" code is the end of the stream --
+  not followed by an EOD marker (zero length DataBlock).
 
-    Return -3 if we encounter errors in the LZW stream.
+  Iff we can't read a byte because we've hit the end of the image,
+  we return *endOfImageP = true.
 -----------------------------------------------------------------------------*/
-    int retval;
-
-    if (!stackIsEmpty(&decompP->stack))
-        retval = popStack(&decompP->stack);
-    else if (decompP->fresh) {
+    if (!stackIsEmpty(&decompP->stack)) {
+        *errorP = NULL;
+        *endOfImageP = FALSE;
+        *dataReadP = popStack(&decompP->stack);
+    } else if (decompP->fresh) {
         decompP->fresh = FALSE;
-        /* Read off all initial clear codes, read the first non-clear code,
-           and return it.  There are no strings in the table yet, so the next
-           code must be a direct true data code.
-        */
-        do {
-            decompP->firstcode =
-                getCode(decompP->ifP, decompP->codeSize, FALSE);
-            decompP->prevcode = decompP->firstcode;
-        } while (decompP->firstcode == decompP->clear_code);
-        if (decompP->firstcode == decompP->end_code) {
-            if (!zeroDataBlock)
-                readThroughEod(decompP->ifP);
-            retval = -2;
-        } else
-            retval = decompP->firstcode;
+
+        lzwReadByteFresh(&getCodeState, decompP, endOfImageP, dataReadP,
+                         errorP);
     } else {
-        int code;
-        code = getCode(decompP->ifP, decompP->codeSize, FALSE);
-        if (code == -1)
-            retval = -1;
-        else {
-            assert(code >= 0);  /* -1 is only possible error return */
-            if (code == decompP->clear_code) {
-                resetDecompressor(decompP);
-                retval = lzwReadByte(decompP);
-            } else {
-                if (code == decompP->end_code) {
-                    if (!zeroDataBlock)
-                        readThroughEod(decompP->ifP);
-                    retval = -2;
+        unsigned int code;
+        bool eof;
+        getCode_get(&getCodeState, decompP->ifP, decompP->codeSize,
+                    &eof, &code, errorP);
+        if (!*errorP) {
+            if (eof)
+                asprintfN(errorP,
+                          "Premature end of file; no proper GIF closing");
+            else {
+                if (code == decompP->clear_code) {
+                    resetDecompressor(decompP);
+                    lzwReadByte(decompP, dataReadP, endOfImageP, errorP);
                 } else {
-                    bool error;
-                    expandCodeOntoStack(decompP, code, &error);
-                    if (error)
-                        retval = -3;
-                    else
-                        retval = popStack(&decompP->stack);
+                    if (code == decompP->end_code) {
+                        if (!zeroDataBlock)
+                            readThroughEod(decompP->ifP);
+                        *endOfImageP = TRUE;
+                        *errorP = NULL;
+                    } else {
+                        *endOfImageP = FALSE;
+                        expandCodeOntoStack(decompP, code, errorP);
+                        if (!*errorP)
+                            *dataReadP = popStack(&decompP->stack);
+                    }
                 }
             }
         }
     }
-    return retval;
 }
 
 
@@ -1027,6 +1145,124 @@ addPixelToRaster(unsigned int       const cmapIndex,
 
 
 static void
+verifyPixelRead(bool          const endOfImage,
+                const char *  const readError,
+                unsigned int  const cols,
+                unsigned int  const rows,
+                unsigned int  const failedRowNum,
+                const char ** const errorP) {
+
+    if (readError)
+        *errorP = strdup(readError);
+    else {
+        if (endOfImage)
+            asprintfN(errorP,
+                      "Error in GIF image: Not enough raster data to fill "
+                      "%u x %u dimensions.  Ran out of raster data in "
+                      "row %u.  The image has proper ending sequence, so "
+                      "this is not just a truncated file.",
+                      cols, rows, failedRowNum);
+        else
+            *errorP = NULL;
+    }
+}
+
+
+
+static void
+readRaster(struct decompressor * const decompP,
+           xel **                const xels, 
+           unsigned int          const cols,
+           unsigned int          const rows,
+           gifColorMap                 cmap, 
+           unsigned int          const cmapSize,
+           bool                  const interlace,
+           int                   const transparentIndex,
+           bit **                const alphabits,
+           bool                  const tolerateBadInput) {
+                   
+    struct pnmBuffer pnmBuffer;
+    enum pass pass;
+    bool fillingMissingPixels;
+
+    pass = MULT8PLUS0;
+    pnmBuffer.xels = xels;
+    pnmBuffer.col  = 0;
+    pnmBuffer.row  = 0;
+    fillingMissingPixels = false;  /* initial value */
+
+    while (pnmBuffer.row < rows) {
+        unsigned int colorIndex;
+
+        if (fillingMissingPixels)
+            colorIndex = 0;
+        else {
+            const char * error;
+
+            const char * readError;
+            unsigned int readColorIndex;
+            bool endOfImage;
+
+            lzwReadByte(decompP, &readColorIndex, &endOfImage, &readError);
+
+            verifyPixelRead(endOfImage, readError, cols, rows, pnmBuffer.row,
+                            &error);
+
+            if (readError)
+                strfree(readError);
+
+            if (error) {
+                if (tolerateBadInput) {
+                    pm_message("WARNING: %s.  "
+                               "Filling bottom %u rows with arbitrary color",
+                               error, rows - pnmBuffer.row);
+                    fillingMissingPixels = true;
+                } else
+                    pm_error("Unable to read input image.  %s.  Use the "
+                             "-repair option to try to salvage some of "
+                             "the image",
+                             error);
+
+                colorIndex = 0;
+            } else
+                colorIndex = readColorIndex;
+        }
+        addPixelToRaster(colorIndex, &pnmBuffer, cols, rows, cmap, cmapSize,
+                         interlace, transparentIndex, alphabits, &pass);
+    }
+}
+
+
+
+static void
+skipExtraneousData(struct decompressor * const decompP) {
+
+    unsigned int byteRead;
+    bool endOfImage;
+    const char * error;
+
+    lzwReadByte(decompP, &byteRead, &endOfImage, &error);
+
+    if (error)
+        strfree(error);
+    else if (!endOfImage) {
+        pm_message("Extraneous data at end of image.  "
+                   "Skipped to end of image");
+
+        while (!endOfImage && !error)
+            lzwReadByte(decompP, &byteRead, &endOfImage, &error);
+
+        if (error) {
+            pm_message("Error encountered skipping to end of image: %s",
+                       error);
+            strfree(error);
+        }
+    }
+}
+
+
+
+static void
 readImageData(FILE *       const ifP, 
               xel **       const xels, 
               unsigned int const cols,
@@ -1035,20 +1271,13 @@ readImageData(FILE *       const ifP,
               unsigned int const cmapSize,
               bool         const interlace,
               int          const transparentIndex,
-              bit **       const alphabits) {
+              bit **       const alphabits,
+              bool         const tolerateBadInput) {
 
     unsigned char lzwMinCodeSize;      
-    enum pass pass;
     struct decompressor decomp;
-    struct pnmBuffer pnmBuffer;
     bool gotMinCodeSize;
 
-    pass = MULT8PLUS0;
-
-    pnmBuffer.xels = xels;
-    pnmBuffer.col  = 0;
-    pnmBuffer.row  = 0;
-
     gotMinCodeSize =  ReadOK(ifP, &lzwMinCodeSize, 1);
     if (!gotMinCodeSize)
         pm_error("GIF stream ends (or read error) "
@@ -1062,29 +1291,10 @@ readImageData(FILE *       const ifP,
 
     lzwInit(&decomp, ifP, lzwMinCodeSize);
 
-    while (pnmBuffer.row < rows) {
-        int const rc = lzwReadByte(&decomp);
+    readRaster(&decomp, xels, cols, rows, cmap, cmapSize, interlace,
+               transparentIndex, alphabits, tolerateBadInput);
 
-        switch (rc) {
-        case -3:
-            pm_error("Error in GIF input stream");
-            break;
-        case -2:
-            pm_error("Error in GIF image: Not enough raster data to fill "
-                     "%u x %u dimensions.  Ran out of raster data in "
-                     "row %u", cols, rows, pnmBuffer.row);
-            break;
-        case -1:
-            pm_error("Premature end of file; no proper GIF closing");
-            break;
-        default:
-            addPixelToRaster(rc, &pnmBuffer, cols, rows, cmap, cmapSize,
-                             interlace, transparentIndex, alphabits, &pass);
-        }
-    }
-    if (lzwReadByte(&decomp) >= 0)
-        pm_message("Extraneous data at end of image.  "
-                   "Skipped to end of image");
+    skipExtraneousData(&decomp);
 
     lzwTerm(&decomp);
 }
@@ -1092,33 +1302,36 @@ readImageData(FILE *       const ifP,
 
 
 static void
-writePnm(FILE *outfile, xel ** const xels, 
-         const int cols, const int rows,
-         const int hasGray, const int hasColor) {
+writePnm(FILE * const outfileP,
+         xel ** const xels, 
+         int    const cols,
+         int    const rows,
+         int    const hasGray,
+         int    const hasColor) {
 /*----------------------------------------------------------------------------
-   Write a PNM image to the current position of file 'outfile' with
+   Write a PNM image to the current position of file *outfileP with
    dimensions 'cols' x 'rows' and raster 'xels'.
    
    Make it PBM, PGM, or PBM according to 'hasGray' and 'hasColor'.
 -----------------------------------------------------------------------------*/
     int format;
-    const char *format_name;
+    const char * formatName;
            
     if (hasColor) {
         format = PPM_FORMAT;
-        format_name = "PPM";
+        formatName = "PPM";
     } else if (hasGray) {
         format = PGM_FORMAT;
-        format_name = "PGM";
+        formatName = "PGM";
     } else {
         format = PBM_FORMAT;
-        format_name = "PBM";
+        formatName = "PBM";
     }
     if (verbose) 
-        pm_message("writing a %s file", format_name);
+        pm_message("writing a %s file", formatName);
     
-    if (outfile) 
-        pnm_writepnm(outfile, xels, cols, rows,
+    if (outfileP) 
+        pnm_writepnm(outfileP, xels, cols, rows,
                      (xelval) GIFMAXVAL, format, FALSE);
 }
 
@@ -1172,7 +1385,7 @@ readGifHeader(FILE * const gifFile, struct gifScreen * const gifScreenP) {
     if (verbose)
         pm_message("GIF format version is '%s'", version);
     
-    if ((!STREQ(version, "87a")) && (!STREQ(version, "89a")))
+    if ((!streq(version, "87a")) && (!streq(version, "89a")))
         pm_error("bad version number, not '87a' or '89a'" );
     
     if (! ReadOK(gifFile,buf,7))
@@ -1223,7 +1436,8 @@ readGifHeader(FILE * const gifFile, struct gifScreen * const gifScreenP) {
 static void
 readExtensions(FILE*          const ifP, 
                struct gif89 * const gif89P,
-               bool *         const eodP) {
+               bool *         const eodP,
+               const char **  const errorP) {
 /*----------------------------------------------------------------------------
    Read extension blocks from the GIF stream to which the file *ifP is
    positioned.  Read up through the image separator that begins the
@@ -1232,33 +1446,50 @@ readExtensions(FILE*          const ifP,
    If we encounter EOD (end of GIF stream) before we find an image 
    separator, we return *eodP == TRUE.  Else *eodP == FALSE.
 
-   If we hit end of file before an EOD marker, we abort the program with
-   an error message.
+   If we hit end of file before an EOD marker, we fail.
 -----------------------------------------------------------------------------*/
     bool imageStart;
     bool eod;
 
+    *errorP = NULL;  /* initial value */
+
     eod = FALSE;
     imageStart = FALSE;
 
     /* Read the image descriptor */
-    while (!imageStart && !eod) {
+    while (!imageStart && !eod && !*errorP) {
         unsigned char c;
+        const char * error;
 
-        if (! ReadOK(ifP,&c,1))
-            pm_error("EOF / read error on image data" );
+        readFile(ifP, &c, 1, &error);
 
-        if (c == ';') {         /* GIF terminator */
-            eod = TRUE;
-        } else if (c == '!') {         /* Extension */
-            if (! ReadOK(ifP,&c,1))
-                pm_error("EOF / "
-                         "read error on extension function code");
-            doExtension(ifP, c, gif89P);
-        } else if (c == ',') 
-            imageStart = TRUE;
-        else 
-            pm_message("bogus character 0x%02x, ignoring", (int) c );
+        if (error) {
+            asprintfN(errorP, "File read error where start of image "
+                      "descriptor or end of GIF expected.  %s",
+                      error);
+            strfree(error);
+        } else {
+            if (c == ';') {         /* GIF terminator */
+                eod = TRUE;
+            } else if (c == '!') {         /* Extension */
+                unsigned char functionCode;
+                const char * error;
+
+                readFile(ifP, &functionCode, 1, &error);
+
+                if (error) {
+                    asprintfN(errorP, "Failed to read function code "
+                              "of GIF extension (immediately after the '!' "
+                              "extension delimiter) from input.  %s", error);
+                    strfree(error);
+                } else {
+                    doExtension(ifP, functionCode, gif89P);
+                }
+            } else if (c == ',') 
+                imageStart = TRUE;
+            else 
+                pm_message("bogus character 0x%02x, ignoring", (int)c);
+        }
     }
     *eodP = eod;
 }
@@ -1272,9 +1503,9 @@ reportImageInfo(unsigned int const cols,
                 unsigned int const localColorMapSize,
                 bool         const interlaced) {
 
-
     pm_message("reading %u by %u%s GIF image",
                cols, rows, interlaced ? " interlaced" : "" );
+
     if (useGlobalColormap)
         pm_message("  Uses global colormap");
     else
@@ -1289,7 +1520,8 @@ convertImage(FILE *           const ifP,
              FILE *           const imageout_file, 
              FILE *           const alphafile, 
              struct gifScreen       gifScreen,
-             struct gif89     const gif89) {
+             struct gif89     const gif89,
+             bool             const tolerateBadInput) {
 /*----------------------------------------------------------------------------
    Read a single GIF image from the current position of file 'ifP'.
 
@@ -1340,7 +1572,8 @@ convertImage(FILE *           const ifP,
                      &hasGray, &hasColor);
         transparencyMessage(gif89.transparent, localColorMap);
         readImageData(ifP, xels, cols, rows, localColorMap, localColorMapSize,
-                      interlaced, gif89.transparent, alphabits);
+                      interlaced, gif89.transparent, alphabits,
+                      tolerateBadInput);
         if (!skipIt) {
             writePnm(imageout_file, xels, cols, rows,
                      hasGray, hasColor);
@@ -1349,7 +1582,8 @@ convertImage(FILE *           const ifP,
         transparencyMessage(gif89.transparent, gifScreen.ColorMap);
         readImageData(ifP, xels, cols, rows, 
                       gifScreen.ColorMap, gifScreen.ColorMapSize,
-                      interlaced, gif89.transparent, alphabits);
+                      interlaced, gif89.transparent, alphabits,
+                      tolerateBadInput);
         if (!skipIt) {
             writePnm(imageout_file, xels, cols, rows,
                      gifScreen.hasGray, gifScreen.hasColor);
@@ -1367,12 +1601,33 @@ convertImage(FILE *           const ifP,
 
 
 static void
+disposeOfReadExtensionsError(const char * const error,
+                             bool         const tolerateBadInput,
+                             unsigned int const imageSeq,
+                             bool *       const eodP) {
+    if (error) {
+        if (tolerateBadInput)
+            pm_message("Error accessing Image %u of stream; no further "
+                       "images can be accessed.  %s",
+                       imageSeq, error);
+        else
+            pm_error("Error accessing Image %u of stream.  %s",
+                     imageSeq, error);
+        strfree(error);
+        *eodP = TRUE;
+    }
+}
+
+
+
+static void
 convertImages(FILE * const ifP, 
               bool   const allImages,
               int    const requestedImageSeq, 
               bool   const drainStream,
               FILE * const imageout_file, 
-              FILE * const alphafile) {
+              FILE * const alphafile,
+              bool   const tolerateBadInput) {
 /*----------------------------------------------------------------------------
    Read a GIF stream from file 'ifP' and write one or more images from
    it as PNM images to file 'imageout_file'.  If the images have transparency
@@ -1407,20 +1662,25 @@ convertImages(FILE * const ifP,
          !eod && (imageSeq <= requestedImageSeq || allImages || drainStream);
          ++imageSeq) {
 
-        readExtensions(ifP, &gif89, &eod);
+        const char * error;
+
+        readExtensions(ifP, &gif89, &eod, &error);
+
+        disposeOfReadExtensionsError(error, tolerateBadInput, imageSeq, &eod);
 
         if (eod) {
             /* GIF stream ends before image with sequence imageSeq */
             if (!allImages && (imageSeq <= requestedImageSeq))
                 pm_error("You requested Image %d, but "
                          "only %d image%s found in GIF stream",
-                         requestedImageSeq+1,
-                         imageSeq, imageSeq>1?"s":"" );
+                         requestedImageSeq + 1,
+                         imageSeq, imageSeq > 1 ? "s" : "");
         } else {
             if (verbose)
                 pm_message("Reading Image Sequence %d", imageSeq);
             convertImage(ifP, !allImages && (imageSeq != requestedImageSeq), 
-                         imageout_file, alphafile, gifScreen, gif89);
+                         imageout_file, alphafile, gifScreen, gif89,
+                         tolerateBadInput);
         }
     }
 }
@@ -1447,19 +1707,20 @@ main(int argc, char **argv) {
     else
         alpha_file = pm_openw(cmdline.alpha_filename);
 
-    if (alpha_file && STREQ(cmdline.alpha_filename, "-"))
+    if (alpha_file && streq(cmdline.alpha_filename, "-"))
         imageout_file = NULL;
     else
         imageout_file = stdout;
 
     convertImages(ifP, cmdline.all_images, cmdline.image_no, 
-                  !cmdline.quitearly, imageout_file, alpha_file);
+                  !cmdline.quitearly, imageout_file, alpha_file,
+                  cmdline.repair);
 
     pm_close(ifP);
     if (imageout_file != NULL) 
-        pm_close( imageout_file );
+        pm_close(imageout_file);
     if (alpha_file != NULL)
-        pm_close( alpha_file );
+        pm_close(alpha_file);
 
     return 0;
 }
diff --git a/converter/other/hdifftopam.c b/converter/other/hdifftopam.c
index 444d6068..7bfeed9b 100644
--- a/converter/other/hdifftopam.c
+++ b/converter/other/hdifftopam.c
@@ -9,6 +9,7 @@
 #include <string.h>
 #include <stdio.h>
 
+#include "pm_c_util.h"
 #include "pam.h"
 #include "shhopt.h"
 #include "nstring.h"
@@ -96,7 +97,7 @@ main(int argc, char *argv[]) {
     tuple * outrow;
     tuple * prevrow;
 
-    pnm_init( &argc, argv );
+    pnm_init(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
@@ -106,7 +107,7 @@ main(int argc, char *argv[]) {
 
     if (diffpam.format != PAM_FORMAT) 
         pm_error("Input must be a PAM file, not PNM");
-    else if (!STREQ(diffpam.tuple_type, "hdiff")) 
+    else if (!streq(diffpam.tuple_type, "hdiff")) 
         pm_error("Input tuple type is '%s'.  Must be 'hdiff'",
                  diffpam.tuple_type);
 
diff --git a/converter/other/infotopam.c b/converter/other/infotopam.c
index 4f29eb07..21fa8ee2 100644
--- a/converter/other/infotopam.c
+++ b/converter/other/infotopam.c
@@ -65,6 +65,7 @@
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  */
 
+#include "pm_c_util.h"
 #include "pam.h"
 #include "shhopt.h"
 #include "mallocvar.h"
diff --git a/converter/other/jbig/Makefile b/converter/other/jbig/Makefile
index 1b9d7535..b5f4e14a 100644
--- a/converter/other/jbig/Makefile
+++ b/converter/other/jbig/Makefile
@@ -5,13 +5,13 @@ endif
 SUBDIR = converter/other/jbig
 VPATH=.:$(SRCDIR)/$(SUBDIR)
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
 LIBJBIG_OBJECTS = jbig.o jbig_tab.o
 
-INCLUDES =
+EXTERN_INCLUDES =
 ifneq ($(JBIGHDR_DIR),NONE)
-  INCLUDES += -I$(JBIGHDR_DIR)
+  EXTERN_INCLUDES += -I$(JBIGHDR_DIR)
 endif
 
 ifneq ($(JBIGHDR_DIR),NONE)
@@ -33,7 +33,7 @@ MERGE_OBJECTS = $(BINARIES:%=%.o2) $(LIBJBIG_OBJECTS)
 
 all: $(BINARIES)
 
-include $(SRCDIR)/Makefile.common
+include $(SRCDIR)/common.mk
 
 $(BINARIES): %: %.o $(JBIGLIB_DEP) $(NETPBMLIB) $(LIBOPT)
 	$(LD) -o $@ $< \
diff --git a/converter/other/jpeg2000/Makefile b/converter/other/jpeg2000/Makefile
index bf3f5e4a..f4fee87f 100644
--- a/converter/other/jpeg2000/Makefile
+++ b/converter/other/jpeg2000/Makefile
@@ -7,11 +7,11 @@ VPATH=.:$(SRCDIR)/$(SUBDIR)
 
 SUBDIRS = libjasper
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
-INCLUDES =
+EXTERN_INCLUDES =
 ifneq ($(JASPERHDR_DIR),NONE)
-  INCLUDES += -I$(JASPERHDR_DIR)
+  EXTERN_INCLUDES += -I$(JASPERHDR_DIR)
 endif
 
 
@@ -55,7 +55,7 @@ MERGEBINARIES = $(BINARIES)
 .PHONY: all
 all: $(BINARIES)
 
-include $(SRCDIR)/Makefile.common
+include $(SRCDIR)/common.mk
 
 LIBOPTS = $(shell $(LIBOPT) $(NETPBMLIB) $(JASPERLIB_USE))
 
diff --git a/converter/other/jpeg2000/jpeg2ktopam.c b/converter/other/jpeg2000/jpeg2ktopam.c
index e11e9fb4..e6db7658 100644
--- a/converter/other/jpeg2000/jpeg2ktopam.c
+++ b/converter/other/jpeg2000/jpeg2ktopam.c
@@ -13,6 +13,7 @@
 #define _XOPEN_SOURCE 600
 #include <string.h>
 
+#include "pm_c_util.h"
 #include "pam.h"
 #include "shhopt.h"
 #include "nstring.h"
diff --git a/converter/other/jpeg2000/libjasper/Makefile b/converter/other/jpeg2000/libjasper/Makefile
index 965f15ad..ddbd148a 100644
--- a/converter/other/jpeg2000/libjasper/Makefile
+++ b/converter/other/jpeg2000/libjasper/Makefile
@@ -5,7 +5,7 @@ endif
 SUBDIR = converter/other/jpeg2000/libjasper
 VPATH=.:$(SRCDIR)/$(SUBDIR)
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
 SUBDIRS = base jp2 jpc
 LIB_OBJECTS =
@@ -17,7 +17,7 @@ JASPERSRCDIR = $(SRCDIR)/$(SUBDIR)
 
 all: libjasper.a
 
-include $(SRCDIR)/$(SUBDIR)/Makefile.common
+include $(SRCDIR)/$(SUBDIR)/common.mk
 
 # We cheat a bit here -- the real dependencies are all the .o files listed
 # in the part list, but since we don't know what those are, we just do a
diff --git a/converter/other/jpeg2000/libjasper/base/Makefile b/converter/other/jpeg2000/libjasper/base/Makefile
index 0ee65b5e..ad84f043 100644
--- a/converter/other/jpeg2000/libjasper/base/Makefile
+++ b/converter/other/jpeg2000/libjasper/base/Makefile
@@ -7,7 +7,7 @@ VPATH=.:$(SRCDIR)/$(SUBDIR)
 
 JASPERSRCDIR=$(SRCDIR)/$(SUBDIR)/..
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
 LIB_OBJECTS = jas_debug.o jas_getopt.o jas_image.o jas_init.o \
 	      jas_malloc.o jas_seq.o jas_stream.o jas_string.o \
@@ -17,5 +17,5 @@ MERGE_OBJECTS =
 
 all: partlist $(LIB_OBJECTS)
 
-include $(JASPERSRCDIR)/Makefile.common
+include $(JASPERSRCDIR)/common.mk
 
diff --git a/converter/other/jpeg2000/libjasper/base/jas_stream.c b/converter/other/jpeg2000/libjasper/base/jas_stream.c
index f450aabf..4c84e6c2 100644
--- a/converter/other/jpeg2000/libjasper/base/jas_stream.c
+++ b/converter/other/jpeg2000/libjasper/base/jas_stream.c
@@ -130,6 +130,8 @@
 #include <io.h>
 #endif
 
+#include "pm.h"
+
 #include "jasper/jas_types.h"
 #include "jasper/jas_stream.h"
 #include "jasper/jas_malloc.h"
@@ -380,51 +382,6 @@ jas_stream_t *jas_stream_freopen(const char *path, const char *mode, FILE *fp)
 }
 
 
-static int
-tmpfilex(void) {
-/*----------------------------------------------------------------------------
-   This is a copy of pm_tmpfile() without the libnetpbm dependencies
-   and returning a file descriptor instead of stream.
------------------------------------------------------------------------------*/
-    const char libname[] = "jasper";
-
-    int fd;
-    char buf[FILENAME_MAX];
-    const char * tbuf;
-    unsigned int fnamelen;
-
-    fnamelen = strlen(libname) + 10; /* "/" + "_XXXXXX\0" */
-
-    tbuf = getenv("TMPDIR");
-
-    if ((tbuf != NULL) && (strlen(tbuf) > FILENAME_MAX - fnamelen))
-        /* length of TMPDIR value too big for buf */
-        tbuf = NULL;
-
-    buf[FILENAME_MAX - fnamelen -1] = 0;
-
-    if ((tbuf == NULL) || (strlen(tbuf) == 0))
-        /* environment variable not suitable to construct file name.
-           Use default.
-        */
-        strncpy(buf, TMPDIR, sizeof(buf) - fnamelen);
-    else
-        strncpy(buf, tbuf, sizeof(buf) - fnamelen);
-
-    if (buf[strlen(buf) - 1] != '/')
-        strcat (buf, "/");
-    
-    strcat(buf, libname);
-    strcat(buf, "_XXXXXX");
-
-    fd = mkstemp(buf);
-
-    if (fd >= 0)
-        unlink(buf);
-
-    return fd;
-}
-
 jas_stream_t *jas_stream_tmpfile()
 {
 	jas_stream_t *stream;
@@ -444,13 +401,23 @@ jas_stream_t *jas_stream_tmpfile()
 		return 0;
 	}
 	stream->obj_ = obj;
-    
-    /* This is a Netpbm enhancement.  Original Jasper library uses
-       tmpnam(), which is unsafe.
-    */
-    if ((*obj = tmpfilex()) < 0) {
-		jas_stream_destroy(stream);
-		return 0;
+
+    {
+        /* This is a Netpbm enhancement.  Original Jasper library uses
+           tmpnam(), which is unsafe.
+        */
+        jmp_buf jmpbuf;
+        int rc;
+        
+        rc = setjmp(jmpbuf);
+        if (rc == 0) {
+            pm_setjmpbuf(&jmpbuf);
+            *obj = pm_tmpfile_fd();
+        } else {
+            /* pm_tmpfile_fd() threw an error */
+            jas_stream_destroy(stream);
+            return 0;
+        }
     }
 	/* Use full buffering. */
 	jas_stream_initbuf(stream, JAS_STREAM_FULLBUF, 0, 0);
diff --git a/converter/other/jpeg2000/libjasper/Makefile.common b/converter/other/jpeg2000/libjasper/common.mk
index 525e9c2d..687a9f3f 100644
--- a/converter/other/jpeg2000/libjasper/Makefile.common
+++ b/converter/other/jpeg2000/libjasper/common.mk
@@ -1,4 +1,3 @@
-# -*-makefile-*-    <-- an Emacs control
 # This is common rules for the libjasper subdirectories.
 #
 # Set the following variables before including this:
@@ -19,14 +18,14 @@ $(SUBDIRS:%=%/partlist): %/partlist: $(CURDIR)/%
 	$(MAKE) -C $(dir $@) -f $(SRCDIR)/$(SUBDIR)/$(dir $@)Makefile \
 	    SRCDIR=$(SRCDIR) BUILDDIR=$(BUILDDIR) $(notdir $@) 
 
-INCLUDES = -I$(JASPERSRCDIR)/include -Iimportinc
+include $(SRCDIR)/common.mk
 
-include $(SRCDIR)/Makefile.common
+INCLUDES = -I$(JASPERSRCDIR)/include -Iimportinc
 
 DEFS = -DHAVE_LIBM=1 -DSTDC_HEADERS=1 -DHAVE_FCNTL_H=1 -DHAVE_LIMITS_H=1 -DHAVE_UNISTD_H=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STDDEF_H=1 -DEXCLUDE_BMP_SUPPORT -DEXCLUDE_RAS_SUPPORT -DEXCLUDE_MIF_SUPPORT -DEXCLUDE_JPG_SUPPORT -DEXCLUDE_PGX_SUPPORT -DEXCLUDE_PNM_SUPPORT
 
 $(LIB_OBJECTS):%.o:%.c
-	$(CC) -c $(INCLUDES) $(DEFS) $(CFLAGS) $(CADD) $<
+	$(CC) -c $(INCLUDES) $(DEFS) $(CPPFLAGS) $(CFLAGS) $(CADD) $<
 
 $(LIB_OBJECTS): importinc
 
diff --git a/converter/other/jpeg2000/libjasper/include/jasper/jas_types.h.orig b/converter/other/jpeg2000/libjasper/include/jasper/jas_types.h.orig
new file mode 100644
index 00000000..10c1152d
--- /dev/null
+++ b/converter/other/jpeg2000/libjasper/include/jasper/jas_types.h.orig
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 1999-2000 Image Power, Inc. and the University of
+ *   British Columbia.
+ * Copyright (c) 2001-2002 Michael David Adams.
+ * All rights reserved.
+ */
+
+/* __START_OF_JASPER_LICENSE__
+ * 
+ * JasPer Software License
+ * 
+ * IMAGE POWER JPEG-2000 PUBLIC LICENSE
+ * ************************************
+ * 
+ * GRANT:
+ * 
+ * Permission is hereby granted, free of charge, to any person (the "User")
+ * obtaining a copy of this software and associated documentation, to deal
+ * in the JasPer Software without restriction, including without limitation
+ * the right to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the JasPer Software (in source and binary forms),
+ * and to permit persons to whom the JasPer Software is furnished to do so,
+ * provided further that the License Conditions below are met.
+ * 
+ * License Conditions
+ * ******************
+ * 
+ * A.  Redistributions of source code must retain the above copyright notice,
+ * and this list of conditions, and the following disclaimer.
+ * 
+ * B.  Redistributions in binary form must reproduce the above copyright
+ * notice, and this list of conditions, and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * 
+ * C.  Neither the name of Image Power, Inc. nor any other contributor
+ * (including, but not limited to, the University of British Columbia and
+ * Michael David Adams) may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * 
+ * D.  User agrees that it shall not commence any action against Image Power,
+ * Inc., the University of British Columbia, Michael David Adams, or any
+ * other contributors (collectively "Licensors") for infringement of any
+ * intellectual property rights ("IPR") held by the User in respect of any
+ * technology that User owns or has a right to license or sublicense and
+ * which is an element required in order to claim compliance with ISO/IEC
+ * 15444-1 (i.e., JPEG-2000 Part 1).  "IPR" means all intellectual property
+ * rights worldwide arising under statutory or common law, and whether
+ * or not perfected, including, without limitation, all (i) patents and
+ * patent applications owned or licensable by User; (ii) rights associated
+ * with works of authorship including copyrights, copyright applications,
+ * copyright registrations, mask work rights, mask work applications,
+ * mask work registrations; (iii) rights relating to the protection of
+ * trade secrets and confidential information; (iv) any right analogous
+ * to those set forth in subsections (i), (ii), or (iii) and any other
+ * proprietary rights relating to intangible property (other than trademark,
+ * trade dress, or service mark rights); and (v) divisions, continuations,
+ * renewals, reissues and extensions of the foregoing (as and to the extent
+ * applicable) now existing, hereafter filed, issued or acquired.
+ * 
+ * E.  If User commences an infringement action against any Licensor(s) then
+ * such Licensor(s) shall have the right to terminate User's license and
+ * all sublicenses that have been granted hereunder by User to other parties.
+ * 
+ * F.  This software is for use only in hardware or software products that
+ * are compliant with ISO/IEC 15444-1 (i.e., JPEG-2000 Part 1).  No license
+ * or right to this Software is granted for products that do not comply
+ * with ISO/IEC 15444-1.  The JPEG-2000 Part 1 standard can be purchased
+ * from the ISO.
+ * 
+ * THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
+ * NO USE OF THE JASPER SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER
+ * THIS DISCLAIMER.  THE JASPER SOFTWARE IS PROVIDED BY THE LICENSORS AND
+ * CONTRIBUTORS UNDER THIS LICENSE ON AN ``AS-IS'' BASIS, WITHOUT WARRANTY
+ * OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION,
+ * WARRANTIES THAT THE JASPER SOFTWARE IS FREE OF DEFECTS, IS MERCHANTABLE,
+ * IS FIT FOR A PARTICULAR PURPOSE OR IS NON-INFRINGING.  THOSE INTENDING
+ * TO USE THE JASPER SOFTWARE OR MODIFICATIONS THEREOF FOR USE IN HARDWARE
+ * OR SOFTWARE PRODUCTS ARE ADVISED THAT THEIR USE MAY INFRINGE EXISTING
+ * PATENTS, COPYRIGHTS, TRADEMARKS, OR OTHER INTELLECTUAL PROPERTY RIGHTS.
+ * THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE JASPER SOFTWARE
+ * IS WITH THE USER.  SHOULD ANY PART OF THE JASPER SOFTWARE PROVE DEFECTIVE
+ * IN ANY RESPECT, THE USER (AND NOT THE INITIAL DEVELOPERS, THE UNIVERSITY
+ * OF BRITISH COLUMBIA, IMAGE POWER, INC., MICHAEL DAVID ADAMS, OR ANY
+ * OTHER CONTRIBUTOR) SHALL ASSUME THE COST OF ANY NECESSARY SERVICING,
+ * REPAIR OR CORRECTION.  UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY,
+ * WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL THE
+ * INITIAL DEVELOPER, THE UNIVERSITY OF BRITISH COLUMBIA, IMAGE POWER, INC.,
+ * MICHAEL DAVID ADAMS, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF THE
+ * JASPER SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO
+ * THE USER OR ANY OTHER PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION,
+ * DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR
+ * MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF
+ * SUCH PARTY HAD BEEN INFORMED, OR OUGHT TO HAVE KNOWN, OF THE POSSIBILITY
+ * OF SUCH DAMAGES.  THE JASPER SOFTWARE AND UNDERLYING TECHNOLOGY ARE NOT
+ * FAULT-TOLERANT AND ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE OR
+ * RESALE AS ON-LINE CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING
+ * FAIL-SAFE PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES,
+ * AIRCRAFT NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT
+ * LIFE SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
+ * JASPER SOFTWARE OR UNDERLYING TECHNOLOGY OR PRODUCT COULD LEAD DIRECTLY
+ * TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL OR ENVIRONMENTAL DAMAGE
+ * ("HIGH RISK ACTIVITIES").  LICENSOR SPECIFICALLY DISCLAIMS ANY EXPRESS
+ * OR IMPLIED WARRANTY OF FITNESS FOR HIGH RISK ACTIVITIES.  USER WILL NOT
+ * KNOWINGLY USE, DISTRIBUTE OR RESELL THE JASPER SOFTWARE OR UNDERLYING
+ * TECHNOLOGY OR PRODUCTS FOR HIGH RISK ACTIVITIES AND WILL ENSURE THAT ITS
+ * CUSTOMERS AND END-USERS OF ITS PRODUCTS ARE PROVIDED WITH A COPY OF THE
+ * NOTICE SPECIFIED IN THIS SECTION.
+ * 
+ * __END_OF_JASPER_LICENSE__
+ */
+
+/*
+ * Primitive Types
+ *
+ * $Id$
+ */
+
+#ifndef JAS_TYPES_H
+#define JAS_TYPES_H
+
+#if defined(HAVE_STDLIB_H)
+#include <stdlib.h>
+#endif
+#if defined(HAVE_STDDEF_H)
+#include <stddef.h>
+#endif
+#if defined(HAVE_SYS_TYPES_H)
+#include <sys/types.h>
+#endif
+
+#if defined(HAVE_STDBOOL_H)
+/*
+ * The C language implementation does correctly provide the standard header
+ * file "stdbool.h".
+ */
+#include <stdbool.h>
+#else
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * The C language implementation does not provide the standard header file
+ * "stdbool.h" as required by ISO/IEC 9899:1999.  Try to compensate for this
+ * braindamage below.
+ */
+#if !defined(bool)
+#define	bool	int
+#endif
+#if !defined(true)
+#define true	1
+#endif
+#if !defined(false)
+#define	false	0
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+/* pm_config.h defines the FAST integer types if possible (typically by
+   including <inttypes.h>.  If not, the following try to take care
+   of it.
+*/
+/**********/
+#if !defined(INT_FAST8_MAX)
+typedef signed char int_fast8_t;
+#define INT_FAST8_MIN	(-127)
+#define INT_FAST8_MAX	128
+#endif
+/**********/
+#if !defined(UINT_FAST8_MAX)
+typedef unsigned char uint_fast8_t;
+#define UINT_FAST8_MAX	255
+#endif
+/**********/
+#if !defined(INT_FAST16_MAX)
+typedef short int_fast16_t;
+#define INT_FAST16_MIN	SHRT_MIN
+#define INT_FAST16_MAX	SHRT_MAX
+#endif
+/**********/
+#if !defined(UINT_FAST16_MAX)
+typedef unsigned short uint_fast16_t;
+#define UINT_FAST16_MAX	USHRT_MAX
+#endif
+/**********/
+#if !defined(INT_FAST32_MAX)
+typedef int int_fast32_t;
+#define INT_FAST32_MIN	INT_MIN
+#define INT_FAST32_MAX	INT_MAX
+#endif
+/**********/
+#if !defined(UINT_FAST32_MAX)
+typedef unsigned int uint_fast32_t;
+#define UINT_FAST32_MAX	UINT_MAX
+#endif
+/**********/
+#if !defined(INT_FAST64_MAX)
+typedef int int_fast64_t;
+#define INT_FAST64_MIN	LLONG_MIN
+#define INT_FAST64_MAX	LLONG_MAX
+#endif
+/**********/
+#if !defined(UINT_FAST64_MAX)
+typedef unsigned int uint_fast64_t;
+#define UINT_FAST64_MAX	ULLONG_MAX
+#endif
+/**********/
+#endif
+
+/* The below macro is intended to be used for type casts.  By using this
+  macro, type casts can be easily located in the source code with
+  tools like "grep". */
+#define	JAS_CAST(t, e) \
+	((t) (e))
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/converter/other/jpeg2000/libjasper/jp2/Makefile b/converter/other/jpeg2000/libjasper/jp2/Makefile
index 254b7f56..65838cc2 100644
--- a/converter/other/jpeg2000/libjasper/jp2/Makefile
+++ b/converter/other/jpeg2000/libjasper/jp2/Makefile
@@ -7,7 +7,7 @@ VPATH=.:$(SRCDIR)/$(SUBDIR)
 
 JASPERSRCDIR=$(SRCDIR)/$(SUBDIR)/..
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
 LIB_OBJECTS = jp2_cod.o jp2_dec.o jp2_enc.o
 
@@ -15,5 +15,5 @@ MERGE_OBJECTS =
 
 all: partlist $(LIB_OBJECTS)
 
-include $(JASPERSRCDIR)/Makefile.common
+include $(JASPERSRCDIR)/common.mk
 
diff --git a/converter/other/jpeg2000/libjasper/jpc/Makefile b/converter/other/jpeg2000/libjasper/jpc/Makefile
index e176bd48..ffc4c64f 100644
--- a/converter/other/jpeg2000/libjasper/jpc/Makefile
+++ b/converter/other/jpeg2000/libjasper/jpc/Makefile
@@ -7,7 +7,7 @@ VPATH=.:$(SRCDIR)/$(SUBDIR)
 
 JASPERSRCDIR=$(SRCDIR)/$(SUBDIR)/..
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
 LIB_OBJECTS = jpc_bs.o jpc_cs.o jpc_dec.o jpc_enc.o \
 	jpc_math.o jpc_mct.o jpc_mqcod.o jpc_mqdec.o jpc_mqenc.o \
@@ -18,5 +18,5 @@ MERGE_OBJECTS =
 
 all: partlist $(LIB_OBJECTS)
 
-include $(JASPERSRCDIR)/Makefile.common
+include $(JASPERSRCDIR)/common.mk
 
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_math.c b/converter/other/jpeg2000/libjasper/jpc/jpc_math.c
index d860847d..72e3ac37 100644
--- a/converter/other/jpeg2000/libjasper/jpc/jpc_math.c
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_math.c
@@ -1,3 +1,72 @@
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include "jpc_math.h"
+
+
+
+/* Calculate the integer quantity floor(log2(x)), where x is a positive
+  integer. */
+int
+jpc_floorlog2(int const arg) {
+
+	int y;
+    int x;
+
+	assert(arg > 0);
+
+	y = 0;
+    x = arg;
+	while (x > 1) {
+		x >>= 1;
+		++y;
+	}
+	return y;
+}
+
+
+
+/*
+  jpc_floorlog2() and jpc_firstone() do the same thing.
+  The only difference is how input 0 is handled.
+
+n                  : 0 1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 
+ceil(log2(n))      : x 0  1  2  2  3  3  3  3  4  4  4  4  4  4  4  4  5  5  5 
+floor(log2(n))     : x 0  1  1  2  2  2  2  3  3  3  3  3  3  3  3  4  4  4  4 
+31-__builtin_clz(n): x 0  1  1  2  2  2  2  3  3  3  3  3  3  3  3  4  4  4  4 
+jpc_floorlog2(n)   : x 0  1  1  2  2  2  2  3  3  3  3  3  3  3  3  4  4  4  4 
+jpc_firstone(n)    :-1 0  1  1  2  2  2  2  3  3  3  3  3  3  3  3  4  4  4  4 
+
+*/
+
+
+
+int
+jpc_firstone(int const arg) {
+/*---------------------------------------------------------------------------- 
+  Calculate the bit position of the first leading one in a nonnegative
+  integer.
+-----------------------------------------------------------------------------*/
+	int n;
+    int x;
+
+	assert(arg >= 0);
+
+	n = -1;
+    x = arg;
+	while (x > 0) {
+		x >>= 1;
+		++n;
+	}
+	return n;
+}
+
+
+
 /*
  * Copyright (c) 1999-2000 Image Power, Inc. and the University of
  *   British Columbia.
@@ -109,62 +178,3 @@
  * 
  * __END_OF_JASPER_LICENSE__
  */
-
-/*
- * Math Library
- *
- * $Id$
- */
-
-/******************************************************************************\
-* Includes
-\******************************************************************************/
-
-#include <assert.h>
-#include <stdio.h>
-#include <string.h>
-#include <math.h>
-#include <stdlib.h>
-#include <stdarg.h>
-
-#include "jpc_math.h"
-
-/******************************************************************************\
-* Miscellaneous Functions
-\******************************************************************************/
-
-/* Calculate the integer quantity floor(log2(x)), where x is a positive
-  integer. */
-int jpc_floorlog2(int x)
-{
-	int y;
-
-	/* The argument must be positive. */
-	assert(x > 0);
-
-	y = 0;
-	while (x > 1) {
-		x >>= 1;
-		++y;
-	}
-	return y;
-}
-
-/* Calculate the bit position of the first leading one in a nonnegative
-  integer. */
-/* This function is the basically the same as ceillog2(x), except that the
-  allowable range for x is slightly different. */
-int jpc_firstone(int x)
-{
-	int n;
-
-	/* The argument must be nonnegative. */
-	assert(x >= 0);
-
-	n = -1;
-	while (x > 0) {
-		x >>= 1;
-		++n;
-	}
-	return n;
-}
diff --git a/converter/other/jpeg2000/libjasper/jpc/jpc_qmfb.c b/converter/other/jpeg2000/libjasper/jpc/jpc_qmfb.c
index 1ed0dd90..1d41d5c5 100644
--- a/converter/other/jpeg2000/libjasper/jpc/jpc_qmfb.c
+++ b/converter/other/jpeg2000/libjasper/jpc/jpc_qmfb.c
@@ -205,16 +205,6 @@ static void jpc_qmfb1d_split(jpc_fix_t *startptr, int startind, int endind,
 	llen = lendind - lstartind;
 	hlen = hendind - hstartind;
 
-#if defined(WIN32)
-	/* Get a buffer. */
-	if (bufsize > QMFB_SPLITBUFSIZE) {
-		if (!(buf = jas_malloc(bufsize * sizeof(jpc_fix_t)))) {
-			/* We have no choice but to commit suicide in this case. */
-			abort();
-		}
-	}
-#endif
-
 	if (hstartind < lstartind) {
 		/* The first sample in the input signal is to appear
 		  in the highpass subband signal. */
@@ -294,13 +284,6 @@ static void jpc_qmfb1d_split(jpc_fix_t *startptr, int startind, int endind,
 			hptr -= step;
 		}
 	}
-
-#if defined(WIN32)
-	/* If the split buffer was allocated on the heap, free this memory. */
-	if (buf != splitbuf) {
-		jas_free(buf);
-	}
-#endif
 }
 
 static void jpc_qmfb1d_join(jpc_fix_t *startptr, int startind, int endind,
@@ -320,16 +303,6 @@ static void jpc_qmfb1d_join(jpc_fix_t *startptr, int startind, int endind,
 	register int n;
 	int state;
 
-#if defined(WIN32)
-	/* Allocate memory for the join buffer from the heap. */
-	if (bufsize > QMFB_JOINBUFSIZE) {
-		if (!(buf = jas_malloc(bufsize * sizeof(jpc_fix_t)))) {
-			/* We have no choice but to commit suicide. */
-			abort();
-		}
-	}
-#endif
-
 	twostep = step << 1;
 	llen = lendind - lstartind;
 	hlen = hendind - hstartind;
@@ -414,13 +387,6 @@ static void jpc_qmfb1d_join(jpc_fix_t *startptr, int startind, int endind,
 			state ^= 1;
 		}
 	}
-
-#if defined(WIN32)
-	/* If the join buffer was allocated on the heap, free this memory. */
-	if (buf != joinbuf) {
-		jas_free(buf);
-	}
-#endif
 }
 
 /******************************************************************************\
diff --git a/converter/other/jpeg2000/pamtojpeg2k.c b/converter/other/jpeg2000/pamtojpeg2k.c
index 851d2bf9..70774725 100644
--- a/converter/other/jpeg2000/pamtojpeg2k.c
+++ b/converter/other/jpeg2000/pamtojpeg2k.c
@@ -13,6 +13,7 @@
 #define _XOPEN_SOURCE 600
 #include <string.h>
 
+#include "pm_c_util.h"
 #include "pam.h"
 #include "shhopt.h"
 #include "nstring.h"
diff --git a/converter/other/jpegdatasource.c b/converter/other/jpegdatasource.c
index 5c1070e4..1f53c2a4 100644
--- a/converter/other/jpegdatasource.c
+++ b/converter/other/jpegdatasource.c
@@ -45,6 +45,11 @@ struct sourceManager {
     */
     struct jpeg_source_mgr jpegSourceMgr;
     FILE * ifP;
+    bool prematureEof;
+        /* We have been asked for data and were unable to comply because
+           the file had no more to give (so we supplied EOI markers
+           instead).
+        */
     JOCTET * currentBuffer;
     JOCTET * nextBuffer;
     unsigned int bytesInNextBuffer;
@@ -66,6 +71,10 @@ dsInitSource(j_decompress_ptr const cinfoP) {
 
 
 
+static const JOCTET jfifEoiMarker[] = {0xff, JPEG_EOI};
+    /* An EOI (end of image) marker */
+
+
 static boolean
 dsFillInputBuffer(j_decompress_ptr const cinfoP) {
 /*----------------------------------------------------------------------------
@@ -74,9 +83,19 @@ dsFillInputBuffer(j_decompress_ptr const cinfoP) {
 -----------------------------------------------------------------------------*/
     struct sourceManager * const srcP = (struct sourceManager *) cinfoP->src;
 
-    if (srcP->bytesInNextBuffer == 0) 
-        pm_error("End-of-file encountered in the middle of JPEG image.");
-    else {
+    if (srcP->bytesInNextBuffer == 0) {
+        /* The decompressor expects more bytes, but there aren't any, so
+           the file is corrupted -- probably truncated.  We want the
+           decompressor to decompress whatever it's read so far, so we
+           synthesize an EOI marker here, but we also set error state
+           in the source manager.  The decompressor will recognize the
+           truncation and pad out the image with gray.
+        */
+        srcP->prematureEof = TRUE;
+        
+        srcP->jpegSourceMgr.next_input_byte = jfifEoiMarker;
+        srcP->jpegSourceMgr.bytes_in_buffer = sizeof(jfifEoiMarker);
+    } else {
         /* Rotate the buffers */
         srcP->jpegSourceMgr.next_input_byte = srcP->nextBuffer;
         srcP->jpegSourceMgr.bytes_in_buffer = srcP->bytesInNextBuffer;
@@ -87,7 +106,7 @@ dsFillInputBuffer(j_decompress_ptr const cinfoP) {
             srcP->currentBuffer = tmp;
         }
 
-        /* Fill the new 'next' buffer */
+        /* Fill the new "next" buffer */
         srcP->bytesInNextBuffer = 
             fread(srcP->nextBuffer, 1, BUFFER_SIZE, srcP->ifP);
     }
@@ -139,6 +158,14 @@ dsDataLeft(struct sourceManager * const srcP) {
 
 
 
+bool
+dsPrematureEof(struct sourceManager * const srcP) {
+
+    return srcP->prematureEof;
+}
+
+
+
 struct sourceManager * 
 dsCreateSource(const char * const fileName) {
 
@@ -156,6 +183,7 @@ dsCreateSource(const char * const fileName) {
     srcP->jpegSourceMgr.resync_to_restart = jpeg_resync_to_restart;
     srcP->jpegSourceMgr.term_source = dsTermSource;
     
+    srcP->prematureEof = FALSE;
     srcP->currentBuffer = srcP->buffer1;
     srcP->nextBuffer = srcP->buffer2;
     srcP->jpegSourceMgr.bytes_in_buffer = 
diff --git a/converter/other/jpegdatasource.h b/converter/other/jpegdatasource.h
index 07f17389..58648fe4 100644
--- a/converter/other/jpegdatasource.h
+++ b/converter/other/jpegdatasource.h
@@ -12,6 +12,9 @@ dsDestroySource(struct sourceManager * const srcP);
 bool
 dsDataLeft(struct sourceManager * const srcP);
 
+bool
+dsPrematureEof(struct sourceManager * const srcP);
+
 struct jpeg_source_mgr *
 dsJpegSourceMgr(struct sourceManager * const srcP);
 
diff --git a/converter/other/jpegtopnm.c b/converter/other/jpegtopnm.c
index 60ae7e42..07a7dfb0 100644
--- a/converter/other/jpegtopnm.c
+++ b/converter/other/jpegtopnm.c
@@ -61,6 +61,8 @@
    itself, but doesn't.
 */
 #include <jpeglib.h>
+
+#include "pm_c_util.h"
 #include "pnm.h"
 #include "shhopt.h"
 #include "mallocvar.h"
@@ -109,6 +111,7 @@ struct cmdlineInfo {
     unsigned int comments;
     unsigned int dumpexif;
     unsigned int multiple;
+    unsigned int repair;
 };
 
 
@@ -116,9 +119,9 @@ static bool displayComments;
     /* User wants comments from the JPEG to be displayed */
 
 static void 
-interpret_maxmemory (bool         const maxmemorySpec,
-                     const char * const maxmemory, 
-                     long int *   const max_memory_to_use_p) { 
+interpret_maxmemory(bool         const maxmemorySpec,
+                    const char * const maxmemory, 
+                    long int *   const max_memory_to_use_p) { 
 /*----------------------------------------------------------------------------
    Interpret the "maxmemory" command line option.
 -----------------------------------------------------------------------------*/
@@ -157,8 +160,9 @@ interpret_adobe(const int adobe, const int notadobe,
 
 
 static void
-parse_command_line(const int argc, char ** argv,
-                   struct cmdlineInfo *cmdlineP) {
+parseCommandLine(int                  const argc,
+                 char **              const argv,
+                 struct cmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that many of the strings that this function returns in the
    *cmdlineP structure are actually in the supplied argv array.  And
@@ -173,10 +177,10 @@ parse_command_line(const int argc, char ** argv,
          */
     optStruct3 opt;
 
-    int i;  /* local loop variable */
+    unsigned int i;  /* local loop variable */
 
-    char *maxmemory;
-    char *dctval;
+    const char * maxmemory;
+    const char * dctval;
     unsigned int adobe, notadobe;
 
     unsigned int tracelevelSpec, exifSpec, dctvalSpec, maxmemorySpec;
@@ -206,6 +210,7 @@ parse_command_line(const int argc, char ** argv,
             &exifSpec, 0);
     OPTENT3(0, "dumpexif",    OPT_FLAG,   NULL, &cmdlineP->dumpexif,      0);
     OPTENT3(0, "multiple",    OPT_FLAG,   NULL, &cmdlineP->multiple,      0);
+    OPTENT3(0, "repair",      OPT_FLAG,   NULL, &cmdlineP->repair,        0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
@@ -213,7 +218,8 @@ parse_command_line(const int argc, char ** argv,
 
     /* Make private copy of arguments for optParseOptions to corrupt */
     argc_parse = argc;
-    for (i=0; i < argc; i++) argv_parse[i] = argv[i];
+    for (i=0; i < argc; ++i)
+        argv_parse[i] = argv[i];
 
     optParseOptions3( &argc_parse, argv_parse, opt, sizeof(opt), 0);
         /* Uses and sets argc_parse, argv_parse, 
@@ -236,11 +242,11 @@ parse_command_line(const int argc, char ** argv,
     if (!dctvalSpec)
         cmdlineP->dct_method = JDCT_DEFAULT;
     else {
-        if (STREQ(dctval, "int"))
+        if (streq(dctval, "int"))
             cmdlineP->dct_method = JDCT_ISLOW;
-        else if (STREQ(dctval, "fast"))
+        else if (streq(dctval, "fast"))
             cmdlineP->dct_method = JDCT_IFAST;
-        else if (STREQ(dctval, "float"))
+        else if (streq(dctval, "float"))
             cmdlineP->dct_method = JDCT_FLOAT;
         else pm_error("Invalid value for the --dct option: '%s'.", dctval);
     }
@@ -458,32 +464,34 @@ read_rgb(JSAMPLE *ptr, const enum colorspace color_space,
    copy_pixel_row().  But it would be impractical to allocate and free
    the storage with every call to copy_pixel_row().
 */
-static xel *pnmbuffer;      /* Output buffer.  Input to pnm_writepnmrow() */
+static xel * pnmbuffer;      /* Output buffer.  Input to pnm_writepnmrow() */
 
 static void
-copy_pixel_row(const JSAMPROW jpegbuffer, const int width, 
-               const unsigned int samples_per_pixel, 
-               const enum colorspace color_space,
-               const unsigned int maxval,
-               FILE * const output_file, const int output_type) {
-  JSAMPLE *ptr;
-  unsigned int output_cursor;     /* Cursor into output buffer 'pnmbuffer' */
-
-  ptr = jpegbuffer;  /* Start at beginning of input row */
-
-  for (output_cursor = 0; output_cursor < width; output_cursor++) {
-      xel current_pixel;
-      if (samples_per_pixel >= 3) {
-          const rgb_type * const rgb_p = read_rgb(ptr, color_space, maxval);
-          PPM_ASSIGN(current_pixel, rgb_p->r, rgb_p->g, rgb_p->b);
-      } else {
-          PNM_ASSIGN1(current_pixel, GETJSAMPLE(*ptr));
-      }
-      ptr += samples_per_pixel;  /* move to next pixel of input */
-      pnmbuffer[output_cursor] = current_pixel;
-  }
-  pnm_writepnmrow(output_file, pnmbuffer, width,
-                  maxval, output_type, FALSE);
+copyPixelRow(JSAMPROW        const jpegbuffer,
+             unsigned int    const width, 
+             unsigned int    const samplesPerPixel, 
+             enum colorspace const colorSpace,
+             FILE *          const ofP,
+             int             const format,
+             xelval          const maxval) {
+
+    JSAMPLE * ptr;
+    unsigned int outputCursor;     /* Cursor into output buffer 'pnmbuffer' */
+
+    ptr = &jpegbuffer[0];  /* Start at beginning of input row */
+    
+    for (outputCursor = 0; outputCursor < width; ++outputCursor) {
+        xel currentPixel;
+        if (samplesPerPixel >= 3) {
+            const rgb_type * const rgb_p = read_rgb(ptr, colorSpace, maxval);
+            PPM_ASSIGN(currentPixel, rgb_p->r, rgb_p->g, rgb_p->b);
+        } else {
+            PNM_ASSIGN1(currentPixel, GETJSAMPLE(*ptr));
+        }
+        ptr += samplesPerPixel;  /* move to next pixel of input */
+        pnmbuffer[outputCursor] = currentPixel;
+    }
+    pnm_writepnmrow(ofP, pnmbuffer, width, maxval, format, FALSE);
 }
 
 
@@ -796,17 +804,42 @@ computeColorSpace(struct jpeg_decompress_struct * const cinfoP,
 
 
 static void
+convertRaster(struct jpeg_decompress_struct * const cinfoP,
+              enum colorspace                 const color_space,
+              FILE *                          const ofP,
+              xelval                          const format,
+              unsigned int                    const maxval) {
+              
+    JSAMPROW jpegbuffer;  /* Input buffer.  Filled by jpeg_scanlines() */
+
+    jpegbuffer = ((*cinfoP->mem->alloc_sarray)
+                  ((j_common_ptr) cinfoP, JPOOL_IMAGE,
+                   cinfoP->output_width * cinfoP->output_components, 
+                   (JDIMENSION) 1)
+        )[0];
+
+    while (cinfoP->output_scanline < cinfoP->output_height) {
+        jpeg_read_scanlines(cinfoP, &jpegbuffer, 1);
+        if (ofP)
+            copyPixelRow(jpegbuffer, cinfoP->output_width, 
+                         cinfoP->out_color_components,
+                         color_space, ofP, format, maxval);
+    }
+}
+
+
+
+static void
 convertImage(FILE *                          const ofP, 
              struct cmdlineInfo              const cmdline,
              struct jpeg_decompress_struct * const cinfoP) {
 
-    int output_type;
+    int format;
         /* The type of output file, PGM or PPM.  Value is either PPM_TYPE
            or PGM_TYPE, which conveniently also pass as format values
            PPM_FORMAT and PGM_FORMAT.
         */
-    JSAMPROW jpegbuffer;  /* Input buffer.  Filled by jpeg_scanlines() */
-    unsigned int maxval;  
+    xelval maxval;  
         /* The maximum value of a sample (color component), both in the input
            and the output.
         */
@@ -817,43 +850,30 @@ convertImage(FILE *                          const ofP,
                    cmdline.dct_method, 
                    cmdline.max_memory_to_use, cmdline.nosmooth);
                    
-    set_color_spaces(cinfoP->jpeg_color_space, &output_type, 
+    set_color_spaces(cinfoP->jpeg_color_space, &format,
                      &cinfoP->out_color_space);
 
-    maxval = (1 << cinfoP->data_precision) - 1;
+    maxval = pm_bitstomaxval(cinfoP->data_precision);
 
     if (cmdline.verbose) 
-        tellDetails(*cinfoP, maxval, output_type);
+        tellDetails(*cinfoP, maxval, format);
 
     /* Calculate output image dimensions so we can allocate space */
     jpeg_calc_output_dimensions(cinfoP);
 
-    jpegbuffer = ((*cinfoP->mem->alloc_sarray)
-                  ((j_common_ptr) cinfoP, JPOOL_IMAGE,
-                   cinfoP->output_width * cinfoP->output_components, 
-                   (JDIMENSION) 1)
-        )[0];
-
     /* Start decompressor */
     jpeg_start_decompress(cinfoP);
 
     if (ofP)
         /* Write pnm output header */
         pnm_writepnminit(ofP, cinfoP->output_width, cinfoP->output_height,
-                         maxval, output_type, FALSE);
+                         maxval, format, FALSE);
 
     pnmbuffer = pnm_allocrow(cinfoP->output_width);
     
     color_space = computeColorSpace(cinfoP, cmdline.inklevel);
-
-    /* Process data */
-    while (cinfoP->output_scanline < cinfoP->output_height) {
-        jpeg_read_scanlines(cinfoP, &jpegbuffer, 1);
-        if (ofP)
-            copy_pixel_row(jpegbuffer, cinfoP->output_width, 
-                           cinfoP->out_color_components,
-                           color_space, maxval, ofP, output_type);
-    }
+    
+    convertRaster(cinfoP, color_space, ofP, format, maxval);
 
     if (cmdline.comments)
         print_comments(*cinfoP);
@@ -905,17 +925,25 @@ convertImages(FILE *                          const ofP,
             convertImage(ofP, cmdline, cinfoP);
         }
     } else {
-        if (dsDataLeft(sourceManagerP))
+        if (dsDataLeft(sourceManagerP)) {
             convertImage(ofP, cmdline, cinfoP);
-        else
+        } else
             pm_error("Input stream is empty");
     }
+    if (dsPrematureEof(sourceManagerP)) {
+        if (cmdline.repair)
+            pm_message("Premature EOF on input; repaired by padding end "
+                       "of image.");
+        else
+            pm_error("Premature EOF on input.  Use -repair to salvage.");
+    }
 }
 
 
 
 int
 main(int argc, char **argv) {
+
     FILE * ofP;
     struct cmdlineInfo cmdline;
     struct jpeg_decompress_struct cinfo;
@@ -924,9 +952,9 @@ main(int argc, char **argv) {
 
     pnm_init(&argc, argv);
 
-    parse_command_line(argc, argv, &cmdline);
+    parseCommandLine(argc, argv, &cmdline);
 
-    if (cmdline.exif_filespec && STREQ(cmdline.exif_filespec, "-"))
+    if (cmdline.exif_filespec && streq(cmdline.exif_filespec, "-"))
         /* He's got exif going to stdout, so there can be no image output */
         ofP = NULL;
     else
@@ -967,4 +995,3 @@ main(int argc, char **argv) {
   
     exit(jerr.num_warnings > 0 ? EXIT_WARNING : EXIT_SUCCESS);
 }
-
diff --git a/converter/other/pamtodjvurle.c b/converter/other/pamtodjvurle.c
index ae35e81d..2d26eeb0 100644
--- a/converter/other/pamtodjvurle.c
+++ b/converter/other/pamtodjvurle.c
@@ -16,6 +16,7 @@
 #include <stdio.h>
 #include <assert.h>
 
+#include "pm_c_util.h"
 #include "pam.h"
 #include "pammap.h"
 #include "shhopt.h"
@@ -179,7 +180,7 @@ writeRleRun(FILE *       const ofP,
   
   'transcolor' is a 3-deep tuple with the same maxval as the image.
 -----------------------------------------------------------------------------*/
-    uint32n rlevalue;         /* RLE-encoded color/valuex */
+    uint32_t rlevalue;         /* RLE-encoded color/valuex */
     int index;
 
     if (count > 0) {
diff --git a/converter/other/pamtofits.c b/converter/other/pamtofits.c
index ec271ff3..7a1c70de 100644
--- a/converter/other/pamtofits.c
+++ b/converter/other/pamtofits.c
@@ -20,10 +20,17 @@
 ** in the FITS header, but do not cause the data to be rescaled.
 */
 
+/*
+  The official specification of FITS format (which is for more than
+  just visual images) is at
+  ftp://legacy.gsfc.nasa.gov/fits_info/fits_office/fits_standard.pdf
+*/
+
 #include <assert.h>
 #include <string.h>
 #include <stdio.h>
 
+#include "pm_c_util.h"
 #include "mallocvar.h"
 #include "shhopt.h"
 #include "nstring.h"
@@ -39,10 +46,9 @@ struct cmdlineInfo {
 
 
 static void 
-parseCommandLine(int argc, 
-                 char ** argv, 
-                 struct cmdlineInfo  * const cmdlineP) {
-/* --------------------------------------------------------------------------
+parseCommandLine(int argc, char ** argv,
+                 struct cmdlineInfo * const cmdlineP) {
+/*--------------------------------------------------------------------------
    Parse program command line described in Unix standard form by argc
    and argv.  Return the information in the options as *cmdlineP.  
 
@@ -52,8 +58,8 @@ parseCommandLine(int argc,
    Note that the strings we return are stored in the storage that
    was passed to us as the argv array.  We also trash *argv.
 --------------------------------------------------------------------------*/
-    optEntry *option_def;
-    /* Instructions to optParseOptions3 on how to parse our options. */
+    optEntry * option_def;
+        /* Instructions to optParseOptions3 on how to parse our options. */
     optStruct3 opt;
 
     unsigned int minSpec;
@@ -98,7 +104,6 @@ parseCommandLine(int argc,
 
 
 
-
 static void
 writeHeaderCard(const char * const s) {
 /*----------------------------------------------------------------------------
@@ -203,6 +208,14 @@ writeRaster(struct pam * const pamP,
             unsigned int const bitpix,
             int          const offset) {
 
+    /* Note: the FITS specification does not give the association between
+       file position and image position (i.e. is the first pixel in the
+       file the top left, bottom left, etc.).  We use the common sense,
+       popular order of row major, top to bottom, left to right.  This
+       has been the case and accepted since 1989, but in 2008, we discovered
+       that Gimp and ImageMagick do bottom to top.
+    */
+
     unsigned int plane;
 
     for (plane = 0; plane < pamP->depth; ++plane) {
diff --git a/converter/other/pamtogif.c b/converter/other/pamtogif.c
new file mode 100644
index 00000000..0c8c0f9e
--- /dev/null
+++ b/converter/other/pamtogif.c
@@ -0,0 +1,1977 @@
+/*=============================================================================
+                              pamtogif
+===============================================================================
+  Convert a Netpbm image to GIF
+
+  History and copyright information is at the end of the file.
+=============================================================================*/
+
+#include <assert.h>
+#include <string.h>
+
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "shhopt.h"
+#include "nstring.h"
+#include "pam.h"
+#include "pammap.h"
+
+#define MAXCMAPSIZE 256
+
+static unsigned int const gifMaxval = 255;
+
+static bool verbose;
+
+
+typedef int stringCode;
+    /* A code to be place in the GIF raster.  It represents
+       a string of one or more pixels.  You interpret this in the context
+       of a current code size.  The lower half of the values representable
+       in the current code size represent singleton strings and the value
+       is simply the value of the one pixel in the string.  The first two
+       values in the upper half of the range are the clear code and EOF
+       code, respectively.  The rest of the values represent multi-pixel
+       strings.  The mapping between value and the sequence of pixels
+       changes throughout the image.
+
+       A variable of this type sometimes has the value -1 instead of
+       a string code due to cheesy programming.
+
+       Ergo, this data structure must be signed and at least BITS bits
+       wide plus sign bit.
+    */
+
+
+struct cmap {
+    /* This is the information for the GIF colormap (aka palette). */
+
+    struct pam pam;
+        /* Gives depth and maxval for colors in color[] */
+    tuple color[MAXCMAPSIZE];
+        /* Maps a color index, as is found in the raster part of the
+           GIF, to color.
+        */
+    unsigned int cmapSize;
+        /* Number of entries in the GIF colormap.  I.e. number of colors
+           in the image, plus possibly one fake transparency color.
+        */
+    bool haveTransparent;
+        /* The colormap contains an entry for transparent pixels */
+    unsigned int transparent;
+        /* color index number in GIF palette of the color that is to
+           be transparent.
+
+           Meaningful only if 'haveTransparent' is true.
+        */
+    tuplehash tuplehash;
+        /* A hash table to translate color to GIF colormap index. */
+};
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char *input_filespec; /* Filespec of input file */
+    const char *alphacolor;     /* -alphacolor option value or default */
+    unsigned int interlace;     /* -interlace option value */
+    unsigned int sort;          /* -sort option value */
+    const char *mapfile;        /* -mapfile option value.  NULL if none. */
+    const char *transparent;    /* -transparent option value.  NULL if none. */
+    const char *comment;        /* -comment option value; NULL if none */
+    unsigned int nolzw;         /* -nolzw option */
+    float aspect;               /* -aspect option value (the ratio).  */
+    unsigned int verbose;
+};
+
+
+
+
+
+static unsigned int
+pamAlphaPlane(struct pam * const pamP) {
+
+    unsigned int alphaPlane;
+
+    if (streq(pamP->tuple_type, "RGB_ALPHA"))
+        alphaPlane = 3;
+    else if (streq(pamP->tuple_type, "GRAYSCALE_ALPHA"))
+        alphaPlane = 2;
+    else if (streq(pamP->tuple_type, "BLACKANDWHITE_ALPHA"))
+        alphaPlane = 2;
+    else
+        alphaPlane = 0;
+    
+    if (alphaPlane >= pamP->depth)
+        pm_error("Tuple type is '%s', but depth (%u) is less than %u",
+                 pamP->tuple_type, pamP->depth, alphaPlane + 1);
+
+    return alphaPlane;
+}
+
+
+
+static void
+parseCommandLine(int argc, char ** argv,
+                 struct cmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   Parse the program arguments (given by argc and argv) into a form
+   the program can deal with more easily -- a cmdline_info structure.
+   If the syntax is invalid, issue a message and exit the program via
+   pm_error().
+
+   Note that the file spec array we return is stored in the storage that
+   was passed to us as the argv array.
+-----------------------------------------------------------------------------*/
+    optEntry * option_def;  /* malloc'ed */
+    optStruct3 opt;  /* set by OPTENT3 */
+    unsigned int option_def_index;
+
+    unsigned int aspectSpec;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0,   "interlace",   OPT_FLAG,   
+            NULL,                       &cmdlineP->interlace, 0);
+    OPTENT3(0,   "sort",        OPT_FLAG,   
+            NULL,                       &cmdlineP->sort, 0);
+    OPTENT3(0,   "nolzw",       OPT_FLAG,   
+            NULL,                       &cmdlineP->nolzw, 0);
+    OPTENT3(0,   "mapfile",     OPT_STRING, 
+            &cmdlineP->mapfile,        NULL, 0);
+    OPTENT3(0,   "transparent", OPT_STRING, 
+            &cmdlineP->transparent,    NULL, 0);
+    OPTENT3(0,   "comment",     OPT_STRING, 
+            &cmdlineP->comment,        NULL, 0);
+    OPTENT3(0,   "alphacolor",  OPT_STRING, 
+            &cmdlineP->alphacolor,     NULL, 0);
+    OPTENT3(0,   "aspect",      OPT_FLOAT, 
+            &cmdlineP->aspect,         &aspectSpec, 0);
+    OPTENT3(0,   "verbose",     OPT_FLAG, 
+            NULL,                      &cmdlineP->verbose, 0);
+    
+    /* Set the defaults */
+    cmdlineP->mapfile = NULL;
+    cmdlineP->transparent = NULL;  /* no transparency */
+    cmdlineP->comment = NULL;      /* no comment */
+    cmdlineP->alphacolor = "rgb:0/0/0";      
+        /* We could say "black" here, but then we depend on the color names
+           database existing.
+        */
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (argc-1 == 0) 
+        cmdlineP->input_filespec = "-";
+    else if (argc-1 != 1)
+        pm_error("Program takes zero or one argument (filename).  You "
+                 "specified %d", argc-1);
+    else
+        cmdlineP->input_filespec = argv[1];
+        
+    if (aspectSpec) { 
+        if (cmdlineP->aspect < 0.25  || cmdlineP->aspect > 4.21875)
+            pm_error("Invalid -aspect value: %f.  "
+                     "GIF allows only the range 0.25-4.0 .",
+                     cmdlineP->aspect);
+        else if (cmdlineP->aspect > 4.0)
+            pm_message("Warning: "
+                       "You specified an aspect ratio over 4.0: %f.  "
+                       "This will result in an invalid GIF.",
+                       cmdlineP->aspect);
+    } else
+        cmdlineP->aspect = 1.0;
+}
+
+
+
+/*
+ * Write out a word to the GIF file
+ */
+static void
+Putword(int const w, FILE * const fp) {
+
+    fputc( w & 0xff, fp );
+    fputc( (w / 256) & 0xff, fp );
+}
+
+
+
+static int
+closestColor(tuple         const color,
+             struct pam *  const pamP,
+             struct cmap * const cmapP) {
+/*----------------------------------------------------------------------------
+   Return the colormap index of the color in the colormap *cmapP
+   that is closest to the color 'color', whose format is specified by 
+   *pamP.
+
+   Also add 'color' to the colormap hash, with the colormap index we
+   are returning.  Caller must ensure that the color is not already in
+   there.
+-----------------------------------------------------------------------------*/
+    unsigned int const nComp = pamP->depth >= 3 ? 3 : 1;
+        /* Number of color components (not alpha) in 'color' */
+    
+    unsigned int i;
+    unsigned int imin, dmin;
+    bool fits;
+
+    dmin = UINT_MAX;
+    imin = 0;
+    for (i = 0; i < cmapP->cmapSize; ++i) {
+        unsigned int distance;
+        unsigned int plane;
+
+        for (distance = 0, plane = 0; plane < nComp; ++plane)
+            /* Divide by 4 is to avoid arithmetic overflow */
+            distance += SQR(color[plane] - cmapP->color[i][plane]) / 4;
+
+        if (distance < dmin) {
+            dmin = distance;
+            imin = i; 
+        } 
+    }
+    pnm_addtotuplehash(pamP, cmapP->tuplehash, color, imin, &fits);
+
+    return imin;
+}
+
+
+
+enum pass {MULT8PLUS0, MULT8PLUS4, MULT4PLUS2, MULT2PLUS1};
+
+
+typedef struct {
+    struct pam pam;
+        /* Description of input file/image.  The position of the file
+           is also part of the state of this rowReader.
+        */
+    pm_filepos rasterPos;
+        /* Position in file fileP of the start of the raster */
+    bool interlace;
+        /* We're accessing the image in interlace fashion */
+    bool eof;
+        /* The image is at EOF (we have returned all of the rows) */
+    unsigned int nextRow;
+        /* Number of row to which input file is positioned;
+           meaningless if 'eof'.
+        */
+    enum pass pass;
+        /* The interlace pass.  Undefined if !interlace */
+    tuple * discardBuffer;
+        /* A bitbucket for rows we read in order to advance the file
+           position.
+        */
+} rowReader;
+
+
+
+static rowReader *
+rowReader_create(struct pam * const pamP,
+                 pm_filepos   const rasterPos,
+                 bool         const interlace) {
+
+    rowReader * rdrP;
+
+    MALLOCVAR_NOFAIL(rdrP);
+
+    rdrP->pam         = *pamP;
+    rdrP->rasterPos   = rasterPos;
+    rdrP->interlace   = interlace;
+    rdrP->eof         = FALSE;
+    rdrP->pass        = MULT8PLUS0;
+
+    pm_seek2(rdrP->pam.file, &rasterPos, sizeof(rasterPos));
+    rdrP->nextRow = 0;
+
+    rdrP->discardBuffer = pnm_allocpamrow(&rdrP->pam);
+
+    return rdrP;
+}
+
+
+
+static void
+rowReader_destroy(rowReader * const rdrP) {
+
+    pnm_freepamrow(rdrP->discardBuffer);
+    free(rdrP);
+}
+
+
+
+static void
+rowReaderSkipRows(rowReader *  const rdrP,
+                  unsigned int const rowCount,
+                  bool *       const eofP) {
+/*----------------------------------------------------------------------------
+   Skip over the next 'rowCount' rows of the input file.
+
+   Iff there aren't at least 'rowCount' rows left, return *eofP == TRUE.
+-----------------------------------------------------------------------------*/
+    if (rdrP->nextRow + rowCount >= rdrP->pam.height)
+        *eofP = TRUE;
+    else {
+        /* This could be made faster if need be by adding a libnetpbm
+           row skip function.  Except with the plain formats, that could
+           just compute the next row position and fseek() to it.
+           pnm_readpamrow() with NULL for the output pointer would be a
+           good interface for a row skip function.
+        */
+        unsigned int i;
+
+        *eofP = FALSE;
+
+        for (i = 0; i < rowCount; ++i)
+            pnm_readpamrow(&rdrP->pam, rdrP->discardBuffer);
+
+        rdrP->nextRow += rowCount;
+    }
+}
+
+
+
+static void
+rowReaderGotoNextInterlaceRow(rowReader * const rdrP) {
+/*----------------------------------------------------------------------------
+  Position reader to the next row in the interlace pattern, assuming it
+  is now positioned immediately after the current row.
+-----------------------------------------------------------------------------*/
+    bool endOfPass;
+
+    /* There are 4 passes:
+       MULT8PLUS0: Rows 0, 8, 16, 24, 32, etc.
+       MULT8PLUS4: Rows 4, 12, 20, 28, etc.
+       MULT4PLUS2: Rows 2, 6, 10, 14, etc.
+       MULT2PLUS1: Rows 1, 3, 5, 7, 9, etc.
+    */
+    
+    switch (rdrP->pass) {
+    case MULT8PLUS0:
+        rowReaderSkipRows(rdrP, 7, &endOfPass);
+        break;
+    case MULT8PLUS4:
+        rowReaderSkipRows(rdrP, 7, &endOfPass);
+        break;
+    case MULT4PLUS2:
+        rowReaderSkipRows(rdrP, 3, &endOfPass);
+        break;
+    case MULT2PLUS1:
+        rowReaderSkipRows(rdrP, 1, &endOfPass);
+        break;
+    }
+
+    /* Note that if there are more than 4 rows, the sequence of passes
+       is sequential, but when there are fewer than 4, reading may skip
+       e.g. from MULT8PLUS0 to MULT4PLUS2.
+    */
+    while (endOfPass && !rdrP->eof) {
+        pm_seek2(rdrP->pam.file, &rdrP->rasterPos, sizeof(rdrP->rasterPos));
+        rdrP->nextRow = 0;
+
+        switch (rdrP->pass) {
+        case MULT8PLUS0:
+            rdrP->pass = MULT8PLUS4;
+            rowReaderSkipRows(rdrP, 4, &endOfPass);
+            break;
+        case MULT8PLUS4:
+            rdrP->pass = MULT4PLUS2;
+            rowReaderSkipRows(rdrP, 2, &endOfPass);
+            break;
+        case MULT4PLUS2:
+            rdrP->pass = MULT2PLUS1;
+            rowReaderSkipRows(rdrP, 1, &endOfPass);
+            break;
+        case MULT2PLUS1:
+            rdrP->eof = TRUE;
+            break;
+        }
+    }
+}
+
+
+
+
+static void
+rowReaderGotoNextStraightRow(rowReader * const rdrP) {
+/*----------------------------------------------------------------------------
+  Position reader to the next row in a straight, non-interlace
+  pattern, assuming the file is now positioned immediately after the
+  current row.
+
+  This is trivial, since the next row _is_ immediately after the current
+  row, except in the case that there are no more rows.
+-----------------------------------------------------------------------------*/
+    if (rdrP->nextRow >= rdrP->pam.height)
+        rdrP->eof = TRUE;
+}
+
+
+
+static void
+rowReader_read(rowReader * const rdrP,
+               tuple *     const tuplerow) {
+
+    if (rdrP->eof)
+        pm_error("INTERNAL ERROR: rowReader attempted to read beyond end "
+                 "of image");
+
+    pnm_readpamrow(&rdrP->pam, tuplerow);
+    ++rdrP->nextRow;
+    
+    if (rdrP->interlace)
+        rowReaderGotoNextInterlaceRow(rdrP);
+    else
+        rowReaderGotoNextStraightRow(rdrP);
+}
+
+
+
+static unsigned int
+gifPixel(struct pam *   const pamP,
+         tuple          const tuple,
+         unsigned int   const alphaPlane,
+         sample         const alphaThreshold, 
+         struct cmap *  const cmapP) {
+/*----------------------------------------------------------------------------
+   Return as *colorIndexP the colormap index of the tuple 'tuple',
+   whose format is described by *pamP, using colormap *cmapP.
+
+   'alphaThreshold' is the alpha level below which we consider a
+   pixel transparent for GIF purposes.
+-----------------------------------------------------------------------------*/
+    int colorIndex;
+
+    if (alphaPlane && tuple[alphaPlane] < alphaThreshold &&
+        cmapP->haveTransparent)
+        colorIndex = cmapP->transparent;
+    else {
+        int found;
+
+        pnm_lookuptuple(pamP, cmapP->tuplehash, tuple,
+                        &found, &colorIndex);
+        
+        if (!found)
+            colorIndex = closestColor(tuple, pamP, cmapP);
+    }
+    assert(colorIndex >= 0);
+    return (unsigned int) colorIndex;
+}
+
+
+
+static void
+writeTransparentColorIndexExtension(FILE *       const ofP,
+                                    unsigned int const transColorIndex) {
+/*----------------------------------------------------------------------------
+   Write out extension for transparent color index.
+-----------------------------------------------------------------------------*/
+    fputc('!',  ofP);
+    fputc(0xf9, ofP);
+    fputc(4,    ofP);
+    fputc(1,    ofP);
+    fputc(0,    ofP);
+    fputc(0,    ofP);
+    fputc(transColorIndex, ofP);
+    fputc(0,    ofP);
+}
+
+
+
+static void
+writeCommentExtension(FILE * const ofP,
+                      char   const comment[]) {
+/*----------------------------------------------------------------------------
+   Write out extension for a comment
+-----------------------------------------------------------------------------*/
+    unsigned int const maxSegmentSize = 255;
+
+    const char * segment;
+    
+    fputc('!',  ofP);   /* Identifies an extension */
+    fputc(0xfe, ofP);   /* Identifies a comment */
+
+    /* Write it out in segments no longer than 255 characters */
+    for (segment = &comment[0]; 
+         segment < comment + strlen(comment); 
+         segment += maxSegmentSize) {
+
+        unsigned int const lengthThisSegment =
+            MIN(maxSegmentSize, strlen(segment));
+
+        fputc(lengthThisSegment, ofP);
+
+        fwrite(segment, 1, lengthThisSegment, ofP);
+    }
+
+    fputc(0, ofP);   /* No more comment blocks in this extension */
+}
+
+
+
+/***************************************************************************
+ *
+ *  GIFCOMPR.C       - GIF Image compression routines
+ *
+ *  Lempel-Ziv compression based on 'compress'.  GIF modifications by
+ *  David Rowley (mgardi@watdcsu.waterloo.edu)
+ *
+ ***************************************************************************/
+
+/*
+ * General DEFINEs
+ */
+
+#define BITS    12
+
+/*
+ *
+ * GIF Image compression - modified 'compress'
+ *
+ * Based on: compress.c - File compression ala IEEE Computer, June 1984.
+ *
+ * By Authors:  Spencer W. Thomas       (decvax!harpo!utah-cs!utah-gr!thomas)
+ *              Jim McKie               (decvax!mcvax!jim)
+ *              Steve Davies            (decvax!vax135!petsd!peora!srd)
+ *              Ken Turkowski           (decvax!decwrl!turtlevax!ken)
+ *              James A. Woods          (decvax!ihnp4!ames!jaw)
+ *              Joe Orost               (decvax!vax135!petsd!joe)
+ *
+ */
+
+
+static stringCode const maxCodeLimitLzw = (stringCode)1 << BITS;
+       /* One beyond the largest string code that can exist in GIF */ 
+       /* Used only in assertions  */
+
+
+struct hashTableEntry {
+    stringCode fcode;   /* -1 means unused slot */
+    unsigned int ent;
+};    
+
+
+
+/***************************************************************************
+*                          BYTE OUTPUTTER                 
+***************************************************************************/
+
+typedef struct {
+    FILE * fileP;  /* The file to which to output */
+    unsigned int count;
+        /* Number of bytes so far in the current data block */
+    unsigned char buffer[256];
+        /* The current data block, under construction */
+} byteBuffer;
+
+
+
+static byteBuffer *
+byteBuffer_create(FILE * const fileP) {
+
+    byteBuffer * byteBufferP;
+
+    MALLOCVAR_NOFAIL(byteBufferP);
+
+    byteBufferP->fileP = fileP;
+    byteBufferP->count = 0;
+
+    return byteBufferP;
+}
+
+
+
+static void
+byteBuffer_destroy(byteBuffer * const byteBufferP) {
+
+    free(byteBufferP);
+}
+
+
+
+static void
+byteBuffer_flush(byteBuffer * const byteBufferP) {
+/*----------------------------------------------------------------------------
+   Write the current data block to the output file, then reset the current 
+   data block to empty.
+-----------------------------------------------------------------------------*/
+    if (byteBufferP->count > 0 ) {
+        if (verbose)
+            pm_message("Writing %u byte block", byteBufferP->count);
+        fputc(byteBufferP->count, byteBufferP->fileP);
+        fwrite(byteBufferP->buffer, 1, byteBufferP->count, byteBufferP->fileP);
+        byteBufferP->count = 0;
+    }
+}
+
+
+
+static void
+byteBuffer_flushFile(byteBuffer * const byteBufferP) {
+    
+    fflush(byteBufferP->fileP);
+    
+    if (ferror(byteBufferP->fileP))
+        pm_error("error writing output file");
+}
+
+
+
+static void
+byteBuffer_out(byteBuffer *  const byteBufferP,
+               unsigned char const c) {
+/*----------------------------------------------------------------------------
+  Add a byte to the end of the current data block, and if it is now 254
+  characters, flush the data block to the output file.
+-----------------------------------------------------------------------------*/
+    byteBufferP->buffer[byteBufferP->count++] = c;
+    if (byteBufferP->count >= 254)
+        byteBuffer_flush(byteBufferP);
+}
+
+
+
+/***************************************************************************
+*                          GIF CODE OUTPUTTER                 
+***************************************************************************/
+
+typedef struct {
+    byteBuffer * byteBufferP;
+    unsigned int initBits;
+    unsigned int nBits;
+        /* Number of bits to put in output for each code */
+    stringCode maxCode;                  /* maximum code, given n_bits */
+    stringCode maxCodeLimit;
+        /* LZW: One beyond the largest string code that can exist in GIF.
+           Uncompressed: a ceiling to prevent code size from ratcheting up.
+           In either case, output code never reaches this value.
+        */  
+    unsigned long curAccum;
+    int curBits;
+    unsigned int codeCount;
+        /* Number of codes that have been output to this buffer (doesn't
+           matter if they have gone out the other side yet or not) since
+           the last flush (or ever, if no last flush).  The main use of this
+           is debugging -- when something fails, you can see in a debugger
+           where in the image it was, then set a trap for there.
+        */
+} codeBuffer;
+
+
+
+static codeBuffer *
+codeBuffer_create(FILE *       const ofP,
+                  unsigned int const initBits,
+                  bool         const lzw) {
+
+    codeBuffer * codeBufferP;
+
+    MALLOCVAR_NOFAIL(codeBufferP);
+
+    codeBufferP->initBits    = initBits;
+    codeBufferP->nBits       = codeBufferP->initBits;
+    codeBufferP->maxCode     = (1 << codeBufferP->nBits) - 1;
+    codeBufferP->maxCodeLimit = lzw ?
+        (stringCode)1 << BITS : (stringCode) (1 << codeBufferP->nBits) - 1; 
+    codeBufferP->byteBufferP = byteBuffer_create(ofP);
+    codeBufferP->curAccum    = 0;
+    codeBufferP->curBits     = 0;
+    codeBufferP->codeCount   = 0;
+
+    return codeBufferP;
+}
+
+
+
+static void
+codeBuffer_destroy(codeBuffer * const codeBufferP) {
+
+    byteBuffer_destroy(codeBufferP->byteBufferP);
+
+    free(codeBufferP);
+}
+
+
+
+static void
+codeBuffer_resetCodeSize(codeBuffer * const codeBufferP) {
+
+    codeBufferP->nBits = codeBufferP->initBits;
+
+    assert(codeBufferP->nBits <= BITS);
+
+    codeBufferP->maxCode = (1 << codeBufferP->nBits) - 1;
+}
+
+
+
+static void
+codeBuffer_increaseCodeSize(codeBuffer * const codeBufferP) {
+
+    ++codeBufferP->nBits;
+
+    assert(codeBufferP->nBits <= BITS);
+
+    codeBufferP->maxCode = (1 << codeBufferP->nBits) - 1;
+}
+
+static void
+codeBuffer_output(codeBuffer * const codeBufferP,
+                  stringCode   const code) {
+/*----------------------------------------------------------------------------
+   Output one GIF code to the file, through the code buffer.
+
+   The code is represented as N bits in the file -- the lower
+   N bits of 'code'.  N is a the current code size of *codeBufferP.
+   
+   Id 'code' is the maximum possible code for the current code size
+   for *codeBufferP, increase that code size (unless it's already 
+   maxed out).
+-----------------------------------------------------------------------------*/
+    assert (code <= codeBufferP->maxCode);
+
+    codeBufferP->curAccum &= (1 << codeBufferP->curBits) - 1;
+
+    if (codeBufferP->curBits > 0)
+        codeBufferP->curAccum |= ((unsigned long)code << codeBufferP->curBits);
+    else
+        codeBufferP->curAccum = code;
+
+    codeBufferP->curBits += codeBufferP->nBits;
+
+    while (codeBufferP->curBits >= 8) {
+        byteBuffer_out(codeBufferP->byteBufferP,
+                       codeBufferP->curAccum & 0xff);
+        codeBufferP->curAccum >>= 8;
+        codeBufferP->curBits -= 8;
+    }
+
+    ++codeBufferP->codeCount;
+}
+
+
+
+static void
+codeBuffer_flush(codeBuffer * const codeBufferP) {
+
+    /* Output the possible partial byte in the buffer */
+
+    if (codeBufferP->curBits > 0) {
+        byteBuffer_out(codeBufferP->byteBufferP,
+                       codeBufferP->curAccum & 0xff);
+        codeBufferP->curBits = 0;
+    }
+    byteBuffer_flush(codeBufferP->byteBufferP);
+    
+    byteBuffer_flushFile(codeBufferP->byteBufferP);
+
+    if (verbose)
+        pm_message("%u strings of pixels written to file",
+                   codeBufferP->codeCount);
+    codeBufferP->codeCount = 0;
+}
+
+
+
+typedef struct {
+    codeBuffer * codeBufferP;
+        /* The place to which we write our string codes.
+
+           Constant.
+        */
+    bool lzw;
+        /* We're actually doing LZW compression.  False means we follow
+           the algorithm enough tht an LZW decompressor will recover the
+           proper data, but always using one code per pixel, and therefore
+           not effecting any compression and not using the LZW patent.
+        */
+    unsigned int hsize;
+        /* The number of slots in the hash table.  This variable to
+           enhance overall performance by reducing memory use when
+           encoding smaller gifs. 
+         */
+        
+    unsigned int hshift;
+        /* This is how many bits we shift left a string code in forming the
+           primary hash of the concatenation of that string with another.
+           Constant.
+        */
+
+    /* Codes less than 'clearCode' are singleton pixel codes - each
+       represents the pixel value equal to it.
+
+       Codes greater than 'eofCode' are multipixel string codes.  Each
+       represents a string of pixels that is defined by the preceding
+       stream.
+    */
+    stringCode clearCode;
+        /* The code in an LZW stream that means to clear the string
+           dictionary and start fresh.
+
+           Constant.
+        */
+    stringCode eofCode;
+        /* The code in an LZW stream that means there's no more coming
+
+           Constant.
+        */
+    stringCode initCodeLimit;
+        /* The value of 'codeLimit' at the start of a block.
+
+           Constant.
+        */
+
+    stringCode codeLimit;
+        /* One beyond the maximum code possible with the current code
+           size.
+        */
+
+    struct hashTableEntry * hashTable;
+    stringCode nextUnusedCode;
+        /* Numerically next code available to assign to a a multi-pixel
+           string.  Note that codes for multi-pixel strings are in the
+           upper half of the range of codes, always greater than
+           'clearCode'.
+        */
+
+    stringCode stringSoFar;
+        /* The code for the string we have built so far.  This code indicates
+           one or more pixels that we have encoded but not yet output
+           because we're hoping to match an even longer string.
+
+           Valid only when 'buildingString' is true.
+
+           In the non-lzw case the single pixel to output.
+        */
+    bool buildingString;
+        /* We are in the middle of building a string; 'stringSoFar' describes
+           the pixels in it so far.  The only time this is false is at the
+           very beginning of the stream.
+ 
+           Ignored in the non-lzw case. 
+        */
+} lzwCompressor;
+
+
+
+
+static unsigned int
+nSignificantBits( unsigned int const arg ){
+
+#if HAVE_GCC_BITCOUNT
+
+    return (arg == 0) ? 0 : 8 * sizeof(unsigned int) - __builtin_clz(arg);
+
+#else
+
+    unsigned int i = 0;
+    while (arg >> i != 0)
+        ++i;
+
+    return i;
+#endif
+}
+
+
+
+static lzwCompressor *
+lzw_create(FILE *       const ofP,
+           unsigned int const initBits,
+           bool         const lzw,
+           unsigned int const pixelCount) {
+
+    unsigned int const hsizeTable[] = {257, 521, 1031, 2053, 4099, 5003};
+    /* If the image has 4096 or fewer pixels we use prime numbers slightly
+       above powers of two between 8 and 12.  In this case the hash table
+       never fills up; clear code is never emitted.
+    
+       Above that we use a table with 4096 slots plus 20% extra.
+       When this is not enough the clear code is emitted.
+       Due to the extra 20% the table itself never fills up.
+       
+       lzw.hsize and lzw.hshift stay constant through the image.
+
+       Variable hsize is a performance enhancement based on the fact that
+       the encoder never needs more codes than the number of pixels in
+       the image.  Typically, the ratio of pixels to codes is around
+       10:1 to 20:1.
+   
+       Logic works with fixed values lzw.hsize=5003 and t=13.
+    */
+
+    lzwCompressor * lzwP;
+       
+    MALLOCVAR_NOFAIL(lzwP);
+
+    /* Constants */
+    lzwP->lzw = lzw;
+
+    lzwP->clearCode     = 1 << (initBits - 1);
+    lzwP->eofCode       = lzwP->clearCode + 1;
+    lzwP->initCodeLimit = 1 << initBits;
+
+    if (lzw) {
+        unsigned int const t =
+            MIN(13, MAX(8, nSignificantBits(pixelCount +lzwP->eofCode - 2)));
+            /* Index into hsizeTable */
+    
+        lzwP->hsize = hsizeTable[t-8];
+
+        lzwP->hshift = (t == 13 ? 12 : t) - nSignificantBits(MAXCMAPSIZE-1);
+
+        MALLOCARRAY(lzwP->hashTable, lzwP->hsize);
+        
+        if (lzwP->hashTable == NULL)
+            pm_error("Couldn't get memory for %u-entry hash table.",
+                     lzwP->hsize);
+    } else {
+        /* No LZW compression.  We don't need a stringcode hash table */  
+        lzwP->hashTable = NULL;
+        lzwP->hsize     = 0;
+    }
+
+    lzwP->buildingString = FALSE;
+
+    lzwP->codeBufferP = codeBuffer_create(ofP, initBits, lzw);
+
+    return lzwP;
+}
+
+
+
+static void
+lzw_destroy(lzwCompressor * const lzwP) {
+
+    codeBuffer_destroy(lzwP->codeBufferP);
+
+    free(lzwP->hashTable);
+
+    free(lzwP);
+}
+
+
+
+static void
+lzwHashClear(lzwCompressor * const lzwP) {
+
+    /* Empty the code table */
+
+    unsigned int i;
+
+    for (i = 0; i < lzwP->hsize; ++i)
+        lzwP->hashTable[i].fcode = -1;
+
+    lzwP->nextUnusedCode = lzwP->clearCode + 2;
+}
+
+
+
+static void
+lzw_clearBlock(lzwCompressor * const lzwP) {
+/*----------------------------------------------------------------------------
+  
+-----------------------------------------------------------------------------*/
+    lzwHashClear(lzwP);
+
+    codeBuffer_output(lzwP->codeBufferP, lzwP->clearCode);
+
+    codeBuffer_resetCodeSize(lzwP->codeBufferP);
+
+    lzwP->codeLimit = lzwP->initCodeLimit;
+}
+
+
+
+static void
+lzwAdjustCodeSize(lzwCompressor * const lzwP,
+                  stringCode      const newCode) {
+/*----------------------------------------------------------------------------
+   Assuming we just defined code 'newCode', increase the code size as
+   required so that this code fits.
+
+   The decompressor is mimicking our assignment of that code, so knows that
+   we are making this adjustment, so expects codes of the new size.
+-----------------------------------------------------------------------------*/
+    assert(newCode <= lzwP->codeLimit);
+
+    if (newCode == lzwP->codeLimit) {
+        lzwP->codeLimit *= 2;
+        codeBuffer_increaseCodeSize(lzwP->codeBufferP);
+
+        assert(lzwP->codeLimit <= maxCodeLimitLzw);
+    }
+}
+
+
+
+static void
+lzwOutputCurrentString(lzwCompressor * const lzwP) {
+/*----------------------------------------------------------------------------
+   Put a code for the currently built-up string in the output stream.
+
+   Doing this causes a new string code to be defined (code is
+   lzwP->nextUnusedCode), so Caller must add that to the hash.  If
+   that code's size is beyond the overall limit, we reset the hash
+   (which means future codes will start back at the minimum size) and
+   put a clear code in the stream to tell the decompressor to do the
+   same.  So Caller must add it to the hash _before_ calling us.
+
+   Note that in the non-compressing case, the overall limit is small
+   enough to prevent us from ever defining string codes; we'll always
+   reset the hash.
+
+   There's an odd case that always screws up any attempt to make this
+   code cleaner: At the end of the LZW stream, you have to output the
+   code for the final string even though you don't have a following
+   pixel that would make a longer string.  So there's nothing to add
+   to the hash table and no point in allocating a new string code.
+   But the decompressor doesn't know that we're done, so he allocates
+   the next string code and may therefore increase his code length.
+   If we don't do the same, we will write our one last code -- the EOF
+   code -- in a code length smaller than what the decompressor is
+   expecting, and he will have a premature end of stream.
+
+   So this subroutine does run for that final code flush and does some
+   of the motions of defining a new string code, but this subroutine
+   can't update the hash because in that particular case, there's
+   nothing to add.
+-----------------------------------------------------------------------------*/
+    codeBuffer_output(lzwP->codeBufferP, lzwP->stringSoFar);
+    if (lzwP->nextUnusedCode < lzwP->codeBufferP->maxCodeLimit) {
+        /* Allocate the code for the extended string, which Caller
+           should have already put in the hash so he can use it in the
+           future.  Decompressor knows when it sees the code output
+           above to define a string on its end too, using the same
+           string code we do.
+        */
+        stringCode const newCode = lzwP->nextUnusedCode++;
+
+        /* This code may be too big to fit in the current code size, in
+           which case we have to increase the code size (and decompressor
+           will do the same).
+        */
+        lzwAdjustCodeSize(lzwP, newCode);
+    } else {
+        /* Forget all the strings so far; start building again; tell
+           decompressor to do the same.
+        */
+        lzw_clearBlock(lzwP);
+    }
+}
+
+
+
+static void
+lzw_flush(lzwCompressor * const lzwP) {
+
+    if (lzwP->lzw)
+        lzwOutputCurrentString(lzwP);
+        /* Put out the code for the final string. */
+
+    codeBuffer_output(lzwP->codeBufferP, lzwP->eofCode);
+
+    codeBuffer_flush(lzwP->codeBufferP);
+}
+
+
+
+static unsigned int
+primaryHash(stringCode   const baseString,
+            stringCode   const additionalPixel,
+            unsigned int const hshift) {
+
+    unsigned int hash;
+
+    assert(baseString < maxCodeLimitLzw);
+    assert(additionalPixel < MAXCMAPSIZE);
+
+    hash = (additionalPixel << hshift) ^ baseString;
+    
+    return hash;
+}
+
+    
+
+static void
+lookupInHash(lzwCompressor *  const lzwP,
+             unsigned int     const gifPixel,
+             stringCode       const fcode,
+             bool *           const foundP,
+             unsigned short * const codeP,
+             unsigned int *   const hashP) {
+
+    int disp;
+        /* secondary hash stride (after G. Knott) */
+    int i;
+        /* Index into hash table */
+
+    i = primaryHash(lzwP->stringSoFar, gifPixel, lzwP->hshift);
+    disp = (i == 0) ? 1 : lzwP->hsize - i;
+
+    while (lzwP->hashTable[i].fcode != fcode &&
+           lzwP->hashTable[i].fcode >= 0) {
+        i -= disp;
+        if (i < 0)
+            i += lzwP->hsize;
+    }
+
+    if (lzwP->hashTable[i].fcode == fcode) {
+        /* Found fcode in hash table */
+        *foundP = TRUE;
+        *codeP = lzwP->hashTable[i].ent;
+    } else {
+        /* Found where it _should_ be (but it's not) with primary hash */
+        *foundP = FALSE;
+        *hashP = i;
+    }
+}
+
+
+
+static void
+lzw_encodePixel(lzwCompressor * const lzwP,
+                unsigned int    const gifPixel) {
+
+    bool found;
+    unsigned short code;
+    unsigned int hash;
+        /* Index into hash table where the value should go */
+    
+    assert(gifPixel < 256);
+
+    if (!lzwP->buildingString) {
+        /* Start a new string with just this pixel */
+        lzwP->stringSoFar = gifPixel;
+        lzwP->buildingString = TRUE;
+    } else {
+        stringCode const fcode =
+            ((stringCode) gifPixel << BITS) + lzwP->stringSoFar;
+            /* The encoding of the string we've already recognized plus the
+               instant pixel, to be looked up in the hash of known strings.
+            */
+    
+        lookupInHash(lzwP, gifPixel, fcode, &found, &code, &hash);
+
+        if (found)
+            /* With this new pixel, it is still a known string; 'code' is
+               its code
+            */
+            lzwP->stringSoFar = code;
+        else {
+            /* It's no longer a known string.  Output the code for the
+               known prefix of the string, thus defining a new string
+               code for possible later use.  Warning:
+               lzwOutputCurrentString() does more than you think. 
+            */
+
+            lzwP->hashTable[hash].ent   = lzwP->nextUnusedCode;
+            lzwP->hashTable[hash].fcode = fcode;
+
+            lzwOutputCurrentString(lzwP);
+
+            /* This singleton pixel starts the next string */
+            lzwP->stringSoFar = gifPixel;
+        }
+    }
+}
+
+
+
+/*
+ * compress stdin to stdout
+ *
+ * Algorithm:  use open addressing double hashing (no chaining) on the
+ * prefix code / next character combination.  We do a variant of Knuth's
+ * algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
+ * secondary probe.  Here, the modular division first probe is gives way
+ * to a faster exclusive-or manipulation.  Also do block compression with
+ * an adaptive reset, whereby the code table is cleared when the compression
+ * ratio decreases, but after the table fills.  The variable-length output
+ * codes are re-sized at this point, and a special CLEAR code is generated
+ * for the decompressor.  Late addition:  construct the table according to
+ * file size for noticeable speed improvement on small files.  Please direct
+ * questions about this implementation to ames!jaw.
+ */
+
+static void
+writePixelUncompressed(lzwCompressor * const lzwP,
+                       unsigned int    const gifPixel) {
+                      
+    lzwP->stringSoFar = gifPixel;
+    lzwOutputCurrentString(lzwP);
+
+}    
+
+static void
+writeRaster(struct pam *  const pamP,
+            rowReader *   const rowReaderP,
+            unsigned int  const alphaPlane,
+            unsigned int  const alphaThreshold,
+            struct cmap * const cmapP, 
+            unsigned int  const initBits,
+            FILE *        const ofP,
+            bool          const lzw) {
+/*----------------------------------------------------------------------------
+   Write the raster to file 'ofP'.
+
+   Get the raster to write from 'rowReaderP', which gives tuples whose
+   format is described by 'pamP'.
+
+   Use the colormap 'cmapP' to generate the raster ('rowReaderP' gives
+   pixel values as RGB samples; the GIF raster is colormap indices).
+
+   Write the raster using LZW compression, or uncompressed depending
+   on 'lzw'.
+-----------------------------------------------------------------------------*/
+    lzwCompressor * lzwP;
+    tuple * tuplerow;
+    unsigned int nRowsDone;
+        /* Number of rows we have read so far from the the input (the
+           last of which is the one we're working on now).  Note that
+           in case of interlace, this is not the same thing as the row
+           number of the current row.
+        */
+    
+    lzwP = lzw_create(ofP, initBits, lzw, pamP->height * pamP->width);
+
+    tuplerow = pnm_allocpamrow(pamP);
+
+    lzw_clearBlock(lzwP);
+
+    nRowsDone = 0;
+    
+    while (nRowsDone < pamP->height) {
+        unsigned int col;
+
+        rowReader_read(rowReaderP, tuplerow);
+
+        for (col = 0; col < pamP->width; ++col) {
+            unsigned int const colorIndex =
+                gifPixel(pamP, tuplerow[col], alphaPlane, alphaThreshold,
+                         cmapP);
+            
+                /* The value for the pixel in the GIF image.  I.e. the colormap
+                   index.
+                */
+            if (lzw)
+                lzw_encodePixel(lzwP, colorIndex);
+            else
+                writePixelUncompressed(lzwP, colorIndex);    
+        }
+        ++nRowsDone;
+    }
+    /* Gif is no good with no pixels; fortunately, that's impossible: */
+    assert(nRowsDone > 0);
+
+    lzw_flush(lzwP);
+
+    pnm_freepamrow(tuplerow);
+    
+    lzw_destroy(lzwP);
+}
+
+
+
+static void
+writeGlobalColorMap(FILE *              const ofP,
+                    const struct cmap * const cmapP,
+                    unsigned int        const bitsPerPixel) {
+/*----------------------------------------------------------------------------
+  Write out the Global Color Map
+
+  Note that the Global Color Map is always a power of two colors
+  in size, but *cmapP could be smaller than that.  So we pad with
+  black.
+-----------------------------------------------------------------------------*/
+    unsigned int const colorMapSize = 1 << bitsPerPixel;
+
+    struct pam pam;
+    unsigned int i;
+    tuple tupleRgb255;
+
+    if (verbose)
+        pm_message("Writing %u-entry global colormap for %u colors",
+                   colorMapSize, cmapP->cmapSize);
+
+    pam = cmapP->pam;
+    pam.size = PAM_STRUCT_SIZE(allocation_depth);
+    pam.len = pam.size;
+    pnm_setminallocationdepth(&pam, 3);
+
+    tupleRgb255 = pnm_allocpamtuple(&pam);
+
+    for (i = 0; i < colorMapSize; ++i) {
+        if (i < cmapP->cmapSize) {
+            tuple const color = cmapP->color[i];
+
+            assert(i < cmapP->cmapSize);
+
+            pnm_scaletuple(&pam, tupleRgb255, color, 255);
+            pnm_maketuplergb(&pam, tupleRgb255);
+
+            fputc(tupleRgb255[PAM_RED_PLANE], ofP);
+            fputc(tupleRgb255[PAM_GRN_PLANE], ofP);
+            fputc(tupleRgb255[PAM_BLU_PLANE], ofP);
+        } else {
+            fputc(0, ofP);
+            fputc(0, ofP);
+            fputc(0, ofP);
+        }
+    }
+    pnm_freepamtuple(tupleRgb255);
+}
+        
+
+
+static void
+writeGifHeader(FILE *              const ofP,
+               unsigned int        const width,
+               unsigned int        const height, 
+               unsigned int        const background, 
+               unsigned int        const bitsPerPixel,
+               const struct cmap * const cmapP,
+               char                const comment[],
+               float               const aspect) {
+
+    unsigned int const resolution = bitsPerPixel;
+
+    unsigned char b;
+
+    /* Write the Magic header */
+    if (cmapP->haveTransparent || comment || aspect != 1.0 )
+        fwrite("GIF89a", 1, 6, ofP);
+    else
+        fwrite("GIF87a", 1, 6, ofP);
+
+    /* Write out the screen width and height */
+    Putword(width,  ofP);
+    Putword(height, ofP);
+
+    /* Indicate that there is a global color map */
+    b = 0x80;       /* Yes, there is a color map */
+
+    /* OR in the resolution */
+    b |= (resolution - 1) << 4;
+
+    /* OR in the Bits per Pixel */
+    b |= (bitsPerPixel - 1);
+
+    /* Write it out */
+    fputc(b, ofP);
+
+    /* Write out the Background color */
+    assert((unsigned char)background == background);
+    fputc(background, ofP);
+
+    {
+        int const aspectValue = aspect == 1.0 ? 0 : ROUND(aspect * 64) - 15;
+        assert(0 <= aspectValue && aspectValue <= 255); 
+        fputc(aspectValue, ofP);
+    }
+    writeGlobalColorMap(ofP, cmapP, bitsPerPixel);
+
+    if (cmapP->haveTransparent) 
+        writeTransparentColorIndexExtension(ofP, cmapP->transparent);
+
+    if (comment)
+        writeCommentExtension(ofP, comment);
+}
+
+
+
+static void
+writeImageHeader(FILE *       const ofP,
+                 unsigned int const leftOffset,
+                 unsigned int const topOffset,
+                 unsigned int const gWidth,
+                 unsigned int const gHeight,
+                 bool         const gInterlace,
+                 unsigned int const initCodeSize) {
+
+    Putword(leftOffset, ofP);
+    Putword(topOffset,  ofP);
+    Putword(gWidth,     ofP);
+    Putword(gHeight,    ofP);
+
+    /* Write out whether or not the image is interlaced */
+    if (gInterlace)
+        fputc(0x40, ofP);
+    else
+        fputc(0x00, ofP);
+
+    /* Write out the initial code size */
+    fputc(initCodeSize, ofP);
+}
+
+
+
+static void
+reportImageInfo(bool         const interlace,
+                unsigned int const background,
+                unsigned int const bitsPerPixel) {
+
+    if (verbose) {
+        if (interlace)
+            pm_message("interlaced");
+        else
+            pm_message("not interlaced");
+        pm_message("Background color index = %u", background);
+        pm_message("%u bits per pixel", bitsPerPixel);
+    }
+}
+
+
+
+static void
+gifEncode(struct pam *  const pamP,
+          FILE *        const ofP, 
+          pm_filepos    const rasterPos,
+          bool          const gInterlace,
+          int           const background, 
+          unsigned int  const bitsPerPixel,
+          struct cmap * const cmapP,
+          char          const comment[],
+          float         const aspect,
+          bool          const lzw) {
+
+    unsigned int const leftOffset = 0;
+    unsigned int const topOffset  = 0;
+
+    unsigned int const initCodeSize = bitsPerPixel <= 1 ? 2 : bitsPerPixel;
+        /* The initial code size */
+
+    sample const alphaThreshold = (pamP->maxval + 1) / 2;
+        /* Levels below this in the alpha plane indicate transparent
+           pixels in the output image.
+        */
+
+    unsigned int const alphaPlane = pamAlphaPlane(pamP);
+
+    rowReader * rowReaderP;
+
+    reportImageInfo(gInterlace, background, bitsPerPixel);
+
+    if (pamP->width > 65535)
+        pm_error("Image width %u too large for GIF format.  (Max 65535)",
+                 pamP->width);
+     
+    if (pamP->height > 65535)
+        pm_error("Image height %u too large for GIF format.  (Max 65535)",
+                 pamP->height);
+
+    writeGifHeader(ofP, pamP->width, pamP->height, background,
+                   bitsPerPixel, cmapP, comment, aspect);
+
+    /* Write an Image separator */
+    fputc(',', ofP);
+
+    writeImageHeader(ofP, leftOffset, topOffset, pamP->width, pamP->height,
+                     gInterlace, initCodeSize);
+
+    rowReaderP = rowReader_create(pamP, rasterPos, gInterlace);
+
+    /* Write the actual raster */
+
+    writeRaster(pamP, rowReaderP, alphaPlane, alphaThreshold,
+                cmapP, initCodeSize + 1, ofP, lzw);
+
+    rowReader_destroy(rowReaderP);
+
+    /* Write out a zero length data block (to end the series) */
+    fputc(0, ofP);
+
+    /* Write the GIF file terminator */
+    fputc(';', ofP);
+}
+
+
+
+static void
+reportTransparent(struct cmap * const cmapP) {
+
+    if (verbose) {
+        if (cmapP->haveTransparent) {
+            tuple const color = cmapP->color[cmapP->transparent];
+            pm_message("Color %u (%lu, %lu, %lu) is transparent",
+                       cmapP->transparent,
+                       color[PAM_RED_PLANE],
+                       color[PAM_GRN_PLANE],
+                       color[PAM_BLU_PLANE]);
+        } else
+            pm_message("No transparent color");
+    }
+}
+
+
+
+static void
+computeTransparent(char          const colorarg[], 
+                   bool          const usingFakeTrans,
+                   unsigned int  const fakeTransparent,
+                   struct cmap * const cmapP) {
+/*----------------------------------------------------------------------------
+   Figure out the color index (index into the colormap) of the color
+   that is to be transparent in the GIF.
+
+   colorarg[] is the string that specifies the color the user wants to
+   be transparent (e.g. "red", "#fefefe").  Its maxval is the maxval
+   of the colormap.  'cmap' is the full colormap except that its
+   'transparent' component isn't valid.
+
+   colorarg[] is a standard Netpbm color specification, except that
+   may have a "=" prefix, which means it specifies a particular exact
+   color, as opposed to without the "=", which means "the color that
+   is closest to this and actually in the image."
+
+   colorarg[] null means the color didn't ask for a particular color
+   to be transparent.
+
+   Establish no transparent color if colorarg[] specifies an exact
+   color and that color is not in the image.  Also issue an
+   informational message.
+
+   'usingFakeTrans' means pixels will be transparent because of something
+   other than their foreground color, and 'fakeTransparent' is the
+   color map index for transparent colors.
+-----------------------------------------------------------------------------*/
+    if (colorarg) {
+        const char * colorspec;
+        bool exact;
+        tuple transcolor;
+        bool found;
+        int colorindex;
+        
+        if (colorarg[0] == '=') {
+            colorspec = &colorarg[1];
+            exact = TRUE;
+        } else {
+            colorspec = colorarg;
+            exact = FALSE;
+        }
+
+        transcolor = pnm_parsecolor(colorspec, cmapP->pam.maxval);
+        pnm_lookuptuple(&cmapP->pam, cmapP->tuplehash, transcolor, &found,
+                        &colorindex);
+        
+        if (found) {
+            cmapP->haveTransparent = TRUE;
+            cmapP->transparent = colorindex;
+        } else if (!exact) {
+            cmapP->haveTransparent = TRUE;
+            cmapP->transparent = closestColor(transcolor, &cmapP->pam, cmapP);
+        } else {
+            cmapP->haveTransparent = FALSE;
+            pm_message("Warning: specified transparent color "
+                       "does not occur in image.");
+        }
+    } else if (usingFakeTrans) {
+        cmapP->haveTransparent = TRUE;
+        cmapP->transparent = fakeTransparent;
+    } else
+        cmapP->haveTransparent = FALSE;
+
+    reportTransparent(cmapP);
+}
+
+
+
+static unsigned int
+sortOrderColor(tuple const tuple) {
+
+    return ((tuple[PAM_RED_PLANE] * MAXCMAPSIZE) +
+            tuple[PAM_GRN_PLANE]) * MAXCMAPSIZE +
+           tuple[PAM_BLU_PLANE];
+}
+
+
+
+#ifndef LITERAL_FN_DEF_MATCH
+static qsort_comparison_fn sortCompareColor;
+#endif
+
+static int
+sortCompareColor(const void * const entry1P,
+                 const void * const entry2P) {
+
+    struct tupleint * const * const tupleint1PP = entry1P;
+    struct tupleint * const * const tupleint2PP = entry2P;
+
+    return (sortOrderColor((*tupleint1PP)->tuple) 
+            - sortOrderColor((*tupleint2PP)->tuple));
+}
+
+
+
+#ifndef LITERAL_FN_DEF_MATCH
+static qsort_comparison_fn sortCompareGray;
+#endif
+
+static int
+sortCompareGray(const void * const entry1P,
+                const void * const entry2P){
+
+    struct tupleint * const * const tupleint1PP = entry1P;
+    struct tupleint * const * const tupleint2PP = entry2P;
+
+    return ((*tupleint1PP)->tuple[0] - (*tupleint2PP)->tuple[0]);
+}
+
+
+
+static void
+sortTupletable(struct pam * const mapPamP,
+               unsigned int const colors,
+               tupletable   const tuplefreq) {
+/*----------------------------------------------------------------------------
+   Sort the colormap *cmapP.
+
+   Sort the colormap by red intensity, then by green intensity,
+   then by blue intensity.
+-----------------------------------------------------------------------------*/
+
+    pm_message("sorting colormap");
+
+    if (mapPamP->depth < 3)
+        qsort(tuplefreq, colors, sizeof(tuplefreq[0]), sortCompareGray);
+    else
+        qsort(tuplefreq, colors, sizeof(tuplefreq[0]), sortCompareColor); 
+
+}
+
+
+
+static void
+addToColormap(struct cmap *  const cmapP, 
+              const char *   const colorspec, 
+              unsigned int * const newIndexP) {
+/*----------------------------------------------------------------------------
+  Add a new entry to the colormap.  Make the color that specified by
+  'colorspec', and return the index of the new entry as *newIndexP.
+
+  'colorspec' is a color specification given by the user, e.g.
+  "red" or "rgb:ff/03/0d".  The maxval for this color specification is
+  that for the colormap *cmapP.
+-----------------------------------------------------------------------------*/
+    tuple const transcolor = pnm_parsecolor(colorspec, cmapP->pam.maxval);
+
+    unsigned int const colorIndex = cmapP->cmapSize++;
+
+    cmapP->color[colorIndex] = pnm_allocpamtuple(&cmapP->pam);
+
+    if (cmapP->pam.depth < 3) {
+        if (!pnm_rgbtupleisgray(transcolor))
+            pm_error("Image is grayscale, but color '%s' is not gray.  "
+                     "It is (%lu, %lu, %lu)",
+                     colorspec,
+                     transcolor[PAM_RED_PLANE],
+                     transcolor[PAM_GRN_PLANE],
+                     transcolor[PAM_BLU_PLANE]);
+        else
+            cmapP->color[colorIndex][0] = transcolor[0];
+    } else {
+        pnm_assigntuple(&cmapP->pam, cmapP->color[colorIndex], transcolor);
+    }
+    *newIndexP = colorIndex;
+}
+
+
+
+static void
+colormapFromFile(char               const filespec[],
+                 unsigned int       const maxcolors,
+                 tupletable *       const tupletableP, 
+                 struct pam *       const mapPamP,
+                 unsigned int *     const colorCountP) {
+/*----------------------------------------------------------------------------
+   Read a colormap from the Netpbm file filespec[].  Return a
+   tupletable of the colors in it (which is practically a colormap) as
+   *tupletableP and the format of those tuples as *mapPamP.  Return
+   the number of colors as *colorsCountP.
+-----------------------------------------------------------------------------*/
+    FILE * mapfileP;
+    tuple ** colors;
+    unsigned int colorCount;
+
+    mapfileP = pm_openr(filespec);
+    colors = pnm_readpam(mapfileP, mapPamP, PAM_STRUCT_SIZE(tuple_type));
+    pm_close(mapfileP);
+
+    pm_message("computing other colormap ...");
+    
+    *tupletableP = 
+        pnm_computetuplefreqtable(mapPamP, colors, maxcolors, &colorCount);
+
+    *colorCountP = colorCount;
+
+    pnm_freepamarray(colors, mapPamP); 
+}
+
+
+
+static void
+readAndValidateColormapFromFile(char           const filename[],
+                                unsigned int   const maxcolors,
+                                tupletable *   const tuplefreqP, 
+                                struct pam *   const mapPamP,
+                                unsigned int * const colorCountP,
+                                unsigned int   const nInputComp,
+                                sample         const inputMaxval) {
+/*----------------------------------------------------------------------------
+   Read the colormap from a separate colormap file named filename[],
+   and make sure it's consistent with an image with 'nInputComp'
+   color components (e.g. 3 for RGB) and a maxval of 'inputMaxval'.
+-----------------------------------------------------------------------------*/
+    colormapFromFile(filename, maxcolors, tuplefreqP, mapPamP, colorCountP);
+
+    if (mapPamP->depth != nInputComp)
+        pm_error("Depth of map file (%u) does not match number of "
+                 "color components in input file (%u)",
+                 mapPamP->depth, nInputComp);
+    if (mapPamP->maxval != inputMaxval)
+        pm_error("Maxval of map file (%lu) does not match maxval of "
+                 "input file (%lu)", mapPamP->maxval, inputMaxval);
+}
+
+
+
+static void
+computeColormapBw(struct pam *   const pamP,
+                  struct pam *   const mapPamP,
+                  unsigned int * const colorCountP,
+                  tupletable   * const tuplefreqP) {
+/*----------------------------------------------------------------------------
+  Shortcut for black and white (e.g. PBM).  We know that there are
+  only two colors.  Users who know that only one color is present in
+  the image should specify -sort at the command line.  Example:
+
+   $ pbmmake -w 600 400 | pamtogif -sort > canvas.gif
+-----------------------------------------------------------------------------*/
+    tupletable const colormap = pnm_alloctupletable(pamP, 2);
+    
+    *mapPamP = *pamP;
+    mapPamP->depth = 1;
+
+    colormap[0]->value = 1;
+    colormap[0]->tuple[0] = PAM_BLACK;
+    colormap[1]->value = 1;
+    colormap[1]->tuple[0] = PAM_BW_WHITE;
+    
+    *tuplefreqP  = colormap;
+    *colorCountP = 2;
+}
+  
+    
+
+static void
+computeColormapFromInput(struct pam *   const pamP,
+                         unsigned int   const maxcolors,
+                         unsigned int   const nInputComp,
+                         struct pam *   const mapPamP,
+                         unsigned int * const colorCountP,
+                         tupletable *   const tuplefreqP) {
+    
+    tupletable tuplefreq;
+
+    pm_message("computing colormap...");
+
+    tuplefreq = pnm_computetuplefreqtable3(
+        pamP, NULL, maxcolors, nInputComp, pamP->maxval, colorCountP);
+
+    *mapPamP = *pamP;
+    mapPamP->depth = nInputComp;
+
+    *tuplefreqP = tuplefreq;
+}
+
+
+
+static void
+computeLibnetpbmColormap(struct pam *   const pamP,
+                         bool           const haveAlpha,
+                         const char *   const mapfile,
+                         tuple *        const color,
+                         tuplehash *    const tuplehashP,
+                         struct pam *   const mapPamP,
+                         unsigned int * const colorCountP,
+                         bool           const sort) {
+/*----------------------------------------------------------------------------
+   Compute a colormap, libnetpbm style, for the image described by
+   'pamP', which is positioned to the raster.
+
+   If 'mapfile' is non-null, Use the colors in that (Netpbm) file for
+   the color map instead of the colors in 'pamP'.
+
+   Return the colormap as color[] and *tuplehashP.  Return the format
+   of those tuples as *mapPamP.
+
+   The tuples of the color map have a meaningful depth of 1 (grayscale) or 3
+   (color) and *mapPamP reflects that.
+
+   While we're at it, count the colors and validate that there aren't
+   too many.  Return the count as *colorCountP.  In determining if there are
+   too many, allow one slot for a fake transparency color if 'haveAlpha'
+   is true.  If there are too many, issue an error message and abort the
+   program.
+
+   'sort' means to sort the colormap by red intensity, then by green
+   intensity, then by blue intensity, as opposed to arbitrary order.
+-----------------------------------------------------------------------------*/
+    unsigned int const maxcolors = haveAlpha ? MAXCMAPSIZE - 1 : MAXCMAPSIZE;
+        /* The most colors we can tolerate in the image.  If we have
+           our own made-up entry in the colormap for transparency, it
+           isn't included in this count.
+        */
+    unsigned int const nInputComp = haveAlpha ? pamP->depth - 1 : pamP->depth;
+        /* Number of color components (not alpha) in the input image */
+
+    unsigned int i;
+    tupletable tuplefreq;
+    unsigned int colorCount;
+
+    if (mapfile)
+        readAndValidateColormapFromFile(mapfile, maxcolors, &tuplefreq,
+                                        mapPamP, &colorCount,
+                                        nInputComp, pamP->maxval);
+    else if (nInputComp == 1 && pamP->maxval == 1 && !sort &&
+             pamP->height * pamP->width > 1)
+        computeColormapBw(pamP, mapPamP, &colorCount, &tuplefreq);
+    else
+        computeColormapFromInput(pamP, maxcolors, nInputComp, 
+                                 mapPamP, &colorCount, &tuplefreq);
+    
+    if (tuplefreq == NULL)
+        pm_error("too many colors - try doing a 'pnmquant %u'", maxcolors);
+
+    pm_message("%u colors found", colorCount);
+
+    if (sort)
+        sortTupletable(mapPamP, colorCount, tuplefreq);
+
+    for (i = 0; i < colorCount; ++i) {
+        color[i] = pnm_allocpamtuple(mapPamP);
+        pnm_assigntuple(mapPamP, color[i], tuplefreq[i]->tuple);
+    }
+
+    /* And make a hash table for fast lookup. */
+    *tuplehashP =
+        pnm_computetupletablehash(mapPamP, tuplefreq, colorCount);
+
+    *colorCountP = colorCount;
+
+    pnm_freetupletable(mapPamP, tuplefreq);
+}
+
+
+
+static void
+destroyCmap(struct cmap * const cmapP) {
+
+    unsigned int colorIndex;
+    
+    for (colorIndex = 0; colorIndex < cmapP->cmapSize; ++colorIndex)
+        pnm_freepamtuple(cmapP->color[colorIndex]);
+
+    pnm_destroytuplehash(cmapP->tuplehash);
+}
+
+
+
+int
+main(int argc, char *argv[]) {
+    struct cmdlineInfo cmdline;
+    FILE * ifP;
+    struct pam pam;
+    unsigned int bitsPerPixel;
+    pm_filepos rasterPos;
+
+    struct cmap cmap;
+        /* The colormap, with all its accessories */
+    unsigned int fakeTransparent;
+        /* colormap index of the fake transparency color we're using to
+           implement the alpha mask.  Undefined if we're not doing an alpha
+           mask.
+        */
+    
+    pnm_init(&argc, argv);
+    
+    parseCommandLine(argc, argv, &cmdline);
+    
+    verbose = cmdline.verbose;
+    
+    ifP = pm_openr_seekable(cmdline.input_filespec);
+    
+    pnm_readpaminit(ifP, &pam, PAM_STRUCT_SIZE(tuple_type));
+    
+    pm_tell2(ifP, &rasterPos, sizeof(rasterPos));
+    
+    computeLibnetpbmColormap(&pam, !!pamAlphaPlane(&pam), cmdline.mapfile, 
+                             cmap.color, &cmap.tuplehash,
+                             &cmap.pam, &cmap.cmapSize, cmdline.sort);
+    
+    assert(cmap.pam.maxval == pam.maxval);
+
+    if (pamAlphaPlane(&pam)) {
+        /* Add a fake entry to the end of the colormap for transparency.  
+           Make its color black. 
+        */
+        addToColormap(&cmap, cmdline.alphacolor, &fakeTransparent);
+    }
+
+    bitsPerPixel = cmap.cmapSize == 1 ? 1 : nSignificantBits(cmap.cmapSize-1);
+
+    computeTransparent(cmdline.transparent,
+                       !!pamAlphaPlane(&pam), fakeTransparent, &cmap);
+
+    /* All set, let's do it. */
+    gifEncode(&pam, stdout, rasterPos,
+              cmdline.interlace, 0, bitsPerPixel, &cmap, cmdline.comment,
+              cmdline.aspect, !cmdline.nolzw);
+    
+    destroyCmap(&cmap);
+
+    pm_close(ifP);
+    pm_close(stdout);
+    
+    return 0;
+}
+
+
+
+/*============================================================================
+  Original version, named 'ppmgif' was by Jef Poskanzer in 1989, based
+  on GIFENCOD by David Rowley <mgardi@watdscu.waterloo.edu>.A Lempel-Zim
+  compression based on "compress".
+
+  Switched to use libnetpbm PAM facilities (ergo process PAM images)
+  and renamed 'pamtogif' by Bryan Henderson November 2006.
+
+  The non-LZW GIF generation stuff was adapted from the Independent
+  JPEG Group's djpeg on 2001.09.29.  In 2006.12 the output subroutines
+  were rewritten; now no uncompressed output subroutines are derived from
+  the Independent JPEG Group's source code.
+  
+  2007.01  Changed sort routine to qsort.  (afu)
+  2007.03  Implemented variable hash table size, PBM color table
+           shortcut and "-aspect" command line option.   (afu)
+
+ 
+  Copyright (C) 1989 by Jef Poskanzer.
+ 
+  Permission to use, copy, modify, and distribute this software and its
+  documentation for any purpose and without fee is hereby granted, provided
+  that the above copyright notice appear in all copies and that both that
+  copyright notice and this permission notice appear in supporting
+  documentation.  This software is provided "as is" without express or
+  implied warranty.
+ 
+  The Graphics Interchange Format(c) is the Copyright property of
+  CompuServe Incorporated.  GIF(sm) is a Service Mark property of
+  CompuServe Incorporated.
+============================================================================*/
+
diff --git a/converter/other/pamtohdiff.c b/converter/other/pamtohdiff.c
index 0e1ff00f..2d5f6a61 100644
--- a/converter/other/pamtohdiff.c
+++ b/converter/other/pamtohdiff.c
@@ -16,6 +16,7 @@
 #include <string.h>
 #include <stdio.h>
 
+#include "pm_c_util.h"
 #include "pam.h"
 #include "shhopt.h"
 
diff --git a/converter/other/pamtohtmltbl.c b/converter/other/pamtohtmltbl.c
index 0afbfea0..5335ff9f 100644
--- a/converter/other/pamtohtmltbl.c
+++ b/converter/other/pamtohtmltbl.c
@@ -2,16 +2,17 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include "pam.h"
-#include "shhopt.h"
+#include "pm_c_util.h"
 #include "mallocvar.h"
+#include "shhopt.h"
+#include "pam.h"
 
 struct cmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    const char *inputFilespec;  /* '-' if stdin */
-    const char *transparent;  /* NULL if none */
+    const char * inputFileName;  /* '-' if stdin */
+    const char * transparent;  /* NULL if none */
     unsigned int verbose;
 };
 
@@ -19,8 +20,8 @@ struct cmdlineInfo {
 
 
 static void
-parseCommandLine ( int argc, char ** argv,
-                   struct cmdlineInfo *cmdlineP ) {
+parseCommandLine(int argc, char ** argv,
+                 struct cmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    parse program command line described in Unix standard form by argc
    and argv.  Return the information in the options as *cmdlineP.  
@@ -31,7 +32,7 @@ parseCommandLine ( int argc, char ** argv,
    Note that the strings we return are stored in the storage that
    was passed to us as the argv array.  We also trash *argv.
 -----------------------------------------------------------------------------*/
-    optEntry *option_def = malloc(100*sizeof(optEntry));
+    optEntry * option_def;
         /* Instructions to optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
@@ -40,6 +41,8 @@ parseCommandLine ( int argc, char ** argv,
 
     unsigned int transparentSpec;
 
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
     option_def_index = 0;   /* incremented by OPTENT3 */
     OPTENT3(0, "verbose",     OPT_FLAG,   NULL,                  
             &cmdlineP->verbose,       0 );
@@ -58,9 +61,9 @@ parseCommandLine ( int argc, char ** argv,
         cmdlineP->transparent = NULL;
 
     if (argc-1 < 1)
-        cmdlineP->inputFilespec = "-";
+        cmdlineP->inputFileName = "-";
     else if (argc-1 == 1)
-        cmdlineP->inputFilespec = argv[1];
+        cmdlineP->inputFileName = argv[1];
     else
         pm_error("Too many arguments.  Program takes at most one argument: "
                  "input file name");
@@ -135,31 +138,30 @@ findSameColorRectangle(struct pam *   const pamP,
     mx=0; my=0;
     cnx = pamP->width - col; cny = pamP->height - row;
 
-    for (i=0; (!mx)||(!my); i++) {
+    for (i = 0; !mx || !my; ++i) {
         int j;
-        /*fprintf(stderr,"\n[%d]",i);*/
-        for (j=0; j<=i; j++) {
+        for (j = 0; j <= i; ++j) {
             if (!my) {
-                if (i>=cny) 
-                    my=cny;
-                else
-                    if (((!mx) || (j<mx)) && (j < cnx)) {
-                        /*fprintf(stderr,"%d/%d ",j,i);*/
+                if (i >= cny) 
+                    my = cny;
+                else {
+                    if ((!mx || j < mx) && (j < cnx)) {
                         if (!pnm_tupleequal(pamP, tuples[row+i][col+j],
                                             rectangleColor)) 
                             my = i;
                     }
+                }
             }
             if (!mx) {
-                if (i>=cnx) 
-                    mx=cnx;
-                else
-                    if (((!my) || (j<my)) && (j < cny)) {
-                        /*fprintf(stderr,"%d/%d ",i,j);*/
+                if (i >= cnx) 
+                    mx = cnx;
+                else {
+                    if ((!my || (j < my)) && (j < cny)) {
                         if (!pnm_tupleequal(pamP, tuples[row+j][col+i],
                                             rectangleColor)) 
                             mx = i;
                     }
+                }
             }
         }
     }
@@ -170,7 +172,8 @@ findSameColorRectangle(struct pam *   const pamP,
 
 
 static bool **
-allocOutputtedArray(unsigned int const width, unsigned int const height) {
+allocOutputtedArray(unsigned int const width,
+                    unsigned int const height) {
 
     bool ** outputted;
     unsigned int row;
@@ -180,15 +183,9 @@ allocOutputtedArray(unsigned int const width, unsigned int const height) {
         pm_error("Unable to allocate space for 'outputted' array");
 
     for (row = 0; row < height; ++row) {
-        unsigned int col;
-
         MALLOCARRAY(outputted[row], width);
         if (outputted[row] == NULL)
             pm_error("Unable to allocate space for 'outputted' array");
-
-        for (col = 0; col < width ; ++col)
-          outputted[row][col] = FALSE;
-
     }
     return outputted;
 }
@@ -196,7 +193,8 @@ allocOutputtedArray(unsigned int const width, unsigned int const height) {
 
 
 static void
-freeOutputtedArray(bool ** const outputted, unsigned int const height) {
+freeOutputtedArray(bool **       const outputted,
+                   unsigned int const height) {
 
     unsigned int row;
 
@@ -206,13 +204,40 @@ freeOutputtedArray(bool ** const outputted, unsigned int const height) {
 
 
 
+                       
 static void
-markOutputted(bool ** const outputted,
+markNotOutputted(bool **      const outputted,
+                 unsigned int const upperLeftCol,
+                 unsigned int const upperLeftRow,
+                 unsigned int const width,
+                 unsigned int const height) {
+/*----------------------------------------------------------------------------
+   Mark every pixel in the specified rectangle as not having been output
+   yet.
+-----------------------------------------------------------------------------*/
+    unsigned int const lowerRightCol = upperLeftCol + width;
+    unsigned int const lowerRightRow = upperLeftRow + height;
+    unsigned int row;
+    
+    for (row = upperLeftRow; row < lowerRightRow; ++row) {
+        unsigned int col;
+        for (col = upperLeftCol; col < lowerRightCol; ++col) 
+            outputted[row][col] = FALSE;
+    }
+}
+
+
+
+static void
+markOutputted(bool **      const outputted,
               unsigned int const upperLeftCol,
               unsigned int const upperLeftRow,
               unsigned int const width,
               unsigned int const height) {
-
+/*----------------------------------------------------------------------------
+   Mark every pixel in the specified rectangle as having been output
+   already.
+-----------------------------------------------------------------------------*/
     unsigned int const lowerRightCol = upperLeftCol + width;
     unsigned int const lowerRightRow = upperLeftRow + height;
     unsigned int row;
@@ -232,16 +257,19 @@ main(int argc, char **argv) {
     FILE * ifP;
     struct pam inpam;
     tuple ** tuples;
-    int row;
+    unsigned int row;
     unsigned int rectWidth, rectHeight;
     bool ** outputted;
+        /* Two dimensional array.  outputted[ROW][COL] means the pixel
+           at row ROW, column COL has already been outputted.
+        */
     tuple transparentColor;
 
     pnm_init(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
-    ifP = pm_openr(cmdline.inputFilespec);
+    ifP = pm_openr(cmdline.inputFileName);
 
     tuples = pnm_readpam(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
 
@@ -259,8 +287,12 @@ main(int argc, char **argv) {
 
     printf("<TABLE WIDTH=%d HEIGHT=%d BORDER=0 CELLSPACING=0 CELLPADDING=0>\n",
            inpam.width, inpam.height);
+
+    markNotOutputted(outputted, 0, 0, inpam.width, inpam.height);
+        /* No pixel has been outputted yet */
+
     for (row = 0; row < inpam.height; ++row) {
-        int col;
+        unsigned int col;
         printf("<TR>\n");
         pripix(&inpam, tuples[row][0], 1, 1, transparentColor); 
         markOutputted(outputted, 0, row, 1, 1);
@@ -270,7 +302,7 @@ main(int argc, char **argv) {
                 findSameColorRectangle(&inpam, tuples, row, col, 
                                        &rectWidth, &rectHeight);
                 if (cmdline.verbose)
-                    pm_message("[%d/%d] [%d/%d]",
+                    pm_message("[%u/%u] [%u/%u]",
                                col, row, rectWidth, rectHeight);
                 pripix(&inpam, tuples[row][col], rectWidth, rectHeight, 
                        transparentColor);
@@ -286,5 +318,5 @@ main(int argc, char **argv) {
     pnm_freepamarray(tuples, &inpam);
     freeOutputtedArray(outputted, inpam.height);
 
-    exit(0);
+    return 0;
 }
diff --git a/converter/other/pamtompfont.c b/converter/other/pamtompfont.c
new file mode 100644
index 00000000..ba170fef
--- /dev/null
+++ b/converter/other/pamtompfont.c
@@ -0,0 +1,182 @@
+/*----------------------------------------------------------------------------
+                               pamtompfont
+------------------------------------------------------------------------------
+  Part of the Netpbm package.
+
+  Convert a PAM image to an Mplayer bitmap font.
+
+  It is obvious that this format was designed to be an image format and
+  adopted by Mplayer for it's fonts (before Mplayer got the ability to
+  use Freetype to read standard font formats such as TrueType).  But
+  I have no idea what the format was originally.
+
+  In the Mplayer font subset of the format, the image is always grayscale
+  (one byte per pixel) with no palette.
+
+  By Bryan Henderson, San Jose CA 2008.05.18
+
+  Contributed to the public domain by its author.
+-----------------------------------------------------------------------------*/
+
+#include <string.h>
+#include <assert.h>
+
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "shhopt.h"
+#include "pm.h"
+#include "pam.h"
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * inputFilename;
+};
+
+
+static void
+parseCommandLine(int argc, char ** argv,
+                 struct cmdlineInfo *cmdlineP) {
+/*----------------------------------------------------------------------------
+   Note that the file spec array we return is stored in the storage that
+   was passed to us as the argv array.
+-----------------------------------------------------------------------------*/
+    optEntry *option_def;
+        /* Instructions to OptParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+
+    OPTENTINIT;
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (argc-1 == 0) 
+        cmdlineP->inputFilename = "-";
+    else if (argc-1 != 1)
+        pm_error("Program takes zero or one argument (filename).  You "
+                 "specified %d", argc-1);
+    else
+        cmdlineP->inputFilename = argv[1];
+}
+
+
+
+static void
+validateInput(struct pam * const inpamP) {
+
+    /* The image format does provide for RGB images, but Mplayer doesn't
+       understand that format (and doesn't even recognize it as something
+       it doesn't understand)
+    */
+
+    if (inpamP->depth != 1)
+        pm_error("Input must have depth 1.  This image's depth is %u",
+                 inpamP->depth);
+}
+
+
+
+static void
+writeMpFontHeader(FILE *       const ofP,
+                  struct pam * const inpamP) {
+/*----------------------------------------------------------------------------
+   Write the 32 byte header.
+-----------------------------------------------------------------------------*/
+    fwrite("mhwanh", 1, 6, ofP);  /* Signature */
+
+    fputc(0, ofP);  /* pad */
+    fputc(0, ofP);  /* pad */
+
+    /* Write the old 16 bit width field.  Zero means use the 32 bit one
+       below instead.
+    */
+    pm_writebigshort(ofP, 0);
+
+    /* Height */
+    pm_writebigshort(ofP, inpamP->height);
+
+    /* Number of colors in palette.  Zero means not paletted image */
+    pm_writebigshort(ofP, 0);
+
+    {
+        unsigned int i;
+        for (i = 0; i < 14; ++i)
+            fputc(0, ofP);  /* pad */
+    }
+    /* Width */
+    pm_writebiglong(ofP, inpamP->width);
+}
+
+
+
+static void
+convertRaster(struct pam * const inpamP,
+              FILE *       const ofP) {
+            
+    tuple * tuplerow;
+    unsigned char * outrow;
+    unsigned int row;
+
+    assert(inpamP->depth == 1);
+
+    tuplerow = pnm_allocpamrow(inpamP);
+
+    MALLOCARRAY(outrow, inpamP->width);
+
+    if (outrow == NULL)
+        pm_error("Unable to allocate space for a %u-column output buffer",
+                 inpamP->width);
+
+    for (row = 0; row < inpamP->height; ++row) {
+        unsigned int col;
+
+        pnm_readpamrow(inpamP, tuplerow);
+
+        for (col = 0; col < inpamP->width; ++col) {
+            outrow[col] =
+                pnm_scalesample(tuplerow[col][0], inpamP->maxval, 255);
+        }
+        
+        fwrite(outrow, 1, inpamP->width, ofP);
+    }
+    free(outrow);
+    pnm_freepamrow(tuplerow);
+}
+
+
+
+int
+main(int argc, char *argv[]) {
+
+    struct cmdlineInfo cmdline;
+    FILE * ifP;
+    struct pam inpam;   /* Input PAM image */
+
+    pnm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFilename);
+
+    pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
+
+    validateInput(&inpam);
+
+    writeMpFontHeader(stdout, &inpam);
+
+    convertRaster(&inpam, stdout);
+
+    return 0;
+}
diff --git a/converter/other/pamtooctaveimg.c b/converter/other/pamtooctaveimg.c
new file mode 100644
index 00000000..b090281d
--- /dev/null
+++ b/converter/other/pamtooctaveimg.c
@@ -0,0 +1,241 @@
+/* ----------------------------------------------------------------------
+ *
+ * Convert a Netpbm file to the GNU Octave image format
+ * by Scott Pakin <scott+pbm@pakin.org>
+ *
+ * ----------------------------------------------------------------------
+ *
+ * Copyright information is at end of file.
+ * ----------------------------------------------------------------------
+ */
+
+#include <assert.h>
+#include <stdio.h>
+
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "nstring.h"
+#include "pam.h"
+#include "pammap.h"
+
+typedef struct {
+    double comp[3];
+        /* comp[0] is red; comp[1] is green; comp[2] is blue */
+} octaveColor;
+
+typedef struct {
+    struct pam pam;
+    unsigned int nColors;
+    tuplehash hash;
+    unsigned int paletteAlloc;
+        /* 'palette' array has this many slots allocated.  Only the first
+           'nColors' are meaningful.
+        */
+    octaveColor * palette;
+    double normalizer;
+        /* 1/maxval */
+} cmap;
+
+
+
+static void
+initCmap(cmap * const cmapP,
+         sample const maxval) {
+
+    cmapP->pam.size             = sizeof(cmapP->pam.size);
+    cmapP->pam.len              = PAM_STRUCT_SIZE(tuple_type);
+    cmapP->pam.depth            = 3;
+    cmapP->pam.maxval           = maxval;
+    cmapP->pam.bytes_per_sample = pnm_bytespersample(maxval);
+
+    cmapP->normalizer   = 1.0/maxval;
+    cmapP->nColors      = 0;
+    cmapP->paletteAlloc = 0;
+    cmapP->palette      = NULL;
+    cmapP->hash         = pnm_createtuplehash();
+}
+
+
+
+static void
+termCmap(cmap * const cmapP) {
+    pnm_destroytuplehash(cmapP->hash);
+
+    free(cmapP->palette);
+}
+
+
+
+static void
+findOrAddColor(tuple          const color,
+               cmap *         const cmapP,
+               unsigned int * const colorIndexP) {
+/*----------------------------------------------------------------------------
+  Return as *colorIndexP the colormap index of color 'color' in
+  colormap *cmapP.  If the color isn't in the map, give it a new
+  colormap index, put it in the colormap, and return that.
+-----------------------------------------------------------------------------*/
+    bool found;
+    int colorIndex;
+
+    pnm_lookuptuple(&cmapP->pam, cmapP->hash, color, &found, &colorIndex);
+
+    if (!found) {
+        bool fits;
+        unsigned int plane;
+
+        colorIndex = cmapP->nColors++;
+
+        if (cmapP->nColors > cmapP->paletteAlloc) {
+            cmapP->paletteAlloc *= 2;
+            REALLOCARRAY(cmapP->palette, cmapP->nColors);
+        }
+        for (plane = 0; plane < 3; ++plane)
+            cmapP->palette[colorIndex].comp[plane] =
+                color[plane] * cmapP->normalizer;
+
+        pnm_addtotuplehash(&cmapP->pam, cmapP->hash, color, colorIndex, &fits);
+
+        if (!fits)
+            pm_error("Out of memory constructing color map, on %uth color",
+                     cmapP->nColors);
+    }
+    *colorIndexP = colorIndex;
+}
+
+
+
+static void
+outputColormap(FILE * const ofP,
+               cmap   const cmap) {
+/*----------------------------------------------------------------------------
+  Output the colormap as a GNU Octave matrix.
+-----------------------------------------------------------------------------*/
+    unsigned int colorIndex;
+
+    fprintf(ofP, "# name: map\n");
+    fprintf(ofP, "# type: matrix\n");
+    fprintf(ofP, "# rows: %u\n", cmap.nColors);
+    fprintf(ofP, "# columns: 3\n");
+
+    for (colorIndex = 0; colorIndex < cmap.nColors; ++colorIndex) {
+        unsigned int plane;
+
+        assert(cmap.pam.depth == 3);
+
+        for (plane = 0; plane < 3; ++plane)
+            fprintf(ofP, " %.10f", cmap.palette[colorIndex].comp[plane]);
+
+        fprintf(ofP, "\n");
+    }
+}
+
+
+
+static void
+convertToOctave(FILE * const ifP,
+                FILE * const ofP) {
+
+    struct pam inpam;
+    tuple * inRow;
+    unsigned int row;
+    cmap cmap;
+
+    pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(allocation_depth));
+
+    pnm_setminallocationdepth(&inpam, 3);
+    
+    /* Output the image as a GNU Octave matrix.  For each row of the
+     * input file we immediately output indexes into the colormap then,
+     * when we're finished, we output the colormap as a second
+     * matrix. */
+    fprintf(ofP, "# name: img\n");
+    fprintf(ofP, "# type: matrix\n");
+    fprintf(ofP, "# rows: %u\n", inpam.height);
+    fprintf(ofP, "# columns: %u\n", inpam.width);
+
+    initCmap(&cmap, inpam.maxval);
+
+    inRow = pnm_allocpamrow(&inpam);
+    for (row = 0; row < inpam.height; ++row) {
+        unsigned int col;
+        pnm_readpamrow(&inpam, inRow);
+
+        pnm_makerowrgb(&inpam, inRow);
+
+        for (col = 0; col < inpam.width; ++col) {
+            unsigned int colorIndex;
+            findOrAddColor(inRow[col], &cmap, &colorIndex);
+            fprintf(ofP, " %u", colorIndex + 1);
+        }
+        fprintf(ofP, "\n");
+    }
+    pm_message("%u colors in palette", cmap.nColors);
+
+    pnm_freepamrow(inRow);
+    outputColormap(ofP, cmap);
+
+    termCmap(&cmap);
+}
+
+
+
+int
+main(int argc, char *argv[]) {
+
+    FILE * ifP;
+    const char * inputName;
+
+    pnm_init(&argc, argv);
+
+    inputName = argc-1 > 0 ? argv[1] : "-";
+
+    ifP = pm_openr(inputName);
+    
+    if (streq(inputName, "-"))
+        fprintf(stdout, "# Created by pamtooctave\n");
+    else
+        fprintf(stdout, "# Created from '%s' by pamtooctave\n", inputName);
+
+    convertToOctave(ifP, stdout);
+    
+    pm_close(ifP);
+
+    return 0;
+}
+
+
+
+/*
+ * Copyright (C) 2007 Scott Pakin <scott+pbm@pakin.org>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials provided
+ *    with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior written
+ *    permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ----------------------------------------------------------------------
+ */
diff --git a/converter/other/pamtopam.c b/converter/other/pamtopam.c
new file mode 100644
index 00000000..cae54060
--- /dev/null
+++ b/converter/other/pamtopam.c
@@ -0,0 +1,57 @@
+/*=============================================================================
+                               pamtopam
+===============================================================================
+  Part of the Netpbm package.
+
+  Copy PAM and PNM (i.e. PBM, PGM, or PPM) images from Standard Input
+  to Standard Output (while converting PNM images to PAM)
+
+  By Paul Bolle October 2007.
+
+  Contributed to the public domain by its author.
+=============================================================================*/
+
+#include "pm_c_util.h"
+#include "pam.h"
+
+int
+main(int argc, const char * argv[]) {
+
+    bool       eof;     /* no more images in input stream */
+    struct pam inpam;   /* Input PAM image */
+    struct pam outpam;  /* Output PAM image */
+
+    pm_proginit(&argc, argv);
+
+    if (argc-1 != 0)
+        pm_error("Program takes no arguments.  Input is from Standard Input");
+
+    eof = FALSE;
+    while (!eof) {
+        pnm_readpaminit(stdin, &inpam, PAM_STRUCT_SIZE(tuple_type));
+
+        outpam = inpam;
+        outpam.file = stdout;
+        outpam.format = PAM_FORMAT;
+
+        pnm_writepaminit(&outpam);
+
+        {
+            tuple * tuplerow;
+
+            tuplerow = pnm_allocpamrow(&inpam);
+            {
+                unsigned int row;
+
+                for (row = 0; row < inpam.height; ++row) {
+                    pnm_readpamrow(&inpam, tuplerow);
+                    pnm_writepamrow(&outpam, tuplerow);
+                }
+            }
+            pnm_freepamrow(tuplerow);
+        }
+        pnm_nextimage(stdin, &eof);
+    }
+
+    return 0;
+}
diff --git a/converter/other/pamtopfm.c b/converter/other/pamtopfm.c
index ee44eeb5..129b8eee 100644
--- a/converter/other/pamtopfm.c
+++ b/converter/other/pamtopfm.c
@@ -15,6 +15,7 @@
 #include <string.h>
 #include <assert.h>
 
+#include "pm_c_util.h"
 #include "pam.h"
 #include "pm_gamma.h"
 #include "shhopt.h"
diff --git a/converter/other/pamtopnm.c b/converter/other/pamtopnm.c
index cc1164da..86f6514c 100644
--- a/converter/other/pamtopnm.c
+++ b/converter/other/pamtopnm.c
@@ -12,6 +12,7 @@
 
 #include <string.h>
 
+#include "pm_c_util.h"
 #include "pam.h"
 #include "shhopt.h"
 #include "mallocvar.h"
@@ -108,6 +109,7 @@ main(int argc, char *argv[]) {
 
     struct cmdlineInfo cmdline;
     FILE* ifP;
+    bool eof;   /* no more images in input stream */
     struct pam inpam;   /* Input PAM image */
     struct pam outpam;  /* Output PNM image */
 
@@ -117,39 +119,45 @@ main(int argc, char *argv[]) {
 
     ifP = pm_openr(cmdline.inputFilespec);
 
-    pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
+    eof = FALSE;
+    while (!eof) {
+        pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
 
-    validateTupleType(inpam, cmdline.assume);
+        validateTupleType(inpam, cmdline.assume);
 
-    outpam = inpam;
-    outpam.file = stdout;
-    
-    if (inpam.depth < 3) {
-        outpam.depth = 1;
-        if (inpam.maxval == 1)
-            outpam.format = PBM_FORMAT;
-        else 
-            outpam.format = PGM_FORMAT;
-    } else {
-        outpam.depth = 3;
-        outpam.format = PPM_FORMAT;
-    }
+        outpam = inpam;
+        outpam.file = stdout;
+        
+        if (inpam.depth < 3) {
+            outpam.depth = 1;
+            if (inpam.maxval == 1)
+                outpam.format = PBM_FORMAT;
+            else 
+                outpam.format = PGM_FORMAT;
+        } else {
+            outpam.depth = 3;
+            outpam.format = PPM_FORMAT;
+        }
 
-    pnm_writepaminit(&outpam);
+        pnm_writepaminit(&outpam);
 
-    {
-        tuple *tuplerow;
-        
-        tuplerow = pnm_allocpamrow(&inpam);      
-        { 
-            int row;
+        {
+            tuple *tuplerow;
             
-            for (row = 0; row < inpam.height; row++) {
-                pnm_readpamrow(&inpam, tuplerow);
-                pnm_writepamrow(&outpam, tuplerow);
+            tuplerow = pnm_allocpamrow(&inpam);      
+            { 
+                int row;
+                
+                for (row = 0; row < inpam.height; row++) {
+                    pnm_readpamrow(&inpam, tuplerow);
+                    pnm_writepamrow(&outpam, tuplerow);
+                }
             }
+            pnm_freepamrow(tuplerow);        
         }
-        pnm_freepamrow(tuplerow);        
+
+        pnm_nextimage(ifP, &eof);
     }
+
     return 0;
 }
diff --git a/converter/other/pamtosvg/Makefile b/converter/other/pamtosvg/Makefile
index ba03fd68..8b033020 100644
--- a/converter/other/pamtosvg/Makefile
+++ b/converter/other/pamtosvg/Makefile
@@ -5,7 +5,7 @@ endif
 SUBDIR = converter/other/pamtosvg
 VPATH=.:$(SRCDIR)/$(SUBDIR)
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
 BINARIES = pamtosvg
 
@@ -47,7 +47,7 @@ MERGEBINARIES = $(BINARIES)
 
 all: $(BINARIES)
 
-include $(SRCDIR)/Makefile.common
+include $(SRCDIR)/common.mk
 
 pamtosvg: $(PAMTOSVG_OBJECTS) $(NETPBMLIB) $(LIBOPT)
 	$(LD) -o $@ $(PAMTOSVG_OBJECTS) \
diff --git a/converter/other/pamtosvg/curve.c b/converter/other/pamtosvg/curve.c
index cc8aeb59..369a0cd3 100644
--- a/converter/other/pamtosvg/curve.c
+++ b/converter/other/pamtosvg/curve.c
@@ -42,18 +42,19 @@ int_to_real_coord(pm_pixelcoord const int_coord) {
 
 /* Return an entirely empty curve.  */
 
-curve_type
-new_curve (void)
-{
-  curve_type curve;
-  MALLOCVAR_NOFAIL(curve);
-  curve->point_list = NULL;
-  CURVE_LENGTH (curve) = 0;
-  CURVE_CYCLIC (curve) = false;
-  CURVE_START_TANGENT (curve) = CURVE_END_TANGENT (curve) = NULL;
-  PREVIOUS_CURVE (curve) = NEXT_CURVE (curve) = NULL;
+curve *
+new_curve(void) {
+  curve * curveP;
 
-  return curve;
+  MALLOCVAR_NOFAIL(curveP);
+
+  curveP->point_list = NULL;
+  CURVE_LENGTH(curveP) = 0;
+  CURVE_CYCLIC(curveP) = false;
+  PREVIOUS_CURVE(curveP)  = NULL;
+  NEXT_CURVE(curveP)      = NULL;
+
+  return curveP;
 }
 
 
@@ -71,23 +72,39 @@ copy_most_of_curve (curve_type old_curve)
   return curve;
 }
 
+void
+move_curve(curve * const dstP,
+           curve * const srcP) {
+
+    /* Move ownership of dynamically allocated memory from source 
+       to destination; destroy source.
+    */
+
+   if (CURVE_LENGTH(dstP) > 0)
+       free(dstP->point_list);
+    
+   *dstP = *srcP;
+
+   free(srcP);
+}
+
+
 
 /* The length of CURVE will be zero if we ended up not being able to fit
    it (which in turn implies a problem elsewhere in the program, but at
    any rate, we shouldn't try here to free the nonexistent curve).  */
 
 void
-free_curve (curve_type curve)
-{
-  if (CURVE_LENGTH (curve) > 0)
-    free (curve->point_list);
-  if (CURVE_START_TANGENT (curve))
-    free (CURVE_START_TANGENT (curve));
-  if (CURVE_END_TANGENT (curve))
-    free (CURVE_END_TANGENT (curve));
+free_curve(curve * const curveP) {
+
+   if (CURVE_LENGTH(curveP) > 0)
+       free(curveP->point_list);
+
+   free(curveP);
 }
 
 
+
 void
 append_point(curve_type  const curve,
              float_coord const coord) {
@@ -123,99 +140,84 @@ append_pixel(curve_type    const curve,
     }									\
   while (0)
 
+
+
 void
-log_curve (curve_type curve, bool print_t)
-{
-  unsigned this_point;
-
-  if (!log_file) return;
-
-  LOG1 ("curve id = %lx:\n", (unsigned long) curve);
-  LOG1 ("  length = %u.\n", CURVE_LENGTH (curve));
-  if (CURVE_CYCLIC (curve))
-    LOG ("  cyclic.\n");
-
-  /* It should suffice to check just one of the tangents for being null
-     -- either they both should be, or neither should be.  */
-  if (CURVE_START_TANGENT (curve) != NULL)
-    LOG4 ("  tangents = (%.3f,%.3f) & (%.3f,%.3f).\n",
-          CURVE_START_TANGENT (curve)->dx, CURVE_START_TANGENT (curve)->dy,
-          CURVE_END_TANGENT (curve)->dx, CURVE_END_TANGENT (curve)->dy);
-
-  LOG ("  ");
-
-  /* If the curve is short enough, don't use ellipses.  */
-  if (CURVE_LENGTH (curve) <= NUM_TO_PRINT * 2)
-    {
-      for (this_point = 0; this_point < CURVE_LENGTH (curve); this_point++)
-        {
-          LOG_CURVE_POINT (curve, this_point, print_t);
-          LOG (" ");
-
-          if (this_point != CURVE_LENGTH (curve) - 1
-              && (this_point + 1) % NUM_TO_PRINT == 0)
-            LOG ("\n  ");
+log_curve(curve * const curveP,
+          bool    const print_t) {
+
+    if (!log_file)
+        return;
+
+    LOG1("curve id = %lx:\n", (unsigned long) curveP);
+    LOG1("  length = %u.\n", CURVE_LENGTH(curveP));
+    if (CURVE_CYCLIC(curveP))
+        LOG("  cyclic.\n");
+
+    LOG("  ");
+
+    /* If the curve is short enough, don't use ellipses.  */
+    if (CURVE_LENGTH(curveP) <= NUM_TO_PRINT * 2) {
+        unsigned int thisPoint;
+    
+        for (thisPoint = 0; thisPoint < CURVE_LENGTH(curveP); ++thisPoint) {
+            LOG_CURVE_POINT(curveP, thisPoint, print_t);
+            LOG(" ");
+
+            if (thisPoint != CURVE_LENGTH(curveP) - 1
+                && (thisPoint + 1) % NUM_TO_PRINT == 0)
+                LOG("\n  ");
         }
-    }
-  else
-    {
-      for (this_point = 0;
-           this_point < NUM_TO_PRINT && this_point < CURVE_LENGTH (curve);
-           this_point++)
-        {
-          LOG_CURVE_POINT (curve, this_point, print_t);
-          LOG (" ");
+    } else {
+        unsigned int thisPoint;
+        for (thisPoint = 0;
+             thisPoint < NUM_TO_PRINT && thisPoint < CURVE_LENGTH(curveP);
+             ++thisPoint) {
+            LOG_CURVE_POINT(curveP, thisPoint, print_t);
+            LOG(" ");
         }
 
-      LOG ("...\n   ...");
+        LOG("...\n   ...");
 
-      for (this_point = CURVE_LENGTH (curve) - NUM_TO_PRINT;
-           this_point < CURVE_LENGTH (curve);
-           this_point++)
-        {
-          LOG (" ");
-          LOG_CURVE_POINT (curve, this_point, print_t);
+        for (thisPoint = CURVE_LENGTH(curveP) - NUM_TO_PRINT;
+             thisPoint < CURVE_LENGTH(curveP);
+             ++thisPoint) {
+            LOG(" ");
+            LOG_CURVE_POINT(curveP, thisPoint, print_t);
         }
     }
-
-  LOG (".\n");
+    LOG(".\n");
 }
 
 
 /* Like `log_curve', but write the whole thing.  */
 
 void
-log_entire_curve (curve_type curve)
-{
-  unsigned this_point;
+log_entire_curve(curve * const curveP) {
 
-  if (!log_file) return;
+    unsigned int thisPoint;
 
-  LOG1 ("curve id = %lx:\n", (unsigned long) curve);
-  LOG1 ("  length = %u.\n", CURVE_LENGTH (curve));
-  if (CURVE_CYCLIC (curve))
-    LOG ("  cyclic.\n");
+    if (!log_file)
+        return;
 
-  /* It should suffice to check just one of the tangents for being null
-     -- either they both should be, or neither should be.  */
-  if (CURVE_START_TANGENT (curve) != NULL)
-    LOG4 ("  tangents = (%.3f,%.3f) & (%.3f,%.3f).\n",
-          CURVE_START_TANGENT (curve)->dx, CURVE_START_TANGENT (curve)->dy,
-          CURVE_END_TANGENT (curve)->dx, CURVE_END_TANGENT (curve)->dy);
+    LOG1("curve id = %lx:\n", (unsigned long) curveP);
+    LOG1("  length = %u.\n", CURVE_LENGTH(curveP));
+    if (CURVE_CYCLIC(curveP))
+        LOG("  cyclic.\n");
 
-  LOG (" ");
+    LOG(" ");
 
-  for (this_point = 0; this_point < CURVE_LENGTH (curve); this_point++)
-    {
-      LOG (" ");
-      LOG_CURVE_POINT (curve, this_point, true);
-      /* Compiler warning `Condition is always true' can be ignored */
+    for (thisPoint = 0; thisPoint < CURVE_LENGTH(curveP); ++thisPoint) {
+        LOG(" ");
+        LOG_CURVE_POINT(curveP, thisPoint, true);
+        /* Compiler warning `Condition is always true' can be ignored */
     }
 
-  LOG (".\n");
+    LOG(".\n");
 }
 
 
+
 /* Return an initialized but empty curve list.  */
 
 curve_list_type
@@ -233,19 +235,16 @@ new_curve_list (void)
 /* Free a curve list and all the curves it contains.  */
 
 void
-free_curve_list(curve_list_type * const curve_list) {
+free_curve_list(curve_list_type * const curveListP) {
 
-  unsigned this_curve;
+    unsigned int thisCurve;
 
-  for (this_curve = 0; this_curve < curve_list->length; this_curve++)
-    {
-      free_curve (curve_list->data[this_curve]);
-      free (curve_list->data[this_curve]);
-    }
+    for (thisCurve = 0; thisCurve < curveListP->length; ++thisCurve)
+        free_curve(curveListP->data[thisCurve]);
 
-  /* If the character was empty, it won't have any curves.  */
-  if (curve_list->data != NULL)
-    free (curve_list->data);
+    /* If the character was empty, it won't have any curves.  */
+    if (curveListP->data != NULL)
+        free (curveListP->data);
 }
 
 
diff --git a/converter/other/pamtosvg/curve.h b/converter/other/pamtosvg/curve.h
index ee046620..ba5f1833 100644
--- a/converter/other/pamtosvg/curve.h
+++ b/converter/other/pamtosvg/curve.h
@@ -20,19 +20,27 @@ typedef struct {
 
 
 
-struct curve {
+typedef struct curve {
 /*----------------------------------------------------------------------------
   An ordered list of contiguous points in the raster, with no corners
   in it.  I.e. something that could reasonably be fit to a spline.
 -----------------------------------------------------------------------------*/
     point_type *   point_list;
+        /* Array of the points in the curve.  Malloc'ed.  Size is 'length'.
+           if 'length' is zero, this is meaningless and no memory is
+           allocated.
+        */
     unsigned       length;
+        /* Number of points in the curve */
     bool           cyclic;
-    vector_type *  start_tangent;
-    vector_type *  end_tangent;
+
+    /* 'previous' and 'next' links are for the doubly linked list which is
+       a chain of all curves in an outline.  The chain is a cycle for a
+       closed outline and linear for an open outline.
+    */
     struct curve * previous;
     struct curve * next;
-};
+} curve;
 
 typedef struct curve * curve_type;
 
@@ -61,9 +69,6 @@ typedef struct curve * curve_type;
   ? CURVE_CYCLIC (c) ? (signed int) CURVE_LENGTH (c) + (signed int) (n) - 1 : -1\
   : (signed int) (n) - 1)
 
-/* The tangents at the endpoints are computed using the neighboring curves.  */
-#define CURVE_START_TANGENT(c) ((c)->start_tangent)
-#define CURVE_END_TANGENT(c) ((c)->end_tangent)
 #define PREVIOUS_CURVE(c) ((c)->previous)
 #define NEXT_CURVE(c) ((c)->next)
 
@@ -74,17 +79,23 @@ extern curve_type new_curve (void);
 /* Return a curve the same as C, except without any points.  */
 extern curve_type copy_most_of_curve (curve_type c);
 
-/* Free the memory C uses.  */
-extern void free_curve (curve_type c);
+void
+move_curve(curve * const dstP,
+           curve * const srcP);
+
+void
+free_curve(curve * const curveP);
+
+/* Like `append_pixel', for a point in real coordinates.  */
+void
+append_point(curve_type  const curve,
+             float_coord const coord);
 
 /* Append the point P to the end of C's list.  */
 void
 append_pixel(curve_type    const c,
              pm_pixelcoord const p);
 
-/* Like `append_pixel', for a point in real coordinates.  */
-extern void append_point (curve_type const c, float_coord const p);
-
 /* Write some or all, respectively, of the curve C in human-readable
    form to the log file, if logging is enabled.  */
 extern void log_curve (curve_type c, bool print_t);
@@ -99,11 +110,12 @@ typedef struct {
 /*----------------------------------------------------------------------------
    An ordered list of contiguous curves of a particular color.
 -----------------------------------------------------------------------------*/
-    curve_type * data;
-    unsigned     length;
-    bool         clockwise;
-    pixel        color;
-    bool         open;
+    curve ** data;
+        /* data[i] is the handle of the ith curve in the list */
+    unsigned length;
+    bool     clockwise;
+    pixel    color;
+    bool     open;
         /* The curve list does not form a closed shape;  i.e. the last
            curve doesn't end where the first one starts.
         */
diff --git a/converter/other/pamtosvg/fit.c b/converter/other/pamtosvg/fit.c
index 6bc2fe88..5ba7a2f6 100644
--- a/converter/other/pamtosvg/fit.c
+++ b/converter/other/pamtosvg/fit.c
@@ -54,21 +54,7 @@ typedef struct index_list
 #define INDEX_LIST_LENGTH(i_l)  ((i_l).length)
 #define GET_LAST_INDEX(i_l)  ((i_l).data[INDEX_LIST_LENGTH (i_l) - 1])
 
-static void append_index (index_list_type *, unsigned);
-static void free_index_list (index_list_type *);
-static index_list_type new_index_list (void);
-static void remove_adjacent_corners (index_list_type *, unsigned, bool,
-                     at_exception_type * exception);
-static void filter (curve_type, fitting_opts_type *);
-static void find_vectors
-  (unsigned const, pixel_outline_type const, vector_type * const, vector_type * const, unsigned const);
-static float find_error (curve_type, spline_type, unsigned *,
-               at_exception_type * exception);
-static vector_type find_half_tangent (curve_type, bool start, unsigned *, unsigned);
-static void find_tangent (curve_type, bool, bool, unsigned);
-static void remove_knee_points (curve_type const, bool const);
-static void set_initial_parameter_values (curve_type);
-static float distance (float_coord, float_coord);
+
 
 
 static pm_pixelcoord
@@ -86,6 +72,50 @@ real_to_int_coord(float_coord const real_coord) {
 }
 
 
+/* Lists of array indices (well, that is what we use it for).  */
+
+static index_list_type
+new_index_list (void)
+{
+  index_list_type index_list;
+
+  index_list.data = NULL;
+  INDEX_LIST_LENGTH (index_list) = 0;
+
+  return index_list;
+}
+
+static void
+free_index_list (index_list_type *index_list)
+{
+  if (INDEX_LIST_LENGTH (*index_list) > 0)
+    {
+      free (index_list->data);
+      index_list->data = NULL;
+      INDEX_LIST_LENGTH (*index_list) = 0;
+    }
+}
+
+static void
+append_index (index_list_type *list, unsigned new_index)
+{
+  INDEX_LIST_LENGTH (*list)++;
+  REALLOCARRAY_NOFAIL(list->data, INDEX_LIST_LENGTH(*list));
+  list->data[INDEX_LIST_LENGTH (*list) - 1] = new_index;
+}
+
+
+/* Return the Euclidean distance between P1 and P2.  */
+
+static float
+distance (float_coord p1, float_coord p2)
+{
+  float x = p1.x - p2.x, y = p1.y - p2.y, z = p1.z - p2.z;
+  return (float) sqrt (SQR(x) + SQR(y) + SQR(z));
+}
+
+
+
 static void
 appendCorner(index_list_type *  const cornerListP,
              unsigned int       const pixelSeq,
@@ -102,6 +132,45 @@ appendCorner(index_list_type *  const cornerListP,
 
 
 static void
+find_vectors(unsigned int       const test_index,
+             pixel_outline_type const outline,
+             vector_type *      const in,
+             vector_type *      const out,
+             unsigned int       const corner_surround) {
+/*----------------------------------------------------------------------------
+  Return the difference vectors coming in and going out of the outline
+  OUTLINE at the point whose index is TEST_INDEX.  In Phoenix,
+  Schneider looks at a single point on either side of the point we're
+  considering.  That works for him because his points are not touching.
+  But our points *are* touching, and so we have to look at
+  `corner_surround' points on either side, to get a better picture of
+  the outline's shape.
+-----------------------------------------------------------------------------*/
+    int i;
+    unsigned n_done;
+    pm_pixelcoord const candidate = O_COORDINATE(outline, test_index);
+
+    in->dx  = in->dy  = in->dz  = 0.0;
+    out->dx = out->dy = out->dz = 0.0;
+    
+    /* Add up the differences from p of the `corner_surround' points
+       before p.
+    */
+    for (i = O_PREV(outline, test_index), n_done = 0;
+         n_done < corner_surround;
+         i = O_PREV(outline, i), ++n_done)
+        *in = Vadd(*in, IPsubtract(O_COORDINATE(outline, i), candidate));
+    
+    /* And the points after p. */
+    for (i = O_NEXT (outline, test_index), n_done = 0;
+         n_done < corner_surround;
+         i = O_NEXT(outline, i), ++n_done)
+        *out = Vadd(*out, IPsubtract(O_COORDINATE(outline, i), candidate));
+}
+
+
+
+static void
 lookAheadForBetterCorner(pixel_outline_type  const outline,
                          unsigned int        const basePixelSeq,
                          float               const baseCornerAngle,
@@ -210,6 +279,283 @@ establishCornerSearchLimits(pixel_outline_type  const outline,
 
 
 static void
+remove_adjacent_corners(index_list_type *   const list,
+                        unsigned int        const last_index,
+                        bool                const remove_adj_corners,
+                        at_exception_type * const exception) {
+/*----------------------------------------------------------------------------
+   Remove adjacent points from the index list LIST.  We do this by first
+   sorting the list and then running through it.  Since these lists are
+   quite short, a straight selection sort (e.g., p.139 of the Art of
+   Computer Programming, vol.3) is good enough.  LAST_INDEX is the index
+   of the last pixel on the outline, i.e., the next one is the first
+   pixel. We need this for checking the adjacency of the last corner.
+
+   We need to do this because the adjacent corners turn into
+   two-pixel-long curves, which can be fit only by straight lines.
+-----------------------------------------------------------------------------*/
+  unsigned int j;
+  unsigned int last;
+  index_list_type new_list = new_index_list ();
+
+  for (j = INDEX_LIST_LENGTH (*list) - 1; j > 0; j--)
+    {
+      unsigned search;
+      unsigned temp;
+      /* Find maximal element below `j'.  */
+      unsigned max_index = j;
+
+      for (search = 0; search < j; search++)
+        if (GET_INDEX (*list, search) > GET_INDEX (*list, max_index))
+          max_index = search;
+
+      if (max_index != j)
+        {
+          temp = GET_INDEX (*list, j);
+          GET_INDEX (*list, j) = GET_INDEX (*list, max_index);
+          GET_INDEX (*list, max_index) = temp;
+        }
+    }
+
+  /* The list is sorted.  Now look for adjacent entries.  Each time
+     through the loop we insert the current entry and, if appropriate,
+     the next entry.  */
+  for (j = 0; j < INDEX_LIST_LENGTH (*list) - 1; j++)
+    {
+      unsigned current = GET_INDEX (*list, j);
+      unsigned next = GET_INDEX (*list, j + 1);
+
+      /* We should never have inserted the same element twice.  */
+      /* assert (current != next); */
+
+      if ((remove_adj_corners) && ((next == current + 1) || (next == current)))
+        j++;
+
+      append_index (&new_list, current);
+    }
+
+  /* Don't append the last element if it is 1) adjacent to the previous
+     one; or 2) adjacent to the very first one.  */
+  last = GET_LAST_INDEX (*list);
+  if (INDEX_LIST_LENGTH (new_list) == 0
+      || !(last == GET_LAST_INDEX (new_list) + 1
+           || (last == last_index && GET_INDEX (*list, 0) == 0)))
+    append_index (&new_list, last);
+
+  free_index_list (list);
+  *list = new_list;
+}
+
+/* A ``knee'' is a point which forms a ``right angle'' with its
+   predecessor and successor.  See the documentation (the `Removing
+   knees' section) for an example and more details.
+
+   The argument CLOCKWISE tells us which direction we're moving.  (We
+   can't figure that information out from just the single segment with
+   which we are given to work.)
+
+   We should never find two consecutive knees.
+
+   Since the first and last points are corners (unless the curve is
+   cyclic), it doesn't make sense to remove those.
+*/
+
+/* This evaluates to true if the vector V is zero in one direction and
+   nonzero in the other.  */
+#define ONLY_ONE_ZERO(v)                                                \
+  (((v).dx == 0.0 && (v).dy != 0.0) || ((v).dy == 0.0 && (v).dx != 0.0))
+
+/* There are four possible cases for knees, one for each of the four
+   corners of a rectangle; and then the cases differ depending on which
+   direction we are going around the curve.  The tests are listed here
+   in the order of upper left, upper right, lower right, lower left.
+   Perhaps there is some simple pattern to the
+   clockwise/counterclockwise differences, but I don't see one.  */
+#define CLOCKWISE_KNEE(prev_delta, next_delta)                                                  \
+  ((prev_delta.dx == -1.0 && next_delta.dy == 1.0)                                              \
+   || (prev_delta.dy == 1.0 && next_delta.dx == 1.0)                                    \
+   || (prev_delta.dx == 1.0 && next_delta.dy == -1.0)                                   \
+   || (prev_delta.dy == -1.0 && next_delta.dx == -1.0))
+
+#define COUNTERCLOCKWISE_KNEE(prev_delta, next_delta)                                   \
+  ((prev_delta.dy == 1.0 && next_delta.dx == -1.0)                                              \
+   || (prev_delta.dx == 1.0 && next_delta.dy == 1.0)                                    \
+   || (prev_delta.dy == -1.0 && next_delta.dx == 1.0)                                   \
+   || (prev_delta.dx == -1.0 && next_delta.dy == -1.0))
+
+
+
+static void
+remove_knee_points(curve * const curveP,
+                   bool    const clockwise) {
+
+    unsigned int const offset = CURVE_CYCLIC(curveP) ? 0 : 1;
+    curve * const trimmedCurveP = copy_most_of_curve(curveP);
+
+    pm_pixelcoord previous;
+    unsigned int i;
+
+    if (!CURVE_CYCLIC(curveP))
+        append_pixel(trimmedCurveP,
+                     real_to_int_coord(CURVE_POINT(curveP, 0)));
+
+    previous = real_to_int_coord(CURVE_POINT(curveP,
+                                             CURVE_PREV(curveP, offset)));
+
+    for (i = offset; i < CURVE_LENGTH(curveP) - offset; ++i) {
+        pm_pixelcoord const current =
+            real_to_int_coord(CURVE_POINT(curveP, i));
+        pm_pixelcoord const next =
+            real_to_int_coord(CURVE_POINT(curveP, CURVE_NEXT(curveP, i)));
+        vector_type const prev_delta = IPsubtract(previous, current);
+        vector_type const next_delta = IPsubtract(next, current);
+
+        if (ONLY_ONE_ZERO(prev_delta) && ONLY_ONE_ZERO(next_delta)
+            && ((clockwise && CLOCKWISE_KNEE(prev_delta, next_delta))
+                || (!clockwise
+                    && COUNTERCLOCKWISE_KNEE(prev_delta, next_delta))))
+            LOG2(" (%d,%d)", current.col, current.row);
+        else {
+            previous = current;
+            append_pixel(trimmedCurveP, current);
+        }
+    }
+
+    if (!CURVE_CYCLIC(curveP))
+        append_pixel(trimmedCurveP,
+                     real_to_int_coord(LAST_CURVE_POINT(curveP)));
+
+    if (CURVE_LENGTH(trimmedCurveP) == CURVE_LENGTH(curveP))
+        LOG(" (none)");
+
+    LOG(".\n");
+
+    move_curve(curveP, trimmedCurveP);
+}
+
+
+
+static void
+filter(curve *             const curveP,
+       fitting_opts_type * const fittingOptsP) {
+/*----------------------------------------------------------------------------
+  Smooth the curve by adding in neighboring points.  Do this
+  fittingOptsP->filter_iterations times.  But don't change the corners.
+-----------------------------------------------------------------------------*/
+    unsigned int const offset = CURVE_CYCLIC(curveP) ? 0 : 1;
+
+    unsigned int iteration, thisPoint;
+    float_coord prevNewPoint;
+    
+    /* We must have at least three points -- the previous one, the current
+       one, and the next one.  But if we don't have at least five, we will
+       probably collapse the curve down onto a single point, which means
+       we won't be able to fit it with a spline.
+    */
+    if (CURVE_LENGTH(curveP) < 5) {
+        LOG1("Length is %u, not enough to filter.\n", CURVE_LENGTH(curveP));
+        return;
+    }
+
+    prevNewPoint.x = FLT_MAX;
+    prevNewPoint.y = FLT_MAX;
+    prevNewPoint.z = FLT_MAX;
+    
+    for (iteration = 0;
+         iteration < fittingOptsP->filter_iterations;
+         ++iteration) {
+        curve * const newcurveP = copy_most_of_curve(curveP);
+
+        bool collapsed;
+        
+        collapsed = false;  /* initial value */
+
+        /* Keep the first point on the curve.  */
+        if (offset)
+            append_point(newcurveP, CURVE_POINT(curveP, 0));
+        
+        for (thisPoint = offset;
+             thisPoint < CURVE_LENGTH(curveP) - offset;
+             ++thisPoint) {
+            vector_type in, out, sum;
+            float_coord newPoint;
+            
+            /* Calculate the vectors in and out, computed by looking
+               at n points on either side of this_point.  Experimental
+               it was found that 2 is optimal.
+            */
+
+            signed int prev, prevprev; /* have to be signed */
+            unsigned int next, nextnext;
+            float_coord candidate = CURVE_POINT(curveP, thisPoint);
+            
+            prev = CURVE_PREV(curveP, thisPoint);
+            prevprev = CURVE_PREV(curveP, prev);
+            next = CURVE_NEXT(curveP, thisPoint);
+            nextnext = CURVE_NEXT(curveP, next);
+            
+            /* Add up the differences from p of the `surround' points
+               before p.
+            */
+            in.dx = in.dy = in.dz = 0.0;
+
+            in = Vadd(in, Psubtract(CURVE_POINT(curveP, prev), candidate));
+            if (prevprev >= 0)
+                in = Vadd(in,
+                          Psubtract(CURVE_POINT(curveP, prevprev), candidate));
+            
+            /* And the points after p.  Don't use more points after p than we
+               ended up with before it.
+            */
+            out.dx = out.dy = out.dz = 0.0;
+            
+            out = Vadd(out, Psubtract(CURVE_POINT(curveP, next), candidate));
+            if (nextnext < CURVE_LENGTH(curveP))
+                out = Vadd(out,
+                           Psubtract(CURVE_POINT(curveP, nextnext),
+                                     candidate));
+            
+            /* Start with the old point.  */
+            newPoint = candidate;
+            sum = Vadd(in, out);
+            /* We added 2*n+2 points, so we have to divide the sum by 2*n+2 */
+            newPoint.x += sum.dx / 6;
+            newPoint.y += sum.dy / 6;
+            newPoint.z += sum.dz / 6;
+            if (fabs(prevNewPoint.x - newPoint.x) < 0.3
+                && fabs (prevNewPoint.y - newPoint.y) < 0.3
+                && fabs (prevNewPoint.z - newPoint.z) < 0.3) {
+                collapsed = true;
+                break;
+            }
+            
+            /* Put the newly computed point into a separate curve, so it
+               doesn't affect future computation (on this iteration).
+            */
+            append_point(newcurveP, prevNewPoint = newPoint);
+        }
+        
+        if (collapsed)
+            free_curve(newcurveP);
+        else {
+            /* Just as with the first point, we have to keep the last
+               point.
+            */
+            if (offset)
+                append_point(newcurveP, LAST_CURVE_POINT(curveP));
+            
+            /* Set the original curve to the newly filtered one, and go
+               again.
+            */
+            move_curve(curveP, newcurveP);
+        }
+    }
+    log_curve(curveP, false);
+}
+
+
+
+static void
 removeAdjacent(index_list_type *   const cornerListP,
                pixel_outline_type  const outline,
                fitting_opts_type * const fittingOptsP,
@@ -340,25 +686,27 @@ makeOutlineOneCurve(pixel_outline_type const outline,
                     curve_list_type *  const curveListP) {
 /*----------------------------------------------------------------------------
    Add to *curveListP a single curve that represents the outline 'outline'.
+
+   That curve does not have beginning and ending slope information.
 -----------------------------------------------------------------------------*/
-    curve_type curve;
+    curve * curveP;
     unsigned int pixelSeq;
 
-    curve = new_curve();
-    
+    curveP = new_curve();
+
     for (pixelSeq = 0; pixelSeq < O_LENGTH(outline); ++pixelSeq)
-        append_pixel(curve, O_COORDINATE(outline, pixelSeq));
+        append_pixel(curveP, O_COORDINATE(outline, pixelSeq));
     
-    if (curveListP->open)
-        CURVE_CYCLIC(curve) = false;
+    if (outline.open)
+        CURVE_CYCLIC(curveP) = false;
     else
-        CURVE_CYCLIC(curve) = true;
+        CURVE_CYCLIC(curveP) = true;
     
     /* Make it a one-curve cycle */
-    NEXT_CURVE(curve)     = curve;
-    PREVIOUS_CURVE(curve) = curve;
+    NEXT_CURVE(curveP)     = curveP;
+    PREVIOUS_CURVE(curveP) = curveP;
 
-    append_curve(curveListP, curve);
+    append_curve(curveListP, curveP);
 }
 
 
@@ -367,12 +715,22 @@ static void
 addCurveStartingAtCorner(pixel_outline_type const outline,
                          index_list_type    const cornerList,
                          unsigned int       const cornerSeq,
-                         curve_list_type *  const curveListP) {
+                         curve_list_type *  const curveListP,
+                         curve **           const curCurvePP) {
+/*----------------------------------------------------------------------------
+   Add to the list *curveListP a new curve that starts at the cornerSeq'th
+   corner in outline 'outline' (whose corners are 'cornerList') and
+   goes to the next corner (or the end of the outline if no next corner).
+
+   Furthermore, add that curve to the curve chain whose end is pointed
+   to by *curCurvePP (NULL means chain is empty).
 
+   Don't include beginning and ending slope information for that curve.
+-----------------------------------------------------------------------------*/
     unsigned int const cornerPixelSeq = GET_INDEX(cornerList, cornerSeq);
     
     unsigned int lastPixelSeq;
-    curve_type curve;
+    curve * curveP;
     unsigned int pixelSeq;
     
     if (cornerSeq + 1 >= cornerList.length)
@@ -382,20 +740,23 @@ addCurveStartingAtCorner(pixel_outline_type const outline,
         /* Go through the next corner */
         lastPixelSeq = GET_INDEX(cornerList, cornerSeq + 1);
     
-    curve = new_curve();
+    curveP = new_curve();
 
     for (pixelSeq = cornerPixelSeq; pixelSeq <= lastPixelSeq; ++pixelSeq)
-        append_pixel(curve, O_COORDINATE(outline, pixelSeq));
+        append_pixel(curveP, O_COORDINATE(outline, pixelSeq));
     
+    append_curve(curveListP, curveP);
     {
-        /* Add curve to end of chain */
-        if (!CURVE_LIST_EMPTY(*curveListP)) {
-            curve_type const previousCurve = LAST_CURVE_LIST_ELT(*curveListP);
-            NEXT_CURVE(previousCurve) = curve;
-            PREVIOUS_CURVE(curve)     = previousCurve;
+        /* Add the new curve to the outline chain */
+
+        curve * const oldCurCurveP = *curCurvePP;
+
+        if (oldCurCurveP) {
+            NEXT_CURVE(oldCurCurveP) = curveP;
+            PREVIOUS_CURVE(curveP)   = oldCurCurveP;
         }
+        *curCurvePP = curveP;
     }
-    append_curve(curveListP, curve);
 }
 
 
@@ -421,53 +782,60 @@ divideOutlineWithCorners(pixel_outline_type const outline,
    corner).
 
    Assume there is at least one corner.
+
+   The curves do not have beginning and ending slope information.
 -----------------------------------------------------------------------------*/
     unsigned int const firstCurveSeq = CURVE_LIST_LENGTH(*curveListP);
         /* Index in curve list of the first curve we add */
     unsigned int cornerSeq;
+    curve * curCurveP;
+        /* Pointer to the curve we most recently added for this outline.
+           Null if none
+        */
 
     assert(cornerList.length > 0);
 
+    curCurveP = NULL;  /* No curves in outline chain yet */
+
     if (outline.open) {
-        /* Start with a curve that contains the point up to the first
+        /* Start with a curve that contains the points up to the first
            corner
         */
-        curve_type curve;
+        curve * curveP;
         unsigned int pixelSeq;
         
-        curve = new_curve();
+        curveP = new_curve();
 
         for (pixelSeq = 0; pixelSeq <= GET_INDEX(cornerList, 0); ++pixelSeq)
-            append_pixel(curve, O_COORDINATE(outline, pixelSeq));
+            append_pixel(curveP, O_COORDINATE(outline, pixelSeq));
 
-        append_curve(curveListP, curve);
-    } else
+        append_curve(curveListP, curveP);
+        curCurveP = curveP;  /* Only curve in outline chain now */
+    } else {
         /* We'll pick up the pixels before the first corner at the end */
-
+    }
     /* Add to the list a curve that starts at each corner and goes
        through the following corner, or the end of the outline if
        there is no following corner.  Do it in order of the corners.
     */
     for (cornerSeq = 0; cornerSeq < cornerList.length; ++cornerSeq)
-        addCurveStartingAtCorner(outline, cornerList, cornerSeq, curveListP);
+        addCurveStartingAtCorner(outline, cornerList, cornerSeq, curveListP,
+                                 &curCurveP);
 
     if (!outline.open) {
         /* Come around to the start of the curve list -- add the pixels
            before the first corner to the last curve, and chain the last
            curve to the first one.
         */
-        curve_type const firstCurve =
-            CURVE_LIST_ELT(*curveListP, firstCurveSeq);
-        curve_type const lastCurve  =
-            LAST_CURVE_LIST_ELT(*curveListP);
+        curve * const firstCurveP = CURVE_LIST_ELT(*curveListP, firstCurveSeq);
 
         unsigned int pixelSeq;
 
         for (pixelSeq = 0; pixelSeq <= GET_INDEX(cornerList, 0); ++pixelSeq)
-            append_pixel(lastCurve, O_COORDINATE(outline, pixelSeq));
+            append_pixel(curCurveP, O_COORDINATE(outline, pixelSeq));
 
-        NEXT_CURVE(lastCurve)      = firstCurve;
-        PREVIOUS_CURVE(firstCurve) = lastCurve;
+        NEXT_CURVE(curCurveP)       = firstCurveP;
+        PREVIOUS_CURVE(firstCurveP) = curCurveP;
     }
 }
 
@@ -501,6 +869,9 @@ split_at_corners(pixel_outline_list_type const pixel_list,
    To preserve this information, we return an array of curve_lists, one
    element (which in turn consists of several curves, one between each
    pair of corners) for each element in PIXEL_LIST.
+
+   The curves we return do not have beginning and ending slope
+   information.
 -----------------------------------------------------------------------------*/
     unsigned outlineSeq;
     curve_list_array_type curve_array;
@@ -797,27 +1168,30 @@ spline_linear_enough(spline_type *             const spline,
 /* Forward declaration for recursion */
 
 static spline_list_type *
-fitCurve(curve_type                const curve,
-         const fitting_opts_type * const fitting_opts,
-         at_exception_type *       const exception);
+fitCurve(curve *                   const curveP,
+         vector_type               const begSlope,
+         vector_type               const endSlope,
+         const fitting_opts_type * const fittingOptsP,
+         at_exception_type *       const exceptionP);
 
 
 
 static spline_list_type *
-fit_with_line(curve_type const curve) {
+fitWithLine(curve * const curveP) {
 /*----------------------------------------------------------------------------
-  This routine returns the curve fitted to a straight line in a very
-  simple way: make the first and last points on the curve be the
-  endpoints of the line.  This simplicity is justified because we are
-  called only on very short curves.
+  Return a list of splines that fit curve *curveP in a very simple way:
+  a single spline which is a straight line through the first and last
+  points on the curve.
+
+  This simplicity is useful only on a very short curve.
 -----------------------------------------------------------------------------*/
     spline_type line;
 
     LOG("Fitting with straight line:\n");
 
     SPLINE_DEGREE(line) = LINEARTYPE;
-    START_POINT(line) = CONTROL1(line) = CURVE_POINT(curve, 0);
-    END_POINT(line) = CONTROL2(line) = LAST_CURVE_POINT(curve);
+    START_POINT(line)   = CONTROL1(line) = CURVE_POINT(curveP, 0);
+    END_POINT(line)     = CONTROL2(line) = LAST_CURVE_POINT(curveP);
 
     /* Make sure that this line is never changed to a cubic.  */
     SPLINE_LINEARITY(line) = 0;
@@ -838,98 +1212,123 @@ fit_with_line(curve_type const curve) {
 #define B3(t) CUBE (t)
 
 static spline_type
-fit_one_spline(curve_type          const curve, 
-               at_exception_type * const exception) {
+fitOneSpline(curve *             const curveP, 
+             vector_type         const begSlope,
+             vector_type         const endSlope,
+             at_exception_type * const exceptionP) {
 /*----------------------------------------------------------------------------
-   Our job here is to find alpha1 (and alpha2), where t1_hat (t2_hat) is
-   the tangent to CURVE at the starting (ending) point, such that:
+  Return a spline that fits the points of curve *curveP,
+  with slope 'begSlope' at its beginning and 'endSlope' at its end.
 
-   control1 = alpha1 * t1_hat + starting point
-   control2 = alpha2 * t2_hat + ending_point
+  Make it a cubic spline.
+-----------------------------------------------------------------------------*/
+    /* We already have the start and end points of the spline, so all
+      we need are the control points.  And we know in what direction
+      each control point is from its respective end point, so all we
+      need to figure out is its distance.  (The control point's
+      distance from the end point is an indication of how long the
+      curve goes in its direction).
 
-   and the resulting spline (starting_point .. control1 and control2 ..
-   ending_point) minimizes the least-square error from CURVE.
+      We call the distance from an end point to the associated control
+      point "alpha".
 
-   See pp.57--59 of the Phoenix thesis.
+      We want to find starting and ending alpha that minimize the
+      least-square error in approximating *curveP with the spline.
+
+      How we do that is a complete mystery to me, but the original author
+      said to see pp.57--59 of the Phoenix thesis.  I haven't seen that.
+
+      In our expression of the math here, we use a struct with "beg" and
+      "end" members where the paper uses a matrix with "1" and "2"
+      subscripts, respectively.  A C array is a closer match to a math
+      matrix, but we think the struct is easier to read.
+
+      The B?(t) here corresponds to B_i^3(U_i) there.
+      The Bernstein polynomials of degree n are defined by
+      B_i^n(t) = { n \choose i } t^i (1-t)^{n-i}, i = 0..n
 
-   The B?(t) here corresponds to B_i^3(U_i) there.
-   The Bernshte\u in polynomials of degree n are defined by
-   B_i^n(t) = { n \choose i } t^i (1-t)^{n-i}, i = 0..n
------------------------------------------------------------------------------*/
-    /* Since our arrays are zero-based, the `C0' and `C1' here correspond
-       to `C1' and `C2' in the paper. 
     */
-    float X_C1_det, C0_X_det, C0_C1_det;
-    float alpha1, alpha2;
+    struct vectorPair {
+        vector_type beg;
+        vector_type end;
+    };
+    struct vectorPair tang;
+
+    float X_Cend_det, Cbeg_X_det, C_det;
     spline_type spline;
-    vector_type start_vector, end_vector;
+    vector_type begVector, endVector;
     unsigned i;
-    vector_type * A;
-    vector_type t1_hat;
-    vector_type t2_hat;
-    float C[2][2] = { { 0.0, 0.0 }, { 0.0, 0.0 } };
-    float X[2] = { 0.0, 0.0 };
+    struct vectorPair * A;  /* malloc'ed array */
+        /* I don't know the meaning of this array, but it is one entry for
+           each point in the curve (A[i] is for the ith point in the curve).
+        */
+    struct {
+        struct { float beg; float end; } beg;
+        struct { float beg; float end; } end;
+    } C;
+    struct { float beg; float end; } X;
 
-    t1_hat = *CURVE_START_TANGENT(curve);  /* initial value */
-    t2_hat = *CURVE_END_TANGENT(curve);    /* initial value */
+    tang.beg = begSlope; tang.end = endSlope;
 
-    MALLOCARRAY_NOFAIL(A, CURVE_LENGTH(curve) * 2);
+    MALLOCARRAY_NOFAIL(A, CURVE_LENGTH(curveP));
 
-    START_POINT(spline) = CURVE_POINT(curve, 0);
-    END_POINT(spline)   = LAST_CURVE_POINT(curve);
-    start_vector = make_vector(START_POINT(spline));
-    end_vector   = make_vector(END_POINT(spline));
+    BEG_POINT(spline) = CURVE_POINT(curveP, 0);
+    END_POINT(spline) = LAST_CURVE_POINT(curveP);
+    begVector = make_vector(BEG_POINT(spline));
+    endVector = make_vector(END_POINT(spline));
 
-    for (i = 0; i < CURVE_LENGTH(curve); ++i) {
-        A[(i << 1) + 0] = Vmult_scalar(t1_hat, B1(CURVE_T(curve, i)));
-        A[(i << 1) + 1] = Vmult_scalar(t2_hat, B2(CURVE_T(curve, i)));
+    for (i = 0; i < CURVE_LENGTH(curveP); ++i) {
+        A[i].beg = Vmult_scalar(tang.beg, B1(CURVE_T(curveP, i)));
+        A[i].end = Vmult_scalar(tang.end, B2(CURVE_T(curveP, i)));
     }
 
-    for (i = 0; i < CURVE_LENGTH(curve); ++i) {
+    C.beg.beg = 0.0; C.beg.end = 0.0; C.end.end = 0.0;  /* initial value */
+
+    X.beg = 0.0; X.end = 0.0; /* initial value */
+
+    for (i = 0; i < CURVE_LENGTH(curveP); ++i) {
+        struct vectorPair * const AP = &A[i];
         vector_type temp, temp0, temp1, temp2, temp3;
-        vector_type * Ai = A + (i << 1);
 
-        C[0][0] += Vdot(Ai[0], Ai[0]);
-        C[0][1] += Vdot(Ai[0], Ai[1]);
-        /* C[1][0] = C[0][1] (this is assigned outside the loop)  */
-        C[1][1] += Vdot(Ai[1], Ai[1]);
+        C.beg.beg += Vdot(AP->beg, AP->beg);
+        C.beg.end += Vdot(AP->beg, AP->end);
+        /* C.end.beg = Vdot(AP->end, AP->beg) is done outside of loop */
+        C.end.end += Vdot(AP->end, AP->end);
 
         /* Now the right-hand side of the equation in the paper.  */
-        temp0 = Vmult_scalar(start_vector, B0(CURVE_T(curve, i)));
-        temp1 = Vmult_scalar(start_vector, B1(CURVE_T(curve, i)));
-        temp2 = Vmult_scalar(end_vector, B2(CURVE_T(curve, i)));
-        temp3 = Vmult_scalar(end_vector, B3(CURVE_T(curve, i)));
+        temp0 = Vmult_scalar(begVector, B0(CURVE_T(curveP, i)));
+        temp1 = Vmult_scalar(begVector, B1(CURVE_T(curveP, i)));
+        temp2 = Vmult_scalar(endVector, B2(CURVE_T(curveP, i)));
+        temp3 = Vmult_scalar(endVector, B3(CURVE_T(curveP, i)));
 
         temp = make_vector(
-            Vsubtract_point(CURVE_POINT(curve, i),
+            Vsubtract_point(CURVE_POINT(curveP, i),
                             Vadd(temp0, Vadd(temp1, Vadd(temp2, temp3)))));
 
-        X[0] += Vdot(temp, Ai[0]);
-        X[1] += Vdot(temp, Ai[1]);
+        X.beg += Vdot(temp, AP->beg);
+        X.end += Vdot(temp, AP->end);
     }
     free(A);
 
-    C[1][0] = C[0][1];
+    C.end.beg = C.beg.end;
     
-    X_C1_det = X[0] * C[1][1] - X[1] * C[0][1];
-    C0_X_det = C[0][0] * X[1] - C[0][1] * X[0];
-    C0_C1_det = C[0][0] * C[1][1] - C[1][0] * C[0][1];
-    if (C0_C1_det == 0.0) {
-        LOG ("zero determinant of C0*C1");
-        at_exception_fatal(exception, "zero determinant of C0*C1");
-        goto cleanup;
-    }
-
-    alpha1 = X_C1_det / C0_C1_det;
-    alpha2 = C0_X_det / C0_C1_det;
-
-    CONTROL1(spline) = Vadd_point(START_POINT(spline),
-                                  Vmult_scalar(t1_hat, alpha1));
-    CONTROL2(spline) = Vadd_point(END_POINT(spline),
-                                  Vmult_scalar(t2_hat, alpha2));
-    SPLINE_DEGREE(spline) = CUBICTYPE;
-
-cleanup:
+    X_Cend_det  = X.beg * C.end.end - X.end * C.beg.end;
+    Cbeg_X_det  = C.beg.beg * X.end - C.beg.end * X.beg;
+    C_det = C.beg.beg * C.end.end - C.end.beg * C.beg.end;
+    if (C_det == 0.0) {
+        LOG("zero determinant of C matrix");
+        at_exception_fatal(exceptionP, "zero determinant of C matrix");
+    } else {
+        struct { float beg; float end; } alpha;  /* constant */
+        alpha.beg = X_Cend_det / C_det;
+        alpha.end = Cbeg_X_det / C_det;
+
+        CONTROL1(spline) = Vadd_point(BEG_POINT(spline),
+                                      Vmult_scalar(tang.beg, alpha.beg));
+        CONTROL2(spline) = Vadd_point(END_POINT(spline),
+                                      Vmult_scalar(tang.end, alpha.end));
+        SPLINE_DEGREE(spline) = CUBICTYPE;
+    }        
     return spline;
 }
 
@@ -951,214 +1350,538 @@ logSplineFit(spline_type const spline) {
 
 
 
-static spline_list_type *
-fit_with_least_squares(curve_type                const curve,
-                       const fitting_opts_type * const fitting_opts,
-                       at_exception_type *       const exception) {
+static vector_type
+findHalfTangentBeg(curve *      const curveP,
+                   unsigned int const tangentSurround) {
 /*----------------------------------------------------------------------------
-  The least squares method is well described in Schneider's thesis.
-  Briefly, we try to fit the entire curve with one spline.  If that
-  fails, we subdivide the curve. 
+  Find the slope in the vicinity of the beginning of the curve
+  *curveP.
+
+  To wit, this is the mean slope between the first point on the curve and
+  each of the 'tangentSurround' following points, up to half the curve.
+
+  For example, if 'tangentSurround' is 3 and the curve is 10 points
+  long, we imagine a line through Point 0 and Point 1, another through
+  Point 0 and Point 2, and a third through Point 0 and Point 3.  We
+  return the mean of the slopes of those 3 lines.
 -----------------------------------------------------------------------------*/
-    float error;
-    float best_error;
-    spline_type spline;
-    spline_type best_spline;
-    spline_list_type * spline_list;
-    unsigned int worst_point;
-    float previous_error;
-    
-    best_error = FLT_MAX;  /* initial value */
-    previous_error = FLT_MAX;  /* initial value */
-    spline_list = NULL;  /* initial value */
-    worst_point = 0;  /* initial value */
+    float_coord const tangentPoint = CURVE_POINT(curveP, 0);
+    vector_type const zeroZero = { 0.0, 0.0 };
+    unsigned int const surround =
+        MIN(CURVE_LENGTH(curveP) / 2, tangentSurround);
 
-    LOG ("\nFitting with least squares:\n");
-    
-    /* Phoenix reduces the number of points with a ``linear spline
-       technique''.  But for fitting letterforms, that is
-       inappropriate.  We want all the points we can get.
-    */
+    unsigned int p;
+    vector_type sum;
+    vector_type mean;
+    unsigned int n;
+
+    for (p = 0, n = 0, sum = zeroZero; p < surround; ++p) {
+        unsigned int const thisIndex = p + 1;
+        float_coord  const thisPoint = CURVE_POINT(curveP, thisIndex);
+
+        /* Perhaps we should weight the tangent from `thisPoint' by some
+           factor dependent on the distance from the tangent point.
+        */
+        sum = Vadd(sum, Pdirection(thisPoint, tangentPoint));
+        ++n;
+    }
+
+    mean = Vmult_scalar(sum, 1.0 / n);
+
+    return mean;
+}
+
+
+
+static vector_type
+findHalfTangentEnd(curve *      const curveP,
+                   unsigned int const tangentSurround) {
+/*----------------------------------------------------------------------------
+  Find the slope in the vicinity of the end of the curve
+  *curveP.
+
+  This is analogous to findHalfTangentBeg(), but at the other end of the
+  curve.
+-----------------------------------------------------------------------------*/
+    float_coord const tangentPoint =
+        CURVE_POINT(curveP, CURVE_LENGTH(curveP) - 1);
+    vector_type const zeroZero = { 0.0, 0.0 };
+    unsigned int const surround =
+        MIN(CURVE_LENGTH(curveP) / 2, tangentSurround);
+
+    unsigned int p;
+    vector_type sum;
+    vector_type mean;
+    unsigned int n;
+
+    for (p = 0, n = 0, sum = zeroZero; p < surround; ++p) {
+        unsigned int const thisIndex = CURVE_LENGTH(curveP) - 1 - p;
+        float_coord  const thisPoint = CURVE_POINT(curveP, thisIndex);
+
+        sum = Vadd(sum, Pdirection(tangentPoint, thisPoint));
+        ++n;
+    }
+
+    mean = Vmult_scalar(sum, 1.0 / n);
+
+    return mean;
+}
+
+
+
+static vector_type
+findHalfTangent(bool         const toStartPoint,
+                curve *      const curveP,
+                unsigned int const tangentSurround) {
+
+    if (toStartPoint)
+        return findHalfTangentBeg(curveP, tangentSurround);
+    else
+        return findHalfTangentEnd(curveP, tangentSurround);
+}
+
+
+
+static void
+findTangent(curve *       const curveP,
+            bool          const toStartPoint,
+            curve *       const adjacentCurveP,
+            unsigned int  const tangentSurroundArg,
+            vector_type * const tangentP) {
+/*----------------------------------------------------------------------------
+  Find an approximation to the slope of *curveP (i.e. slope of tangent
+  line) at an endpoint (the first point if 'toStartPoint' is true,
+  else the last).
+
+  If 'adjacentCurveP' is non-null, consider points on the adjacent
+  curve to *curveP.  The adjacent curve is *adjacentCurveP.  Adjacent
+  means the previous curve in the outline chain for the slope at the
+  start point ('toStartPoint' == true), the next curve otherwise.
+  If *curveP is cyclic, then it is its own adjacent curve.
+  
+  It is important to compute an accurate approximation, because the
+  control points that we eventually decide upon to fit the curve will
+  be placed on the half-lines defined by the slopes and endpoints, and
+  we never recompute the tangent after this.
+-----------------------------------------------------------------------------*/
+    vector_type slope;
+    unsigned int tangentSurround;
+
+    LOG2("  tangent to %s of curve %lx: ",
+         toStartPoint ? "start" : "end", (unsigned long)curveP);
+
+    tangentSurround = tangentSurroundArg;  /* initial value */
+    do {
+        slope = findHalfTangent(toStartPoint, curveP, tangentSurround);
+
+        if (adjacentCurveP) {
+            vector_type const slopeAdj =
+                findHalfTangent(!toStartPoint, adjacentCurveP,
+                                tangentSurround);
+               
+            LOG3("(adjacent curve half tangent (%.3f,%.3f,%.3f)) ",
+                 slopeAdj.dx, slopeAdj.dy, slopeAdj.dz);
+            slope = Vmult_scalar(Vadd(slope, slopeAdj), 0.5);
+        }
+        --tangentSurround;
+    } while (slope.dx == 0.0 && slope.dy == 0.0);
+
+    *tangentP = slope;
     
-    /* It makes no difference whether we first set the `t' values or
-       find the tangents.  This order makes the documentation a little
-       more coherent.
-    */
+    LOG3("(%.3f,%.3f,%.3f).\n",
+         tangentP->dx, tangentP->dy, tangentP->dz);
+}
 
-    LOG("Finding tangents:\n");
-    find_tangent(curve, /* to_start */ true,  /* cross_curve */ false,
-                 fitting_opts->tangent_surround);
-    find_tangent(curve, /* to_start */ false, /* cross_curve */ false,
-                 fitting_opts->tangent_surround);
 
-    set_initial_parameter_values(curve);
 
-    /* Now we loop, subdividing, until CURVE has been fit.  */
-    while (true) {
-        float error;
+static void
+findError(curve *             const curveP,
+          spline_type         const spline,
+          float *             const errorP,
+          unsigned int *      const worstPointP,
+          at_exception_type * const exceptionP) {
+/*----------------------------------------------------------------------------
+  Tell how good a fit 'spline' is for *curveP.
+  
+  Return the error (maximum Euclidian distance between a point on
+  *curveP and the corresponding point on 'spline') as *errorP and the
+  sequence number of the point on the curve where the error is
+  greatest as *worstPointP.
+
+  If there are multiple equally bad points, return an arbitrary one of
+  them as *worstPointP.
+-----------------------------------------------------------------------------*/
+    unsigned int thisPoint;
+    float totalError;
+    float worstError;
+    unsigned int worstPoint;
 
-        spline = fit_one_spline(curve, exception);
-        best_spline = spline;
-        if (at_exception_got_fatal(exception))
-            goto cleanup;
+    assert(CURVE_LENGTH(curveP) > 0);
 
-        logSplineFit(spline);
+    totalError = 0.0;  /* initial value */
+    worstError = FLT_MIN; /* initial value */
+    worstPoint = 0;
         
-        if (SPLINE_DEGREE(spline) == LINEARTYPE)
-            break;
-
-        error = find_error(curve, spline, &worst_point, exception);
-        if (error <= previous_error) {
-            best_error  = error;
-            best_spline = spline;
+    for (thisPoint = 0; thisPoint < CURVE_LENGTH(curveP); ++thisPoint) {
+        float_coord const curvePoint = CURVE_POINT(curveP, thisPoint);
+        float const t = CURVE_T(curveP, thisPoint);
+        float_coord const splinePoint = evaluate_spline(spline, t);
+        float const thisError = distance(curvePoint, splinePoint);
+        if (thisError >= worstError) {
+            worstPoint = thisPoint;
+            worstError = thisError;
         }
-        break;
+        totalError += thisError;
     }
 
-    if (SPLINE_DEGREE(spline) == LINEARTYPE) {
-        spline_list = new_spline_list_with_spline(spline);
-        LOG1("Accepted error of %.3f.\n", error);
-        return spline_list;
+    if (epsilon_equal(totalError, 0.0))
+        LOG("  Every point fits perfectly.\n");
+    else {
+        LOG5("  Worst error (at (%.3f,%.3f,%.3f), point #%u) was %.3f.\n",
+             CURVE_POINT(curveP, worstPoint).x,
+             CURVE_POINT(curveP, worstPoint).y,
+             CURVE_POINT(curveP, worstPoint).z,
+             worstPoint, worstError);
+        LOG1("  Total error was %.3f.\n", totalError);
+        LOG2("  Average error (over %u points) was %.3f.\n",
+                 CURVE_LENGTH(curveP), totalError / CURVE_LENGTH(curveP));
     }
+    assert(worstPoint < CURVE_LENGTH(curveP));
+    *errorP      = worstError;
+    *worstPointP = worstPoint;
+}
+
+
+
+static void
+setInitialParameterValues(curve * const curveP) {
+/*----------------------------------------------------------------------------
+   Fill in the 't' values in *curveP.
 
-    /* Go back to the best fit.  */
-    spline = best_spline;
-    error = best_error;
+   The t value for point P on a curve is the distance P is along the
+   curve from the initial point, normalized so the entire curve is
+   length 1.0 (i.e. t of the initial point is 0.0; t of the final
+   point is 1.0).
 
-    if (error < fitting_opts->error_threshold && !CURVE_CYCLIC(curve)) {
-        /* The points were fitted with a spline.  We end up here
-           whenever a fit is accepted.  We have one more job: see if
-           the ``curve'' that was fit should really be a straight
-           line.
+   There are a lot of curves that pass through the points indicated by
+   *curveP, but for practical computation of t, we just take the
+   piecewise linear locus that runs through all of them.  That means
+   we can just step through *curveP, adding up the distance from one
+   point to the next to get the t value for each point.
+
+   This is the "chord-length parameterization" method, which is
+   described in Plass & Stone.
+-----------------------------------------------------------------------------*/
+    unsigned int p;
+
+    LOG("\nAssigning initial t values:\n  ");
+
+    CURVE_T(curveP, 0) = 0.0;
+
+    for (p = 1; p < CURVE_LENGTH(curveP); ++p) {
+        float_coord const point      = CURVE_POINT(curveP, p);
+        float_coord const previous_p = CURVE_POINT(curveP, p - 1);
+        float const d = distance(point, previous_p);
+        CURVE_T(curveP, p) = CURVE_T(curveP, p - 1) + d;
+    }
+
+    assert(LAST_CURVE_T(curveP) != 0.0);
+
+    /* Normalize to a curve length of 1.0 */
+
+    for (p = 1; p < CURVE_LENGTH(curveP); ++p)
+        CURVE_T(curveP, p) = CURVE_T(curveP, p) / LAST_CURVE_T(curveP);
+
+    log_entire_curve(curveP);
+}
+
+
+
+static void
+subdivideCurve(curve *                   const curveP,
+               unsigned int              const subdivisionIndex,
+               const fitting_opts_type * const fittingOptsP,
+               curve **                  const leftCurvePP,
+               curve **                  const rghtCurvePP,
+               vector_type *             const joinSlopeP) {
+/*----------------------------------------------------------------------------
+  Split curve *curveP into two, at 'subdivisionIndex'.  (Actually,
+  leave *curveP alone, but return as *leftCurvePP and *rghtCurvePP
+  two new curves that are the pieces).
+  
+  Return as *joinSlopeP what should be the slope where the subcurves
+  join, i.e. the slope of the end of the left subcurve and of the start
+  of the right subcurve.
+
+  To be precise, the point with sequence number 'subdivisionIndex'
+  becomes the first pixel of the right-hand curve.
+-----------------------------------------------------------------------------*/
+    curve * leftCurveP;
+    curve * rghtCurveP;
+
+    leftCurveP = new_curve();
+    rghtCurveP = new_curve();
+
+    LOG4("  Subdividing curve %lx into %lx and %lx at point #%u\n",
+         (unsigned long)curveP,
+         (unsigned long)leftCurveP, (unsigned long)rghtCurveP,
+         subdivisionIndex);
+
+    /* The last point of the left-hand curve will also be the first
+       point of the right-hand curve.
+    */
+    assert(subdivisionIndex < CURVE_LENGTH(curveP));
+    CURVE_LENGTH(leftCurveP) = subdivisionIndex + 1;
+    CURVE_LENGTH(rghtCurveP) = CURVE_LENGTH(curveP) - subdivisionIndex;
+
+    MALLOCARRAY_NOFAIL(leftCurveP->point_list, CURVE_LENGTH(leftCurveP));
+    memcpy(leftCurveP->point_list, &curveP->point_list[0],
+           CURVE_LENGTH(leftCurveP) * sizeof(curveP->point_list[0]));
+
+    MALLOCARRAY_NOFAIL(rghtCurveP->point_list, CURVE_LENGTH(rghtCurveP));
+    memcpy(rghtCurveP->point_list, &curveP->point_list[subdivisionIndex],
+           CURVE_LENGTH(rghtCurveP) * sizeof(curveP->point_list[0]));
+
+    /* We have to set up the two curves before finding the slope at
+       the subdivision point.  The slope at that point must be the
+       same for both curves, or noticeable bumps will occur in the
+       character.  But we want to use information on both sides of the
+       point to compute the slope, hence we use adjacentCurveP.
+    */
+    findTangent(leftCurveP,
+                /* toStartPoint: */   false,
+                /* adjacentCurveP: */ rghtCurveP,
+                fittingOptsP->tangent_surround, joinSlopeP);
+
+    *leftCurvePP = leftCurveP;
+    *rghtCurvePP = rghtCurveP;
+}
+
+
+
+static spline_list_type *
+leftRightConcat(const spline_list_type *  const leftSplineListP,
+                const spline_list_type *  const rghtSplineListP,
+                at_exception_type *       const exceptionP) {
+/*----------------------------------------------------------------------------
+   Return a spline list which is the concatenation of the spline lists
+   obtained by splitting a curve in two and fitting each independently.
+   NULL for a spline list pointer means Caller was unable to fit a list
+   of splines to that side of the curve.
+-----------------------------------------------------------------------------*/
+    spline_list_type * retval;
+                
+    retval = new_spline_list();
+
+    if (leftSplineListP == NULL) {
+        LOG("Could not fit spline to left curve.\n");
+        at_exception_warning(exceptionP, "Could not fit left spline list");
+    } else
+        concat_spline_lists(retval, *leftSplineListP);
+    
+    if (rghtSplineListP == NULL) {
+        LOG("Could not fit spline to right curve.\n");
+        at_exception_warning(exceptionP, "Could not fit right spline list");
+    } else
+        concat_spline_lists(retval, *rghtSplineListP);
+
+    return retval;
+}
+
+
+
+static unsigned int
+divisionPoint(curve *      const curveP,
+              unsigned int const worstFitPoint) {
+/*----------------------------------------------------------------------------
+   Return the sequence number of the point at which we should divide
+   curve *curveP for the purpose of doing a separate fit of each side,
+   assuming the point which least matches a single spline is sequence
+   number 'worstFitPoint'.
+
+   We get as close as we can to that while still having at least two
+   points on each side.
+
+   Assume the curve is at least 4 points long.
+
+   The return value is the sequence number of the first point of the
+   second (right-hand) subcurve.
+-----------------------------------------------------------------------------*/
+    assert(CURVE_LENGTH(curveP) >= 4);
+
+    return MAX(2, MIN(worstFitPoint, CURVE_LENGTH(curveP) - 2));
+}
+
+
+
+static spline_list_type *
+divideAndFit(curve *                   const curveP,
+             vector_type               const begSlope,
+             vector_type               const endSlope,
+             unsigned int              const subdivisionIndex,
+             const fitting_opts_type * const fittingOptsP,
+             at_exception_type *       const exceptionP) {
+/*----------------------------------------------------------------------------
+  Same as fitWithLeastSquares() (i.e. return a list of splines that fit
+  the curve *curveP), except assuming no single spline will fit the
+  entire curve.
+
+  Divide it into two curves at 'subdivisionIndex' and fit each
+  separately to a list of splines.  Return the concatenation of those
+  spline lists.
+
+  Assume 'subdivisionIndex' leaves at least two pixels on each side.
+-----------------------------------------------------------------------------*/
+    spline_list_type * retval;
+    curve * leftCurveP;
+        /* The beginning (lower indexes) subcurve */
+    curve * rghtCurveP;
+        /* The other subcurve */
+    vector_type joinSlope;
+        /* The slope of the end of the left subcurve and start of the right
+           subcurve.
         */
-        if (spline_linear_enough(&spline, curve, fitting_opts)) {
-            SPLINE_DEGREE(spline) = LINEARTYPE;
-            LOG("Changed to line.\n");
+    spline_list_type * leftSplineListP;
+    
+    assert(subdivisionIndex > 1);
+    assert(subdivisionIndex < CURVE_LENGTH(curveP)-1);
+    subdivideCurve(curveP, subdivisionIndex, fittingOptsP,
+                   &leftCurveP, &rghtCurveP, &joinSlope);
+
+    leftSplineListP = fitCurve(leftCurveP, begSlope, joinSlope,
+                               fittingOptsP, exceptionP);
+
+    if (!at_exception_got_fatal(exceptionP)) {
+        spline_list_type * rghtSplineListP;
+
+        rghtSplineListP = fitCurve(rghtCurveP, joinSlope, endSlope,
+                                   fittingOptsP, exceptionP);
+
+        if (!at_exception_got_fatal(exceptionP)) {
+            if (leftSplineListP == NULL && rghtSplineListP == NULL)
+                retval = NULL;
+            else
+                retval = leftRightConcat(leftSplineListP, rghtSplineListP,
+                                         exceptionP);
+
+            if (rghtSplineListP) {
+                free_spline_list(*rghtSplineListP);
+                free(rghtSplineListP);
+            }
         }
-        spline_list = new_spline_list_with_spline(spline);
-        LOG1("Accepted error of %.3f.\n", error);
-    } else {
-        /* We couldn't fit the curve acceptably, so subdivide.  */
-        unsigned subdivision_index;
-        spline_list_type * left_spline_list;
-        spline_list_type * right_spline_list;
-        curve_type left_curve, right_curve;
-
-        left_curve  = new_curve();
-        right_curve = new_curve();
-
-        /* Insert 'left_curve', then 'right_curve' after 'curve' in the list */
-        NEXT_CURVE(right_curve) = NEXT_CURVE(curve);
-        PREVIOUS_CURVE(right_curve) = left_curve;
-        NEXT_CURVE(left_curve) = right_curve;
-        PREVIOUS_CURVE(left_curve) = curve;
-        NEXT_CURVE(curve) = left_curve;
-
-        LOG1("\nSubdividing (error %.3f):\n", error);
-        LOG3("  Original point: (%.3f,%.3f), #%u.\n",
-             CURVE_POINT (curve, worst_point).x,
-             CURVE_POINT (curve, worst_point).y, worst_point);
-        subdivision_index = worst_point;
-        LOG3 ("  Final point: (%.3f,%.3f), #%u.\n",
-              CURVE_POINT (curve, subdivision_index).x,
-              CURVE_POINT (curve, subdivision_index).y, subdivision_index);
-
-        /* The last point of the left-hand curve will also be the first
-           point of the right-hand curve.  */
-        CURVE_LENGTH(left_curve)  = subdivision_index + 1;
-        CURVE_LENGTH(right_curve) = CURVE_LENGTH(curve) - subdivision_index;
-        left_curve->point_list = curve->point_list;
-        right_curve->point_list = curve->point_list + subdivision_index;
-
-        /* We want to use the tangents of the curve which we are
-           subdividing for the start tangent for left_curve and the
-           end tangent for right_curve.
-        */
-        CURVE_START_TANGENT(left_curve) = CURVE_START_TANGENT(curve);
-        CURVE_END_TANGENT(right_curve)  = CURVE_END_TANGENT(curve);
-
-        /* We have to set up the two curves before finding the tangent at
-           the subdivision point.  The tangent at that point must be the
-           same for both curves, or noticeable bumps will occur in the
-           character.  But we want to use information on both sides of the
-           point to compute the tangent, hence cross_curve = true.
-        */
-        find_tangent(left_curve, /* to_start_point: */ false,
-                     /* cross_curve: */ true, fitting_opts->tangent_surround);
-        CURVE_START_TANGENT(right_curve) = CURVE_END_TANGENT(left_curve);
-
-        /* Now that we've set up the curves, we can fit them.  */
-        left_spline_list = fitCurve(left_curve, fitting_opts, exception);
-        if (at_exception_got_fatal(exception))
-            /* TODO: Memory allocated for left_curve and right_curve
-               will leak.*/
-            goto cleanup;
+        if (leftSplineListP) {
+            free_spline_list(*leftSplineListP);
+            free(leftSplineListP);
+        }
+    }
 
-        right_spline_list = fitCurve(right_curve, fitting_opts, exception);
-        /* TODO: Memory allocated for left_curve and right_curve
-           will leak.*/
-        if (at_exception_got_fatal(exception))
-            goto cleanup;
-        
-        /* Neither of the subdivided curves could be fit, so fail.  */
-        if (left_spline_list == NULL && right_spline_list == NULL)
-            return NULL;
+    free_curve(leftCurveP);
+    free_curve(rghtCurveP);
 
-        /* Put the two together (or whichever of them exist).  */
-        spline_list = new_spline_list();
+    return retval;
+}
 
-        if (left_spline_list == NULL) {
-            LOG1("Could not fit spline to left curve (%lx).\n",
-                 (unsigned long) left_curve);
-            at_exception_warning(exception, "Could not fit left spline list");
-        } else {
-            concat_spline_lists(spline_list, *left_spline_list);
-            free_spline_list(*left_spline_list);
-            free(left_spline_list);
+
+
+static spline_list_type *
+fitWithLeastSquares(curve *                   const curveP,
+                    vector_type               const begSlope,
+                    vector_type               const endSlope,
+                    const fitting_opts_type * const fittingOptsP,
+                    at_exception_type *       const exceptionP) {
+/*----------------------------------------------------------------------------
+  The least squares method is well described in Schneider's thesis.
+  Briefly, we try to fit the entire curve with one spline.  If that
+  fails, we subdivide the curve. 
+-----------------------------------------------------------------------------*/
+    spline_list_type * retval;
+    spline_type spline;
+    
+    LOG("\nFitting with least squares:\n");
+    
+    /* Phoenix reduces the number of points with a "linear spline
+       technique."  But for fitting letterforms, that is
+       inappropriate.  We want all the points we can get.
+    */
+    
+    setInitialParameterValues(curveP);
+
+    if (CURVE_CYCLIC(curveP) && CURVE_LENGTH(curveP) < 4) {
+        unsigned i;
+        for (i = 0; i < CURVE_LENGTH(curveP); ++i) {
+            float_coord const point = CURVE_POINT(curveP, i);
+            fprintf(stderr, "point %u = (%f, %f)\n", i, point.x, point.y);
         }
+    }
+
+    /* Try a single spline over whole curve */
+
+    spline = fitOneSpline(curveP, begSlope, endSlope, exceptionP);
+    if (!at_exception_got_fatal(exceptionP)) {
+        float error;
+        unsigned int worstPoint;
+
+        logSplineFit(spline);
         
-        if (right_spline_list == NULL) {
-            LOG1("Could not fit spline to right curve (%lx).\n",
-                 (unsigned long) right_curve);
-            at_exception_warning(exception, "Could not fit right spline list");
+        findError(curveP, spline, &error, &worstPoint, exceptionP);
+        assert(worstPoint < CURVE_LENGTH(curveP));
+
+        if (error < fittingOptsP->error_threshold && !CURVE_CYCLIC(curveP)) {
+            /* The points were fitted adequately with a spline.  But
+               see if the "curve" that was fit should really just be a
+               straight line.
+            */
+            if (spline_linear_enough(&spline, curveP, fittingOptsP)) {
+                SPLINE_DEGREE(spline) = LINEARTYPE;
+                LOG("Changed to line.\n");
+            }
+            retval = new_spline_list_with_spline(spline);
+            LOG1("Accepted error of %.3f.\n", error);
         } else {
-            concat_spline_lists(spline_list, *right_spline_list);
-            free_spline_list(*right_spline_list);
-            free(right_spline_list);
+            /* We couldn't fit the curve acceptably with a single spline,
+               so divide into two curves and try to fit each separately.
+            */
+            unsigned int const divIndex = divisionPoint(curveP, worstPoint);
+            LOG1("\nSubdividing at point #%u\n", divIndex);
+            LOG4("  Worst match point: (%.3f,%.3f), #%u.  Error %.3f\n",
+                 CURVE_POINT(curveP, worstPoint).x,
+                 CURVE_POINT(curveP, worstPoint).y, worstPoint, error);
+
+            retval = divideAndFit(curveP, begSlope, endSlope, divIndex,
+                                  fittingOptsP, exceptionP);
         }
-        if (CURVE_END_TANGENT(left_curve))
-            free(CURVE_END_TANGENT(left_curve));
-        free(left_curve);
-        free(right_curve);
-    }
-cleanup:
+    } else
+        retval = NULL; /* quiet compiler warning */
 
-    return spline_list;
+    return retval;
 }
 
 
 
 static spline_list_type *
-fitCurve(curve_type                const curve,
+fitCurve(curve *                   const curveP,
+         vector_type               const begSlope,
+         vector_type               const endSlope,
          const fitting_opts_type * const fittingOptsP,
-         at_exception_type *       const exception) {
+         at_exception_type *       const exceptionP) {
 /*----------------------------------------------------------------------------
   Transform a set of locations to a list of splines (the fewer the
-  better).  We are guaranteed that CURVE does not contain any corners.
+  better).  We are guaranteed that *curveP does not contain any corners.
   We return NULL if we cannot fit the points at all.
 -----------------------------------------------------------------------------*/
     spline_list_type * fittedSplinesP;
 
-    if (CURVE_LENGTH(curve) < 2) {
-        LOG("Tried to fit curve with less than two points");
-        at_exception_warning(exception, 
+    if (CURVE_LENGTH(curveP) < 2) {
+        LOG("Tried to fit curve with fewer than two points");
+        at_exception_warning(exceptionP, 
                              "Tried to fit curve with less than two points");
         fittedSplinesP = NULL;
-    } else if (CURVE_LENGTH(curve) < 4)
-        fittedSplinesP = fit_with_line(curve);
+    } else if (CURVE_LENGTH(curveP) < 4)
+        fittedSplinesP = fitWithLine(curveP);
     else
         fittedSplinesP =
-            fit_with_least_squares(curve, fittingOptsP, exception);
+            fitWithLeastSquares(curveP, begSlope, endSlope, fittingOptsP,
+                                exceptionP);
 
     return fittedSplinesP;
 }
@@ -1185,13 +1908,24 @@ fitCurves(curve_list_type           const curveList,
          curveSeq < curveList.length && !at_exception_got_fatal(exceptionP);
          ++curveSeq) {
 
-        curve_type const currentCurve = CURVE_LIST_ELT(curveList, curveSeq);
+        curve * const curveP = CURVE_LIST_ELT(curveList, curveSeq);
 
+        vector_type begSlope, endSlope;
         spline_list_type * curveSplinesP;
 
-        LOG1("\nFitting curve #%u:\n", curveSeq);
+        LOG2("\nFitting curve #%u (%lx):\n", curveSeq, (unsigned long)curveP);
 
-        curveSplinesP = fitCurve(currentCurve, fittingOptsP, exceptionP);
+        LOG("Finding tangents:\n");
+        findTangent(curveP, /* toStart */ true,
+                    CURVE_CYCLIC(curveP) ? curveP : NULL,
+                    fittingOptsP->tangent_surround,
+                    &begSlope);
+        findTangent(curveP, /* toStart */ false,
+                    CURVE_CYCLIC(curveP) ? curveP : NULL,
+                    fittingOptsP->tangent_surround, &endSlope);
+
+        curveSplinesP = fitCurve(curveP, begSlope, endSlope, fittingOptsP,
+                                 exceptionP);
         if (!at_exception_got_fatal(exceptionP)) {
             if (curveSplinesP == NULL) {
                 LOG1("Could not fit curve #%u", curveSeq);
@@ -1369,7 +2103,6 @@ fit_outlines_to_splines(pixel_outline_list_type  const pixelOutlineList,
                        exception, notifyProgress, progressData,
                        testCancel, testcancelData, splineListArrayP);
 
-
     free_curve_list_array(&curveListArray, notifyProgress, progressData);
     
     flush_log_output();
@@ -1377,548 +2110,3 @@ fit_outlines_to_splines(pixel_outline_list_type  const pixelOutlineList,
 
 
 
-
-static void
-find_vectors(unsigned int       const test_index,
-             pixel_outline_type const outline,
-             vector_type *      const in,
-             vector_type *      const out,
-             unsigned int       const corner_surround) {
-/*----------------------------------------------------------------------------
-  Return the difference vectors coming in and going out of the outline
-  OUTLINE at the point whose index is TEST_INDEX.  In Phoenix,
-  Schneider looks at a single point on either side of the point we're
-  considering.  That works for him because his points are not touching.
-  But our points *are* touching, and so we have to look at
-  `corner_surround' points on either side, to get a better picture of
-  the outline's shape.
------------------------------------------------------------------------------*/
-    int i;
-    unsigned n_done;
-    pm_pixelcoord const candidate = O_COORDINATE(outline, test_index);
-
-    in->dx  = in->dy  = in->dz  = 0.0;
-    out->dx = out->dy = out->dz = 0.0;
-    
-    /* Add up the differences from p of the `corner_surround' points
-       before p.
-    */
-    for (i = O_PREV(outline, test_index), n_done = 0;
-         n_done < corner_surround;
-         i = O_PREV(outline, i), ++n_done)
-        *in = Vadd(*in, IPsubtract(O_COORDINATE(outline, i), candidate));
-    
-    /* And the points after p. */
-    for (i = O_NEXT (outline, test_index), n_done = 0;
-         n_done < corner_surround;
-         i = O_NEXT(outline, i), ++n_done)
-        *out = Vadd(*out, IPsubtract(O_COORDINATE(outline, i), candidate));
-}
-
-
-
-/* Remove adjacent points from the index list LIST.  We do this by first
-   sorting the list and then running through it.  Since these lists are
-   quite short, a straight selection sort (e.g., p.139 of the Art of
-   Computer Programming, vol.3) is good enough.  LAST_INDEX is the index
-   of the last pixel on the outline, i.e., the next one is the first
-   pixel. We need this for checking the adjacency of the last corner.
-
-   We need to do this because the adjacent corners turn into
-   two-pixel-long curves, which can only be fit by straight lines.  */
-
-static void
-remove_adjacent_corners (index_list_type *list, unsigned last_index,
-             bool remove_adj_corners,
-             at_exception_type * exception)
-             
-{
-  unsigned j;
-  unsigned last;
-  index_list_type new_list = new_index_list ();
-
-  for (j = INDEX_LIST_LENGTH (*list) - 1; j > 0; j--)
-    {
-      unsigned search;
-      unsigned temp;
-      /* Find maximal element below `j'.  */
-      unsigned max_index = j;
-
-      for (search = 0; search < j; search++)
-        if (GET_INDEX (*list, search) > GET_INDEX (*list, max_index))
-          max_index = search;
-
-      if (max_index != j)
-        {
-          temp = GET_INDEX (*list, j);
-          GET_INDEX (*list, j) = GET_INDEX (*list, max_index);
-          GET_INDEX (*list, max_index) = temp;
-      
-      /* xx -- really have to sort?  */
-      LOG ("needed exchange");
-      at_exception_warning(exception, "needed exchange");
-        }
-    }
-
-  /* The list is sorted.  Now look for adjacent entries.  Each time
-     through the loop we insert the current entry and, if appropriate,
-     the next entry.  */
-  for (j = 0; j < INDEX_LIST_LENGTH (*list) - 1; j++)
-    {
-      unsigned current = GET_INDEX (*list, j);
-      unsigned next = GET_INDEX (*list, j + 1);
-
-      /* We should never have inserted the same element twice.  */
-      /* assert (current != next); */
-
-      if ((remove_adj_corners) && ((next == current + 1) || (next == current)))
-        j++;
-
-      append_index (&new_list, current);
-    }
-
-  /* Don't append the last element if it is 1) adjacent to the previous
-     one; or 2) adjacent to the very first one.  */
-  last = GET_LAST_INDEX (*list);
-  if (INDEX_LIST_LENGTH (new_list) == 0
-      || !(last == GET_LAST_INDEX (new_list) + 1
-           || (last == last_index && GET_INDEX (*list, 0) == 0)))
-    append_index (&new_list, last);
-
-  free_index_list (list);
-  *list = new_list;
-}
-
-/* A ``knee'' is a point which forms a ``right angle'' with its
-   predecessor and successor.  See the documentation (the `Removing
-   knees' section) for an example and more details.
-
-   The argument CLOCKWISE tells us which direction we're moving.  (We
-   can't figure that information out from just the single segment with
-   which we are given to work.)
-
-   We should never find two consecutive knees.
-
-   Since the first and last points are corners (unless the curve is
-   cyclic), it doesn't make sense to remove those.  */
-
-/* This evaluates to true if the vector V is zero in one direction and
-   nonzero in the other.  */
-#define ONLY_ONE_ZERO(v)                                                \
-  (((v).dx == 0.0 && (v).dy != 0.0) || ((v).dy == 0.0 && (v).dx != 0.0))
-
-/* There are four possible cases for knees, one for each of the four
-   corners of a rectangle; and then the cases differ depending on which
-   direction we are going around the curve.  The tests are listed here
-   in the order of upper left, upper right, lower right, lower left.
-   Perhaps there is some simple pattern to the
-   clockwise/counterclockwise differences, but I don't see one.  */
-#define CLOCKWISE_KNEE(prev_delta, next_delta)                                                  \
-  ((prev_delta.dx == -1.0 && next_delta.dy == 1.0)                                              \
-   || (prev_delta.dy == 1.0 && next_delta.dx == 1.0)                                    \
-   || (prev_delta.dx == 1.0 && next_delta.dy == -1.0)                                   \
-   || (prev_delta.dy == -1.0 && next_delta.dx == -1.0))
-
-#define COUNTERCLOCKWISE_KNEE(prev_delta, next_delta)                                   \
-  ((prev_delta.dy == 1.0 && next_delta.dx == -1.0)                                              \
-   || (prev_delta.dx == 1.0 && next_delta.dy == 1.0)                                    \
-   || (prev_delta.dy == -1.0 && next_delta.dx == 1.0)                                   \
-   || (prev_delta.dx == -1.0 && next_delta.dy == -1.0))
-
-
-
-static void
-remove_knee_points(curve_type const curve,
-                   bool       const clockwise) {
-
-      unsigned const offset = (CURVE_CYCLIC(curve) == true) ? 0 : 1;
-      curve_type const trimmed_curve = copy_most_of_curve(curve);
-
-      pm_pixelcoord previous;
-      unsigned i;
-
-      if (!CURVE_CYCLIC(curve))
-          append_pixel(trimmed_curve,
-                       real_to_int_coord(CURVE_POINT(curve, 0)));
-
-      previous = real_to_int_coord(CURVE_POINT(curve,
-                                               CURVE_PREV(curve, offset)));
-
-      for (i = offset; i < CURVE_LENGTH (curve) - offset; ++i) {
-          pm_pixelcoord const current =
-              real_to_int_coord(CURVE_POINT(curve, i));
-          pm_pixelcoord const next =
-              real_to_int_coord(CURVE_POINT(curve, CURVE_NEXT(curve, i)));
-          vector_type const prev_delta = IPsubtract(previous, current);
-          vector_type const next_delta = IPsubtract(next, current);
-
-          if (ONLY_ONE_ZERO(prev_delta) && ONLY_ONE_ZERO(next_delta)
-              && ((clockwise && CLOCKWISE_KNEE(prev_delta, next_delta))
-                  || (!clockwise
-                      && COUNTERCLOCKWISE_KNEE(prev_delta, next_delta))))
-              LOG2(" (%d,%d)", current.col, current.row);
-          else {
-              previous = current;
-              append_pixel(trimmed_curve, current);
-          }
-      }
-
-      if (!CURVE_CYCLIC(curve))
-          append_pixel(trimmed_curve,
-                       real_to_int_coord(LAST_CURVE_POINT(curve)));
-
-      if (CURVE_LENGTH(trimmed_curve) == CURVE_LENGTH(curve))
-          LOG(" (none)");
-
-      LOG(".\n");
-
-      free_curve(curve);
-      *curve = *trimmed_curve;
-      free(trimmed_curve);      /* free_curve? --- Masatake */
-}
-
-
-
-/* Smooth the curve by adding in neighboring points.  Do this
-   `filter_iterations' times.  But don't change the corners.  */
-
-static void
-filter (curve_type curve, fitting_opts_type *fitting_opts)
-{
-  unsigned iteration, this_point;
-  unsigned offset = (CURVE_CYCLIC (curve) == true) ? 0 : 1;
-  float_coord prev_new_point;
-
-  /* We must have at least three points---the previous one, the current
-     one, and the next one.  But if we don't have at least five, we will
-     probably collapse the curve down onto a single point, which means
-     we won't be able to fit it with a spline.  */
-  if (CURVE_LENGTH (curve) < 5)
-    {
-      LOG1 ("Length is %u, not enough to filter.\n", CURVE_LENGTH (curve));
-      return;
-    }
-
-  prev_new_point.x = FLT_MAX;
-  prev_new_point.y = FLT_MAX;
-  prev_new_point.z = FLT_MAX;
-
-  for (iteration = 0; iteration < fitting_opts->filter_iterations;
-   iteration++)
-    {
-      curve_type newcurve = copy_most_of_curve (curve);
-      bool collapsed = false;
-
-      /* Keep the first point on the curve.  */
-      if (offset)
-        append_point (newcurve, CURVE_POINT (curve, 0));
-
-      for (this_point = offset; this_point < CURVE_LENGTH (curve) - offset;
-           this_point++)
-        {
-          vector_type in, out, sum;
-          float_coord new_point;
-
-          /* Calculate the vectors in and out, computed by looking at n points
-             on either side of this_point. Experimental it was found that 2 is
-             optimal. */
-
-          signed int prev, prevprev; /* have to be signed */
-          unsigned int next, nextnext;
-          float_coord candidate = CURVE_POINT (curve, this_point);
-
-          prev = CURVE_PREV (curve, this_point);
-          prevprev = CURVE_PREV (curve, prev);
-          next = CURVE_NEXT (curve, this_point);
-          nextnext = CURVE_NEXT (curve, next);
-
-          /* Add up the differences from p of the `surround' points
-             before p.  */
-          in.dx = in.dy = in.dz = 0.0;
-
-          in = Vadd (in, Psubtract (CURVE_POINT (curve, prev), candidate));
-          if (prevprev >= 0)
-              in = Vadd (in, Psubtract (CURVE_POINT (curve, prevprev), candidate));
-
-          /* And the points after p.  Don't use more points after p than we
-             ended up with before it.  */
-          out.dx = out.dy = out.dz = 0.0;
-
-          out = Vadd (out, Psubtract (CURVE_POINT (curve, next), candidate));
-          if (nextnext < CURVE_LENGTH (curve))
-              out = Vadd (out, Psubtract (CURVE_POINT (curve, nextnext), candidate));
-
-          /* Start with the old point.  */
-          new_point = candidate;
-          sum = Vadd (in, out);
-          /* We added 2*n+2 points, so we have to divide the sum by 2*n+2 */
-          new_point.x += sum.dx / 6;
-          new_point.y += sum.dy / 6;
-          new_point.z += sum.dz / 6;
-          if (fabs (prev_new_point.x - new_point.x) < 0.3
-              && fabs (prev_new_point.y - new_point.y) < 0.3
-              && fabs (prev_new_point.z - new_point.z) < 0.3)
-            {
-              collapsed = true;
-              break;
-            }
-
-
-          /* Put the newly computed point into a separate curve, so it
-             doesn't affect future computation (on this iteration).  */
-          append_point (newcurve, prev_new_point = new_point);
-        }
-
-      if (collapsed)
-    free_curve (newcurve);
-      else
-    {
-          /* Just as with the first point, we have to keep the last point.  */
-          if (offset)
-        append_point (newcurve, LAST_CURVE_POINT (curve));
-      
-          /* Set the original curve to the newly filtered one, and go again.  */
-          free_curve (curve);
-          *curve = *newcurve;
-    }
-      free (newcurve);
-    }
-
-  log_curve (curve, false);
-}
-
-
-
-/* Find reasonable values for t for each point on CURVE.  The method is
-   called chord-length parameterization, which is described in Plass &
-   Stone.  The basic idea is just to use the distance from one point to
-   the next as the t value, normalized to produce values that increase
-   from zero for the first point to one for the last point.  */
-
-static void
-set_initial_parameter_values (curve_type curve)
-{
-  unsigned p;
-
-  LOG ("\nAssigning initial t values:\n  ");
-
-  CURVE_T (curve, 0) = 0.0;
-
-  for (p = 1; p < CURVE_LENGTH (curve); p++)
-    {
-      float_coord point = CURVE_POINT (curve, p),
-                           previous_p = CURVE_POINT (curve, p - 1);
-      float d = distance (point, previous_p);
-      CURVE_T (curve, p) = CURVE_T (curve, p - 1) + d;
-    }
-
-  assert (LAST_CURVE_T (curve) != 0.0);
-
-  for (p = 1; p < CURVE_LENGTH (curve); p++)
-    CURVE_T (curve, p) = CURVE_T (curve, p) / LAST_CURVE_T (curve);
-
-  log_entire_curve (curve);
-}
-
-/* Find an approximation to the tangent to an endpoint of CURVE (to the
-   first point if TO_START_POINT is true, else the last).  If
-   CROSS_CURVE is true, consider points on the adjacent curve to CURVE.
-
-   It is important to compute an accurate approximation, because the
-   control points that we eventually decide upon to fit the curve will
-   be placed on the half-lines defined by the tangents and
-   endpoints...and we never recompute the tangent after this.  */
-
-static void
-find_tangent (curve_type curve, bool to_start_point, bool cross_curve,
-  unsigned tangent_surround)
-{
-  vector_type tangent;
-  vector_type **curve_tangent = (to_start_point == true) ? &(CURVE_START_TANGENT (curve))
-                                               : &(CURVE_END_TANGENT (curve));
-  unsigned n_points = 0;
-
-  LOG1 ("  tangent to %s: ", (to_start_point == true) ? "start" : "end");
-
-  if (*curve_tangent == NULL)
-    {
-        MALLOCVAR_NOFAIL(*curve_tangent);
-      do
-        {
-          tangent = find_half_tangent (curve, to_start_point, &n_points,
-            tangent_surround);
-
-          if ((cross_curve == true) || (CURVE_CYCLIC (curve) == true))
-            {
-              curve_type adjacent_curve
-                = (to_start_point == true) ? PREVIOUS_CURVE (curve) : NEXT_CURVE (curve);
-              vector_type tangent2
-                = (to_start_point == false) ? find_half_tangent (adjacent_curve, true, &n_points,
-                tangent_surround) : find_half_tangent (adjacent_curve, true, &n_points,
-                tangent_surround);
-
-              LOG3 ("(adjacent curve half tangent (%.3f,%.3f,%.3f)) ",
-                tangent2.dx, tangent2.dy, tangent2.dz);
-              tangent = Vadd (tangent, tangent2);
-            }
-          tangent_surround--;
-
-        }
-      while (tangent.dx == 0.0 && tangent.dy == 0.0);
-
-      assert (n_points > 0);
-      **curve_tangent = Vmult_scalar (tangent, (float)(1.0 / n_points));
-      if ((CURVE_CYCLIC (curve) == true) && CURVE_START_TANGENT (curve))
-          *CURVE_START_TANGENT (curve) = **curve_tangent;
-      if  ((CURVE_CYCLIC (curve) == true) && CURVE_END_TANGENT (curve))
-          *CURVE_END_TANGENT (curve) = **curve_tangent;
-    }
-  else
-    LOG ("(already computed) ");
-
-  LOG3 ("(%.3f,%.3f,%.3f).\n", (*curve_tangent)->dx, (*curve_tangent)->dy, (*curve_tangent)->dz);
-}
-
-/* Find the change in y and change in x for `tangent_surround' (a global)
-   points along CURVE.  Increment N_POINTS by the number of points we
-   actually look at.  */
-
-static vector_type
-find_half_tangent (curve_type c, bool to_start_point, unsigned *n_points,
-  unsigned tangent_surround)
-{
-  unsigned p;
-  int factor = to_start_point ? 1 : -1;
-  unsigned tangent_index = to_start_point ? 0 : c->length - 1;
-  float_coord tangent_point = CURVE_POINT (c, tangent_index);
-  vector_type tangent = { 0.0, 0.0 };
-  unsigned int surround;
-
-  if ((surround = CURVE_LENGTH (c) / 2) > tangent_surround)
-    surround = tangent_surround;
-
-  for (p = 1; p <= surround; p++)
-    {
-      int this_index = p * factor + tangent_index;
-      float_coord this_point;
-
-      if (this_index < 0 || this_index >= (int) c->length)
-        break;
-
-      this_point = CURVE_POINT (c, p * factor + tangent_index);
-
-      /* Perhaps we should weight the tangent from `this_point' by some
-         factor dependent on the distance from the tangent point.  */
-      tangent = Vadd (tangent,
-                      Vmult_scalar (Psubtract (this_point, tangent_point),
-                                    (float) factor));
-      (*n_points)++;
-    }
-
-  return tangent;
-}
-
-/* When this routine is called, we have computed a spline representation
-   for the digitized curve.  The question is, how good is it?  If the
-   fit is very good indeed, we might have an error of zero on each
-   point, and then WORST_POINT becomes irrelevant.  But normally, we
-   return the error at the worst point, and the index of that point in
-   WORST_POINT.  The error computation itself is the Euclidean distance
-   from the original curve CURVE to the fitted spline SPLINE.  */
-
-static float
-find_error (curve_type curve, spline_type spline, unsigned *worst_point,
-        at_exception_type * exception)
-{
-  unsigned this_point;
-  float total_error = 0.0;
-  float worst_error = FLT_MIN;
-
-  *worst_point = CURVE_LENGTH (curve) + 1;   /* A sentinel value.  */
-
-  for (this_point = 0; this_point < CURVE_LENGTH (curve); this_point++)
-    {
-      float_coord curve_point = CURVE_POINT (curve, this_point);
-      float t = CURVE_T (curve, this_point);
-      float_coord spline_point = evaluate_spline (spline, t);
-      float this_error = distance (curve_point, spline_point);
-      if (this_error >= worst_error)
-        {
-         *worst_point = this_point;
-          worst_error = this_error;
-        }
-      total_error += this_error;
-    }
-
-  if (*worst_point == CURVE_LENGTH (curve) + 1)
-    { /* Didn't have any ``worst point''; the error should be zero.  */
-      *worst_point = 0;
-      if (epsilon_equal (total_error, 0.0))
-        LOG ("  Every point fit perfectly.\n");
-      else
-    {
-      LOG("No worst point found; something is wrong");
-      at_exception_warning(exception, "No worst point found; something is wrong");
-    }
-    }
-  else
-    {
-      if (epsilon_equal (total_error, 0.0))
-        LOG ("  Every point fit perfectly.\n");
-      else
-        {
-          LOG5 ("  Worst error (at (%.3f,%.3f,%.3f), point #%u) was %.3f.\n",
-              CURVE_POINT (curve, *worst_point).x,
-              CURVE_POINT (curve, *worst_point).y,
-              CURVE_POINT (curve, *worst_point).z, *worst_point, worst_error);
-          LOG1 ("  Total error was %.3f.\n", total_error);
-          LOG2 ("  Average error (over %u points) was %.3f.\n",
-              CURVE_LENGTH (curve), total_error / CURVE_LENGTH (curve));
-        }
-    }
-
-  return worst_error;
-}
-
-
-/* Lists of array indices (well, that is what we use it for).  */
-
-static index_list_type
-new_index_list (void)
-{
-  index_list_type index_list;
-
-  index_list.data = NULL;
-  INDEX_LIST_LENGTH (index_list) = 0;
-
-  return index_list;
-}
-
-static void
-free_index_list (index_list_type *index_list)
-{
-  if (INDEX_LIST_LENGTH (*index_list) > 0)
-    {
-      free (index_list->data);
-      index_list->data = NULL;
-      INDEX_LIST_LENGTH (*index_list) = 0;
-    }
-}
-
-static void
-append_index (index_list_type *list, unsigned new_index)
-{
-  INDEX_LIST_LENGTH (*list)++;
-  REALLOCARRAY_NOFAIL(list->data, INDEX_LIST_LENGTH(*list));
-  list->data[INDEX_LIST_LENGTH (*list) - 1] = new_index;
-}
-
-
-/* Return the Euclidean distance between P1 and P2.  */
-
-static float
-distance (float_coord p1, float_coord p2)
-{
-  float x = p1.x - p2.x, y = p1.y - p2.y, z = p1.z - p2.z;
-  return (float) sqrt (SQR(x) + SQR(y) + SQR(z));
-}
diff --git a/converter/other/pamtosvg/pamtosvg.c b/converter/other/pamtosvg/pamtosvg.c
index 7262204c..dbe67c74 100644
--- a/converter/other/pamtosvg/pamtosvg.c
+++ b/converter/other/pamtosvg/pamtosvg.c
@@ -4,6 +4,7 @@
 #include <assert.h>
 #include <math.h>
 
+#include "pm_c_util.h"
 #include "mallocvar.h"
 #include "nstring.h"
 #include "shhopt.h"
@@ -211,6 +212,7 @@ parseCommandLine(int argc,
             pm_error("Too many arguments (%u).  The only non-option argument "
                      "is the input file name.", argc-1);
     }
+    free(option_def);
 }
 
 
@@ -389,7 +391,7 @@ main(int argc, char * argv[]) {
     pm_close(ifP);
     if (cmdline.log)
         pm_close(log_file);
-    
+
     at_splines_free(splinesP);
     at_bitmap_free(bitmapP);
 
diff --git a/converter/other/pamtosvg/pamtosvg.test b/converter/other/pamtosvg/pamtosvg.test
index df3a07d3..3217287e 100644
--- a/converter/other/pamtosvg/pamtosvg.test
+++ b/converter/other/pamtosvg/pamtosvg.test
@@ -1,6 +1,8 @@
+echo "Test 1.  Should print nothing"
 # This will print nothing if successful (diff will find no difference)
-ppmmake black 20 20 | ppmdraw -script="line 5 2 15 17" | pamtosvg | \
+ppmmake black 20 20 | ppmdraw -script="line 5 2 15 17" | ./pamtosvg | \
   diff testline.svg -
 
+echo "Test 2.  Should print nothing"
 # This will print nothing if successful (diff will find no difference)
-pamtosvg ../../../../testgrid.pbm | diff testgrid.svg -
+./pamtosvg ../../../testgrid.pbm | diff testgrid.svg -
diff --git a/converter/other/pamtosvg/pxl-outline.c b/converter/other/pamtosvg/pxl-outline.c
index a55a8b95..a1fa5299 100644
--- a/converter/other/pamtosvg/pxl-outline.c
+++ b/converter/other/pamtosvg/pxl-outline.c
@@ -83,8 +83,9 @@ getBitmapColor(bitmap_type  const bitmap,
                unsigned int const row,
                unsigned int const col) {
 
+    unsigned char * const p = BITMAP_PIXEL(bitmap, row, col);
+
     pixel pix;
-    unsigned char *p = BITMAP_PIXEL (bitmap, row, col);
   
     if (bitmap.np >= 3)
         PPM_ASSIGN(pix, p[0], p[1], p[2]);
@@ -248,19 +249,19 @@ next_unmarked_pixel(unsigned int *   const row,
 
 
 static pixel_outline_type
-find_one_centerline(bitmap_type    const bitmap,
-                    direction_type const original_dir,
-                    unsigned int   const original_row,
-                    unsigned int   const original_col,
-                    bitmap_type *  const marked) {
+findOneCenterline(bitmap_type    const bitmap,
+                  direction_type const originalDir,
+                  unsigned int   const originalRow,
+                  unsigned int   const originalCol,
+                  bitmap_type *  const marked) {
 
-    direction_type search_dir;
+    direction_type searchDir;
     unsigned int row, col;
     pixel_outline_type outline;
 
     outline = new_pixel_outline();
     outline.open  = false;
-    outline.color = getBitmapColor(bitmap, original_row, original_col);
+    outline.color = getBitmapColor(bitmap, originalRow, originalCol);
 
     /* Add the starting pixel to the output list, changing from bitmap
        to Cartesian coordinates and specifying the left edge so that
@@ -268,22 +269,22 @@ find_one_centerline(bitmap_type    const bitmap,
     */
     {
         pm_pixelcoord pos;
-        pos.col = original_col; pos.row = bitmap.height - original_row - 1;
+        pos.col = originalCol; pos.row = bitmap.height - originalRow - 1;
         LOG2(" (%d,%d)", pos.col, pos.row);
         append_outline_pixel(&outline, pos);
     }
-    search_dir = original_dir;  /* initial value */
-    row = original_row;         /* initial value */
-    col = original_col;         /* initial values */
+    searchDir = originalDir;  /* initial value */
+    row = originalRow;         /* initial value */
+    col = originalCol;         /* initial values */
 
     for ( ; ; ) {
-        unsigned int const prev_row = row;
-        unsigned int const prev_col = col;
+        unsigned int const prevRow = row;
+        unsigned int const prevCol = col;
 
         /* If there is no adjacent, unmarked pixel, we can't proceed
            any further, so return an open outline.
         */
-        if (!next_unmarked_pixel(&row, &col, &search_dir, bitmap, marked)) {
+        if (!next_unmarked_pixel(&row, &col, &searchDir, bitmap, marked)) {
             outline.open = true;
             break;
         }
@@ -291,12 +292,12 @@ find_one_centerline(bitmap_type    const bitmap,
         /* If we've moved to a new pixel, mark all edges of the previous
            pixel so that it won't be revisited.
         */
-        if (!(prev_row == original_row && prev_col == original_col))
-            mark_dir(prev_row, prev_col, search_dir, marked);
-        mark_dir(row, col, (search_dir + 4) % 8, marked);
+        if (!(prevRow == originalRow && prevCol == originalCol))
+            mark_dir(prevRow, prevCol, searchDir, marked);
+        mark_dir(row, col, (searchDir + 4) % 8, marked);
 
         /* If we've returned to the starting pixel, we're done. */
-        if (row == original_row && col == original_col)
+        if (row == originalRow && col == originalCol)
             break;
 
         
@@ -308,7 +309,7 @@ find_one_centerline(bitmap_type    const bitmap,
             append_outline_pixel(&outline, pos);
         }
     }
-    mark_dir(original_row, original_col, original_dir, marked);
+    mark_dir(originalRow, originalCol, originalDir, marked);
 
     return outline;
 }
@@ -392,7 +393,7 @@ find_centerline_pixels(bitmap_type         const bitmap,
           LOG2("#%u: (%sclockwise) ", O_LIST_LENGTH(outline_list),
                clockwise ? "" : "counter");
 
-          outline = find_one_centerline(bitmap, dir, row, col, &marked);
+          outline = findOneCenterline(bitmap, dir, row, col, &marked);
 
           /* If the outline is open (i.e., we didn't return to the
              starting pixel), search from the starting pixel in the
@@ -450,7 +451,7 @@ find_centerline_pixels(bitmap_type         const bitmap,
               }
               if (okay) {
                   partial_outline =
-                      find_one_centerline(bitmap, dir, row, col, &marked);
+                      findOneCenterline(bitmap, dir, row, col, &marked);
                   concat_pixel_outline(&outline, &partial_outline);
                   if (partial_outline.data)
                       free(partial_outline.data);
diff --git a/converter/other/pamtosvg/spline.c b/converter/other/pamtosvg/spline.c
index 5bdf0c0e..61167ec4 100644
--- a/converter/other/pamtosvg/spline.c
+++ b/converter/other/pamtosvg/spline.c
@@ -105,11 +105,13 @@ new_spline_list_with_spline (spline_type spline)
    elements, since they are arrays in automatic storage.  And we don't
    want to free the list if it was empty.  */
 
+
+
 void
-free_spline_list (spline_list_type spline_list)
-{
-  if (SPLINE_LIST_DATA (spline_list) != NULL)
-    free (SPLINE_LIST_DATA (spline_list));
+free_spline_list(spline_list_type spline_list) {
+
+    if (SPLINE_LIST_DATA(spline_list) != NULL)
+        free(SPLINE_LIST_DATA(spline_list));
 }
 
 
diff --git a/converter/other/pamtosvg/spline.h b/converter/other/pamtosvg/spline.h
index 05a56e23..96ceab4e 100644
--- a/converter/other/pamtosvg/spline.h
+++ b/converter/other/pamtosvg/spline.h
@@ -21,6 +21,7 @@ typedef at_spline_type spline_type;
 #define START_POINT(spl)        ((spl).v[0])
 #define CONTROL1(spl)           ((spl).v[1])
 #define CONTROL2(spl)           ((spl).v[2])
+#define BEG_POINT(spl)          ((spl).v[0])
 #define END_POINT(spl)          ((spl).v[3])
 #define SPLINE_DEGREE(spl)      ((spl).degree)
 #define SPLINE_LINEARITY(spl)   ((spl).linearity)
diff --git a/converter/other/pamtosvg/vector.c b/converter/other/pamtosvg/vector.c
index 18d6de59..7678f73a 100644
--- a/converter/other/pamtosvg/vector.c
+++ b/converter/other/pamtosvg/vector.c
@@ -17,137 +17,150 @@ static float acos_d (float, at_exception_type * excep);
 /* Given the point COORD, return the corresponding vector.  */
 
 vector_type
-make_vector (const float_coord c)
-{
-  vector_type v;
+make_vector(float_coord const c) {
 
-  v.dx = c.x;
-  v.dy = c.y;
-  v.dz = c.z;
+    vector_type v;
+
+    v.dx = c.x;
+    v.dy = c.y;
+    v.dz = c.z;
 
-  return v;
+    return v;
 }
 
 
+
 /* And the converse: given a vector, return the corresponding point.  */
 
 float_coord
-vector_to_point (const vector_type v)
-{
-  float_coord coord;
+vector_to_point(vector_type const v) {
+
+    float_coord coord;
 
-  coord.x = v.dx;
-  coord.y = v.dy;
-  coord.z = v.dz;
+    coord.x = v.dx;
+    coord.y = v.dy;
+    coord.z = v.dz;
 
-  return coord;
+    return coord;
 }
 
 
-float
-magnitude (const vector_type v)
-{
-  return (float) sqrt (v.dx * v.dx + v.dy * v.dy + v.dz * v.dz);
-}
 
+float
+magnitude(vector_type const v) {
 
-vector_type
-normalize (const vector_type v)
-{
-  vector_type new_v;
-  float m = magnitude (v);
+    return sqrt(SQR(v.dx) + SQR(v.dy) + SQR(v.dz));
+}
 
-  /* assert (m > 0.0); */
 
-  if (m > 0.0)
-  {
-    new_v.dx = v.dx / m;
-    new_v.dy = v.dy / m;
-    new_v.dz = v.dz / m;
-  }
-  else
-  {
-	new_v.dx = v.dx;
-    new_v.dy = v.dy;
-    new_v.dz = v.dz;
-  }
 
-  return new_v;
+vector_type
+normalize(vector_type const v) {
+
+    vector_type new_v;
+    float const m = magnitude(v);
+
+    if (m > 0.0) {
+        new_v.dx = v.dx / m;
+        new_v.dy = v.dy / m;
+        new_v.dz = v.dz / m;
+    } else {
+        new_v.dx = v.dx;
+        new_v.dy = v.dy;
+        new_v.dz = v.dz;
+    }
+    
+    return new_v;
 }
 
 
+
 vector_type
-Vadd (const vector_type v1, const vector_type v2)
-{
-  vector_type new_v;
+Vadd(vector_type const v1,
+     vector_type const v2) {
+
+    vector_type new_v;
 
-  new_v.dx = v1.dx + v2.dx;
-  new_v.dy = v1.dy + v2.dy;
-  new_v.dz = v1.dz + v2.dz;
+    new_v.dx = v1.dx + v2.dx;
+    new_v.dy = v1.dy + v2.dy;
+    new_v.dz = v1.dz + v2.dz;
 
-  return new_v;
+    return new_v;
 }
 
 
+
 float
-Vdot (const vector_type v1, const vector_type v2)
-{
-  return v1.dx * v2.dx + v1.dy * v2.dy + v1.dz * v2.dz;
+Vdot(vector_type const v1,
+     vector_type const v2) {
+
+    return v1.dx * v2.dx + v1.dy * v2.dy + v1.dz * v2.dz;
 }
 
 
+
 vector_type
-Vmult_scalar (const vector_type v, const float r)
-{
-  vector_type new_v;
+Vmult_scalar(vector_type const v,
+             float       const r) {
 
-  new_v.dx = v.dx * r;
-  new_v.dy = v.dy * r;
-  new_v.dz = v.dz * r;
+    vector_type new_v;
 
-  return new_v;
+    new_v.dx = v.dx * r;
+    new_v.dy = v.dy * r;
+    new_v.dz = v.dz * r;
+
+    return new_v;
 }
 
 
+
 /* Given the IN_VECTOR and OUT_VECTOR, return the angle between them in
-   degrees, in the range zero to 180.  */
+   degrees, in the range zero to 180.
+*/
 
 float
-Vangle (const vector_type in_vector, 
-	const vector_type out_vector,
-	at_exception_type * exp)
-{
-  vector_type v1 = normalize (in_vector);
-  vector_type v2 = normalize (out_vector);
+Vangle(vector_type         const in_vector, 
+       vector_type         const out_vector,
+       at_exception_type * const exP) {
+
+    vector_type const v1 = normalize(in_vector);
+    vector_type const v2 = normalize(out_vector);
 
-  return acos_d (Vdot (v2, v1), exp);
+    return acos_d(Vdot(v2, v1), exP);
 }
 
 
+
 float_coord
-Vadd_point (const float_coord c, const vector_type v)
-{
-  float_coord new_c;
+Vadd_point(float_coord const c,
+           vector_type const v) {
+
+    float_coord new_c;
 
-  new_c.x = c.x + v.dx;
-  new_c.y = c.y + v.dy;
-  new_c.z = c.z + v.dz;
-  return new_c;
+    new_c.x = c.x + v.dx;
+    new_c.y = c.y + v.dy;
+    new_c.z = c.z + v.dz;
+
+    return new_c;
 }
 
 
+
 float_coord
-Vsubtract_point (const float_coord c, const vector_type v)
-{
-  float_coord new_c;
+Vsubtract_point(float_coord const c,
+                vector_type const v) {
+
+    float_coord new_c;
 
-  new_c.x = c.x - v.dx;
-  new_c.y = c.y - v.dy;
-  new_c.z = c.z - v.dz;
-  return new_c;
+    new_c.x = c.x - v.dx;
+    new_c.y = c.y - v.dy;
+    new_c.z = c.z - v.dz;
+
+    return new_c;
 }
 
 
+
 pm_pixelcoord
 Vadd_int_point(pm_pixelcoord const c,
                vector_type   const v) {
@@ -161,56 +174,73 @@ Vadd_int_point(pm_pixelcoord const c,
 }
 
 
+
 vector_type
-Vabs (const vector_type v)
-{
-  vector_type new_v;
+Vabs(vector_type const v) {
 
-  new_v.dx = (float) fabs (v.dx);
-  new_v.dy = (float) fabs (v.dy);
-  new_v.dz = (float) fabs (v.dz);
-  return new_v;
+    vector_type new_v;
+
+    new_v.dx = (float) fabs (v.dx);
+    new_v.dy = (float) fabs (v.dy);
+    new_v.dz = (float) fabs (v.dz);
+
+    return new_v;
 }
 
 
+
 /* Operations on points.  */
 
 float_coord
-Padd (const float_coord coord1, const float_coord coord2)
-{
-  float_coord sum;
+Padd(float_coord const coord1,
+     float_coord const coord2) {
 
-  sum.x = coord1.x + coord2.x;
-  sum.y = coord1.y + coord2.y;
-  sum.z = coord1.z + coord2.z;
+    float_coord sum;
 
-  return sum;
+    sum.x = coord1.x + coord2.x;
+    sum.y = coord1.y + coord2.y;
+    sum.z = coord1.z + coord2.z;
+
+    return sum;
 }
 
 
+
 float_coord
-Pmult_scalar (const float_coord coord, const float r)
-{
-  float_coord answer;
+Pmult_scalar(float_coord const coord,
+             float       const r) {
+
+    float_coord answer;
 
-  answer.x = coord.x * r;
-  answer.y = coord.y * r;
-  answer.z = coord.z * r;
+    answer.x = coord.x * r;
+    answer.y = coord.y * r;
+    answer.z = coord.z * r;
 
-  return answer;
+    return answer;
 }
 
 
+
 vector_type
-Psubtract (const float_coord c1, const float_coord c2)
-{
-  vector_type v;
+Psubtract(float_coord const c1,
+          float_coord const c2) {
+
+    vector_type v;
 
-  v.dx = c1.x - c2.x;
-  v.dy = c1.y - c2.y;
-  v.dz = c1.z - c2.z;
+    v.dx = c1.x - c2.x;
+    v.dy = c1.y - c2.y;
+    v.dz = c1.z - c2.z;
 
-  return v;
+    return v;
+}
+
+
+
+vector_type
+Pdirection(float_coord const final,
+           float_coord const initial) {
+
+    return normalize(Psubtract(final, initial));
 }
 
 
@@ -233,23 +263,27 @@ IPsubtract(pm_pixelcoord const coord1,
 
 
 static float
-acos_d (float v, at_exception_type * excep)
-{
-  float a;
-
-  if (epsilon_equal (v, 1.0))
-    v = 1.0;
-  else if (epsilon_equal (v, -1.0))
-    v = -1.0;
-
-  errno = 0;
-  a = (float) acos (v);
-  if (errno == ERANGE || errno == EDOM)
-    {
-      at_exception_fatal(excep, strerror(errno));
-      return 0.0;
-    }
-  
-  
-  return a * (float) 180.0 / (float) M_PI;
+acos_d(float               const v,
+       at_exception_type * const excepP) {
+
+    float vAdj;
+    float a;
+    float retval;
+
+    if (epsilon_equal(v, 1.0))
+        vAdj = 1.0;
+    else if (epsilon_equal(v, -1.0))
+        vAdj = -1.0;
+    else
+        vAdj = v;
+
+    errno = 0;
+    a = acos(vAdj);
+    if (errno == ERANGE || errno == EDOM) {
+        at_exception_fatal(excepP, strerror(errno));
+        retval = 0.0;
+    } else
+        retval = a * 180.0 / M_PI;
+
+    return retval;
 }
diff --git a/converter/other/pamtosvg/vector.h b/converter/other/pamtosvg/vector.h
index 74fb2fd2..b5c85c38 100644
--- a/converter/other/pamtosvg/vector.h
+++ b/converter/other/pamtosvg/vector.h
@@ -15,32 +15,58 @@ typedef struct
 
 
 /* Consider a point as a vector from the origin.  */
-extern vector_type make_vector (const float_coord);
+vector_type
+make_vector(float_coord const);
 
 /* And a vector as a point, i.e., a displacement from the origin.  */
-extern float_coord vector_to_point (const vector_type);
+float_coord
+vector_to_point(vector_type const);
 
 
 /* Definitions for these common operations can be found in any decent
    linear algebra book, and most calculus books.  */
 
-extern float magnitude (const vector_type);
-extern vector_type normalize (const vector_type);
+float
+magnitude(vector_type const);
+
+vector_type
+normalize(vector_type const);
+
+vector_type
+Vadd(vector_type const,
+     vector_type const);
 
-extern vector_type Vadd (const vector_type, const vector_type);
-extern float Vdot (const vector_type, const vector_type);
-extern vector_type Vmult_scalar (const vector_type, const float);
-extern float Vangle (const vector_type in, const vector_type out, at_exception_type * exp);
+float
+Vdot(vector_type const,
+     vector_type const);
+
+vector_type
+Vmult_scalar(vector_type const,
+             float       const);
+
+float
+Vangle(vector_type         const in,
+       vector_type         const out,
+       at_exception_type * const exP);
 
 /* These operations could have been named `P..._vector' just as well as
    V..._point, so we may as well allow both names.  */
+
 #define Padd_vector Vadd_point
-extern float_coord Vadd_point
-  (const float_coord, const vector_type);
+
+float_coord
+Vadd_point(float_coord const,
+           vector_type const);
 
 #define Psubtract_vector Vsubtract_point
-extern float_coord Vsubtract_point
-  (const float_coord, const vector_type);
+
+float_coord
+Vsubtract_point(float_coord const,
+                vector_type const);
+
+vector_type
+Pdirection(float_coord const final,
+           float_coord const initial);
 
 /* This returns the rounded sum.  */
 #define IPadd_vector Vadd_int_point
@@ -50,22 +76,28 @@ Vadd_int_point(pm_pixelcoord const c,
                vector_type   const v);
 
 /* Take the absolute value of both components.  */
-extern vector_type Vabs (const vector_type);
+vector_type
+Vabs(vector_type const);
 
 /* Operations on points with real coordinates.  It is not orthogonal,
    but more convenient, to have the subtraction operator return a
    vector, and the addition operator return a point.  */
-extern vector_type Psubtract
-  (const float_coord, const float_coord);
+vector_type
+Psubtract(float_coord const,
+          float_coord const);
 
 vector_type
 IPsubtract(pm_pixelcoord const coord1,
            pm_pixelcoord const coord2);
 
 /* These are heavily used in spline fitting.  */
-extern float_coord Padd (const float_coord,
-                                  const float_coord);
-extern float_coord Pmult_scalar (const float_coord, const float);
 
-#endif
+float_coord
+Padd(float_coord const,
+     float_coord const);
 
+float_coord
+Pmult_scalar(float_coord const,
+             float       const);
+
+#endif
diff --git a/converter/other/pamtotga.c b/converter/other/pamtotga.c
index 6c8769ed..c23b92b1 100644
--- a/converter/other/pamtotga.c
+++ b/converter/other/pamtotga.c
@@ -15,6 +15,7 @@
 
 #include <string.h>
 
+#include "pm_c_util.h"
 #include "pam.h"
 #include "pammap.h"
 #include "shhopt.h"
@@ -270,7 +271,7 @@ computeRunlengths(struct pam * const pamP,
 
 static void
 computeOutName(struct cmdlineInfo const cmdline, 
-               const char ** const outNameP) {
+               const char **      const outNameP) {
     
     char * workarea;
 
diff --git a/converter/other/pamtotiff.c b/converter/other/pamtotiff.c
index 62315f9b..1b31c65b 100644
--- a/converter/other/pamtotiff.c
+++ b/converter/other/pamtotiff.c
@@ -788,16 +788,6 @@ createTiffGenerator(int          const ofd,
 
     const char * option;
 
-    /* Before 10.12 (November 2002), we set O_NONBLOCK here:
-
-       fcntl( 1, F_SETFL, O_NONBLOCK ) ; 
-   
-       I have no idea why.  The comment attached said, 
-
-         acooke dec99 - otherwise blocks on read inside 
-         next line (Linux i386) 
-    */
-
     validateSeekableOutputFile(ofd, outFileName);
 
     if (append)
@@ -1031,6 +1021,8 @@ validateReadableStdout(void) {
   error message about a file I/O error.  We, on the other hand, produce
   a helpful error message.
 -----------------------------------------------------------------------------*/
+#if !defined(WIN32) || defined(__CYGWIN__)
+
     int flags;
 
     flags = fcntl(STDOUT_FILENO, F_GETFL);
@@ -1045,6 +1037,7 @@ validateReadableStdout(void) {
                      "In order to create a multi-image TIFF stream, "
                      "Standard Output must be both readable and writable.");
     }
+#endif
 }
 
 
diff --git a/converter/other/pamtouil.c b/converter/other/pamtouil.c
index b9972abd..1a53be0f 100644
--- a/converter/other/pamtouil.c
+++ b/converter/other/pamtouil.c
@@ -17,6 +17,8 @@
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 #include <ctype.h>
 #include <string.h>
+
+#include "pm_c_util.h"
 #include "pam.h"
 #include "pammap.h"
 #include "colorname.h"
@@ -34,18 +36,18 @@ struct cmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    const char *inputFilespec;  /* Filespecs of input files */
-    char *outname;         /* output filename, less "_icon" */
+    const char * inputFilespec;  /* Filespecs of input files */
+    char * outname;         /* output filename, less "_icon" */
     unsigned int verbose;
 };
 
 
 
 typedef struct {    /* character-pixel mapping */
-    const char* cixel;    /* character string printed for pixel */
-    const char* rgbname;  /* ascii rgb color, either mnemonic or #rgb value */
-    const char* uilname;  /* same, with spaces replaced by underbars */
-    bool        transparent;
+    const char * cixel;    /* character string printed for pixel */
+    const char * rgbname;  /* ascii rgb color, either mnemonic or #rgb value */
+    const char * uilname;  /* same, with spaces replaced by underbars */
+    bool         transparent;
 } cixel_map;
 
 
@@ -95,10 +97,10 @@ parseCommandLine(int argc, char ** argv,
 
         /* Remove trailing "_icon" */
         barPos = strrchr(cmdlineP->outname, '_');
-        if (barPos && STREQ(barPos, "_icon")) 
+        if (barPos && streq(barPos, "_icon")) 
             *barPos = '\0';
     } else {
-        if (STREQ(cmdlineP->inputFilespec, "-"))
+        if (streq(cmdlineP->inputFilespec, "-"))
             cmdlineP->outname = strdup("noname");
         else {
             char * dotPos;
@@ -154,7 +156,8 @@ genNumstr(int  const number,
 
 
 static const char * 
-uilName(const char * const rgbname, bool const transparent) {
+uilName(const char * const rgbname,
+        bool         const transparent) {
 /*----------------------------------------------------------------------------
    Return a string in newly malloc'ed storage which is an appropriate 
    color name for the UIL palette.  It is the same as the rgb name,
@@ -200,8 +203,8 @@ genCmap(struct pam *   const pamP,
     unsigned int colorIndex;
     {
         /* Figure out how many characters per pixel we'll be using.
-        ** Don't want to be forced to link with libm.a, so using a
-        ** division loop rather than a log function.  
+           Don't want to be forced to link with libm.a, so using a
+           division loop rather than a log function.  
         */
         unsigned int i;
         for (*charsppP = 0, i = ncolors; i > 0; ++(*charsppP))
@@ -209,7 +212,7 @@ genCmap(struct pam *   const pamP,
     }
 
     /* Generate the character-pixel string and the rgb name for each colormap
-    ** entry.
+       entry.
     */
     for (colorIndex = 0; colorIndex < ncolors; ++colorIndex) {
         bool nameAlreadyInCmap;
@@ -237,7 +240,7 @@ genCmap(struct pam *   const pamP,
         nameAlreadyInCmap = FALSE;   /* initial assumption */
         for (j = 0; j < colorIndex; ++j) {
             if (cmap[j].rgbname != NULL && 
-                STREQ(colorname, cmap[j].rgbname) &&
+                streq(colorname, cmap[j].rgbname) &&
                 cmap[j].transparent == transparent) {
                 nameAlreadyInCmap = TRUE;
                 indexOfName = j;
@@ -247,12 +250,12 @@ genCmap(struct pam *   const pamP,
             /* Make the entry a cross-reference to the earlier entry */
             cmap[colorIndex].uilname = NULL;
             cmap[colorIndex].rgbname = NULL;
-            cmap[colorIndex].cixel = cmap[indexOfName].cixel;
+            cmap[colorIndex].cixel   = cmap[indexOfName].cixel;
         } else {
             cmap[colorIndex].uilname = uilName(colorname, transparent);
             cmap[colorIndex].rgbname = strdup(colorname);
             if (cmap[colorIndex].rgbname == NULL)
-                pm_error( "out of memory allocating color name" );
+                pm_error("out of memory allocating color name");
             
             cmap[colorIndex].transparent = transparent;
             
@@ -329,15 +332,16 @@ writeRaster(struct pam *  const pamP,
             unsigned int  const ncolors,
             tuplehash     const cht, 
             unsigned int  const charspp) {
-    
-    int row;
-    /* Write out the ascii character-pixel image. */
+/*----------------------------------------------------------------------------
+   Write out the ascii character-pixel image.
+-----------------------------------------------------------------------------*/
+    unsigned int row;
 
     printf("\n");
     printf("%s_icon : exported icon( color_table = %s_rgb,\n",
            outname, outname);
     for (row = 0; row < pamP->height; ++row) {
-        int col;
+        unsigned int col;
 
         printf("    '");
         for (col = 0; col < pamP->width; ++col) {
@@ -360,27 +364,19 @@ writeRaster(struct pam *  const pamP,
 }
 
 
-static void
-freeString(const char * const s) {
-/*----------------------------------------------------------------------------
-   This is just free(), but with type checking for const char *.
------------------------------------------------------------------------------*/
-    free((void *)s);
-}
-
-
 
 static void
-freeCmap(cixel_map cmap[], unsigned int const ncolors) {
+freeCmap(cixel_map    const cmap[],
+         unsigned int const ncolors) {
 
-    int i;
+    unsigned int i;
 
     for (i = 0; i < ncolors; ++i) {
         cixel_map const cmapEntry = cmap[i];
         if (cmapEntry.uilname)
-            freeString(cmapEntry.uilname);
+            strfree(cmapEntry.uilname);
         if (cmapEntry.rgbname)
-            freeString(cmapEntry.rgbname);
+            strfree(cmapEntry.rgbname);
     }
 }
 
@@ -391,8 +387,8 @@ main(int argc, char *argv[]) {
 
     struct cmdlineInfo cmdline;
     struct pam pam;   /* Input PAM image */
-    FILE* ifP;
-    tuple** tuples;
+    FILE * ifP;
+    tuple ** tuples;
     unsigned int ncolors;
     tuplehash cht;
     tupletable chv;
diff --git a/converter/other/pamtoxvmini.c b/converter/other/pamtoxvmini.c
index 3207b0d5..e1aa9b52 100644
--- a/converter/other/pamtoxvmini.c
+++ b/converter/other/pamtoxvmini.c
@@ -67,7 +67,6 @@ makeXvPalette(xvPalette * const xvPaletteP) {
             }
         }
     }
-
 }
 
 
@@ -237,7 +236,7 @@ main(int    argc,
     struct pam pam;
     xvPalette xvPalette;
  
-    ppm_init(&argc, argv);
+    pnm_init(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
diff --git a/converter/other/pfmtopam.c b/converter/other/pfmtopam.c
index 9430f5fa..1617ed31 100644
--- a/converter/other/pfmtopam.c
+++ b/converter/other/pfmtopam.c
@@ -15,6 +15,7 @@
 #include <string.h>
 #include <assert.h>
 
+#include "pm_c_util.h"
 #include "pam.h"
 #include "pm_gamma.h"
 #include "shhopt.h"
diff --git a/converter/other/pgmtopbm.c b/converter/other/pgmtopbm.c
index 98bdc332..f828b716 100644
--- a/converter/other/pgmtopbm.c
+++ b/converter/other/pgmtopbm.c
@@ -11,10 +11,12 @@
 */
 
 #include <assert.h>
+
+#include "pm_c_util.h"
+#include "shhopt.h"
 #include "pgm.h"
 #include "dithers.h"
 #include "mallocvar.h"
-#include "shhopt.h"
 
 enum halftone {QT_FS, QT_THRESH, QT_DITHER8, QT_CLUSTER, QT_HILBERT};
 
@@ -346,6 +348,10 @@ struct converter {
 
 
 
+/*=============================================================================
+                 Converter: fs
+=============================================================================*/
+
 unsigned int const fs_scale      = 1024;
 unsigned int const half_fs_scale = 512;
 
@@ -445,7 +451,7 @@ createFsConverter(unsigned int const cols,
     /* Initialize Floyd-Steinberg error vectors. */
     MALLOCARRAY_NOFAIL(stateP->thiserr, cols + 2);
     MALLOCARRAY_NOFAIL(stateP->nexterr, cols + 2);
-    srand((int)(time(NULL) ^ getpid()));
+    srand(pm_randseed());
 
     {
         /* (random errors in [-fs_scale/8 .. fs_scale/8]) */
@@ -468,6 +474,10 @@ createFsConverter(unsigned int const cols,
 
 
 
+/*=============================================================================
+                 Converter: thresh
+=============================================================================*/
+
 struct threshState {
     gray threshval;
 };
@@ -521,16 +531,28 @@ createThreshConverter(unsigned int const cols,
 
 
 
+/*=============================================================================
+                 Converter: dither8
+=============================================================================*/
+
+struct dither8State {
+    int dither8[16][16];
+};
+
+
+
 static void
 dither8ConvertRow(struct converter * const converterP,
                   unsigned int       const row,
                   gray                     grayrow[],
                   bit                      bitrow[]) {
 
+    struct dither8State * const stateP = converterP->stateP;
+
     unsigned int col;
 
     for (col = 0; col < converterP->cols; ++col)
-        if (grayrow[col] > dither8[row % 16][col % 16])
+        if (grayrow[col] > stateP->dither8[row % 16][col % 16])
             bitrow[col] = PBM_WHITE;
         else
             bitrow[col] = PBM_BLACK;
@@ -538,29 +560,47 @@ dither8ConvertRow(struct converter * const converterP,
 
 
 
+static void
+dither8Destroy(struct converter * const converterP) {
+
+    struct dither8State * const stateP = converterP->stateP;
+
+    free(stateP);
+}
+
+
+
 static struct converter
 createDither8Converter(unsigned int const cols, 
                        gray         const maxval) {
 
     struct converter converter;
+    struct dither8State * stateP;
 
     unsigned int row;
 
+    MALLOCVAR_NOFAIL(stateP);
+
     converter.cols = cols;
     converter.convertRow = &dither8ConvertRow;
-    converter.destroy = NULL;
+    converter.destroy = dither8Destroy;
+    converter.stateP = stateP;
 
     /* Scale dither matrix. */
     for (row = 0; row < 16; ++row) {
         unsigned int col;
         for (col = 0; col < 16; ++col)
-            dither8[row][col] = dither8[row][col] * maxval / 256;
+            stateP->dither8[row][col] = dither8[row][col] * maxval / 256;
     }
     return converter;
 }
 
 
 
+/*=============================================================================
+                 Converter: cluster
+=============================================================================*/
+
 struct clusterState {
     unsigned int radius;
     int ** clusterMatrix;
diff --git a/converter/other/pgmtoppm.c b/converter/other/pgmtoppm.c
index 7693fe0a..7194db49 100644
--- a/converter/other/pgmtoppm.c
+++ b/converter/other/pgmtoppm.c
@@ -10,147 +10,232 @@
 ** implied warranty.
 */
 
+#define _BSD_SOURCE  /* Make sure strdup() is in <string.h> */
 #include <string.h>
+
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "shhopt.h"
 #include "ppm.h"
 
-int
-main( argc, argv )
-    int argc;
-    char* argv[];
-    {
-    FILE* ifp;
-    gray* grayrow;
-    register gray* gP;
-    pixel p;
-    pixel* pixelrow;
-    register pixel* pP;
-    pixel** mappixels;
-    int argn, rows, cols, format, maprows, mapcols, mapmaxcolor, row;
-    register int col;
-    gray maxval;
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * inputFilename;  /* '-' if stdin */
+    const char * map;
+    const char * colorBlack;
+    const char * colorWhite;
+};
+
+
+
+static void
+parseCommandLine(int argc, char ** argv,
+                 struct cmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   parse program command line described in Unix standard form by argc
+   and argv.  Return the information in the options as *cmdlineP.  
+
+   If command line is internally inconsistent (invalid options, etc.),
+   issue error message to stderr and abort program.
+
+   Note that the strings we return are stored in the storage that
+   was passed to us as the argv array.  We also trash *argv.
+-----------------------------------------------------------------------------*/
+    optEntry *option_def;
+        /* Instructions to optParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    unsigned int mapSpec;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "map",            OPT_STRING,    &cmdlineP->map,
+            &mapSpec,            0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (!mapSpec)
+        cmdlineP->map = NULL;
+
+    if (mapSpec) {
+        /* No color argument; only argument is file name */
+        if (argc-1 < 1)
+            cmdlineP->inputFilename = "-";
+        else {
+            cmdlineP->inputFilename = argv[1];
+            if (argc-1 > 1)
+                pm_error("With -map option, there is at most one argument: "
+                         "the file name.  You specified %u", argc-1);
+        }
+    } else {
+        /* Arguments are color or color range and file name */
+        if (argc-1 < 1) {
+            cmdlineP->colorBlack = "black";
+            cmdlineP->colorWhite = "white";
+        } else {
+            char * buffer = strdup(argv[1]);
+            char * hyphenPos = strchr(buffer, '-');
+            if (hyphenPos) {
+                *hyphenPos = '\0';
+                cmdlineP->colorBlack = buffer;
+                cmdlineP->colorWhite = hyphenPos+1;
+            } else {
+                cmdlineP->colorBlack = "black";
+                cmdlineP->colorWhite = buffer;
+            }
+        }
+        if (argc-1 < 2)
+            cmdlineP->inputFilename = "-";
+        else
+            cmdlineP->inputFilename = argv[2];
+        
+        if (argc-1 > 2)
+            pm_error("Program takes at most 2 arguments:  "
+                     "color name/range and input file name.  "
+                     "You specified %u", argc-1);
+    }
+}
+
+
+
+static void
+convertWithMap(FILE * const ifP,
+               unsigned int const cols,
+               unsigned int const rows,
+               gray         const maxval,
+               int          const format,
+               const char * const mapFileName,
+               FILE *       const ofP,
+               gray *       const grayrow,
+               pixel *      const pixelrow) {
+
+    unsigned int row;
+    FILE * mapFileP;
+    int mapcols, maprows;
     pixval mapmaxval;
-    char* color0;
-    char* color1;
+    pixel ** mappixels;
+    unsigned int mapmaxcolor;
+    
+    mapFileP = pm_openr(mapFileName);
+    mappixels = ppm_readppm(mapFileP, &mapcols, &maprows, &mapmaxval);
+    pm_close(mapFileP);
+    mapmaxcolor = maprows * mapcols - 1;
+
+    ppm_writeppminit(ofP, cols, rows, mapmaxval, 0);
+
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+            
+        pgm_readpgmrow(ifP, grayrow, cols, maxval, format);
+
+        for (col = 0; col < cols; ++col) {
+            unsigned int c;
+            if (maxval == mapmaxcolor)
+                c = grayrow[col];
+            else
+                c = grayrow[col] * mapmaxcolor / maxval;
+            pixelrow[col] = mappixels[c / mapcols][c % mapcols];
+        }
+        ppm_writeppmrow(ofP, pixelrow, cols, mapmaxval, 0);
+    }
+    ppm_freearray(mappixels, maprows);
+}
+
+
+
+static void
+convertLinear(FILE * const ifP,
+              unsigned int const cols,
+              unsigned int const rows,
+              gray         const maxval,
+              int          const format,
+              const char * const colorNameBlack,
+              const char * const colorNameWhite,
+              FILE *       const ofP,
+              gray *       const grayrow,
+              pixel *      const pixelrow) {
+
+    pixel colorBlack, colorWhite;
     pixval red0, grn0, blu0, red1, grn1, blu1;
-    const char* const usage = "<colorspec> [pgmfile]\n                 <colorspec1>,<colorspec2> [pgmfile]\n                 -map mapfile [pgmfile]";
-
-
-    ppm_init( &argc, argv );
-
-    argn = 1;
-    mappixels = (pixel**) 0;
-
-    if ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
-	{
-	if ( pm_keymatch( argv[argn], "-map", 2 ) )
-	    {
-	    ++argn;
-	    if ( argn == argc )
-		pm_usage( usage );
-	    ifp = pm_openr( argv[argn] );
-	    mappixels = ppm_readppm( ifp, &mapcols, &maprows, &mapmaxval );
-	    pm_close( ifp );
-	    mapmaxcolor = maprows * mapcols - 1;
-	    }
-	else
-	    pm_usage( usage );
-	++argn;
-	}
-
-    if ( mappixels == (pixel**) 0 )
-	{
-	if ( argn == argc )
-	    pm_usage( usage );
-	color0 = argv[argn];
-	++argn;
-	}
-
-    if ( argn != argc )
-	{
-	ifp = pm_openr( argv[argn] );
-	++argn;
-	}
-    else
-	ifp = stdin;
+    unsigned int row;
+
+    ppm_writeppminit(ofP, cols, rows, maxval, 0);
+
+    colorBlack = ppm_parsecolor(colorNameBlack, maxval);
+    colorWhite = ppm_parsecolor(colorNameWhite, maxval);
+ 
+    red0 = PPM_GETR(colorBlack);
+    grn0 = PPM_GETG(colorBlack);
+    blu0 = PPM_GETB(colorBlack);
+    red1 = PPM_GETR(colorWhite);
+    grn1 = PPM_GETG(colorWhite);
+    blu1 = PPM_GETB(colorWhite);
+
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+
+        pgm_readpgmrow(ifP, grayrow, cols, maxval, format);
+
+        for (col = 0; col < cols; ++col) {
+            gray const input = grayrow[col];
+            PPM_ASSIGN(
+                pixelrow[col],
+                (red0 * (maxval - input) + red1 * input) / maxval,
+                (grn0 * (maxval - input) + grn1 * input) / maxval,
+                (blu0 * (maxval - input) + blu1 * input) / maxval);
+        }
+        ppm_writeppmrow(ofP, pixelrow, cols, maxval, 0);
+    }
+}
 
-    if ( argn != argc )
-	pm_usage( usage );
 
-    pgm_readpgminit( ifp, &cols, &rows, &maxval, &format );
-    grayrow = pgm_allocrow( cols );
-    if ( mappixels == (pixel**) 0 )
-	ppm_writeppminit( stdout, cols, rows, (pixval) maxval, 0 );
-    else
-	ppm_writeppminit( stdout, cols, rows, mapmaxval, 0 );
-    pixelrow = ppm_allocrow( cols );
-
-    if ( mappixels == (pixel**) 0 )
-	{
-	color1 = strchr( color0, '-' );
-	if ( color1 == 0 )
-	    {
-	    color1 = color0;
-	    red0 = 0;
-	    grn0 = 0;
-	    blu0 = 0;
-	    }
-	else
-	    {
-	    *color1 = '\0';
-	    ++color1;
-	    p = ppm_parsecolor( color0, (pixval) maxval );
-	    red0 = PPM_GETR( p );
-	    grn0 = PPM_GETG( p );
-	    blu0 = PPM_GETB( p );
-	    }
-	p = ppm_parsecolor( color1, (pixval) maxval );
-	red1 = PPM_GETR( p );
-	grn1 = PPM_GETG( p );
-	blu1 = PPM_GETB( p );
-	}
-
-    for ( row = 0; row < rows; ++row )
-	{
-	pgm_readpgmrow( ifp, grayrow, cols, maxval, format );
-
-	if ( mappixels == (pixel**) 0 )
-	    {
-	    for ( col = 0, gP = grayrow, pP = pixelrow;
-		  col < cols;
-		  ++col, ++gP, ++pP )
-		PPM_ASSIGN(
-		    *pP,
-		    ( red0 * ( maxval - *gP ) + red1 * *gP ) / maxval,
-		    ( grn0 * ( maxval - *gP ) + grn1 * *gP ) / maxval,
-		    ( blu0 * ( maxval - *gP ) + blu1 * *gP ) / maxval );
-
-	    }
-	else
-	    {
-	    register int c;
-
-	    for ( col = 0, gP = grayrow, pP = pixelrow;
-		  col < cols;
-		  ++col, ++gP, ++pP )
-		{
-		if ( maxval == mapmaxcolor )
-		    c = *gP;
-		else
-		    c = *gP * mapmaxcolor / maxval;
-		*pP = mappixels[c / mapcols][c % mapcols];
-		}
-	    }
-
-    if (!mappixels)
-        ppm_writeppmrow( stdout, pixelrow, cols, (pixval) maxval, 0 );
+
+int
+main(int    argc,
+     char * argv[]) {
+
+    FILE * ifP;
+    struct cmdlineInfo cmdline;
+    gray * grayrow;
+    pixel * pixelrow;
+    int rows, cols, format;
+    gray maxval;
+
+    ppm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFilename);
+
+    pgm_readpgminit(ifP, &cols, &rows, &maxval, &format);
+    grayrow = pgm_allocrow(cols);
+    pixelrow = ppm_allocrow(cols);
+
+    if (cmdline.map)
+        convertWithMap(ifP, cols, rows, maxval, format, cmdline.map,
+                       stdout, grayrow, pixelrow);
     else
-        ppm_writeppmrow( stdout, pixelrow, cols, mapmaxval, 0 );
-	}
+        convertLinear(ifP, cols, rows, maxval, format, 
+                      cmdline.colorBlack, cmdline.colorWhite, stdout,
+                      grayrow, pixelrow);
 
-    pm_close( ifp );
+    ppm_freerow(pixelrow);
+    pgm_freerow(grayrow);
+    pm_close(ifP);
 
     /* If the program failed, it previously aborted with nonzero completion
        code, via various function calls.
     */
     return 0;
-    }
+}
diff --git a/converter/other/pngtopam.c b/converter/other/pngtopam.c
new file mode 100644
index 00000000..89ac100a
--- /dev/null
+++ b/converter/other/pngtopam.c
@@ -0,0 +1,1281 @@
+/*
+** Copyright (C) 1995,1998 by Alexander Lehmann <alex@hal.rhein-main.de>
+**                        and Willem van Schaik <willem@schaik.com>
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+**
+** modeled after giftopnm by David Koblas and
+** with lots of bits pasted from libpng.txt by Guy Eric Schalnat
+*/
+
+#ifndef PNMTOPNG_WARNING_LEVEL
+#  define PNMTOPNG_WARNING_LEVEL 0   /* use 0 for backward compatibility, */
+#endif                               /*  2 for warnings (1 == error) */
+
+
+#include <assert.h>
+#include <math.h>
+#include <float.h>
+#include <png.h>    /* includes zlib.h and setjmp.h */
+#define VERSION "2.37.4 (5 December 1999) +netpbm"
+
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "nstring.h"
+#include "shhopt.h"
+#include "pam.h"
+
+/* A hack until we can remove direct access to png_info from the program */
+#if PNG_LIBPNG_VER >= 10400
+#define trans_values trans_color
+#define TRANS_ALPHA trans_alpha
+#else
+#define TRANS_ALPHA trans
+#endif
+
+typedef struct _jmpbuf_wrapper {
+  jmp_buf jmpbuf;
+} jmpbuf_wrapper;
+
+enum alpha_handling {ALPHA_NONE, ALPHA_ONLY, ALPHA_MIX, ALPHA_IN};
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char *inputFilespec;  /* '-' if stdin */
+    unsigned int verbose;
+    enum alpha_handling alpha;
+    const char * background;
+    float gamma;  /* -1.0 means unspecified */
+    const char * text;
+    unsigned int time;
+};
+
+
+typedef struct {
+/*----------------------------------------------------------------------------
+   A color in a format compatible with the PNG library.
+
+   Note that the PNG library declares types png_color and png_color_16
+   which are similar.
+-----------------------------------------------------------------------------*/
+    png_uint_16 r;
+    png_uint_16 g;
+    png_uint_16 b;
+} pngcolor;
+
+
+static png_uint_16 maxval;
+static bool verbose;
+static jmpbuf_wrapper pngtopnm_jmpbuf_struct;
+
+
+static void
+parseCommandLine(int                  argc, 
+                 const char **        argv,
+                 struct cmdlineInfo * cmdlineP ) {
+/*----------------------------------------------------------------------------
+   Parse program command line described in Unix standard form by argc
+   and argv.  Return the information in the options as *cmdlineP.  
+
+   If command line is internally inconsistent (invalid options, etc.),
+   issue error message to stderr and abort program.
+
+   Note that the strings we return are stored in the storage that
+   was passed to us as the argv array.  We also trash *argv.
+-----------------------------------------------------------------------------*/
+    optEntry * option_def;
+        /* Instructions to optParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    unsigned int alphaSpec, alphapamSpec, mixSpec,
+        backgroundSpec, gammaSpec, textSpec;
+
+    MALLOCARRAY(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "verbose",     OPT_FLAG,   NULL,                  
+            &cmdlineP->verbose,       0);
+    OPTENT3(0, "alpha",       OPT_FLAG,   NULL,                  
+            &alphaSpec,               0);
+    OPTENT3(0, "alphapam",    OPT_FLAG,   NULL,                  
+            &alphapamSpec,            0);
+    OPTENT3(0, "mix",         OPT_FLAG,   NULL,                  
+            &mixSpec,                 0);
+    OPTENT3(0, "background",  OPT_STRING, &cmdlineP->background,
+            &backgroundSpec,          0);
+    OPTENT3(0, "gamma",       OPT_FLOAT,  &cmdlineP->gamma,
+            &gammaSpec,               0);
+    OPTENT3(0, "text",        OPT_STRING, &cmdlineP->text,
+            &textSpec,                0);
+    OPTENT3(0, "time",        OPT_FLAG,   NULL,                  
+            &cmdlineP->time,          0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+
+    if (alphaSpec + mixSpec + alphapamSpec > 1)
+        pm_error("You cannot specify more than one of -alpha -alphapam -mix");
+    else if (alphaSpec)
+        cmdlineP->alpha = ALPHA_ONLY;
+    else if (mixSpec)
+        cmdlineP->alpha = ALPHA_MIX;
+    else if (alphapamSpec)
+        cmdlineP->alpha = ALPHA_IN;
+    else
+        cmdlineP->alpha = ALPHA_NONE;
+
+    if (backgroundSpec && !mixSpec)
+        pm_error("-background is useless without -mix");
+
+    if (!backgroundSpec)
+        cmdlineP->background = NULL;
+
+    if (!gammaSpec)
+        cmdlineP->gamma = -1.0;
+
+    if (!textSpec)
+        cmdlineP->text = NULL;
+
+    if (argc-1 < 1)
+        cmdlineP->inputFilespec = "-";
+    else if (argc-1 == 1)
+        cmdlineP->inputFilespec = argv[1];
+    else
+        pm_error("Program takes at most one argument: input file name.  "
+            "you specified %d", argc-1);
+}
+
+
+
+static png_uint_16
+get_png_val(const png_byte ** const pp,
+            int               const bit_depth) {
+
+    png_uint_16 c;
+    
+    if (bit_depth == 16)
+        c = (*((*pp)++)) << 8;
+    else
+        c = 0;
+
+    c |= (*((*pp)++));
+    
+    return c;
+}
+
+
+
+static bool
+isGrayscale(pngcolor const color) {
+
+    return color.r == color.g && color.r == color.b;
+}
+
+
+
+static sample
+alphaMix(png_uint_16 const foreground,
+         png_uint_16 const background,
+         png_uint_16 const alpha,
+         sample      const maxval) {
+
+    double const opacity      = (double)alpha / maxval;
+    double const transparency = 1.0 - opacity;
+
+    return ROUNDU(foreground * opacity + background * transparency);
+}
+
+
+
+static void
+setTuple(const struct pam *  const pamP,
+         tuple               const tuple,
+         pngcolor            const foreground,
+         pngcolor            const background,
+         enum alpha_handling const alphaHandling,
+         png_uint_16         const alpha) {
+
+    if (alphaHandling == ALPHA_ONLY)
+        tuple[0] = alpha;
+    else if (alphaHandling == ALPHA_NONE ||
+             (alphaHandling == ALPHA_MIX && alpha == maxval)) {
+        if (pamP->depth < 3)
+            tuple[0] = foreground.r;
+        else {
+            tuple[PAM_RED_PLANE] = foreground.r;
+            tuple[PAM_GRN_PLANE] = foreground.g;
+            tuple[PAM_BLU_PLANE] = foreground.b;
+        }
+    } else if (alphaHandling == ALPHA_IN) {
+        if (pamP->depth < 4) {
+            tuple[0] = foreground.r;
+            tuple[PAM_GRAY_TRN_PLANE] = alpha;
+        } else {
+            tuple[PAM_RED_PLANE] = foreground.r;
+            tuple[PAM_GRN_PLANE] = foreground.g;
+            tuple[PAM_BLU_PLANE] = foreground.b;
+            tuple[PAM_TRN_PLANE] = alpha;
+        }    
+    } else {
+        assert(alphaHandling == ALPHA_MIX);
+
+        if (pamP->depth < 3)
+            tuple[0] =
+                alphaMix(foreground.r, background.r, alpha, maxval);
+        else {
+            tuple[PAM_RED_PLANE] =
+                alphaMix(foreground.r, background.r, alpha, maxval);
+            tuple[PAM_GRN_PLANE] =
+                alphaMix(foreground.g, background.g, alpha, maxval);
+            tuple[PAM_BLU_PLANE] =
+                alphaMix(foreground.b, background.b, alpha, maxval);
+        }
+    }
+}
+
+
+
+static png_uint_16
+gamma_correct(png_uint_16 const v,
+              float       const g) {
+
+    if (g != -1.0)
+        return (png_uint_16) ROUNDU(pow((double) v / maxval, (1.0 / g)) *
+                                    maxval);
+    else
+        return v;
+}
+
+
+
+static int iscolor (png_color c)
+{
+  return c.red != c.green || c.green != c.blue;
+}
+
+static void save_text (png_info *info_ptr, FILE *tfp)
+{
+  int i, j, k;
+
+  for (i = 0 ; i < info_ptr->num_text ; i++) {
+    j = 0;
+    while (info_ptr->text[i].key[j] != '\0' && info_ptr->text[i].key[j] != ' ')
+      j++;    
+    if (info_ptr->text[i].key[j] != ' ') {
+      fprintf (tfp, "%s", info_ptr->text[i].key);
+      for (j = strlen (info_ptr->text[i].key) ; j < 15 ; j++)
+        putc (' ', tfp);
+    } else {
+      fprintf (tfp, "\"%s\"", info_ptr->text[i].key);
+      for (j = strlen (info_ptr->text[i].key) ; j < 13 ; j++)
+        putc (' ', tfp);
+    }
+    putc (' ', tfp); /* at least one space between key and text */
+    
+    for (j = 0 ; j < info_ptr->text[i].text_length ; j++) {
+      putc (info_ptr->text[i].text[j], tfp);
+      if (info_ptr->text[i].text[j] == '\n')
+        for (k = 0 ; k < 16 ; k++)
+          putc ((int)' ', tfp);
+    }
+    putc ((int)'\n', tfp);
+  }
+}
+
+static void show_time (png_info *info_ptr)
+{
+    static const char * const month[] = {
+        "", "January", "February", "March", "April", "May", "June",
+        "July", "August", "September", "October", "November", "December"
+    };
+
+  if (info_ptr->valid & PNG_INFO_tIME) {
+    if (info_ptr->mod_time.month < 1 ||
+      info_ptr->mod_time.month >= ARRAY_SIZE(month)) {
+      pm_message("tIME chunk in PNG input is invalid; "
+                 "modification time of image is unknown.  "
+                 "The month value, which should be in the range "
+                 "1-12, is %u", info_ptr->mod_time.month);
+    } else
+    pm_message ("modification time: %02d %s %d %02d:%02d:%02d",
+                info_ptr->mod_time.day, month[info_ptr->mod_time.month],
+                info_ptr->mod_time.year, info_ptr->mod_time.hour,
+                info_ptr->mod_time.minute, info_ptr->mod_time.second);
+  }
+}
+
+static void pngtopnm_error_handler (png_structp png_ptr, png_const_charp msg)
+{
+  jmpbuf_wrapper  *jmpbuf_ptr;
+
+  /* this function, aside from the extra step of retrieving the "error
+   * pointer" (below) and the fact that it exists within the application
+   * rather than within libpng, is essentially identical to libpng's
+   * default error handler.  The second point is critical:  since both
+   * setjmp() and longjmp() are called from the same code, they are
+   * guaranteed to have compatible notions of how big a jmp_buf is,
+   * regardless of whether _BSD_SOURCE or anything else has (or has not)
+   * been defined. */
+
+  pm_message("fatal libpng error: %s", msg);
+
+  jmpbuf_ptr = png_get_error_ptr(png_ptr);
+  if (jmpbuf_ptr == NULL) {
+      /* we are completely hosed now */
+      pm_error("EXTREMELY fatal error: jmpbuf unrecoverable; terminating.");
+  }
+
+  longjmp(jmpbuf_ptr->jmpbuf, 1);
+}
+
+
+
+static void
+dump_png_info(png_info *info_ptr) {
+
+    const char *type_string;
+    const char *filter_string;
+
+    switch (info_ptr->color_type) {
+      case PNG_COLOR_TYPE_GRAY:
+        type_string = "gray";
+        break;
+
+      case PNG_COLOR_TYPE_GRAY_ALPHA:
+        type_string = "gray+alpha";
+        break;
+
+      case PNG_COLOR_TYPE_PALETTE:
+        type_string = "palette";
+        break;
+
+      case PNG_COLOR_TYPE_RGB:
+        type_string = "truecolor";
+        break;
+
+      case PNG_COLOR_TYPE_RGB_ALPHA:
+        type_string = "truecolor+alpha";
+        break;
+    }
+
+    switch (info_ptr->filter_type) {
+    case PNG_FILTER_TYPE_BASE:
+        asprintfN(&filter_string, "base filter");
+        break;
+    default:
+        asprintfN(&filter_string, "unknown filter type %d", 
+                  info_ptr->filter_type);
+    }
+
+    pm_message("reading a %ldw x %ldh image, %d bit%s",
+               info_ptr->width, info_ptr->height,
+               info_ptr->bit_depth, info_ptr->bit_depth > 1 ? "s" : "");
+    pm_message("%s, %s, %s",
+               type_string,
+               info_ptr->interlace_type ? 
+               "Adam7 interlaced" : "not interlaced",
+               filter_string);
+    pm_message("background {index, gray, red, green, blue} = "
+               "{%d, %d, %d, %d, %d}",
+               info_ptr->background.index,
+               info_ptr->background.gray,
+               info_ptr->background.red,
+               info_ptr->background.green,
+               info_ptr->background.blue);
+
+    strfree(filter_string);
+
+    if (info_ptr->valid & PNG_INFO_tRNS)
+        pm_message("tRNS chunk (transparency): %u entries",
+                   info_ptr->num_trans);
+    else
+        pm_message("tRNS chunk (transparency): not present");
+
+    if (info_ptr->valid & PNG_INFO_gAMA)
+        pm_message("gAMA chunk (image gamma): gamma = %4.2f", info_ptr->gamma);
+    else
+        pm_message("gAMA chunk (image gamma): not present");
+
+    if (info_ptr->valid & PNG_INFO_sBIT)
+        pm_message("sBIT chunk: present");
+    else
+        pm_message("sBIT chunk: not present");
+
+    if (info_ptr->valid & PNG_INFO_cHRM)
+        pm_message("cHRM chunk: present");
+    else
+        pm_message("cHRM chunk: not present");
+
+    if (info_ptr->valid & PNG_INFO_PLTE)
+        pm_message("PLTE chunk: %d entries", info_ptr->num_palette);
+    else
+        pm_message("PLTE chunk: not present");
+
+    if (info_ptr->valid & PNG_INFO_bKGD)
+        pm_message("bKGD chunk: present");
+    else
+        pm_message("bKGD chunk: not present");
+
+    if (info_ptr->valid & PNG_INFO_hIST)
+        pm_message("hIST chunk: present");
+    else
+        pm_message("hIST chunk: not present");
+
+    if (info_ptr->valid & PNG_INFO_pHYs)
+        pm_message("pHYs chunk: present");
+    else
+        pm_message("pHYs chunk: not present");
+
+    if (info_ptr->valid & PNG_INFO_oFFs)
+        pm_message("oFFs chunk: present");
+    else
+        pm_message("oFFs chunk: not present");
+
+    if (info_ptr->valid & PNG_INFO_tIME)
+        pm_message("tIME chunk: present");
+    else
+        pm_message("tIME chunk: not present");
+
+    if (info_ptr->valid & PNG_INFO_pCAL)
+        pm_message("pCAL chunk: present");
+    else
+        pm_message("pCAL chunk: not present");
+
+    if (info_ptr->valid & PNG_INFO_sRGB)
+        pm_message("sRGB chunk: present");
+    else
+        pm_message("sRGB chunk: not present");
+}
+
+
+
+static unsigned int
+computePngLineSize(png_info * const pngInfoP) {
+
+    unsigned int const bytesPerSample = pngInfoP->bit_depth == 16 ? 2 : 1;
+
+    unsigned int samplesPerPixel;
+
+    switch (pngInfoP->color_type) {
+    case PNG_COLOR_TYPE_GRAY_ALPHA: samplesPerPixel = 2; break;
+    case PNG_COLOR_TYPE_RGB:        samplesPerPixel = 3; break;
+    case PNG_COLOR_TYPE_RGB_ALPHA:  samplesPerPixel = 4; break;
+    default:                        samplesPerPixel = 1;
+    }
+
+    if (UINT_MAX / bytesPerSample / samplesPerPixel < pngInfoP->width)
+        pm_error("Width %u of PNG is uncomputably large",
+                 (unsigned int)pngInfoP->width);
+       
+    return pngInfoP->width * bytesPerSample * samplesPerPixel;
+}
+
+
+
+static void
+allocPngRaster(png_info *   const pngInfoP,
+               png_byte *** const pngImageP) {
+
+    unsigned int const lineSize = computePngLineSize(pngInfoP);
+
+    png_byte ** pngImage;
+    unsigned int row;
+
+    MALLOCARRAY(pngImage, pngInfoP->height);
+
+    if (pngImage == NULL)
+        pm_error("couldn't allocate space for %u PNG raster rows",
+                 (unsigned int)pngInfoP->height);
+
+    for (row = 0; row < pngInfoP->height; ++row) {
+        MALLOCARRAY(pngImage[row], lineSize);
+        if (pngImage[row] == NULL)
+            pm_error("couldn't allocate space for %uth row of PNG raster",
+                     row);
+    }
+    *pngImageP = pngImage;
+}
+
+
+
+static void
+freePngRaster(png_byte ** const pngRaster,
+              png_info *  const pngInfoP) {
+
+    unsigned int row;
+
+    for (row = 0; row < pngInfoP->height; ++row)
+        free(pngRaster[row]);
+
+    free(pngRaster);
+}
+
+
+
+static bool
+isTransparentColor(pngcolor   const color,
+                   png_info * const pngInfoP,
+                   double     const totalgamma) {
+/*----------------------------------------------------------------------------
+   Return TRUE iff pixels of color 'color' are supposed to be transparent
+   everywhere they occur.  Assume it's an RGB image.
+
+   'color' has been gamma-corrected.
+-----------------------------------------------------------------------------*/
+    bool retval;
+
+    if (pngInfoP->valid & PNG_INFO_tRNS) {
+        const png_color_16 * const transColorP = &pngInfoP->trans_values;
+
+        /* It seems odd that libpng lets you get gamma-corrected pixel
+           values, but not gamma-corrected transparency or background
+           values.  But as that is the case, we have to gamma-correct
+           the transparency values.
+
+           Note that because we compare the gamma-corrected values and
+           there may be many-to-one mapping of uncorrected to corrected
+           values, more pixels may be transparent than what the user
+           intended.
+
+           We could fix this by not letting libpng gamma-correct the
+           pixels, and just do it ourselves.
+        */
+    
+        switch (pngInfoP->color_type) {
+        case PNG_COLOR_TYPE_GRAY:
+            retval = color.r == gamma_correct(transColorP->gray, totalgamma);
+            break;
+        default:
+            retval = 
+                color.r == gamma_correct(transColorP->red,   totalgamma) &&
+                color.g == gamma_correct(transColorP->green, totalgamma) &&
+                color.b == gamma_correct(transColorP->blue,  totalgamma);
+        }
+    } else 
+        retval = FALSE;
+
+    return retval;
+}
+
+
+
+#define SIG_CHECK_SIZE 4
+
+static void
+read_sig_buf(FILE * const ifP) {
+
+    unsigned char sig_buf[SIG_CHECK_SIZE];
+    size_t bytesRead;
+
+    bytesRead = fread(sig_buf, 1, SIG_CHECK_SIZE, ifP);
+    if (bytesRead != SIG_CHECK_SIZE)
+        pm_error ("input file is empty or too short");
+
+    if (png_sig_cmp(sig_buf, (png_size_t) 0, (png_size_t) SIG_CHECK_SIZE)
+        != 0)
+        pm_error ("input file is not a PNG file");
+}
+
+
+
+static void
+setupGammaCorrection(png_struct * const png_ptr,
+                     png_info *   const info_ptr,
+                     float        const displaygamma,
+                     float *      const totalgammaP) {
+
+    if (displaygamma == -1.0)
+        *totalgammaP = -1.0;
+    else {
+        float imageGamma;
+        if (info_ptr->valid & PNG_INFO_gAMA)
+            imageGamma = info_ptr->gamma;
+        else {
+            if (verbose)
+                pm_message("PNG doesn't specify image gamma.  Assuming 1.0");
+            imageGamma = 1.0;
+        }
+
+        if (fabs(displaygamma * imageGamma - 1.0) < .01) {
+            *totalgammaP = -1.0;
+            if (verbose)
+                pm_message("image gamma %4.2f matches "
+                           "display gamma %4.2f.  No conversion.",
+                           imageGamma, displaygamma);
+        } else {
+            png_set_gamma(png_ptr, displaygamma, imageGamma);
+            *totalgammaP = imageGamma * displaygamma;
+            /* in case of gamma-corrections, sBIT's as in the
+               PNG-file are not valid anymore 
+            */
+            info_ptr->valid &= ~PNG_INFO_sBIT;
+            if (verbose)
+                pm_message("image gamma is %4.2f, "
+                           "converted for display gamma of %4.2f",
+                           imageGamma, displaygamma);
+        }
+    }
+}
+
+
+
+static bool
+paletteHasPartialTransparency(png_info * const info_ptr) {
+
+    bool retval;
+
+    if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
+        if (info_ptr->valid & PNG_INFO_tRNS) {
+            bool foundGray;
+            unsigned int i;
+            
+            for (i = 0, foundGray = FALSE;
+                 i < info_ptr->num_trans && !foundGray;
+                 ++i) {
+                if (info_ptr->TRANS_ALPHA[i] != 0 &&
+                    info_ptr->TRANS_ALPHA[i] != maxval) {
+                    foundGray = TRUE;
+                }
+            }
+            retval = foundGray;
+        } else
+            retval = FALSE;
+    } else
+        retval = FALSE;
+
+    return retval;
+}
+
+
+
+static void
+getComponentSbitFg(png_info * const pngInfoP,
+                   png_byte * const fgSbitP,
+                   bool *     const notUniformP) {
+
+    if (pngInfoP->color_type == PNG_COLOR_TYPE_RGB ||
+        pngInfoP->color_type == PNG_COLOR_TYPE_RGB_ALPHA ||
+        pngInfoP->color_type == PNG_COLOR_TYPE_PALETTE) {
+        if (pngInfoP->sig_bit.red == pngInfoP->sig_bit.blue &&
+            pngInfoP->sig_bit.red == pngInfoP->sig_bit.green) {
+            *notUniformP = false;
+            *fgSbitP     = pngInfoP->sig_bit.red;
+        } else
+            *notUniformP = true;
+    } else {
+        /* It has only a gray channel so it's obviously uniform */
+        *notUniformP = false;
+        *fgSbitP     = pngInfoP->sig_bit.gray;
+    }
+}
+
+
+
+static void
+getComponentSbit(png_info *          const pngInfoP,
+                 enum alpha_handling const alphaHandling,
+                 png_byte *          const componentSbitP,
+                 bool *              const notUniformP) {
+
+    switch (alphaHandling) {
+
+    case ALPHA_ONLY:
+        /* We care only about the alpha channel, so the uniform Sbit is
+           the alpha Sbit
+        */
+        *notUniformP = false;
+        *componentSbitP = pngInfoP->sig_bit.alpha;
+        break;
+    case ALPHA_NONE:
+    case ALPHA_MIX:
+        /* We aren't going to produce an alpha channel, so we care only
+           about the uniformity of the foreground channels.
+        */
+        getComponentSbitFg(pngInfoP, componentSbitP, notUniformP);
+        break;
+    case ALPHA_IN: {
+        /* We care about both the foreground and the alpha */
+        bool fgNotUniform;
+        png_byte fgSbit;
+        
+        getComponentSbitFg(pngInfoP, &fgSbit, &fgNotUniform);
+
+        if (fgNotUniform)
+            *notUniformP = true;
+        else {
+            if (fgSbit == pngInfoP->sig_bit.alpha) {
+                *notUniformP    = false;
+                *componentSbitP = fgSbit;
+            } else
+                *notUniformP = true;
+        }
+    } break;
+    }
+}
+
+                 
+
+static void
+shiftPalette(png_info *   const pngInfoP,
+             unsigned int const shift) {
+/*----------------------------------------------------------------------------
+   Shift every component of every color in the PNG palette right by
+   'shift' bits because sBIT chunk says only those are significant.
+-----------------------------------------------------------------------------*/
+    if (shift > 7)
+        pm_error("Invalid PNG: paletted image can't have "
+                 "more than 8 significant bits per component, "
+                 "but sBIT chunk says %u bits",
+                 shift);
+    else {
+        unsigned int i;
+        
+        for (i = 0; i < pngInfoP->num_palette; ++i) {
+            pngInfoP->palette[i].red   >>= (8 - shift);
+            pngInfoP->palette[i].green >>= (8 - shift);
+            pngInfoP->palette[i].blue  >>= (8 - shift);
+        }
+    }
+}
+
+
+
+static void
+computeMaxvalFromSbit(png_struct *        const pngP,
+                      png_info *          const pngInfoP,
+                      enum alpha_handling const alphaHandling,
+                      png_uint_16 *       const maxvalP,
+                      bool *              const succeededP,
+                      int *               const errorlevelP) {
+
+    /* sBIT handling is very tricky. If we are extracting only the
+       image, we can use the sBIT info for grayscale and color images,
+       if the three values agree. If we extract the transparency/alpha
+       mask, sBIT is irrelevant for trans and valid for alpha. If we
+       mix both, the multiplication may result in values that require
+       the normal bit depth, so we will use the sBIT info only for
+       transparency, if we know that only solid and fully transparent
+       is used 
+    */
+
+    bool notUniform;
+        /* The sBIT chunk says the number of significant high-order bits
+           in each component varies among the components we care about.
+        */
+    png_byte componentSigBit;
+        /* The number of high-order significant bits in each RGB component.
+           Meaningless if they aren't all the same (i.e. 'notUniform')
+        */
+
+    getComponentSbit(pngInfoP, alphaHandling, &componentSigBit, &notUniform);
+
+    if (notUniform) {
+        pm_message("This program cannot handle "
+                   "different bit depths for color channels");
+        pm_message("writing file with %u bit resolution", pngInfoP->bit_depth);
+        *succeededP = false;
+        *errorlevelP = PNMTOPNG_WARNING_LEVEL;
+    } else if (componentSigBit > 15) {
+        pm_message("Invalid PNG: says %u significant bits for a component; "
+                   "max possible is 16.  Ignoring sBIT chunk.",
+                   componentSigBit);
+        *succeededP = false;
+        *errorlevelP = PNMTOPNG_WARNING_LEVEL;
+    } else {
+        if (alphaHandling == ALPHA_MIX &&
+            (pngInfoP->color_type == PNG_COLOR_TYPE_RGB_ALPHA ||
+             pngInfoP->color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
+             paletteHasPartialTransparency(pngInfoP)))
+            *succeededP = false;
+        else {
+            if (componentSigBit < pngInfoP->bit_depth) {
+                pm_message("Image has fewer significant bits, "
+                           "writing file with %u bits", componentSigBit);
+                *maxvalP = (1l << componentSigBit) - 1;
+                *succeededP = true;
+
+                if (pngInfoP->color_type == PNG_COLOR_TYPE_PALETTE)
+                    shiftPalette(pngInfoP, componentSigBit);
+                else
+                    png_set_shift(pngP, &pngInfoP->sig_bit);
+            } else
+                *succeededP = false;
+        }
+    }
+}
+
+
+
+static void
+setupSignificantBits(png_struct *        const pngP,
+                     png_info *          const pngInfoP,
+                     enum alpha_handling const alphaHandling,
+                     png_uint_16 *       const maxvalP,
+                     int *               const errorlevelP) {
+/*----------------------------------------------------------------------------
+  Figure out what maxval would best express the information in the PNG
+  described by *pngP and *pngInfoP, with 'alpha' telling which
+  information in the PNG we care about (image or alpha mask).
+
+  Return the result as *maxvalP.
+
+  Also set up *pngP for the corresponding significant bits.
+-----------------------------------------------------------------------------*/
+    bool gotItFromSbit;
+    
+    if (pngInfoP->valid & PNG_INFO_sBIT)
+        computeMaxvalFromSbit(pngP, pngInfoP, alphaHandling,
+                              maxvalP, &gotItFromSbit, errorlevelP);
+    else
+        gotItFromSbit = false;
+
+    if (!gotItFromSbit) {
+        if (pngInfoP->color_type == PNG_COLOR_TYPE_PALETTE) {
+            if (alphaHandling == ALPHA_ONLY) {
+                if (pngInfoP->color_type == PNG_COLOR_TYPE_GRAY ||
+                    pngInfoP->color_type == PNG_COLOR_TYPE_RGB)
+                    /* The alpha mask will be all opaque, so maxval 1
+                       is plenty
+                    */
+                    *maxvalP = 1;
+                else if (paletteHasPartialTransparency(pngInfoP))
+                    /* Use same maxval as PNG transparency palette for
+                       simplicity
+                    */
+                    *maxvalP = 255;
+                else
+                    /* A common case, so we conserve bits */
+                    *maxvalP = 1;
+            } else
+                /* Use same maxval as PNG palette for simplicity */
+                *maxvalP = 255;
+        } else {
+            *maxvalP = (1l << pngInfoP->bit_depth) - 1;
+        }
+    }
+}
+
+
+
+static bool
+imageHasColor(png_info * const info_ptr) {
+
+    bool retval;
+
+    if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY ||
+        info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+
+        retval = FALSE;
+    else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
+        bool foundColor;
+        unsigned int i;
+            
+        for (i = 0, foundColor = FALSE;
+             i < info_ptr->num_palette && !foundColor;
+             ++i) {
+            if (iscolor(info_ptr->palette[i]))
+                foundColor = TRUE;
+        }
+        retval = foundColor;
+    } else
+        retval = TRUE;
+
+    return retval;
+}
+
+
+
+static void
+determineOutputType(png_info *          const pngInfoP,
+                    enum alpha_handling const alphaHandling,
+                    pngcolor            const bgColor,
+                    xelval              const maxval,
+                    int *               const formatP,
+                    unsigned int *      const depthP,
+                    char *              const tupleType) {
+
+    if (alphaHandling == ALPHA_ONLY) {
+        /* The output is a old style pseudo-PNM transparency image */
+        *depthP = 1;
+        *formatP = maxval > 1 ? PGM_FORMAT : PBM_FORMAT;
+    } else {            
+        /* The output is a normal Netpbm image */
+        bool const outputIsColor =
+            imageHasColor(pngInfoP) || !isGrayscale(bgColor);
+
+        if (alphaHandling == ALPHA_IN) {
+            *formatP = PAM_FORMAT;
+            if (outputIsColor) {
+                *depthP = 4;
+                strcpy(tupleType, "RGB_ALPHA");
+            } else {
+                *depthP = 2;
+                strcpy(tupleType, "GRAYSCALE_ALPHA");
+            }
+        } else {
+            if (outputIsColor) {
+                *formatP = PPM_FORMAT;
+                *depthP = 3;
+            } else {
+                *depthP = 1;
+                *formatP = maxval > 1 ? PGM_FORMAT : PBM_FORMAT;
+            }
+        }
+    }
+}
+
+
+
+static void
+getBackgroundColor(png_info *   const info_ptr,
+                   const char * const requestedColor,
+                   float        const totalgamma,
+                   xelval       const maxval,
+                   pngcolor *   const bgColorP) {
+/*----------------------------------------------------------------------------
+   Figure out what the background color should be.  If the user requested
+   a particular color ('requestedColor' not null), that's the one.
+   Otherwise, if the PNG specifies a background color, that's the one.
+   And otherwise, it's white.
+-----------------------------------------------------------------------------*/
+    if (requestedColor) {
+        /* Background was specified from the command-line; we always
+           use that.  I chose to do no gamma-correction in this case;
+           which is a bit arbitrary.  
+        */
+        pixel const backcolor = ppm_parsecolor(requestedColor, maxval);
+
+        bgColorP->r = PPM_GETR(backcolor);
+        bgColorP->g = PPM_GETG(backcolor);
+        bgColorP->b = PPM_GETB(backcolor);
+
+    } else if (info_ptr->valid & PNG_INFO_bKGD) {
+        /* didn't manage to get libpng to work (bugs?) concerning background
+           processing, therefore we do our own.
+        */
+        switch (info_ptr->color_type) {
+        case PNG_COLOR_TYPE_GRAY:
+        case PNG_COLOR_TYPE_GRAY_ALPHA:
+            bgColorP->r = bgColorP->g = bgColorP->b = 
+                gamma_correct(info_ptr->background.gray, totalgamma);
+            break;
+        case PNG_COLOR_TYPE_PALETTE: {
+            png_color const rawBgcolor = 
+                info_ptr->palette[info_ptr->background.index];
+            bgColorP->r = gamma_correct(rawBgcolor.red, totalgamma);
+            bgColorP->g = gamma_correct(rawBgcolor.green, totalgamma);
+            bgColorP->b = gamma_correct(rawBgcolor.blue, totalgamma);
+        }
+        break;
+        case PNG_COLOR_TYPE_RGB:
+        case PNG_COLOR_TYPE_RGB_ALPHA: {
+            png_color_16 const rawBgcolor = info_ptr->background;
+            
+            bgColorP->r = gamma_correct(rawBgcolor.red,   totalgamma);
+            bgColorP->g = gamma_correct(rawBgcolor.green, totalgamma);
+            bgColorP->b = gamma_correct(rawBgcolor.blue,  totalgamma);
+        }
+        break;
+        }
+    } else 
+        /* when no background given, we use white [from version 2.37] */
+        bgColorP->r = bgColorP->g = bgColorP->b = maxval;
+}
+
+
+
+#define GET_PNG_VAL(p) get_png_val(&(p), pngInfoP->bit_depth)
+
+
+
+static void
+makeTupleRow(const struct pam *  const pamP,
+             const tuple *       const tuplerow,
+             png_info *          const pngInfoP,
+             const png_byte *    const pngRasterRow,
+             pngcolor            const bgColor,
+             enum alpha_handling const alphaHandling,
+             double              const totalgamma) {
+
+    const png_byte * pngPixelP;
+    unsigned int col;
+
+    pngPixelP = &pngRasterRow[0];  /* initial value */
+    for (col = 0; col < pngInfoP->width; ++col) {
+        switch (pngInfoP->color_type) {
+        case PNG_COLOR_TYPE_GRAY: {
+            pngcolor fgColor;
+            fgColor.r = fgColor.g = fgColor.b = GET_PNG_VAL(pngPixelP);
+            setTuple(pamP, tuplerow[col], fgColor, bgColor, alphaHandling,
+                   isTransparentColor(fgColor, pngInfoP, totalgamma) ?
+                   0 : maxval);
+        }
+        break;
+
+        case PNG_COLOR_TYPE_GRAY_ALPHA: {
+            pngcolor fgColor;
+            png_uint_16 alpha;
+
+            fgColor.r = fgColor.g = fgColor.b = GET_PNG_VAL(pngPixelP);
+            alpha = GET_PNG_VAL(pngPixelP);
+            setTuple(pamP, tuplerow[col], fgColor, bgColor,
+                     alphaHandling, alpha);
+        }
+        break;
+
+        case PNG_COLOR_TYPE_PALETTE: {
+            png_uint_16 const index        = GET_PNG_VAL(pngPixelP);
+            png_color   const paletteColor = pngInfoP->palette[index];
+
+            pngcolor fgColor;
+
+            fgColor.r = paletteColor.red;
+            fgColor.g = paletteColor.green;
+            fgColor.b = paletteColor.blue;
+
+            setTuple(pamP, tuplerow[col], fgColor, bgColor, alphaHandling,
+                     (pngInfoP->valid & PNG_INFO_tRNS) &&
+                     index < pngInfoP->num_trans ?
+                     pngInfoP->TRANS_ALPHA[index] : maxval);
+        }
+        break;
+                
+        case PNG_COLOR_TYPE_RGB: {
+            pngcolor fgColor;
+
+            fgColor.r = GET_PNG_VAL(pngPixelP);
+            fgColor.g = GET_PNG_VAL(pngPixelP);
+            fgColor.b = GET_PNG_VAL(pngPixelP);
+            setTuple(pamP, tuplerow[col], fgColor, bgColor, alphaHandling,
+                     isTransparentColor(fgColor, pngInfoP, totalgamma) ?
+                     0 : maxval);
+        }
+        break;
+
+        case PNG_COLOR_TYPE_RGB_ALPHA: {
+            pngcolor fgColor;
+            png_uint_16 alpha;
+
+            fgColor.r = GET_PNG_VAL(pngPixelP);
+            fgColor.g = GET_PNG_VAL(pngPixelP);
+            fgColor.b = GET_PNG_VAL(pngPixelP);
+            alpha     = GET_PNG_VAL(pngPixelP);
+            setTuple(pamP, tuplerow[col], fgColor, bgColor,
+                     alphaHandling, alpha);
+        }
+        break;
+
+        default:
+            pm_error("unknown PNG color type: %d", pngInfoP->color_type);
+        }
+    }
+}
+
+
+
+static void
+reportOutputFormat(const struct pam * const pamP) {
+
+    switch (pamP->format) {
+
+    case PBM_FORMAT:
+        pm_message("Writing a PBM file");
+        break;
+    case PGM_FORMAT:
+        pm_message("Writing a PGM file with maxval %lu", pamP->maxval);
+        break;
+    case PPM_FORMAT:
+        pm_message("Writing a PPM file with maxval %lu", pamP->maxval);
+        break;
+    case PAM_FORMAT:
+        pm_message("Writing a PAM file with tuple type %s, maxval %u",
+                   pamP->tuple_type, maxval);
+        break;
+    default:
+        assert(false); /* Every possible value handled above */
+    }
+}
+    
+
+
+static void
+writeNetpbm(struct pam *        const pamP,
+            png_info *          const pngInfoP,
+            png_byte **         const pngRaster,
+            pngcolor            const bgColor,
+            enum alpha_handling const alphaHandling,
+            double              const totalgamma) {
+/*----------------------------------------------------------------------------
+   Write a Netpbm image of either the image or the alpha mask, according to
+   'alphaHandling' that is in the PNG image described by 'pngInfoP' and
+   pngRaster.
+
+   *pamP describes the required output image and is consistent with
+   *pngInfoP.
+
+   Use background color 'bgColor' in the output if the PNG is such that a
+   background color is needed.
+-----------------------------------------------------------------------------*/
+    tuple * tuplerow;
+    unsigned int row;
+
+    if (verbose)
+        reportOutputFormat(pamP);
+
+    pnm_writepaminit(pamP);
+
+    tuplerow = pnm_allocpamrow(pamP);
+
+    for (row = 0; row < pngInfoP->height; ++row) {
+        makeTupleRow(pamP, tuplerow, pngInfoP, pngRaster[row], bgColor,
+                     alphaHandling, totalgamma);
+
+        pnm_writepamrow(pamP, tuplerow);
+    }
+    pnm_freepamrow(tuplerow);
+}
+
+
+
+static void 
+convertpng(FILE *             const ifp, 
+           FILE *             const tfp, 
+           struct cmdlineInfo const cmdline,
+           int *              const errorlevelP) {
+
+    png_struct * png_ptr;
+    png_info * info_ptr;
+    png_byte ** png_image;
+    pngcolor bgColor;
+    float totalgamma;
+    struct pam pam;
+
+    *errorlevelP = 0;
+
+    read_sig_buf(ifp);
+
+    png_ptr = png_create_read_struct(
+        PNG_LIBPNG_VER_STRING,
+        &pngtopnm_jmpbuf_struct, pngtopnm_error_handler, NULL);
+    if (png_ptr == NULL)
+        pm_error("cannot allocate main libpng structure (png_ptr)");
+
+    info_ptr = png_create_info_struct (png_ptr);
+    if (info_ptr == NULL)
+        pm_error("cannot allocate LIBPNG structures");
+
+    if (setjmp(pngtopnm_jmpbuf_struct.jmpbuf))
+        pm_error ("setjmp returns error condition");
+
+    png_init_io (png_ptr, ifp);
+    png_set_sig_bytes (png_ptr, SIG_CHECK_SIZE);
+    png_read_info (png_ptr, info_ptr);
+
+    allocPngRaster(info_ptr, &png_image);
+
+    if (info_ptr->bit_depth < 8)
+        png_set_packing (png_ptr);
+
+    setupGammaCorrection(png_ptr, info_ptr, cmdline.gamma, &totalgamma);
+
+    setupSignificantBits(png_ptr, info_ptr, cmdline.alpha,
+                         &maxval, errorlevelP);
+
+    getBackgroundColor(info_ptr, cmdline.background, totalgamma, maxval,
+                       &bgColor);
+
+    png_read_image(png_ptr, png_image);
+    png_read_end(png_ptr, info_ptr);
+
+    if (verbose)
+        /* Note that some of info_ptr is not defined until png_read_end() 
+           completes.  That's because it comes from chunks that are at the
+           end of the stream.
+        */
+        dump_png_info(info_ptr);
+
+    if (cmdline.time)
+        show_time(info_ptr);
+    if (tfp)
+        save_text(info_ptr, tfp);
+
+    if (info_ptr->valid & PNG_INFO_pHYs) {
+        float const r =
+            (float)info_ptr->x_pixels_per_unit / info_ptr->y_pixels_per_unit;
+        if (r != 1.0) {
+            pm_message ("warning - non-square pixels; "
+                        "to fix do a 'pamscale -%cscale %g'",
+                        r < 1.0 ? 'x' : 'y',
+                        r < 1.0 ? 1.0 / r : r );
+            *errorlevelP = PNMTOPNG_WARNING_LEVEL;
+        }
+    }
+
+    pam.size        = sizeof(pam);
+    pam.len         = PAM_STRUCT_SIZE(tuple_type);
+    pam.file        = stdout;
+    pam.plainformat = 0;
+    pam.height      = info_ptr->height;
+    pam.width       = info_ptr->width;
+    pam.maxval      = maxval;
+
+    determineOutputType(info_ptr, cmdline.alpha, bgColor, maxval,
+                        &pam.format, &pam.depth, pam.tuple_type);
+
+    writeNetpbm(&pam, info_ptr, png_image, bgColor, cmdline.alpha, totalgamma);
+
+    fflush(stdout);
+
+    freePngRaster(png_image, info_ptr);
+
+    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+}
+
+
+
+int 
+main(int argc, const char *argv[]) {
+
+    struct cmdlineInfo cmdline;
+    FILE * ifP;
+    FILE * tfP;
+    int errorlevel;
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    verbose = cmdline.verbose;
+
+    ifP = pm_openr(cmdline.inputFilespec);
+
+    if (cmdline.text)
+        tfP = pm_openw(cmdline.text);
+    else
+        tfP = NULL;
+
+    convertpng(ifP, tfP, cmdline, &errorlevel);
+
+    if (tfP)
+        pm_close(tfP);
+
+    pm_close(ifP);
+    pm_close(stdout);
+
+    return errorlevel;
+}
diff --git a/converter/other/pngtopnm.c b/converter/other/pngtopnm.c
index 59149acd..a8ea25a7 100644
--- a/converter/other/pngtopnm.c
+++ b/converter/other/pngtopnm.c
@@ -1,6 +1,6 @@
 /*
 ** pngtopnm.c -
-** read a Portable Network Graphics file and produce a portable anymap
+** read a Portable Network Graphics file and produce a PNM.
 **
 ** Copyright (C) 1995,1998 by Alexander Lehmann <alex@hal.rhein-main.de>
 **                        and Willem van Schaik <willem@schaik.com>
@@ -16,61 +16,29 @@
 ** with lots of bits pasted from libpng.txt by Guy Eric Schalnat
 */
 
-/* 
-   BJH 20000408:  rename PPM_MAXMAXVAL to PPM_OVERALLMAXVAL
-   BJH 20000303:  fix include statement so dependencies work out right.
-*/
-/* GRR 19991203:  moved VERSION to new version.h header file */
-
-/* GRR 19990713:  fixed redundant freeing of png_ptr and info_ptr in setjmp()
- *  blocks and added "pm_close(ifp)" in each.  */
-
-/* GRR 19990317:  declared "clobberable" automatic variables in convertpng()
- *  static to fix Solaris/gcc stack-corruption bug.  Also installed custom
- *  error-handler to avoid jmp_buf size-related problems (i.e., jmp_buf
- *  compiled with one size in libpng and another size here).  */
-
 #ifndef PNMTOPNG_WARNING_LEVEL
 #  define PNMTOPNG_WARNING_LEVEL 0   /* use 0 for backward compatibility, */
 #endif                               /*  2 for warnings (1 == error) */
 
+#include <assert.h>
 #include <math.h>
 #include <float.h>
 #include <png.h>    /* includes zlib.h and setjmp.h */
 #define VERSION "2.37.4 (5 December 1999) +netpbm"
 
-#include "pnm.h"
+#include "pm_c_util.h"
 #include "mallocvar.h"
 #include "nstring.h"
 #include "shhopt.h"
+#include "pnm.h"
 
+/* A hack until we can remove direct access to png_info from the program */
 #if PNG_LIBPNG_VER >= 10400
-#error Your PNG library (<png.h>) is incompatible with this Netpbm source code.
-#error You need either an older PNG library (older than 1.4)
-#error newer Netpbm source code (at least 10.48)
-#endif
-
-typedef struct _jmpbuf_wrapper {
-  jmp_buf jmpbuf;
-} jmpbuf_wrapper;
-
-/* GRR 19991205:  this is used as a test for pre-1999 versions of netpbm and
- *   pbmplus vs. 1999 or later (in which pm_close was split into two)
- */
-#ifdef PBMPLUS_RAWBITS
-#  define pm_closer pm_close
-#  define pm_closew pm_close
+#define TRANS_ALPHA trans_alpha
+#else
+#define TRANS_ALPHA trans
 #endif
 
-#ifndef TRUE
-#  define TRUE 1
-#endif
-#ifndef FALSE
-#  define FALSE 0
-#endif
-#ifndef NONE
-#  define NONE 0
-#endif
 
 enum alpha_handling {ALPHA_NONE, ALPHA_ONLY, ALPHA_MIX};
 
@@ -88,7 +56,7 @@ struct cmdlineInfo {
 };
 
 
-typedef struct pngcolor {
+typedef struct {
 /*----------------------------------------------------------------------------
    A color in a format compatible with the PNG library.
 
@@ -102,15 +70,13 @@ typedef struct pngcolor {
 
 
 static png_uint_16 maxval;
-static int verbose = FALSE;
-static int mtime;
-static jmpbuf_wrapper pngtopnm_jmpbuf_struct;
+static bool verbose;
 
 
 static void
-parseCommandLine(int                 argc, 
-                 char **             argv,
-                 struct cmdlineInfo *cmdlineP ) {
+parseCommandLine(int                  argc, 
+                 const char **        argv,
+                 struct cmdlineInfo * cmdlineP ) {
 /*----------------------------------------------------------------------------
    Parse program command line described in Unix standard form by argc
    and argv.  Return the information in the options as *cmdlineP.  
@@ -121,7 +87,7 @@ parseCommandLine(int                 argc,
    Note that the strings we return are stored in the storage that
    was passed to us as the argv array.  We also trash *argv.
 -----------------------------------------------------------------------------*/
-    optEntry *option_def = malloc(100*sizeof(optEntry));
+    optEntry * option_def;
         /* Instructions to optParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
@@ -130,6 +96,8 @@ parseCommandLine(int                 argc,
 
     unsigned int alphaSpec, mixSpec, backgroundSpec, gammaSpec, textSpec;
 
+    MALLOCARRAY(option_def, 100);
+
     option_def_index = 0;   /* incremented by OPTENT3 */
     OPTENT3(0, "verbose",     OPT_FLAG,   NULL,                  
             &cmdlineP->verbose,       0);
@@ -150,7 +118,7 @@ parseCommandLine(int                 argc,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3( &argc, argv, opt, sizeof(opt), 0);
+    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
 
@@ -186,12 +154,215 @@ parseCommandLine(int                 argc,
 
 
 
+static void
+pngtopnmErrorHandler(png_structp     const png_ptr,
+                     png_const_charp const msg) {
+
+    jmp_buf * jmpbufP;
+
+    /* this function, aside from the extra step of retrieving the "error
+       pointer" (below) and the fact that it exists within the application
+       rather than within libpng, is essentially identical to libpng's
+       default error handler.  The second point is critical:  since both
+       setjmp() and longjmp() are called from the same code, they are
+       guaranteed to have compatible notions of how big a jmp_buf is,
+       regardless of whether _BSD_SOURCE or anything else has (or has not)
+       been defined.
+    */
+
+    pm_message("fatal libpng error: %s", msg);
+
+    jmpbufP = png_get_error_ptr(png_ptr);
+
+    if (!jmpbufP) {
+        /* we are completely hosed now */
+        pm_error("EXTREMELY fatal error: jmpbuf unrecoverable; terminating.");
+    }
+
+    longjmp(*jmpbufP, 1);
+}
+
+
+
+struct pngx {
+    png_structp png_ptr;
+    png_infop info_ptr;
+};
+
+
+
+static void
+pngx_createRead(struct pngx ** const pngxPP,
+                jmp_buf *      const jmpbufP) {
+
+    struct pngx * pngxP;
+
+    MALLOCVAR(pngxP);
+
+    if (!pngxP)
+        pm_error("Failed to allocate memory for PNG object");
+    else {
+        pngxP->png_ptr = png_create_read_struct(
+            PNG_LIBPNG_VER_STRING,
+            jmpbufP, pngtopnmErrorHandler, NULL);
+
+        if (!pngxP->png_ptr)
+            pm_error("cannot allocate main libpng structure (png_ptr)");
+        else {
+            pngxP->info_ptr = png_create_info_struct(pngxP->png_ptr);
+
+            if (!pngxP->info_ptr)
+                pm_error("cannot allocate libpng info structure (info_ptr)");
+            else
+                *pngxPP = pngxP;
+        }
+    }
+}
+
+
+
+static void
+pngx_destroy(struct pngx * const pngxP) {
+
+    png_destroy_read_struct(&pngxP->png_ptr, &pngxP->info_ptr, NULL);
+
+    free(pngxP);
+}
+
+
+
+static bool
+pngx_chunkIsPresent(struct pngx * const pngxP,
+                    uint32_t      const chunkType) {
+
+    return png_get_valid(pngxP->png_ptr, pngxP->info_ptr, chunkType);
+}
+
+
+
+static void
+verifyFileIsPng(FILE *   const ifP,
+                size_t * const consumedByteCtP) {
+
+    unsigned char buffer[4];
+    size_t bytesRead;
+
+    bytesRead = fread(buffer, 1, sizeof(buffer), ifP);
+    if (bytesRead != sizeof(buffer))
+        pm_error("input file is empty or too short");
+
+    if (png_sig_cmp(buffer, (png_size_t) 0, (png_size_t) sizeof(buffer)) != 0)
+        pm_error("input file is not a PNG file "
+                 "(does not have the PNG signature in its first 4 bytes)");
+    else
+        *consumedByteCtP = bytesRead;
+}
+
+
+
+static unsigned int
+computePngLineSize(struct pngx * const pngxP) {
+
+    unsigned int const bytesPerSample =
+        pngxP->info_ptr->bit_depth == 16 ? 2 : 1;
+
+    unsigned int samplesPerPixel;
+
+    switch (pngxP->info_ptr->color_type) {
+    case PNG_COLOR_TYPE_GRAY_ALPHA: samplesPerPixel = 2; break;
+    case PNG_COLOR_TYPE_RGB:        samplesPerPixel = 3; break;
+    case PNG_COLOR_TYPE_RGB_ALPHA:  samplesPerPixel = 4; break;
+    default:                        samplesPerPixel = 1;
+    }
+
+    if (UINT_MAX / bytesPerSample / samplesPerPixel < pngxP->info_ptr->width)
+        pm_error("Width %u of PNG is uncomputably large",
+                 (unsigned int)pngxP->info_ptr->width);
+       
+    return pngxP->info_ptr->width * bytesPerSample * samplesPerPixel;
+}
+
+
+
+static void
+allocPngRaster(struct pngx * const pngxP,
+               png_byte ***  const pngImageP) {
+
+    unsigned int const lineSize = computePngLineSize(pngxP);
+
+    png_byte ** pngImage;
+    unsigned int row;
+
+    MALLOCARRAY(pngImage, pngxP->info_ptr->height);
+
+    if (pngImage == NULL)
+        pm_error("couldn't allocate space for %u PNG raster rows",
+                 (unsigned int)pngxP->info_ptr->height);
+
+    for (row = 0; row < pngxP->info_ptr->height; ++row) {
+        MALLOCARRAY(pngImage[row], lineSize);
+        if (pngImage[row] == NULL)
+            pm_error("couldn't allocate space for %uth row of PNG raster",
+                     row);
+    }
+    *pngImageP = pngImage;
+}
+
+
+
+static void
+freePngRaster(png_byte **   const pngRaster,
+              struct pngx * const pngxP) {
+
+    unsigned int row;
+
+    for (row = 0; row < pngxP->info_ptr->height; ++row)
+        free(pngRaster[row]);
+
+    free(pngRaster);
+}
+
+
+
+static void
+readPng(struct pngx * const pngxP,
+        FILE *        const ifP,
+        png_byte ***  const pngRasterP) {
+
+    size_t sigByteCt;
+    png_byte ** pngRaster;
+            
+    verifyFileIsPng(ifP, &sigByteCt);
+
+    /* Declare that we already read the signature bytes */
+    png_set_sig_bytes(pngxP->png_ptr, (int)sigByteCt);
+
+    png_init_io(pngxP->png_ptr, ifP);
+
+    png_read_info(pngxP->png_ptr, pngxP->info_ptr);
+
+    allocPngRaster(pngxP, &pngRaster);
+
+    if (pngxP->info_ptr->bit_depth < 8)
+        png_set_packing(pngxP->png_ptr);
+
+    png_read_image(pngxP->png_ptr, pngRaster);
+
+    png_read_end(pngxP->png_ptr, pngxP->info_ptr);
+
+    /* Note that some of info_ptr is not defined until png_read_end() 
+       completes.  That's because it comes from chunks that are at the
+       end of the stream.
+    */
+
+    *pngRasterP = pngRaster;
+}
+
 
-#define get_png_val(p) _get_png_val (&(p), info_ptr->bit_depth)
 
 static png_uint_16
-_get_png_val (png_byte ** const pp,
-              int         const bit_depth) {
+get_png_val(const png_byte ** const pp,
+            int               const bit_depth) {
 
     png_uint_16 c;
     
@@ -247,121 +418,95 @@ gamma_correct(png_uint_16 const v,
               float       const g) {
 
     if (g != -1.0)
-        return (png_uint_16) (pow ((double) v / maxval, 
-                                   (1.0 / g)) * maxval + 0.5);
+        return (png_uint_16) ROUNDU(pow((double) v / maxval, (1.0 / g)) *
+                                    maxval);
     else
         return v;
 }
 
 
 
-#ifdef __STDC__
-static int iscolor (png_color c)
-#else
-static int iscolor (c)
-png_color c;
-#endif
-{
-  return c.red != c.green || c.green != c.blue;
+static bool
+iscolor(png_color const c) {
+
+    return c.red != c.green || c.green != c.blue;
 }
 
-#ifdef __STDC__
-static void save_text (png_info *info_ptr, FILE *tfp)
-#else
-static void save_text (info_ptr, tfp)
-png_info *info_ptr;
-FILE *tfp;
-#endif
-{
-  int i, j, k;
-
-  for (i = 0 ; i < info_ptr->num_text ; i++) {
-    j = 0;
-    while (info_ptr->text[i].key[j] != '\0' && info_ptr->text[i].key[j] != ' ')
-      j++;    
-    if (info_ptr->text[i].key[j] != ' ') {
-      fprintf (tfp, "%s", info_ptr->text[i].key);
-      for (j = strlen (info_ptr->text[i].key) ; j < 15 ; j++)
-        putc (' ', tfp);
-    } else {
-      fprintf (tfp, "\"%s\"", info_ptr->text[i].key);
-      for (j = strlen (info_ptr->text[i].key) ; j < 13 ; j++)
-        putc (' ', tfp);
-    }
-    putc (' ', tfp); /* at least one space between key and text */
+
+
+static void
+saveText(struct pngx * const pngxP,
+         FILE *        const tfP) {
+
+    png_info * const info_ptr = pngxP->info_ptr;
+
+    unsigned int i;
+
+    for (i = 0 ; i < info_ptr->num_text; ++i) {
+        unsigned int j;
+        j = 0;
+
+        while (info_ptr->text[i].key[j] != '\0' &&
+               info_ptr->text[i].key[j] != ' ')
+            ++j;    
+
+        if (info_ptr->text[i].key[j] != ' ') {
+            fprintf(tfP, "%s", info_ptr->text[i].key);
+            for (j = strlen (info_ptr->text[i].key); j < 15; ++j)
+                putc(' ', tfP);
+        } else {
+            fprintf(tfP, "\"%s\"", info_ptr->text[i].key);
+            for (j = strlen (info_ptr->text[i].key); j < 13; ++j)
+                putc(' ', tfP);
+        }
+        putc(' ', tfP); /* at least one space between key and text */
     
-    for (j = 0 ; j < info_ptr->text[i].text_length ; j++) {
-      putc (info_ptr->text[i].text[j], tfp);
-      if (info_ptr->text[i].text[j] == '\n')
-        for (k = 0 ; k < 16 ; k++)
-          putc ((int)' ', tfp);
+        for (j = 0; j < info_ptr->text[i].text_length; ++j) {
+            putc(info_ptr->text[i].text[j], tfP);
+            if (info_ptr->text[i].text[j] == '\n') {
+                unsigned int k;
+                for (k = 0; k < 16; ++k)
+                    putc(' ', tfP);
+            }
+        }
+        putc('\n', tfP);
     }
-    putc ((int)'\n', tfp);
-  }
 }
 
-#ifdef __STDC__
-static void show_time (png_info *info_ptr)
-#else
-static void show_time (info_ptr)
-png_info *info_ptr;
-#endif
-{
+
+
+static void
+showTime(struct pngx * const pngxP) {
+
     static const char * const month[] = {
         "", "January", "February", "March", "April", "May", "June",
         "July", "August", "September", "October", "November", "December"
     };
 
-  if (info_ptr->valid & PNG_INFO_tIME) {
-    if (info_ptr->mod_time.month < 1 ||
-      info_ptr->mod_time.month >= ARRAY_SIZE(month)) {
-      pm_message("tIME chunk in PNG input is invalid; "
-                 "modification time of image is unknown.  "
-                 "The month value, which should be in the range "
-                 "1-12, is %u", info_ptr->mod_time.month);
-    } else
-    pm_message ("modification time: %02d %s %d %02d:%02d:%02d",
-                info_ptr->mod_time.day, month[info_ptr->mod_time.month],
-                info_ptr->mod_time.year, info_ptr->mod_time.hour,
-                info_ptr->mod_time.minute, info_ptr->mod_time.second);
-  }
-}
-
-#ifdef __STDC__
-static void pngtopnm_error_handler (png_structp png_ptr, png_const_charp msg)
-#else
-static void pngtopnm_error_handler (png_ptr, msg)
-png_structp png_ptr;
-png_const_charp msg;
-#endif
-{
-  jmpbuf_wrapper  *jmpbuf_ptr;
-
-  /* this function, aside from the extra step of retrieving the "error
-   * pointer" (below) and the fact that it exists within the application
-   * rather than within libpng, is essentially identical to libpng's
-   * default error handler.  The second point is critical:  since both
-   * setjmp() and longjmp() are called from the same code, they are
-   * guaranteed to have compatible notions of how big a jmp_buf is,
-   * regardless of whether _BSD_SOURCE or anything else has (or has not)
-   * been defined. */
-
-  pm_message("fatal libpng error: %s", msg);
-
-  jmpbuf_ptr = png_get_error_ptr(png_ptr);
-  if (jmpbuf_ptr == NULL) {
-      /* we are completely hosed now */
-      pm_error("EXTREMELY fatal error: jmpbuf unrecoverable; terminating.");
-  }
-
-  longjmp(jmpbuf_ptr->jmpbuf, 1);
+    if (pngxP->info_ptr->valid & PNG_INFO_tIME) {
+      if (pngxP->info_ptr->mod_time.month < 1 ||
+        pngxP->info_ptr->mod_time.month >= ARRAY_SIZE(month)) {
+        pm_message("tIME chunk in PNG input is invalid; "
+                   "modification time of image is unknown.  "
+                   "The month value, which should be in the range "
+                   "1-12, is %u", pngxP->info_ptr->mod_time.month);
+      } else
+        pm_message("modification time: %02d %s %d %02d:%02d:%02d",
+                   pngxP->info_ptr->mod_time.day,
+                   month[pngxP->info_ptr->mod_time.month],
+                   pngxP->info_ptr->mod_time.year,
+                   pngxP->info_ptr->mod_time.hour,
+                   pngxP->info_ptr->mod_time.minute,
+                   pngxP->info_ptr->mod_time.second);
+    }
 }
 
 
 
 static void
-dump_png_info(png_info *info_ptr) {
+dumpPngInfo(struct pngx * const pngxP) {
 
+    png_info * const info_ptr = pngxP->info_ptr;
     const char *type_string;
     const char *filter_string;
 
@@ -445,7 +590,7 @@ dump_png_info(png_info *info_ptr) {
     else
         pm_message("bKGD chunk: not present");
 
-    if (info_ptr->valid & PNG_INFO_hIST)
+    if (info_ptr->valid & PNG_INFO_PLTE)
         pm_message("hIST chunk: present");
     else
         pm_message("hIST chunk: not present");
@@ -478,29 +623,62 @@ dump_png_info(png_info *info_ptr) {
 
 
 
+static const png_color_16 *
+transColor(struct pngx * const pngxP) {
+
+    png_bytep trans;
+    int numTrans;
+    png_color_16 * transColor;
+
+    assert(pngx_chunkIsPresent(pngxP, PNG_INFO_tRNS));
+    
+    png_get_tRNS(pngxP->png_ptr, pngxP->info_ptr,
+                 &trans, &numTrans, &transColor);
+
+    return transColor;
+}
+
+
+
 static bool
-isTransparentColor(pngcolor   const color,
-                   png_info * const info_ptr,
-                   double     const totalgamma) {
+isTransparentColor(pngcolor      const color,
+                   struct pngx * const pngxP,
+                   double        const totalgamma) {
 /*----------------------------------------------------------------------------
    Return TRUE iff pixels of color 'color' are supposed to be transparent
    everywhere they occur.  Assume it's an RGB image.
+
+   'color' has been gamma-corrected.
 -----------------------------------------------------------------------------*/
     bool retval;
 
-    if (info_ptr->valid & PNG_INFO_tRNS) {
-        const png_color_16 * const transColorP = &info_ptr->trans_values;
-    
+    if (pngx_chunkIsPresent(pngxP, PNG_INFO_tRNS)) {
+        const png_color_16 * const transColorP = transColor(pngxP);
 
-        /* There seems to be a problem here: you can't compare real
-           numbers for equality.  Also, I'm not sure the gamma
-           corrected/uncorrected color spaces are right here.  
-        */
+        /* It seems odd that libpng lets you get gamma-corrected pixel
+           values, but not gamma-corrected transparency or background
+           values.  But as that is the case, we have to gamma-correct
+           the transparency values.
 
-        retval = 
-            color.r == gamma_correct(transColorP->red,   totalgamma) &&
-            color.g == gamma_correct(transColorP->green, totalgamma) &&
-            color.b == gamma_correct(transColorP->blue,  totalgamma);
+           Note that because we compare the gamma-corrected values and
+           there may be many-to-one mapping of uncorrected to corrected
+           values, more pixels may be transparent than what the user
+           intended.
+
+           We could fix this by not letting libpng gamma-correct the
+           pixels, and just do it ourselves.
+        */
+    
+        switch (pngxP->info_ptr->color_type) {
+        case PNG_COLOR_TYPE_GRAY:
+            retval = color.r == gamma_correct(transColorP->gray, totalgamma);
+            break;
+        default:
+            retval = 
+                color.r == gamma_correct(transColorP->red,   totalgamma) &&
+                color.g == gamma_correct(transColorP->green, totalgamma) &&
+                color.b == gamma_correct(transColorP->blue,  totalgamma);
+        }
     } else 
         retval = FALSE;
 
@@ -509,37 +687,17 @@ isTransparentColor(pngcolor   const color,
 
 
 
-#define SIG_CHECK_SIZE 4
-
 static void
-read_sig_buf(FILE * const ifP) {
-
-    unsigned char sig_buf[SIG_CHECK_SIZE];
-    size_t bytesRead;
-
-    bytesRead = fread(sig_buf, 1, SIG_CHECK_SIZE, ifP);
-    if (bytesRead != SIG_CHECK_SIZE)
-        pm_error ("input file is empty or too short");
-
-    if (png_sig_cmp(sig_buf, (png_size_t) 0, (png_size_t) SIG_CHECK_SIZE)
-        != 0)
-        pm_error ("input file is not a PNG file");
-}
-
-
-
-static void
-setupGammaCorrection(png_struct * const png_ptr,
-                     png_info *   const info_ptr,
-                     float        const displaygamma,
-                     float *      const totalgammaP) {
+setupGammaCorrection(struct pngx * const pngxP,
+                     float         const displaygamma,
+                     float *       const totalgammaP) {
 
     if (displaygamma == -1.0)
         *totalgammaP = -1.0;
     else {
         float imageGamma;
-        if (info_ptr->valid & PNG_INFO_gAMA)
-            imageGamma = info_ptr->gamma;
+        if (pngxP->info_ptr->valid & PNG_INFO_gAMA)
+            imageGamma = pngxP->info_ptr->gamma;
         else {
             if (verbose)
                 pm_message("PNG doesn't specify image gamma.  Assuming 1.0");
@@ -553,12 +711,12 @@ setupGammaCorrection(png_struct * const png_ptr,
                            "display gamma %4.2f.  No conversion.",
                            imageGamma, displaygamma);
         } else {
-            png_set_gamma(png_ptr, displaygamma, imageGamma);
+            png_set_gamma(pngxP->png_ptr, displaygamma, imageGamma);
             *totalgammaP = imageGamma * displaygamma;
             /* in case of gamma-corrections, sBIT's as in the
                PNG-file are not valid anymore 
             */
-            info_ptr->valid &= ~PNG_INFO_sBIT;
+            pngxP->info_ptr->valid &= ~PNG_INFO_sBIT;
             if (verbose)
                 pm_message("image gamma is %4.2f, "
                            "converted for display gamma of %4.2f",
@@ -582,8 +740,8 @@ paletteHasPartialTransparency(png_info * const info_ptr) {
             for (i = 0, foundGray = FALSE;
                  i < info_ptr->num_trans && !foundGray;
                  ++i) {
-                if (info_ptr->trans[i] != 0 &&
-                    info_ptr->trans[i] != maxval) {
+                if (info_ptr->TRANS_ALPHA[i] != 0 &&
+                    info_ptr->TRANS_ALPHA[i] != maxval) {
                     foundGray = TRUE;
                 }
             }
@@ -599,18 +757,19 @@ paletteHasPartialTransparency(png_info * const info_ptr) {
 
 
 static void
-setupSignificantBits(png_struct *        const png_ptr,
-                     png_info *          const info_ptr,
+setupSignificantBits(struct pngx *       const pngxP,
                      enum alpha_handling const alpha,
                      png_uint_16 *       const maxvalP,
-                     int *               const errorlevelP) {
+                     int *               const errorLevelP) {
 /*----------------------------------------------------------------------------
   Figure out what maxval would best express the information in the PNG
-  described by 'png_ptr' and 'info_ptr', with 'alpha' telling which
-  information in the PNG we care about (image or alpha mask).
+  described by *pngxP, with 'alpha' telling which information in the PNG we
+  care about (image or alpha mask).
 
   Return the result as *maxvalP.
 -----------------------------------------------------------------------------*/
+    png_info * const info_ptr = pngxP->info_ptr;
+
     /* Initial assumption of maxval */
     if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
         if (alpha == ALPHA_ONLY) {
@@ -654,7 +813,7 @@ setupSignificantBits(png_struct *        const png_ptr,
                 unsigned int i;
                 trans_mix = TRUE;
                 for (i = 0; i < info_ptr->num_trans; ++i)
-                    if (info_ptr->trans[i] != 0 && info_ptr->trans[i] != 255) {
+                    if (info_ptr->TRANS_ALPHA[i] != 0 && info_ptr->TRANS_ALPHA[i] != 255) {
                         trans_mix = FALSE;
                         break;
                     }
@@ -675,7 +834,7 @@ setupSignificantBits(png_struct *        const png_ptr,
                            "different bit depths for color channels");
                 pm_message("writing file with %d bit resolution",
                            info_ptr->bit_depth);
-                *errorlevelP = PNMTOPNG_WARNING_LEVEL;
+                *errorLevelP = PNMTOPNG_WARNING_LEVEL;
             } else {
                 if ((info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) &&
                     (info_ptr->sig_bit.red < 255)) {
@@ -697,7 +856,7 @@ setupSignificantBits(png_struct *        const png_ptr,
                     if ((info_ptr->color_type == PNG_COLOR_TYPE_RGB ||
                          info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA) &&
                         (info_ptr->sig_bit.red < info_ptr->bit_depth)) {
-                        png_set_shift (png_ptr, &(info_ptr->sig_bit));
+                        png_set_shift(pngxP->png_ptr, &(info_ptr->sig_bit));
                         *maxvalP = (1l << info_ptr->sig_bit.red) - 1;
                         if (verbose)
                             pm_message("image has fewer significant bits, "
@@ -709,7 +868,7 @@ setupSignificantBits(png_struct *        const png_ptr,
                              info_ptr->color_type ==
                                  PNG_COLOR_TYPE_GRAY_ALPHA) &&
                             (info_ptr->sig_bit.gray < info_ptr->bit_depth)) {
-                            png_set_shift (png_ptr, &(info_ptr->sig_bit));
+                            png_set_shift(pngxP->png_ptr, &info_ptr->sig_bit);
                             *maxvalP = (1l << info_ptr->sig_bit.gray) - 1;
                             if (verbose)
                                 pm_message("image has fewer significant bits, "
@@ -723,7 +882,7 @@ setupSignificantBits(png_struct *        const png_ptr,
             if ((info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA ||
                  info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) && 
                 (info_ptr->sig_bit.gray < info_ptr->bit_depth)) {
-                png_set_shift (png_ptr, &(info_ptr->sig_bit));
+                png_set_shift(pngxP->png_ptr, &info_ptr->sig_bit);
                 if (verbose)
                     pm_message ("image has fewer significant bits, "
                                 "writing file with %d bits", 
@@ -739,22 +898,22 @@ setupSignificantBits(png_struct *        const png_ptr,
 
 
 static bool
-imageHasColor(png_info * const info_ptr) {
+imageHasColor(struct pngx * const pngxP) {
 
     bool retval;
 
-    if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY ||
-        info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+    if (pngxP->info_ptr->color_type == PNG_COLOR_TYPE_GRAY ||
+        pngxP->info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
 
         retval = FALSE;
-    else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
+    else if (pngxP->info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) {
         bool foundColor;
         unsigned int i;
             
         for (i = 0, foundColor = FALSE;
-             i < info_ptr->num_palette && !foundColor;
+             i < pngxP->info_ptr->num_palette && !foundColor;
              ++i) {
-            if (iscolor(info_ptr->palette[i]))
+            if (iscolor(pngxP->info_ptr->palette[i]))
                 foundColor = TRUE;
         }
         retval = foundColor;
@@ -767,14 +926,14 @@ imageHasColor(png_info * const info_ptr) {
 
 
 static void
-determineOutputType(png_info *          const info_ptr,
+determineOutputType(struct pngx *       const pngxP,
                     enum alpha_handling const alphaHandling,
                     pngcolor            const bgColor,
                     xelval              const maxval,
                     int *               const pnmTypeP) {
 
     if (alphaHandling != ALPHA_ONLY &&
-        (imageHasColor(info_ptr) || !isGrayscale(bgColor)))
+        (imageHasColor(pngxP) || !isGrayscale(bgColor)))
         *pnmTypeP = PPM_TYPE;
     else {
         if (maxval > 1)
@@ -787,11 +946,11 @@ determineOutputType(png_info *          const info_ptr,
 
 
 static void
-getBackgroundColor(png_info *        const info_ptr,
-                   const char *      const requestedColor,
-                   float             const totalgamma,
-                   xelval            const maxval,
-                   struct pngcolor * const bgColorP) {
+getBackgroundColor(struct pngx * const pngxP,
+                   const char *  const requestedColor,
+                   float         const totalgamma,
+                   xelval        const maxval,
+                   pngcolor *    const bgColorP) {
 /*----------------------------------------------------------------------------
    Figure out what the background color should be.  If the user requested
    a particular color ('requestedColor' not null), that's the one.
@@ -809,19 +968,19 @@ getBackgroundColor(png_info *        const info_ptr,
         bgColorP->g = PPM_GETG(backcolor);
         bgColorP->b = PPM_GETB(backcolor);
 
-    } else if (info_ptr->valid & PNG_INFO_bKGD) {
+    } else if (pngxP->info_ptr->valid & PNG_INFO_bKGD) {
         /* didn't manage to get libpng to work (bugs?) concerning background
            processing, therefore we do our own.
         */
-        switch (info_ptr->color_type) {
+        switch (pngxP->info_ptr->color_type) {
         case PNG_COLOR_TYPE_GRAY:
         case PNG_COLOR_TYPE_GRAY_ALPHA:
             bgColorP->r = bgColorP->g = bgColorP->b = 
-                gamma_correct(info_ptr->background.gray, totalgamma);
+                gamma_correct(pngxP->info_ptr->background.gray, totalgamma);
             break;
         case PNG_COLOR_TYPE_PALETTE: {
             png_color const rawBgcolor = 
-                info_ptr->palette[info_ptr->background.index];
+                pngxP->info_ptr->palette[pngxP->info_ptr->background.index];
             bgColorP->r = gamma_correct(rawBgcolor.red, totalgamma);
             bgColorP->g = gamma_correct(rawBgcolor.green, totalgamma);
             bgColorP->b = gamma_correct(rawBgcolor.blue, totalgamma);
@@ -829,7 +988,7 @@ getBackgroundColor(png_info *        const info_ptr,
         break;
         case PNG_COLOR_TYPE_RGB:
         case PNG_COLOR_TYPE_RGB_ALPHA: {
-            png_color_16 const rawBgcolor = info_ptr->background;
+            png_color_16 const rawBgcolor = pngxP->info_ptr->background;
             
             bgColorP->r = gamma_correct(rawBgcolor.red,   totalgamma);
             bgColorP->g = gamma_correct(rawBgcolor.green, totalgamma);
@@ -845,115 +1004,159 @@ getBackgroundColor(png_info *        const info_ptr,
 
 
 static void
+warnNonsquarePixels(struct pngx * const pngxP,
+                    int *         const errorLevelP) {
+
+    if (pngxP->info_ptr->valid & PNG_INFO_pHYs) {
+        float const r =
+            (float)pngxP->info_ptr->x_pixels_per_unit /
+            pngxP->info_ptr->y_pixels_per_unit;
+
+        if (r != 1.0) {
+            pm_message ("warning - non-square pixels; "
+                        "to fix do a 'pamscale -%cscale %g'",
+                        r < 1.0 ? 'x' : 'y',
+                        r < 1.0 ? 1.0 / r : r );
+            *errorLevelP = PNMTOPNG_WARNING_LEVEL;
+        }
+    }
+}
+
+
+
+#define GET_PNG_VAL(p) get_png_val(&(p), pngxP->info_ptr->bit_depth)
+
+
+
+static void
+makeXelRow(xel *               const xelrow,
+           xelval              const maxval,
+           int                 const pnmType,
+           struct pngx *       const pngxP,
+           const png_byte *    const pngRasterRow,
+           pngcolor            const bgColor,
+           enum alpha_handling const alphaHandling,
+           double              const totalgamma) {
+
+    const png_byte * pngPixelP;
+    unsigned int col;
+
+    pngPixelP = &pngRasterRow[0];  /* initial value */
+    for (col = 0; col < pngxP->info_ptr->width; ++col) {
+        switch (pngxP->info_ptr->color_type) {
+        case PNG_COLOR_TYPE_GRAY: {
+            pngcolor fgColor;
+            fgColor.r = fgColor.g = fgColor.b = GET_PNG_VAL(pngPixelP);
+            setXel(&xelrow[col], fgColor, bgColor, alphaHandling,
+                   isTransparentColor(fgColor, pngxP, totalgamma) ?
+                   0 : maxval);
+        }
+        break;
+
+        case PNG_COLOR_TYPE_GRAY_ALPHA: {
+            pngcolor fgColor;
+            png_uint_16 alpha;
+
+            fgColor.r = fgColor.g = fgColor.b = GET_PNG_VAL(pngPixelP);
+            alpha = GET_PNG_VAL(pngPixelP);
+            setXel(&xelrow[col], fgColor, bgColor, alphaHandling, alpha);
+        }
+        break;
+
+        case PNG_COLOR_TYPE_PALETTE: {
+            png_uint_16 const index        = GET_PNG_VAL(pngPixelP);
+            png_color   const paletteColor = pngxP->info_ptr->palette[index];
+
+            pngcolor fgColor;
+
+            fgColor.r = paletteColor.red;
+            fgColor.g = paletteColor.green;
+            fgColor.b = paletteColor.blue;
+
+            setXel(&xelrow[col], fgColor, bgColor, alphaHandling,
+                   (pngxP->info_ptr->valid & PNG_INFO_tRNS) &&
+                   index < pngxP->info_ptr->num_trans ?
+                   pngxP->info_ptr->TRANS_ALPHA[index] : maxval);
+        }
+        break;
+                
+        case PNG_COLOR_TYPE_RGB: {
+            pngcolor fgColor;
+
+            fgColor.r = GET_PNG_VAL(pngPixelP);
+            fgColor.g = GET_PNG_VAL(pngPixelP);
+            fgColor.b = GET_PNG_VAL(pngPixelP);
+            setXel(&xelrow[col], fgColor, bgColor, alphaHandling,
+                   isTransparentColor(fgColor, pngxP, totalgamma) ?
+                   0 : maxval);
+        }
+        break;
+
+        case PNG_COLOR_TYPE_RGB_ALPHA: {
+            pngcolor fgColor;
+            png_uint_16 alpha;
+
+            fgColor.r = GET_PNG_VAL(pngPixelP);
+            fgColor.g = GET_PNG_VAL(pngPixelP);
+            fgColor.b = GET_PNG_VAL(pngPixelP);
+            alpha     = GET_PNG_VAL(pngPixelP);
+            setXel(&xelrow[col], fgColor, bgColor, alphaHandling, alpha);
+        }
+        break;
+
+        default:
+            pm_error("unknown PNG color type: %d",
+                     pngxP->info_ptr->color_type);
+        }
+    }
+}
+
+
+
+static void
 writePnm(FILE *              const ofP,
          xelval              const maxval,
-         int                 const pnm_type,
-         png_info *          const info_ptr,
-         png_byte **         const png_image,
+         int                 const pnmType,
+         struct pngx *       const pngxP,
+         png_byte **         const pngRaster,
          pngcolor            const bgColor,
-         enum alpha_handling const alpha_handling,
+         enum alpha_handling const alphaHandling,
          double              const totalgamma) {
 /*----------------------------------------------------------------------------
    Write a PNM of either the image or the alpha mask, according to
-   'alpha_handling' that is in the PNG image described by 'info_ptr' and
-   png_image.
+   'alphaHandling' that is in the PNG image described by *pngxP and
+   pngRaster[][].
 
-   'pnm_type' and 'maxval' are of the output image.
+   'pnmType' and 'maxval' are of the output image.
 
    Use background color 'bgColor' in the output if the PNG is such that a
    background color is needed.
 -----------------------------------------------------------------------------*/
+    int const plainFalse = 0;
+
     xel * xelrow;
     unsigned int row;
 
     if (verbose)
-        pm_message ("writing a %s file (maxval=%u)",
-                    pnm_type == PBM_TYPE ? "PBM" :
-                    pnm_type == PGM_TYPE ? "PGM" :
-                    pnm_type == PPM_TYPE ? "PPM" :
-                    "UNKNOWN!", 
-                    maxval);
+        pm_message("writing a %s file (maxval=%u)",
+                   pnmType == PBM_TYPE ? "PBM" :
+                   pnmType == PGM_TYPE ? "PGM" :
+                   pnmType == PPM_TYPE ? "PPM" :
+                   "UNKNOWN!", 
+                   maxval);
     
-    xelrow = pnm_allocrow(info_ptr->width);
-
-    pnm_writepnminit(stdout, info_ptr->width, info_ptr->height, maxval,
-                     pnm_type, FALSE);
-
-    for (row = 0; row < info_ptr->height; ++row) {
-        png_byte * png_pixelP;
-        int col;
-
-        png_pixelP = &png_image[row][0];  /* initial value */
-        for (col = 0; col < info_ptr->width; ++col) {
-            switch (info_ptr->color_type) {
-            case PNG_COLOR_TYPE_GRAY: {
-                pngcolor fgColor;
-                fgColor.r = fgColor.g = fgColor.b = get_png_val(png_pixelP);
-                setXel(&xelrow[col], fgColor, bgColor, alpha_handling,
-                       ((info_ptr->valid & PNG_INFO_tRNS) &&
-                        (fgColor.r == 
-                         gamma_correct(info_ptr->trans_values.gray,
-                                       totalgamma))) ?
-                       0 : maxval);
-            }
-            break;
-
-            case PNG_COLOR_TYPE_GRAY_ALPHA: {
-                pngcolor fgColor;
-                png_uint_16 alpha;
-
-                fgColor.r = fgColor.g = fgColor.b = get_png_val(png_pixelP);
-                alpha = get_png_val(png_pixelP);
-                setXel(&xelrow[col], fgColor, bgColor, alpha_handling, alpha);
-            }
-            break;
+    xelrow = pnm_allocrow(pngxP->info_ptr->width);
 
-            case PNG_COLOR_TYPE_PALETTE: {
-                png_uint_16 const index        = get_png_val(png_pixelP);
-                png_color   const paletteColor = info_ptr->palette[index];
-
-                pngcolor fgColor;
-
-                fgColor.r = paletteColor.red;
-                fgColor.g = paletteColor.green;
-                fgColor.b = paletteColor.blue;
-
-                setXel(&xelrow[col], fgColor, bgColor, alpha_handling,
-                       (info_ptr->valid & PNG_INFO_tRNS) &&
-                       index < info_ptr->num_trans ?
-                       info_ptr->trans[index] : maxval);
-            }
-            break;
-                
-            case PNG_COLOR_TYPE_RGB: {
-                pngcolor fgColor;
-
-                fgColor.r = get_png_val(png_pixelP);
-                fgColor.g = get_png_val(png_pixelP);
-                fgColor.b = get_png_val(png_pixelP);
-                setXel(&xelrow[col], fgColor, bgColor, alpha_handling,
-                       isTransparentColor(fgColor, info_ptr, totalgamma) ?
-                       0 : maxval);
-            }
-            break;
+    pnm_writepnminit(stdout,
+                     pngxP->info_ptr->width, pngxP->info_ptr->height, maxval,
+                     pnmType, plainFalse);
 
-            case PNG_COLOR_TYPE_RGB_ALPHA: {
-                pngcolor fgColor;
-                png_uint_16 alpha;
+    for (row = 0; row < pngxP->info_ptr->height; ++row) {
+        makeXelRow(xelrow, maxval, pnmType, pngxP, pngRaster[row], bgColor,
+                   alphaHandling, totalgamma);
 
-                fgColor.r = get_png_val(png_pixelP);
-                fgColor.g = get_png_val(png_pixelP);
-                fgColor.b = get_png_val(png_pixelP);
-                alpha     = get_png_val(png_pixelP);
-                setXel(&xelrow[col], fgColor, bgColor, alpha_handling, alpha);
-            }
-            break;
-
-            default:
-                pm_error ("unknown PNG color type: %d", info_ptr->color_type);
-            }
-        }
-        pnm_writepnmrow(ofP, xelrow, info_ptr->width, maxval, pnm_type, FALSE);
+        pnm_writepnmrow(ofP, xelrow, pngxP->info_ptr->width, maxval,
+                        pnmType, plainFalse);
     }
     pnm_freerow (xelrow);
 }
@@ -961,154 +1164,86 @@ writePnm(FILE *              const ofP,
 
 
 static void 
-convertpng(FILE *             const ifp, 
-           FILE *             const tfp, 
+convertpng(FILE *             const ifP, 
+           FILE *             const tfP, 
            struct cmdlineInfo const cmdline,
-           int *              const errorlevelP) {
-
-    png_struct *png_ptr;
-    png_info *info_ptr;
-    png_byte **png_image;
-    int x, y;
-    int linesize;
-    int pnm_type;
+           int *              const errorLevelP) {
+
+    png_byte ** pngRaster;
+    int pnmType;
     pngcolor bgColor;
     float totalgamma;
+    jmp_buf jmpbuf;
+    struct pngx * pngxP;
 
-    *errorlevelP = 0;
-
-    read_sig_buf(ifp);
-
-    png_ptr = png_create_read_struct(
-        PNG_LIBPNG_VER_STRING,
-        &pngtopnm_jmpbuf_struct, pngtopnm_error_handler, NULL);
-    if (png_ptr == NULL)
-        pm_error("cannot allocate main libpng structure (png_ptr)");
-
-    info_ptr = png_create_info_struct (png_ptr);
-    if (info_ptr == NULL)
-        pm_error("cannot allocate LIBPNG structures");
+    *errorLevelP = 0;
 
-    if (setjmp(pngtopnm_jmpbuf_struct.jmpbuf))
+    if (setjmp(jmpbuf))
         pm_error ("setjmp returns error condition");
 
-    png_init_io (png_ptr, ifp);
-    png_set_sig_bytes (png_ptr, SIG_CHECK_SIZE);
-    png_read_info (png_ptr, info_ptr);
+    pngx_createRead(&pngxP, &jmpbuf);
 
-    MALLOCARRAY(png_image, info_ptr->height);
-    if (png_image == NULL) {
-        png_destroy_read_struct (&png_ptr, &info_ptr, (png_infopp)NULL);
-        pm_closer (ifp);
-        pm_error ("couldn't allocate space for image");
-    }
+    readPng(pngxP, ifP, &pngRaster);
 
-    if (info_ptr->bit_depth == 16)
-        linesize = 2 * info_ptr->width;
-    else
-        linesize = info_ptr->width;
+    if (verbose)
+        dumpPngInfo(pngxP);
 
-    if (info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
-        linesize *= 2;
-    else
-        if (info_ptr->color_type == PNG_COLOR_TYPE_RGB)
-            linesize *= 3;
-        else
-            if (info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
-                linesize *= 4;
-
-    for (y = 0 ; y < info_ptr->height ; y++) {
-        png_image[y] = malloc (linesize);
-        if (png_image[y] == NULL) {
-            for (x = 0 ; x < y ; x++)
-                free (png_image[x]);
-            free (png_image);
-            png_destroy_read_struct (&png_ptr, &info_ptr, (png_infopp)NULL);
-            pm_closer (ifp);
-            pm_error ("couldn't allocate space for image");
-        }
-    }
+    if (cmdline.time)
+        showTime(pngxP);
+    if (tfP)
+        saveText(pngxP, tfP);
 
-    if (info_ptr->bit_depth < 8)
-        png_set_packing (png_ptr);
+    warnNonsquarePixels(pngxP, errorLevelP);
 
-    setupGammaCorrection(png_ptr, info_ptr, cmdline.gamma, &totalgamma);
+    setupGammaCorrection(pngxP, cmdline.gamma, &totalgamma);
 
-    setupSignificantBits(png_ptr, info_ptr, cmdline.alpha,
-                         &maxval, errorlevelP);
+    setupSignificantBits(pngxP, cmdline.alpha, &maxval, errorLevelP);
 
-    getBackgroundColor(info_ptr, cmdline.background, totalgamma, maxval,
+    getBackgroundColor(pngxP, cmdline.background, totalgamma, maxval,
                        &bgColor);
 
-    png_read_image (png_ptr, png_image);
-    png_read_end (png_ptr, info_ptr);
+    determineOutputType(pngxP, cmdline.alpha, bgColor, maxval, &pnmType);
 
-    if (verbose)
-        /* Note that some of info_ptr is not defined until png_read_end() 
-       completes.  That's because it comes from chunks that are at the
-       end of the stream.
-    */
-        dump_png_info(info_ptr);
-
-    if (mtime)
-        show_time (info_ptr);
-    if (tfp)
-        save_text (info_ptr, tfp);
-
-    if (info_ptr->valid & PNG_INFO_pHYs) {
-        float r;
-        r = (float)info_ptr->x_pixels_per_unit / info_ptr->y_pixels_per_unit;
-        if (r != 1.0) {
-            pm_message ("warning - non-square pixels; "
-                        "to fix do a 'pamscale -%cscale %g'",
-                        r < 1.0 ? 'x' : 'y',
-                        r < 1.0 ? 1.0 / r : r );
-            *errorlevelP = PNMTOPNG_WARNING_LEVEL;
-        }
-    }
-
-    determineOutputType(info_ptr, cmdline.alpha, bgColor, maxval, &pnm_type);
-
-    writePnm(stdout, maxval, pnm_type, info_ptr, png_image, bgColor, 
+    writePnm(stdout, maxval, pnmType, pngxP, pngRaster, bgColor, 
              cmdline.alpha, totalgamma);
 
     fflush(stdout);
-    for (y = 0 ; y < info_ptr->height ; y++)
-        free (png_image[y]);
-    free (png_image);
-    png_destroy_read_struct (&png_ptr, &info_ptr, (png_infopp)NULL);
+
+    freePngRaster(pngRaster, pngxP);
+
+    pngx_destroy(pngxP);
 }
 
 
 
 int 
-main(int argc, char *argv[]) {
+main(int argc, const char *argv[]) {
 
     struct cmdlineInfo cmdline;
-    FILE *ifp, *tfp;
-    int errorlevel;
+    FILE * ifP;
+    FILE * tfP;
+    int errorLevel;
 
-    pnm_init (&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
     verbose = cmdline.verbose;
-    mtime = cmdline.time;
 
-    ifp = pm_openr(cmdline.inputFilespec);
+    ifP = pm_openr(cmdline.inputFilespec);
 
     if (cmdline.text)
-        tfp = pm_openw(cmdline.text);
+        tfP = pm_openw(cmdline.text);
     else
-        tfp = NULL;
+        tfP = NULL;
 
-    convertpng (ifp, tfp, cmdline, &errorlevel);
+    convertpng(ifP, tfP, cmdline, &errorLevel);
 
-    if (tfp)
-        pm_close(tfp);
+    if (tfP)
+        pm_close(tfP);
 
-    pm_close(ifp);
+    pm_close(ifP);
     pm_close(stdout);
 
-    return errorlevel;
+    return errorLevel;
 }
diff --git a/converter/other/pnmtoddif.c b/converter/other/pnmtoddif.c
index 962487f2..ae8c8524 100644
--- a/converter/other/pnmtoddif.c
+++ b/converter/other/pnmtoddif.c
@@ -1,14 +1,4 @@
 /*
- * $Log: pnmtoddif.c,v $
- * Revision 1.6  1993/01/25  08:14:06  neideck
- * Placed into public use form.
- *
- * Revision 1.5  1992/12/02  08:15:18  neideck
- *  Added RCS id.
- *
- */
-
-/*
  * Author:      Burkhard Neidecker-Lutz
  *              Digital CEC Karlsruhe
  *      neideck@nestvx.enet.dec.com 
@@ -31,6 +21,8 @@
 */
 
 #include <string.h>
+
+#include "mallocvar.h"
 #include "pnm.h"
 
 /* The structure we use to convey all sorts of "magic" data to the DDIF */
@@ -166,11 +158,7 @@ wr_int(unsigned char ** buffer, int val)
     } else {
         sign = val < 0 ? 0xff : 0x00;   /* Sign bits */
         length = 4;
-#ifdef __STDC__
         mask  = 0xffu << 24;
-#else
-        mask  = 0xff << 24;
-#endif
         while ((val & mask) == sign) {  /* Find the smallest representation */
             length--;
             mask >>= 8;
@@ -418,21 +406,181 @@ write_trailer(FILE * file)
 
 
 
-int main(int argc, char *argv[])
-{
+
+static void
+convertPbmRaster(FILE *          const ifP,
+                 int             const format,
+                 unsigned int    const cols,
+                 unsigned int    const rows,
+                 FILE *          const ofP,
+                 unsigned int    const bytesPerLine,
+                 unsigned char * const data) {
+                 
+    bit * const pixels = pbm_allocrow(cols);
+
+    unsigned int row;
+
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+        unsigned int k;
+        unsigned int mask;
+        unsigned char * p;
+        size_t bytesWritten;
+
+        pbm_readpbmrow(ifP, pixels, cols, format);
+
+        mask = 0x00;
+        p = &data[0];
+        for (col = 0, k = 0; col < cols; ++col) {
+            if (pixels[col] == PBM_BLACK)
+                mask |= 1 << k;
+            if (k == 7) {
+                *p++ = mask;
+                mask = 0x00;
+                k = 0;
+            } else
+                ++k;
+        }
+        if (k != 7)
+            /* Flush the rest of the column */
+            *p = mask;
+
+        bytesWritten =  fwrite(data, 1, bytesPerLine, ofP);
+        if (bytesWritten != bytesPerLine)
+            pm_error("File write error on Row %u", row);
+    }
+
+    pbm_freerow(pixels);
+}
+
+
+
+static void
+convertPgmRaster(FILE *          const ifP,
+                 int             const format,
+                 xelval          const maxval,
+                 unsigned int    const cols,
+                 unsigned int    const rows,
+                 FILE *          const ofP,
+                 unsigned int    const bytesPerLine,
+                 unsigned char * const data) {
+
+    gray * const pixels = pgm_allocrow(cols);
+
+    unsigned int row;
+
+    for (row = 0; row < rows; ++row) {
+        unsigned char * p;
+        unsigned int col;
+        size_t bytesWritten;
+
+        p = &data[0];
+
+        pgm_readpgmrow(ifP, pixels, cols, maxval, format);
+
+        for (col = 0; col < cols; ++col)
+            *p++ = (unsigned char) pixels[col];
+
+        bytesWritten = fwrite(data, 1, bytesPerLine, ofP);
+        if (bytesWritten != bytesPerLine)
+            pm_error("File write error on Row %u", row);
+    }
+    pgm_freerow(pixels);
+}
+
+
+
+
+static void
+convertPpmRaster(FILE *          const ifP,
+                 int             const format,
+                 xelval          const maxval,
+                 unsigned int    const cols,
+                 unsigned int    const rows,
+                 FILE *          const ofP,
+                 unsigned int    const bytesPerLine,
+                 unsigned char * const data) {
+
+    pixel * const pixels = ppm_allocrow(cols);
+
+    unsigned int row;
+
+    for (row = 0; row < rows; ++row) {
+        unsigned char * p;
+        unsigned int col;
+        size_t bytesWritten;
+
+        p = &data[0];
+
+        ppm_readppmrow(ifP, pixels, cols, maxval, format);
+
+        for (col = 0; col < cols; ++col) {
+            *p++ = PPM_GETR(pixels[col]);
+            *p++ = PPM_GETG(pixels[col]);
+            *p++ = PPM_GETB(pixels[col]);
+        }
+        bytesWritten =  fwrite(data, 1, bytesPerLine, ofP);
+        if (bytesWritten != bytesPerLine)
+            pm_error("File write error on Row %u", row);
+    }
+    ppm_freerow(pixels);
+}
+
+
+
+static void
+convertRaster(FILE *       const ifP,
+              int          const format,
+              xelval       const maxval,
+              unsigned int const cols,
+              unsigned int const rows,
+              FILE *       const ofP,
+              unsigned int const bytesPerLine) {
+
+    unsigned char * data;
+    unsigned char * p;
+
+    MALLOCARRAY(data, bytesPerLine);
+
+    if (data == NULL)
+        pm_error("Couldn't allocate %u-byte line buffer", bytesPerLine);
+
+    p = data;  /* initial value */
+
+    switch (PNM_FORMAT_TYPE(format)) {
+    case PBM_TYPE:
+        convertPbmRaster(ifP, format, cols, rows, ofP, bytesPerLine, data);
+        break;
+    case PGM_TYPE:
+        convertPgmRaster(ifP, format, maxval, cols, rows, ofP, bytesPerLine,
+                         data);
+        break;
+    case PPM_TYPE:
+        convertPpmRaster(ifP, format, maxval, cols, rows, ofP, bytesPerLine,
+                         data);
+        break;
+    default:
+        pm_error("INTERNAL ERROR: impossible format value");
+    }
+
+    free(data);
+}
+
+
+
+int
+main(int argc, char *argv[]) {
     FILE           *ifd;
-    FILE       *ofd;
+    FILE           *ofd;
     int             rows, cols;
     xelval          maxval;
     int             format;
     const char     * const usage = "[-resolution x y] [pnmfile [ddiffile]]";
-    int             i, j;
     char           *outfile;
     int       argn;
     int hor_resolution = 75;
     int ver_resolution = 75;
     imageparams ip;
-    unsigned char  *data, *p;
 
     pnm_init(&argc, argv);
 
@@ -516,90 +664,10 @@ int main(int argc, char *argv[])
         exit(1);
     }
 
-    if (!(p = data = (unsigned char*)  malloc(ip.bytes_per_line))) {
-        perror("allocating line buffer");
-        exit(1);
-    }
-
-    switch (PNM_FORMAT_TYPE(format)) {
-    case PBM_TYPE:
-    {
-        bit            *pixels;
-        int             mask;
-        int             k;
-
-        pixels = pbm_allocrow(cols);
-
-        for (i = 0; i < rows; i++) {
-            pbm_readpbmrow(ifd, pixels, cols, format);
-            mask = 0;
-            p = data;
-            for (j = 0, k = 0; j < cols; j++) {
-                if (pixels[j] == PBM_BLACK) {
-                    mask |= 1 << k;
-                }
-                if (k == 7) {
-                    *p++ = mask;
-                    mask = 0;
-                    k = 0;
-                } else {
-                    k++;
-                }
-            }
-            if (k != 7) {       /* Flush the rest of the column */
-                *p = mask;
-            }
-            if (fwrite(data,1,ip.bytes_per_line,ofd) != ip.bytes_per_line) {
-                perror("Writing image data\n");
-                exit(1);
-            }
-        }
-    }
-    break;
-    case PGM_TYPE:
-    {
-        gray          *pixels = pgm_allocrow(cols);
-
-        for (i = 0; i < rows; i++) {
-            p = data;
-            pgm_readpgmrow(ifd, pixels, cols, maxval, format);
-            for (j = 0; j < cols; j++) {
-                *p++ = (unsigned char) pixels[j];
-            }
-            if (fwrite(data,1,ip.bytes_per_line,ofd) != ip.bytes_per_line) {
-                perror("Writing image data\n");
-                exit(1);
-            }
-        }
-        pgm_freerow(pixels);
-    }
-    break;
-    case PPM_TYPE:
-    {
-        pixel          *pixels = ppm_allocrow(cols);
-
-        for (i = 0; i < rows; i++) {
-            p = data;
-            ppm_readppmrow(ifd, pixels, cols, maxval, format);
-            for (j = 0; j < cols; j++) {
-                *p++ = PPM_GETR(pixels[j]);
-                *p++ = PPM_GETG(pixels[j]);
-                *p++ = PPM_GETB(pixels[j]);
-            }
-            if (fwrite(data,1,ip.bytes_per_line,ofd) != ip.bytes_per_line) {
-                perror("Writing image data\n");
-                exit(1);
-            }
-        }
-        ppm_freerow(pixels);
-    }
-    break;
-    }
+    convertRaster(ifd, format, maxval, cols, rows, ofd, ip.bytes_per_line);
 
     pm_close(ifd);
 
-    free(data);
-
     if (!write_trailer(ofd)) {
         perror("Writing trailer");
         exit(1);
diff --git a/converter/other/pnmtojpeg.c b/converter/other/pnmtojpeg.c
index 9a247633..198aa156 100644
--- a/converter/other/pnmtojpeg.c
+++ b/converter/other/pnmtojpeg.c
@@ -29,6 +29,8 @@
    itself, but doesn't.
 */
 #include <jpeglib.h>
+
+#include "pm_c_util.h"
 #include "pnm.h"
 #include "shhopt.h"
 #include "mallocvar.h"
@@ -330,23 +332,6 @@ parseCommandLine(const int argc, char ** argv,
 }
 
 
-static void
-compute_rescaling_array(JSAMPLE ** const rescale_p, const pixval maxval,
-                        const struct jpeg_compress_struct cinfo);
-static void
-convert_scanlines(struct jpeg_compress_struct * const cinfo_p, FILE * const input_file,
-                  const pixval maxval, const int input_fmt,
-                  JSAMPLE xlate_table[]);
-
-static boolean read_quant_tables (j_compress_ptr cinfo, char * filename,
-                                  int scale_factor, boolean force_baseline);
-
-static boolean read_scan_script (j_compress_ptr cinfo, char * filename);
-
-static boolean set_quant_slots (j_compress_ptr cinfo, char *arg);
-
-static boolean set_sample_factors (j_compress_ptr cinfo, char *arg);
-
 
 static void
 report_compressor(const struct jpeg_compress_struct cinfo) {
@@ -423,6 +408,370 @@ setup_jpeg_density(struct jpeg_compress_struct * const cinfoP,
 
 
 
+/*----------------------------------------------------------------------------
+   The functions below here are essentially the file rdswitch.c from
+   the JPEG library.  They perform the functions specifed by the following
+   pnmtojpeg options:
+
+   -qtables file          Read quantization tables from text file
+   -scans file            Read scan script from text file
+   -qslots N[,N,...]      Set component quantization table selectors
+   -sample HxV[,HxV,...]  Set component sampling factors
+-----------------------------------------------------------------------------*/
+
+static int
+text_getc (FILE * file)
+/* Read next char, skipping over any comments (# to end of line) */
+/* A comment/newline sequence is returned as a newline */
+{
+    register int ch;
+  
+    ch = getc(file);
+    if (ch == '#') {
+        do {
+            ch = getc(file);
+        } while (ch != '\n' && ch != EOF);
+    }
+    return ch;
+}
+
+
+static boolean
+readTextInteger(FILE * const fileP,
+                long * const resultP,
+                int *  const termcharP) {
+/*----------------------------------------------------------------------------
+   Read the next unsigned decimal integer from file 'fileP', skipping
+   white space as necessary.  Return it as *resultP.
+
+   Also read one character after the integer and return it as *termcharP.
+
+   If there is no character after the integer, return *termcharP == EOF.
+
+   Iff the next thing in the file is not a valid unsigned decimal integer,
+   return FALSE.
+-----------------------------------------------------------------------------*/
+    int ch;
+    boolean retval;
+  
+    /* Skip any leading whitespace, detect EOF */
+    do {
+        ch = text_getc(fileP);
+    } while (isspace(ch));
+  
+    if (!isdigit(ch))
+        retval = FALSE;
+    else {
+        long val;
+        val = ch - '0';  /* initial value */
+        while ((ch = text_getc(fileP)) != EOF) {
+            if (! isdigit(ch))
+                break;
+            val *= 10;
+            val += ch - '0';
+        }
+        *resultP = val;
+        retval = TRUE;
+    }
+    *termcharP = ch;
+    return retval;
+}
+
+
+static boolean
+read_scan_integer (FILE * file, long * result, int * termchar)
+/* Variant of readTextInteger that always looks for a non-space termchar;
+ * this simplifies parsing of punctuation in scan scripts.
+ */
+{
+    register int ch;
+
+    if (! readTextInteger(file, result, termchar))
+        return FALSE;
+    ch = *termchar;
+    while (ch != EOF && isspace(ch))
+        ch = text_getc(file);
+    if (isdigit(ch)) {		/* oops, put it back */
+        if (ungetc(ch, file) == EOF)
+            return FALSE;
+        ch = ' ';
+    } else {
+        /* Any separators other than ';' and ':' are ignored;
+         * this allows user to insert commas, etc, if desired.
+         */
+        if (ch != EOF && ch != ';' && ch != ':')
+            ch = ' ';
+    }
+    *termchar = ch;
+    return TRUE;
+}
+
+
+
+static boolean
+read_scan_script(j_compress_ptr const cinfo,
+                 const char *   const filename) {
+/*----------------------------------------------------------------------------
+  Read a scan script from the specified text file.
+  Each entry in the file defines one scan to be emitted.
+  Entries are separated by semicolons ';'.
+  An entry contains one to four component indexes,
+  optionally followed by a colon ':' and four progressive-JPEG parameters.
+  The component indexes denote which component(s) are to be transmitted
+  in the current scan.  The first component has index 0.
+  Sequential JPEG is used if the progressive-JPEG parameters are omitted.
+  The file is free format text: any whitespace may appear between numbers
+  and the ':' and ';' punctuation marks.  Also, other punctuation (such
+  as commas or dashes) can be placed between numbers if desired.
+  Comments preceded by '#' may be included in the file.
+  Note: we do very little validity checking here;
+  jcmaster.c will validate the script parameters.
+-----------------------------------------------------------------------------*/
+    FILE * fp;
+    unsigned int nscans;
+    unsigned int ncomps;
+    int termchar;
+    long val;
+#define MAX_SCANS  100      /* quite arbitrary limit */
+    jpeg_scan_info scans[MAX_SCANS];
+
+    fp = fopen(filename, "r");
+    if (fp == NULL) {
+        pm_message("Can't open scan definition file %s", filename);
+        return FALSE;
+    }
+    nscans = 0;
+
+    while (read_scan_integer(fp, &val, &termchar)) {
+        ++nscans;  /* We got another scan */
+        if (nscans > MAX_SCANS) {
+            pm_message("Too many scans defined in file %s", filename);
+            fclose(fp);
+            return FALSE;
+        }
+        scans[nscans-1].component_index[0] = (int) val;
+        ncomps = 1;
+        while (termchar == ' ') {
+            if (ncomps >= MAX_COMPS_IN_SCAN) {
+                pm_message("Too many components in one scan in file %s", 
+                           filename);
+                fclose(fp);
+                return FALSE;
+            }
+            if (! read_scan_integer(fp, &val, &termchar))
+                goto bogus;
+            scans[nscans-1].component_index[ncomps] = (int) val;
+            ++ncomps;
+        }
+        scans[nscans-1].comps_in_scan = ncomps;
+        if (termchar == ':') {
+            if (! read_scan_integer(fp, &val, &termchar) || termchar != ' ')
+                goto bogus;
+            scans[nscans-1].Ss = (int) val;
+            if (! read_scan_integer(fp, &val, &termchar) || termchar != ' ')
+                goto bogus;
+            scans[nscans-1].Se = (int) val;
+            if (! read_scan_integer(fp, &val, &termchar) || termchar != ' ')
+                goto bogus;
+            scans[nscans-1].Ah = (int) val;
+            if (! read_scan_integer(fp, &val, &termchar))
+                goto bogus;
+            scans[nscans-1].Al = (int) val;
+        } else {
+            /* set non-progressive parameters */
+            scans[nscans-1].Ss = 0;
+            scans[nscans-1].Se = DCTSIZE2-1;
+            scans[nscans-1].Ah = 0;
+            scans[nscans-1].Al = 0;
+        }
+        if (termchar != ';' && termchar != EOF) {
+        bogus:
+            pm_message("Invalid scan entry format in file %s", filename);
+            fclose(fp);
+            return FALSE;
+        }
+    }
+
+    if (termchar != EOF) {
+        pm_message("Non-numeric data in file %s", filename);
+        fclose(fp);
+        return FALSE;
+    }
+
+    if (nscans > 0) {
+        /* Stash completed scan list in cinfo structure.  NOTE: in
+           this program, JPOOL_IMAGE is the right lifetime for this
+           data, but if you want to compress multiple images you'd
+           want JPOOL_PERMANENT.  
+        */
+        const unsigned int scan_info_size = nscans * sizeof(jpeg_scan_info);
+        jpeg_scan_info * const scan_info = 
+            (jpeg_scan_info *)
+            (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+                                        scan_info_size);
+        memcpy(scan_info, scans, scan_info_size);
+        cinfo->scan_info = scan_info;
+        cinfo->num_scans = nscans;
+    }
+
+    fclose(fp);
+    return TRUE;
+}
+
+
+
+static boolean
+read_quant_tables (j_compress_ptr cinfo, char * filename,
+                   int scale_factor, boolean force_baseline)
+/* Read a set of quantization tables from the specified file.
+ * The file is plain ASCII text: decimal numbers with whitespace between.
+ * Comments preceded by '#' may be included in the file.
+ * There may be one to NUM_QUANT_TBLS tables in the file, each of 64 values.
+ * The tables are implicitly numbered 0,1,etc.
+ * NOTE: does not affect the qslots mapping, which will default to selecting
+ * table 0 for luminance (or primary) components, 1 for chrominance components.
+ * You must use -qslots if you want a different component->table mapping.
+ */
+{
+    FILE * fp;
+    boolean retval;
+
+    fp = fopen(filename, "rb");
+    if (fp == NULL) {
+        pm_message("Can't open table file %s", filename);
+        retval = FALSE;
+    } else {
+        boolean eof, error;
+        unsigned int tblno;
+
+        for (tblno = 0, eof = FALSE, error = FALSE; !eof && !error; ++tblno) {
+            long val;
+            int termchar;
+            boolean gotOne;
+
+            gotOne = readTextInteger(fp, &val, &termchar);
+            if (gotOne) {
+                /* read 1st element of table */
+                if (tblno >= NUM_QUANT_TBLS) {
+                    pm_message("Too many tables in file %s", filename);
+                    error = TRUE;
+                } else { 
+                    unsigned int table[DCTSIZE2];
+                    unsigned int i;
+
+                    table[0] = (unsigned int) val;
+                    for (i = 1; i < DCTSIZE2 && !error; ++i) {
+                        if (! readTextInteger(fp, &val, &termchar)) {
+                            pm_message("Invalid table data in file %s",
+                                       filename);
+                            error = TRUE;
+                        } else
+                            table[i] = (unsigned int) val;
+                    }
+                    if (!error)
+                        jpeg_add_quant_table(
+                            cinfo, tblno, table, scale_factor, force_baseline);
+                }
+            } else {
+                if (termchar == EOF)
+                    eof = TRUE;
+                else {
+                    pm_message("Non-numeric data in file %s", filename);
+                    error = TRUE;
+                }
+            }
+        }
+
+        fclose(fp);
+        retval = !error;
+    }
+        
+    return retval;
+}
+
+
+
+static boolean
+set_quant_slots (j_compress_ptr cinfo, char *arg)
+/* Process a quantization-table-selectors parameter string, of the form
+ *     N[,N,...]
+ * If there are more components than parameters, the last value is replicated.
+ */
+{
+    int val = 0;			/* default table # */
+    int ci;
+    char ch;
+
+    for (ci = 0; ci < MAX_COMPONENTS; ci++) {
+        if (*arg) {
+            ch = ',';			/* if not set by sscanf, will be ',' */
+            if (sscanf(arg, "%d%c", &val, &ch) < 1)
+                return FALSE;
+            if (ch != ',')		/* syntax check */
+                return FALSE;
+            if (val < 0 || val >= NUM_QUANT_TBLS) {
+                pm_message("Invalid quantization table number: %d.  "
+                           "JPEG quantization tables are numbered 0..%d",
+                           val, NUM_QUANT_TBLS - 1);
+                return FALSE;
+            }
+            cinfo->comp_info[ci].quant_tbl_no = val;
+            while (*arg && *arg++ != ',') 
+                /* advance to next segment of arg string */
+                ;
+        } else {
+            /* reached end of parameter, set remaining components to last tbl*/
+            cinfo->comp_info[ci].quant_tbl_no = val;
+        }
+    }
+    return TRUE;
+}
+
+
+static boolean
+set_sample_factors (j_compress_ptr cinfo, char *arg)
+/* Process a sample-factors parameter string, of the form
+ *     HxV[,HxV,...]
+ * If there are more components than parameters, "1x1" is assumed for the rest.
+ */
+{
+    int ci, val1, val2;
+    char ch1, ch2;
+
+    for (ci = 0; ci < MAX_COMPONENTS; ci++) {
+        if (*arg) {
+            ch2 = ',';		/* if not set by sscanf, will be ',' */
+            if (sscanf(arg, "%d%c%d%c", &val1, &ch1, &val2, &ch2) < 3)
+                return FALSE;
+            if ((ch1 != 'x' && ch1 != 'X') || ch2 != ',') /* syntax check */
+                return FALSE;
+            if (val1 <= 0 || val1 > 4) {
+                pm_message("Invalid sampling factor: %d.  " 
+                           "JPEG sampling factors must be 1..4", val1);
+                return FALSE;
+            }
+            if (val2 <= 0 || val2 > 4) {
+                pm_message("Invalid sampling factor: %d.  "
+                           "JPEG sampling factors must be 1..4", val2);
+                return FALSE;
+            }
+            cinfo->comp_info[ci].h_samp_factor = val1;
+            cinfo->comp_info[ci].v_samp_factor = val2;
+            while (*arg && *arg++ != ',') 
+                /* advance to next segment of arg string */
+                ;
+        } else {
+            /* reached end of parameter, set remaining components 
+               to 1x1 sampling */
+            cinfo->comp_info[ci].h_samp_factor = 1;
+            cinfo->comp_info[ci].v_samp_factor = 1;
+        }
+    }
+    return TRUE;
+}
+
+
+
 static void
 setup_jpeg(struct jpeg_compress_struct * const cinfoP,
            struct jpeg_error_mgr       * const jerrP,
@@ -686,354 +1035,21 @@ convert_scanlines(struct jpeg_compress_struct * const cinfo_p,
   }
 
   pnm_freerow(pnm_buffer);
-  /* Dont' worry about the compressor input buffer; it gets freed 
+  /* Don't worry about the compressor input buffer; it gets freed 
      automatically
   */
-
-}
-
-/*----------------------------------------------------------------------------
-   The functions below here are essentially the file rdswitch.c from
-   the JPEG library.  They perform the functions specifed by the following
-   pnmtojpeg options:
-
-   -qtables file          Read quantization tables from text file
-   -scans file            Read scan script from text file
-   -qslots N[,N,...]      Set component quantization table selectors
-   -sample HxV[,HxV,...]  Set component sampling factors
------------------------------------------------------------------------------*/
-
-static int
-text_getc (FILE * file)
-/* Read next char, skipping over any comments (# to end of line) */
-/* A comment/newline sequence is returned as a newline */
-{
-    register int ch;
-  
-    ch = getc(file);
-    if (ch == '#') {
-        do {
-            ch = getc(file);
-        } while (ch != '\n' && ch != EOF);
-    }
-    return ch;
-}
-
-
-static boolean
-read_text_integer (FILE * file, long * result, int * termchar)
-/* Read an unsigned decimal integer from a file, store it in result */
-/* Reads one trailing character after the integer; returns it in termchar */
-{
-    register int ch;
-    register long val;
-  
-    /* Skip any leading whitespace, detect EOF */
-    do {
-        ch = text_getc(file);
-        if (ch == EOF) {
-            *termchar = ch;
-            return FALSE;
-        }
-    } while (isspace(ch));
-  
-    if (! isdigit(ch)) {
-        *termchar = ch;
-        return FALSE;
-    }
-
-    val = ch - '0';
-    while ((ch = text_getc(file)) != EOF) {
-        if (! isdigit(ch))
-            break;
-        val *= 10;
-        val += ch - '0';
-    }
-    *result = val;
-    *termchar = ch;
-    return TRUE;
-}
-
-
-static boolean
-read_quant_tables (j_compress_ptr cinfo, char * filename,
-                   int scale_factor, boolean force_baseline)
-/* Read a set of quantization tables from the specified file.
- * The file is plain ASCII text: decimal numbers with whitespace between.
- * Comments preceded by '#' may be included in the file.
- * There may be one to NUM_QUANT_TBLS tables in the file, each of 64 values.
- * The tables are implicitly numbered 0,1,etc.
- * NOTE: does not affect the qslots mapping, which will default to selecting
- * table 0 for luminance (or primary) components, 1 for chrominance components.
- * You must use -qslots if you want a different component->table mapping.
- */
-{
-    FILE * fp;
-    int tblno, i, termchar;
-    long val;
-    unsigned int table[DCTSIZE2];
-
-    if ((fp = fopen(filename, "rb")) == NULL) {
-        pm_message("Can't open table file %s", filename);
-        return FALSE;
-    }
-    tblno = 0;
-
-    while (read_text_integer(fp, &val, &termchar)) { /* read 1st element of table */
-        if (tblno >= NUM_QUANT_TBLS) {
-            pm_message("Too many tables in file %s", filename);
-            fclose(fp);
-            return FALSE;
-        }
-        table[0] = (unsigned int) val;
-        for (i = 1; i < DCTSIZE2; i++) {
-            if (! read_text_integer(fp, &val, &termchar)) {
-                pm_message("Invalid table data in file %s", filename);
-                fclose(fp);
-                return FALSE;
-            }
-            table[i] = (unsigned int) val;
-        }
-        jpeg_add_quant_table(cinfo, tblno, table, scale_factor, 
-                             force_baseline);
-        tblno++;
-    }
-
-    if (termchar != EOF) {
-        pm_message("Non-numeric data in file %s", filename);
-        fclose(fp);
-        return FALSE;
-    }
-
-    fclose(fp);
-    return TRUE;
-}
-
-
-static boolean
-read_scan_integer (FILE * file, long * result, int * termchar)
-/* Variant of read_text_integer that always looks for a non-space termchar;
- * this simplifies parsing of punctuation in scan scripts.
- */
-{
-    register int ch;
-
-    if (! read_text_integer(file, result, termchar))
-        return FALSE;
-    ch = *termchar;
-    while (ch != EOF && isspace(ch))
-        ch = text_getc(file);
-    if (isdigit(ch)) {		/* oops, put it back */
-        if (ungetc(ch, file) == EOF)
-            return FALSE;
-        ch = ' ';
-    } else {
-        /* Any separators other than ';' and ':' are ignored;
-         * this allows user to insert commas, etc, if desired.
-         */
-        if (ch != EOF && ch != ';' && ch != ':')
-            ch = ' ';
-    }
-    *termchar = ch;
-    return TRUE;
-}
-
-
-boolean
-read_scan_script (j_compress_ptr cinfo, char * filename)
-/* Read a scan script from the specified text file.
- * Each entry in the file defines one scan to be emitted.
- * Entries are separated by semicolons ';'.
- * An entry contains one to four component indexes,
- * optionally followed by a colon ':' and four progressive-JPEG parameters.
- * The component indexes denote which component(s) are to be transmitted
- * in the current scan.  The first component has index 0.
- * Sequential JPEG is used if the progressive-JPEG parameters are omitted.
- * The file is free format text: any whitespace may appear between numbers
- * and the ':' and ';' punctuation marks.  Also, other punctuation (such
- * as commas or dashes) can be placed between numbers if desired.
- * Comments preceded by '#' may be included in the file.
- * Note: we do very little validity checking here;
- * jcmaster.c will validate the script parameters.
- */
-{
-    FILE * fp;
-    int nscans, ncomps, termchar;
-    long val;
-#define MAX_SCANS  100      /* quite arbitrary limit */
-    jpeg_scan_info scans[MAX_SCANS];
-
-    if ((fp = fopen(filename, "r")) == NULL) {
-        pm_message("Can't open scan definition file %s", filename);
-        return FALSE;
-    }
-    nscans = 0;
-
-    while (read_scan_integer(fp, &val, &termchar)) {
-        nscans++;  /* We got another scan */
-        if (nscans > MAX_SCANS) {
-            pm_message("Too many scans defined in file %s", filename);
-            fclose(fp);
-            return FALSE;
-        }
-        scans[nscans-1].component_index[0] = (int) val;
-        ncomps = 1;
-        while (termchar == ' ') {
-            if (ncomps >= MAX_COMPS_IN_SCAN) {
-                pm_message("Too many components in one scan in file %s", 
-                           filename);
-                fclose(fp);
-                return FALSE;
-            }
-            if (! read_scan_integer(fp, &val, &termchar))
-                goto bogus;
-            scans[nscans-1].component_index[ncomps] = (int) val;
-            ncomps++;
-        }
-        scans[nscans-1].comps_in_scan = ncomps;
-        if (termchar == ':') {
-            if (! read_scan_integer(fp, &val, &termchar) || termchar != ' ')
-                goto bogus;
-            scans[nscans-1].Ss = (int) val;
-            if (! read_scan_integer(fp, &val, &termchar) || termchar != ' ')
-                goto bogus;
-            scans[nscans-1].Se = (int) val;
-            if (! read_scan_integer(fp, &val, &termchar) || termchar != ' ')
-                goto bogus;
-            scans[nscans-1].Ah = (int) val;
-            if (! read_scan_integer(fp, &val, &termchar))
-                goto bogus;
-            scans[nscans-1].Al = (int) val;
-        } else {
-            /* set non-progressive parameters */
-            scans[nscans-1].Ss = 0;
-            scans[nscans-1].Se = DCTSIZE2-1;
-            scans[nscans-1].Ah = 0;
-            scans[nscans-1].Al = 0;
-        }
-        if (termchar != ';' && termchar != EOF) {
-        bogus:
-            pm_message("Invalid scan entry format in file %s", filename);
-            fclose(fp);
-            return FALSE;
-        }
-    }
-
-    if (termchar != EOF) {
-        pm_message("Non-numeric data in file %s", filename);
-        fclose(fp);
-        return FALSE;
-    }
-
-    if (nscans > 0) {
-        /* Stash completed scan list in cinfo structure.  NOTE: in
-         * this program, JPOOL_IMAGE is the right lifetime for this
-         * data, but if you want to compress multiple images you'd
-         * want JPOOL_PERMANENT.  
-         */
-        const unsigned int scan_info_size = nscans * sizeof(jpeg_scan_info);
-        jpeg_scan_info * const scan_info = 
-            (jpeg_scan_info *)
-            (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
-                                        scan_info_size);
-        memcpy(scan_info, scans, scan_info_size);
-        cinfo->scan_info = scan_info;
-        cinfo->num_scans = nscans;
-    }
-
-    fclose(fp);
-    return TRUE;
-}
-
-
-static boolean
-set_quant_slots (j_compress_ptr cinfo, char *arg)
-/* Process a quantization-table-selectors parameter string, of the form
- *     N[,N,...]
- * If there are more components than parameters, the last value is replicated.
- */
-{
-    int val = 0;			/* default table # */
-    int ci;
-    char ch;
-
-    for (ci = 0; ci < MAX_COMPONENTS; ci++) {
-        if (*arg) {
-            ch = ',';			/* if not set by sscanf, will be ',' */
-            if (sscanf(arg, "%d%c", &val, &ch) < 1)
-                return FALSE;
-            if (ch != ',')		/* syntax check */
-                return FALSE;
-            if (val < 0 || val >= NUM_QUANT_TBLS) {
-                pm_message("Invalid quantization table number: %d.  "
-                           "JPEG quantization tables are numbered 0..%d",
-                           val, NUM_QUANT_TBLS - 1);
-                return FALSE;
-            }
-            cinfo->comp_info[ci].quant_tbl_no = val;
-            while (*arg && *arg++ != ',') 
-                /* advance to next segment of arg string */
-                ;
-        } else {
-            /* reached end of parameter, set remaining components to last tbl*/
-            cinfo->comp_info[ci].quant_tbl_no = val;
-        }
-    }
-    return TRUE;
-}
-
-
-static boolean
-set_sample_factors (j_compress_ptr cinfo, char *arg)
-/* Process a sample-factors parameter string, of the form
- *     HxV[,HxV,...]
- * If there are more components than parameters, "1x1" is assumed for the rest.
- */
-{
-    int ci, val1, val2;
-    char ch1, ch2;
-
-    for (ci = 0; ci < MAX_COMPONENTS; ci++) {
-        if (*arg) {
-            ch2 = ',';		/* if not set by sscanf, will be ',' */
-            if (sscanf(arg, "%d%c%d%c", &val1, &ch1, &val2, &ch2) < 3)
-                return FALSE;
-            if ((ch1 != 'x' && ch1 != 'X') || ch2 != ',') /* syntax check */
-                return FALSE;
-            if (val1 <= 0 || val1 > 4) {
-                pm_message("Invalid sampling factor: %d.  " 
-                           "JPEG sampling factors must be 1..4", val1);
-                return FALSE;
-            }
-            if (val2 <= 0 || val2 > 4) {
-                pm_message("Invalid sampling factor: %d.  "
-                           "JPEG sampling factors must be 1..4", val2);
-                return FALSE;
-            }
-            cinfo->comp_info[ci].h_samp_factor = val1;
-            cinfo->comp_info[ci].v_samp_factor = val2;
-            while (*arg && *arg++ != ',') 
-                /* advance to next segment of arg string */
-                ;
-        } else {
-            /* reached end of parameter, set remaining components 
-               to 1x1 sampling */
-            cinfo->comp_info[ci].h_samp_factor = 1;
-            cinfo->comp_info[ci].v_samp_factor = 1;
-        }
-    }
-    return TRUE;
 }
 
 
 
 int
-main(int argc, char ** argv) {
+main(int     argc,
+     char ** argv) {
 
     struct cmdlineInfo cmdline;
     struct jpeg_compress_struct cinfo;
     struct jpeg_error_mgr jerr;
-    FILE *input_file;
+    FILE * input_file;
     FILE * output_file;
     int height;  
         /* height of the input image in rows, as specified by its header */
@@ -1089,7 +1105,7 @@ main(int argc, char ** argv) {
 
     /* Close files, if we opened them */
     if (input_file != stdin)
-        fclose(input_file);
+        pm_close(input_file);
 
     /* Program may have exited with non-zero completion code via
        various function calls above. 
diff --git a/converter/other/pnmtopalm/Makefile b/converter/other/pnmtopalm/Makefile
index 4a8fa02a..7f99f95a 100644
--- a/converter/other/pnmtopalm/Makefile
+++ b/converter/other/pnmtopalm/Makefile
@@ -5,7 +5,7 @@ endif
 SUBDIR = converter/other/pnmtopalm
 VPATH=.:$(SRCDIR)/$(SUBDIR)
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
 BINARIES = palmtopnm pnmtopalm
 SCRIPTS =
@@ -16,7 +16,7 @@ DATAFILES = palmcolor8.map palmgray1.map palmgray2.map palmgray4.map
 
 all: $(BINARIES)
 
-include $(SRCDIR)/Makefile.common
+include $(SRCDIR)/common.mk
 
 LIBOPTS = $(shell $(LIBOPT) $(NETPBMLIB))
 
@@ -24,8 +24,9 @@ $(BINARIES): %: %.o palmcolormap.o $(NETPBMLIB) $(LIBOPT)
 	$(LD) -o $@ $< palmcolormap.o $(LIBOPTS) \
 	  $(MATHLIB) $(LDFLAGS) $(LDLIBS) $(RPATH) $(LADD)
 
-gen_palm_colormap : $(SUBDIR)/gen_palm_colormap.c palmcolormap.o
-	$(CC) $(INCLUDES) $(CFLAGS) -o $@ $< palmcolormap.o \
+gen_palm_colormap : % : %.c palmcolormap.o
+	$(CC) -I importinc $(CPPFLAGS) $(CFLAGS) -o $@ \
+	  $< palmcolormap.o \
 	  $(LIBOPTS) $(MATHLIB) $(LDFLAGS) $(LDLIBS) $(LADD)
 
 
diff --git a/converter/other/pnmtopalm/gen_palm_colormap.c b/converter/other/pnmtopalm/gen_palm_colormap.c
index 4b65e631..c7172c6b 100644
--- a/converter/other/pnmtopalm/gen_palm_colormap.c
+++ b/converter/other/pnmtopalm/gen_palm_colormap.c
@@ -1,24 +1,46 @@
 /* gen_palm_colormap.c - generate a ppm file containing the default Palm colormap
  *
- * Bill Janssen  <bill@janssen.org>
+ * Based on an earlier version by Bill Janssen  <bill@janssen.org>
  */
 
-#include "pnm.h"
+#include "ppm.h"
+#include "pm_c_util.h"
 
 #include "palm.h"
 
-int main( int argc, char **argv ) {
+int
+main(int     argc,
+     char ** argv) {
 
-  int i;
-  Color_s current;
-  Colormap default_map = palmcolor_build_default_8bit_colormap ();
+    Colormap defaultMap;
+    unsigned int i;
+    pixel pix;
+    
+    defaultMap = palmcolor_build_default_8bit_colormap();
+    qsort (defaultMap->color_entries, defaultMap->ncolors,
+           sizeof(Color_s), palmcolor_compare_indices);
 
-  printf("P3\n%d 1\n255\n", default_map->ncolors);
-  for (i = 0;  i < default_map->ncolors;  i++) {
-    current = default_map->color_entries[i];
-    printf ("%d %d %d\n", (current & 0xFF0000) >> 16, (current & 0xFF00) >> 8, (current & 0xFF));
-    /* printf ("%x:  %d %d %d\n", (current & 0xFF000000) >> 24, (current & 0xFF0000) >> 16, (current & 0xFF00) >> 8, (current & 0xFF)); */
-  };
-  return 0;
+    ppm_writeppminit(stdout, 256, 1, 255, TRUE);
+
+    for (i = 0; i < defaultMap->ncolors; ++i) {
+        Color_s const current = defaultMap->color_entries[i];
+
+        PPM_ASSIGN(pix,
+                   (current >> 16) & 0xff,
+                   (current >>  8) & 0xff,
+                   (current >>  0) & 0xff);
+
+        ppm_writeppmrow(stdout, &pix, 1, 255, TRUE);
+    }
+
+    /* palmcolor_build_default_8bit_colormap() builds a map of the 231 default
+     * palm colors and 1 extra black pixel. Add another 24 extra black pixels
+     * as per spec. */
+    PPM_ASSIGN(pix, 0, 0, 0);
+    for (i = 0; i < 256 - defaultMap->ncolors; ++i) {
+        ppm_writeppmrow(stdout, &pix, 1, 255, TRUE);
+    }
+
+    return 0;
 }
 
diff --git a/converter/other/pnmtopalm/palmcolor8.map b/converter/other/pnmtopalm/palmcolor8.map
index 2e054616..a4840118 100644
--- a/converter/other/pnmtopalm/palmcolor8.map
+++ b/converter/other/pnmtopalm/palmcolor8.map
@@ -1,235 +1,259 @@
 P3
-232 1
+256 1
 255
-0 0 0
-0 0 0
-0 0 51
-0 0 102
-0 0 153
-0 0 204
+255 255 255
+255 204 255
+255 153 255
+255 102 255
+255 51 255
+255 0 255
+255 255 204
+255 204 204
+255 153 204
+255 102 204
+255 51 204
+255 0 204
+255 255 153
+255 204 153
+255 153 153
+255 102 153
+255 51 153
+255 0 153
+204 255 255
+204 204 255
+204 153 255
+204 102 255
+204 51 255
+204 0 255
+204 255 204
+204 204 204
+204 153 204
+204 102 204
+204 51 204
+204 0 204
+204 255 153
+204 204 153
+204 153 153
+204 102 153
+204 51 153
+204 0 153
+153 255 255
+153 204 255
+153 153 255
+153 102 255
+153 51 255
+153 0 255
+153 255 204
+153 204 204
+153 153 204
+153 102 204
+153 51 204
+153 0 204
+153 255 153
+153 204 153
+153 153 153
+153 102 153
+153 51 153
+153 0 153
+102 255 255
+102 204 255
+102 153 255
+102 102 255
+102 51 255
+102 0 255
+102 255 204
+102 204 204
+102 153 204
+102 102 204
+102 51 204
+102 0 204
+102 255 153
+102 204 153
+102 153 153
+102 102 153
+102 51 153
+102 0 153
+51 255 255
+51 204 255
+51 153 255
+51 102 255
+51 51 255
+51 0 255
+51 255 204
+51 204 204
+51 153 204
+51 102 204
+51 51 204
+51 0 204
+51 255 153
+51 204 153
+51 153 153
+51 102 153
+51 51 153
+51 0 153
+0 255 255
+0 204 255
+0 153 255
+0 102 255
+0 51 255
 0 0 255
-0 51 0
-0 51 51
-0 51 102
-0 51 153
+0 255 204
+0 204 204
+0 153 204
+0 102 204
 0 51 204
-0 51 255
-0 102 0
-0 102 51
-0 102 102
+0 0 204
+0 255 153
+0 204 153
+0 153 153
 0 102 153
-0 102 204
-0 102 255
-0 128 0
-0 128 128
-0 153 0
-0 153 51
+0 51 153
+0 0 153
+255 255 102
+255 204 102
+255 153 102
+255 102 102
+255 51 102
+255 0 102
+255 255 51
+255 204 51
+255 153 51
+255 102 51
+255 51 51
+255 0 51
+255 255 0
+255 204 0
+255 153 0
+255 102 0
+255 51 0
+255 0 0
+204 255 102
+204 204 102
+204 153 102
+204 102 102
+204 51 102
+204 0 102
+204 255 51
+204 204 51
+204 153 51
+204 102 51
+204 51 51
+204 0 51
+204 255 0
+204 204 0
+204 153 0
+204 102 0
+204 51 0
+204 0 0
+153 255 102
+153 204 102
+153 153 102
+153 102 102
+153 51 102
+153 0 102
+153 255 51
+153 204 51
+153 153 51
+153 102 51
+153 51 51
+153 0 51
+153 255 0
+153 204 0
+153 153 0
+153 102 0
+153 51 0
+153 0 0
+102 255 102
+102 204 102
+102 153 102
+102 102 102
+102 51 102
+102 0 102
+102 255 51
+102 204 51
+102 153 51
+102 102 51
+102 51 51
+102 0 51
+102 255 0
+102 204 0
+102 153 0
+102 102 0
+102 51 0
+102 0 0
+51 255 102
+51 204 102
+51 153 102
+51 102 102
+51 51 102
+51 0 102
+51 255 51
+51 204 51
+51 153 51
+51 102 51
+51 51 51
+51 0 51
+51 255 0
+51 204 0
+51 153 0
+51 102 0
+51 51 0
+51 0 0
+0 255 102
+0 204 102
 0 153 102
-0 153 153
-0 153 204
-0 153 255
-0 204 0
+0 102 102
+0 51 102
+0 0 102
+0 255 51
 0 204 51
-0 204 102
-0 204 153
-0 204 204
-0 204 255
+0 153 51
+0 102 51
+0 51 51
+0 0 51
 0 255 0
-0 255 51
-0 255 102
-0 255 153
-0 255 204
-0 255 255
+0 204 0
+0 153 0
+0 102 0
+0 51 0
 17 17 17
 34 34 34
-51 0 0
-51 0 51
-51 0 102
-51 0 153
-51 0 204
-51 0 255
-51 51 0
-51 51 51
-51 51 102
-51 51 153
-51 51 204
-51 51 255
-51 102 0
-51 102 51
-51 102 102
-51 102 153
-51 102 204
-51 102 255
-51 153 0
-51 153 51
-51 153 102
-51 153 153
-51 153 204
-51 153 255
-51 204 0
-51 204 51
-51 204 102
-51 204 153
-51 204 204
-51 204 255
-51 255 0
-51 255 51
-51 255 102
-51 255 153
-51 255 204
-51 255 255
 68 68 68
 85 85 85
-102 0 0
-102 0 51
-102 0 102
-102 0 153
-102 0 204
-102 0 255
-102 51 0
-102 51 51
-102 51 102
-102 51 153
-102 51 204
-102 51 255
-102 102 0
-102 102 51
-102 102 102
-102 102 153
-102 102 204
-102 102 255
-102 153 0
-102 153 51
-102 153 102
-102 153 153
-102 153 204
-102 153 255
-102 204 0
-102 204 51
-102 204 102
-102 204 153
-102 204 204
-102 204 255
-102 255 0
-102 255 51
-102 255 102
-102 255 153
-102 255 204
-102 255 255
 119 119 119
-128 0 0
-128 0 128
 136 136 136
-153 0 0
-153 0 51
-153 0 102
-153 0 153
-153 0 204
-153 0 255
-153 51 0
-153 51 51
-153 51 102
-153 51 153
-153 51 204
-153 51 255
-153 102 0
-153 102 51
-153 102 102
-153 102 153
-153 102 204
-153 102 255
-153 153 0
-153 153 51
-153 153 102
-153 153 153
-153 153 204
-153 153 255
-153 204 0
-153 204 51
-153 204 102
-153 204 153
-153 204 204
-153 204 255
-153 255 0
-153 255 51
-153 255 102
-153 255 153
-153 255 204
-153 255 255
 170 170 170
 187 187 187
-192 192 192
-204 0 0
-204 0 51
-204 0 102
-204 0 153
-204 0 204
-204 0 255
-204 51 0
-204 51 51
-204 51 102
-204 51 153
-204 51 204
-204 51 255
-204 102 0
-204 102 51
-204 102 102
-204 102 153
-204 102 204
-204 102 255
-204 153 0
-204 153 51
-204 153 102
-204 153 153
-204 153 204
-204 153 255
-204 204 0
-204 204 51
-204 204 102
-204 204 153
-204 204 204
-204 204 255
-204 255 0
-204 255 51
-204 255 102
-204 255 153
-204 255 204
-204 255 255
 221 221 221
 238 238 238
-255 0 0
-255 0 51
-255 0 102
-255 0 153
-255 0 204
-255 0 255
-255 51 0
-255 51 51
-255 51 102
-255 51 153
-255 51 204
-255 51 255
-255 102 0
-255 102 51
-255 102 102
-255 102 153
-255 102 204
-255 102 255
-255 153 0
-255 153 51
-255 153 102
-255 153 153
-255 153 204
-255 153 255
-255 204 0
-255 204 51
-255 204 102
-255 204 153
-255 204 204
-255 204 255
-255 255 0
-255 255 51
-255 255 102
-255 255 153
-255 255 204
-255 255 255
+192 192 192
+128 0 0
+128 0 128
+0 128 0
+0 128 128
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
+0 0 0
diff --git a/converter/other/pnmtopalm/palmtopnm.c b/converter/other/pnmtopalm/palmtopnm.c
index ee43be7a..88088817 100644
--- a/converter/other/pnmtopalm/palmtopnm.c
+++ b/converter/other/pnmtopalm/palmtopnm.c
@@ -15,6 +15,7 @@
 #include <string.h>
 #include <assert.h>
 
+#include "pm_c_util.h"
 #include "pnm.h"
 #include "shhopt.h"
 #include "mallocvar.h"
diff --git a/converter/other/pnmtopalm/pnmtopalm.c b/converter/other/pnmtopalm/pnmtopalm.c
index d5f79619..90737b78 100644
--- a/converter/other/pnmtopalm/pnmtopalm.c
+++ b/converter/other/pnmtopalm/pnmtopalm.c
@@ -21,6 +21,7 @@
 #include <assert.h>
 #include <limits.h>
 
+#include "pm_c_util.h"
 #include "pnm.h"
 #include "palm.h"
 #include "shhopt.h"
diff --git a/converter/other/pnmtopclxl.c b/converter/other/pnmtopclxl.c
index 23c7bd3a..e16afb14 100644
--- a/converter/other/pnmtopclxl.c
+++ b/converter/other/pnmtopclxl.c
@@ -29,6 +29,7 @@
 #include <sys/types.h>
 #include <ctype.h>
 
+#include "pm_c_util.h"
 #include "pam.h"
 #include "shhopt.h"
 #include "mallocvar.h"
@@ -153,7 +154,7 @@ parseCommandLine(int argc, char ** argv,
         bool found;
         int i;
         for (i = 0, found=FALSE; xlPaperFormats[i].name && !found; ++i) {
-            if (STREQ(xlPaperFormats[i].name, formatOpt)) {
+            if (streq(xlPaperFormats[i].name, formatOpt)) {
                 found = TRUE;
                 cmdlineP->format = xlPaperFormats[i].xl_nr;
             }
@@ -178,7 +179,7 @@ parseCommandLine(int argc, char ** argv,
         cmdlineP->sourceP->name = "-";
         cmdlineP->sourceP->next = NULL;
     } else {
-        int i;
+        unsigned int i;
         InputSource ** nextLinkP;
 
         nextLinkP = &cmdlineP->sourceP;
diff --git a/converter/other/pnmtopng.c b/converter/other/pnmtopng.c
index 92c38a2a..52f69423 100644
--- a/converter/other/pnmtopng.c
+++ b/converter/other/pnmtopng.c
@@ -52,8 +52,6 @@
    because xels were only 24 bits.  Now they're 96.
 */
    
-#define GRR_GRAY_PALETTE_FIX
-
 #ifndef PNMTOPNG_WARNING_LEVEL
 #  define PNMTOPNG_WARNING_LEVEL 0   /* use 0 for backward compatibility, */
 #endif                               /*  2 for warnings (1 == error) */
@@ -62,6 +60,8 @@
 #include <string.h> /* strcat() */
 #include <limits.h>
 #include <png.h>    /* includes zlib.h and setjmp.h */
+
+#include "pm_c_util.h"
 #include "pnm.h"
 #include "pngtxt.h"
 #include "shhopt.h"
@@ -69,10 +69,12 @@
 #include "nstring.h"
 #include "version.h"
 
+/* A hack until we can remove direct access to png_info from the program */
 #if PNG_LIBPNG_VER >= 10400
-#error Your PNG library (<png.h>) is incompatible with this Netpbm source code.
-#error You need either an older PNG library (older than 1.4)
-#error newer Netpbm source code (at least 10.47.04)
+#define trans_values trans_color
+#define TRANS_ALPHA trans_alpha
+#else
+#define TRANS_ALPHA trans
 #endif
 
 
@@ -390,7 +392,7 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3( &argc, argv, opt, sizeof(opt), 0);
+    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
 
@@ -471,7 +473,7 @@ parseCommandLine(int argc, char ** argv,
     }
 
     if (cmdlineP->zlibCompression.methodSpec) {
-        if (STREQ(compMethod, "deflated"))
+        if (streq(compMethod, "deflated"))
             cmdlineP->zlibCompression.method = Z_DEFLATED;
         else
             pm_error("The only valid value for -method is 'deflated'.  "
@@ -479,9 +481,9 @@ parseCommandLine(int argc, char ** argv,
     }
 
     if (cmdlineP->zlibCompression.strategySpec) {
-        if (STREQ(compStrategy, "huffman_only"))
+        if (streq(compStrategy, "huffman_only"))
             cmdlineP->zlibCompression.strategy = Z_HUFFMAN_ONLY;
-        else if (STREQ(compStrategy, "filtered"))
+        else if (streq(compStrategy, "filtered"))
             cmdlineP->zlibCompression.strategy = Z_FILTERED;
         else
             pm_error("Valid values for -strategy are 'huffman_only' and "
@@ -1060,7 +1062,7 @@ findRedundantBits(FILE *         const ifp,
 /*----------------------------------------------------------------------------
    Find out if we can use just a subset of the bits from each input
    sample.  Often, people create an image with e.g. 8 bit samples from
-   one that has e.g. only 4 bit samples by scaling by 256/16, which is
+   one that has e.g. only 4 bit samples by scaling by 255/15, which is
    the same as repeating the bits.  E.g.  1011 becomes 10111011.  We
    detect this case.  We return as *meaningfulBitsP the minimum number
    of bits, starting from the least significant end, that contain
@@ -1352,9 +1354,9 @@ computeUnsortedAlphaPalette(FILE *           const ifP,
 
 
 static void
-sortAlphaPalette(gray *         const alphas_of_color[],
-                 unsigned int   const alphas_first_index[],
-                 unsigned int   const alphas_of_color_cnt[],
+sortAlphaPalette(gray *         const alphasOfColor[],
+                 unsigned int   const alphasFirstIndex[],
+                 unsigned int   const alphasOfColorCnt[],
                  unsigned int   const colors,
                  gray           const alphaMaxval,
                  unsigned int         mapping[],
@@ -1370,40 +1372,56 @@ sortAlphaPalette(gray *         const alphas_of_color[],
    palette of the alpha/color pair whose index is x in the unsorted
    PNG palette.  This mapping sorts the palette so that opaque entries
    are last.
+
+   The unsorted PNG palette is sorted enough that all entries for a particular
+   color (with varying transparencies) are contiguous.  alphasFirstIndex[x] is
+   the index in the unsorted PNG palette of the first entry with color x
+   (where x is an index into some other palette).  alphasOfColorCnt[x] is the
+   number of non-opaque entries in the unsorted PNG palette with color x.
+
+   alphasOfColor[x][y] is the y'th alpha value for color x, in no particular
+   order.
+
+   Return as *transSizeP the number of non-opaque entries in the palette
+   (i.e. the index in the palette of the first opaque entry).
 -----------------------------------------------------------------------------*/
-    unsigned int bot_idx;
-    unsigned int top_idx;
-    unsigned int colorIndex;
+    if (colors == 0)
+        *transSizeP = 0;
+    else {
+        unsigned int bot_idx;
+        unsigned int top_idx;
+        unsigned int colorIndex;
     
-    /* We start one index at the bottom of the palette index range
-       and another at the top.  We run through the unsorted palette,
-       and when we see an opaque entry, we map it to the current top
-       cursor and bump it down.  When we see a non-opaque entry, we map 
-       it to the current bottom cursor and bump it up.  Because the input
-       and output palettes are the same size, the two cursors should meet
-       right when we process the last entry of the unsorted palette.
-    */    
-    bot_idx = 0;
-    top_idx = alphas_first_index[colors-1] + alphas_of_color_cnt[colors-1] - 1;
+        /* We start one index at the bottom of the palette index range
+           and another at the top.  We run through the unsorted palette,
+           and when we see an opaque entry, we map it to the current top
+           cursor and bump it down.  When we see a non-opaque entry, we map 
+           it to the current bottom cursor and bump it up.  Because the input
+           and output palettes are the same size, the two cursors should meet
+           right when we process the last entry of the unsorted palette.
+        */    
+        bot_idx = 0;
+        top_idx = alphasFirstIndex[colors-1] + alphasOfColorCnt[colors-1] - 1;
     
-    for (colorIndex = 0;  colorIndex < colors;  ++colorIndex) {
-        unsigned int j;
-        for (j = 0; j < alphas_of_color_cnt[colorIndex]; ++j) {
-            unsigned int const paletteIndex = 
-                alphas_first_index[colorIndex] + j;
-            if (alphas_of_color[colorIndex][j] == alphaMaxval)
-                mapping[paletteIndex] = top_idx--;
-            else
-                mapping[paletteIndex] = bot_idx++;
+        for (colorIndex = 0;  colorIndex < colors;  ++colorIndex) {
+            unsigned int j;
+            for (j = 0; j < alphasOfColorCnt[colorIndex]; ++j) {
+                unsigned int const paletteIndex = 
+                    alphasFirstIndex[colorIndex] + j;
+                if (alphasOfColor[colorIndex][j] == alphaMaxval)
+                    mapping[paletteIndex] = top_idx--;
+                else
+                    mapping[paletteIndex] = bot_idx++;
+            }
         }
+        /* indices should have just crossed paths */
+        if (bot_idx != top_idx + 1) {
+            pm_error ("internal inconsistency: "
+                      "remapped bot_idx = %u, top_idx = %u",
+                      bot_idx, top_idx);
+        }
+        *transSizeP = bot_idx;
     }
-    /* indices should have just crossed paths */
-    if (bot_idx != top_idx + 1) {
-        pm_error ("internal inconsistency: "
-                  "remapped bot_idx = %u, top_idx = %u",
-                  bot_idx, top_idx);
-    }
-    *transSizeP = bot_idx;
 }
 
 
@@ -1453,7 +1471,7 @@ compute_alpha_palette(FILE *         const ifP,
     getChv(ifP, rasterPos, cols, rows, maxval, format, MAXCOLORS, 
            &chv, &colors);
 
-    assert(colors < ARRAY_SIZE(alphas_of_color));
+    assert(colors <= ARRAY_SIZE(alphas_of_color));
 
     computeUnsortedAlphaPalette(ifP, cols, rows, maxval, format, rasterPos,
                                 alpha_mask, chv, colors,
@@ -1817,6 +1835,7 @@ computeColorMap(FILE *         const ifP,
                 int            const cols,
                 int            const rows,
                 xelval         const maxval,
+                int            const pnmType,
                 int            const format,
                 bool           const force,
                 FILE *         const pfP,
@@ -1864,7 +1883,7 @@ computeColorMap(FILE *         const ifP,
                   maxval, PALETTEMAXVAL);
     else {
         unsigned int bitsPerPixel;
-        computePixelWidth(PNM_FORMAT_TYPE(format), pnm_meaningful_bits, alpha,
+        computePixelWidth(pnmType, pnm_meaningful_bits, alpha,
                           NULL, &bitsPerPixel);
 
         if (!pfP && bitsPerPixel == 1)
@@ -2042,7 +2061,7 @@ createPngPalette(pixel              palette_pnm[],
     for (i = 0; i < transSize; ++i) {
         unsigned int const newmv = PALETTEMAXVAL;
         unsigned int const oldmv = alpha_maxval;
-        trans[i] = (trans_pnm[i] * newmv + (oldmv/2)) / oldmv;
+        trans[i] = ROUNDDIV(trans_pnm[i] * newmv, oldmv);
     }
 }
 
@@ -2347,12 +2366,12 @@ convertpnm(struct cmdlineInfo const cmdline,
          of the input image.
       */
   int transexact;  
-    /* boolean: the user wants only the exact color he specified to be
-       transparent; not just something close to it.
-    */
+      /* boolean: the user wants only the exact color he specified to be
+         transparent; not just something close to it.
+      */
   int transparent;
   bool alpha;
-    /* There will be an alpha mask */
+      /* There will be an alpha mask */
   unsigned int pnm_meaningful_bits;
   pixel backcolor;
       /* The background color, with maxval equal to that of the input
@@ -2397,7 +2416,7 @@ convertpnm(struct cmdlineInfo const cmdline,
       */
   unsigned int fulldepth;
       /* The total number of bits per pixel in the (uncompressed) png
-         raster, including all channels 
+         raster, including all channels.
       */
   pm_filepos rasterPos;  
       /* file position in input image file of start of image (i.e. after
@@ -2470,8 +2489,8 @@ convertpnm(struct cmdlineInfo const cmdline,
          to ppm_parsecolor() because ppm_parsecolor() does a cheap maxval
          scaling, and this is more precise.
       */
-      PPM_DEPTH (transcolor, ppm_parsecolor(transstring2, maxmaxval),
-                 maxmaxval, maxval);
+      PPM_DEPTH(transcolor, ppm_parsecolor(transstring2, maxmaxval),
+                maxmaxval, maxval);
   }
   if (cmdline.alpha) {
     pixel alpha_transcolor;
@@ -2550,7 +2569,7 @@ convertpnm(struct cmdlineInfo const cmdline,
   findRedundantBits(ifp, rasterPos, cols, rows, maxval, format, alpha,
                     cmdline.force, &pnm_meaningful_bits);
   
-  computeColorMap(ifp, rasterPos, cols, rows, maxval, format,
+  computeColorMap(ifp, rasterPos, cols, rows, maxval, pnm_type, format,
                   cmdline.force, pfp,
                   alpha, transparent >= 0, transcolor, transexact, 
                   !!cmdline.background, backcolor,
@@ -2630,7 +2649,7 @@ convertpnm(struct cmdlineInfo const cmdline,
     info_ptr->num_palette = palette_size;
     if (trans_size > 0) {
         info_ptr->valid |= PNG_INFO_tRNS;
-        info_ptr->trans = trans;
+        info_ptr->TRANS_ALPHA = trans;
         info_ptr->num_trans = trans_size;   /* omit opaque values */
     }
     /* creating hIST chunk */
@@ -2809,7 +2828,7 @@ main(int argc, char *argv[]) {
 
     int errorlevel;
     
-    pnm_init (&argc, argv);
+    pnm_init(&argc, argv);
     
     parseCommandLine(argc, argv, &cmdline);
     
@@ -2852,3 +2871,6 @@ main(int argc, char *argv[]) {
 
     return errorlevel;
 }
+
+
+
diff --git a/converter/other/pnmtops.c b/converter/other/pnmtops.c
index fb4d7648..20395952 100644
--- a/converter/other/pnmtops.c
+++ b/converter/other/pnmtops.c
@@ -34,6 +34,8 @@
 
 #include <string.h>
 #include <assert.h>
+
+#include "pm_c_util.h"
 #include "pam.h"
 #include "mallocvar.h"
 #include "shhopt.h"
@@ -275,6 +277,7 @@ putitem(void) {
         putchar('\n');
         itemsinline = 0;
     }
+    assert(item >> 8 == 0);
     putchar(hexits[item >> 4]);
     putchar(hexits[item & 15]);
     ++itemsinline;
@@ -496,7 +499,7 @@ destroyBmepsOutputEncoder(struct bmepsoe * const bmepsoeP) {
 static void
 outputBmepsSample(struct bmepsoe * const bmepsoeP,
                   unsigned int     const sampleValue,
-          unsigned int     const bitsPerSample) {
+                  unsigned int     const bitsPerSample) {
 
     if (bitsPerSample == 8)
         oe_byte_add(bmepsoeP->oeP, sampleValue);
@@ -954,8 +957,35 @@ putEnd(bool         const showpage,
 
 
 static void
+warnUserAboutReducedDepth(unsigned int const bitsGot,
+                          unsigned int const bitsWanted,
+                          unsigned int const postscriptLevel,
+                          bool         const psFilter) {
+
+    if (bitsGot < bitsWanted) {
+        pm_message("Postscript will have %u bits of color resolution, "
+                   "though the input has %u bits.",
+                   bitsGot, bitsWanted);
+
+        if (postscriptLevel < 2)
+            pm_message("Postscript level %u has a maximum depth of 8 bits.  "
+                       "You could get up to 12 with -level=2 and -psfilter.",
+                       postscriptLevel);
+        else {
+            if (!psFilter)
+                pm_message("You can get up to 12 bits with -psfilter");
+            else
+                pm_message("The Postscript maximum is 12.");
+        }
+    }
+}
+
+
+
+static void
 computeDepth(xelval         const inputMaxval,
              unsigned int   const postscriptLevel, 
+             bool           const psFilter,
              unsigned int * const bitspersampleP,
              unsigned int * const psMaxvalP) {
 /*----------------------------------------------------------------------------
@@ -971,30 +1001,24 @@ computeDepth(xelval         const inputMaxval,
         *bitspersampleP = 2;
     else if (bitsRequiredByMaxval <= 4)
         *bitspersampleP = 4;
-    else        
+    else if (bitsRequiredByMaxval <= 8)
         *bitspersampleP = 8;
-
-    /* There is supposedly a 12 bits per pixel Postscript format, but
-       what?  We produce a raster that is composed of bytes, each
-       coded as a pair of hexadecimal characters and representing 8,
-       4, 2, or 1 pixels.  We also have the RLE format, where some of
-       those bytes are run lengths.
-    */
-
-    if (*bitspersampleP < bitsRequiredByMaxval) {
-        if (bitsRequiredByMaxval <= 12 && postscriptLevel >= 2)
-            pm_message("Maxval of input requires %u bit samples for full "
-                       "resolution, and Postscript level %u is capable "
-                       "of representing that many, but this program "
-                       "doesn't know how.  So we are using %u",
-                       bitsRequiredByMaxval, postscriptLevel, *bitspersampleP);
+    else {
+        /* Post script level 2 defines a format with 12 bits per sample,
+           but I don't know the details of that format (both RLE and
+           non-RLE variations) and existing native raster generation code
+           simply can't handle bps > 8.  But the built-in filters know
+           how to do 12 bps.
+        */
+        if (postscriptLevel >= 2 && psFilter)
+            *bitspersampleP = 12;
         else
-            pm_message("Maxval of input requires %u bit samples for full "
-                       "resolution, but we are using the Postscript level %u "
-                       "maximum of %u",
-                       bitsRequiredByMaxval, postscriptLevel, *bitspersampleP);
+            *bitspersampleP = 8;
     }
 
+    warnUserAboutReducedDepth(*bitspersampleP, bitsRequiredByMaxval,
+                              postscriptLevel, psFilter);
+
     *psMaxvalP = pm_bitstomaxval(*bitspersampleP);
 
     if (verbose)
@@ -1047,8 +1071,7 @@ convertRowPsFilter(struct pam *     const pamP,
     unsigned int const stragglers =
         (((bitsPerSample * pamP->depth) % 8) * pamP->width) % 8;
         /* Number of bits at the right edge that don't fill out a
-           whole byte
-        */
+           whole byte */
 
     unsigned int col;
     tuple scaledTuple;
@@ -1183,7 +1206,8 @@ convertPage(FILE * const ifP,
     if (color)
         pm_message("generating color Postscript program.");
 
-    computeDepth(inpam.maxval, postscriptLevel, &bitspersample, &psMaxval);
+    computeDepth(inpam.maxval, postscriptLevel, psFilter,
+                 &bitspersample, &psMaxval);
     {
         unsigned int const realBitsPerLine = inpam.width * bitspersample;
         unsigned int const paddedBitsPerLine = ((realBitsPerLine + 7) / 8) * 8;
@@ -1275,7 +1299,7 @@ main(int argc, char * argv[]) {
 
     ifp = pm_openr(cmdline.inputFileName);
 
-    if (STREQ(cmdline.inputFileName, "-"))
+    if (streq(cmdline.inputFileName, "-"))
         name = strdup("noname");
     else
         name = basebasename(cmdline.inputFileName);
@@ -1313,7 +1337,7 @@ main(int argc, char * argv[]) {
     strfree(name);
 
     pm_close(ifp);
-    
+
     return 0;
 }
 
diff --git a/converter/other/pnmtorast.c b/converter/other/pnmtorast.c
index 7d1ae05a..605e815c 100644
--- a/converter/other/pnmtorast.c
+++ b/converter/other/pnmtorast.c
@@ -15,15 +15,79 @@
 #include "mallocvar.h"
 
 #define MAXCOLORS 256
-static colormap_t* make_pr_colormap ARGS(( colorhist_vector chv, int colors ));
-static colormap_t* make_gray_pr_colormap ARGS(( void ));
-static colormap_t* alloc_pr_colormap ARGS(( void ));
+
+
+
+static colormap_t *
+alloc_pr_colormap(void) {
+
+    colormap_t* pr_colormapP;
+
+    MALLOCVAR(pr_colormapP);
+    if ( pr_colormapP == NULL )
+        pm_error( "out of memory" );
+    pr_colormapP->type = RMT_EQUAL_RGB;
+    pr_colormapP->length = MAXCOLORS;
+    MALLOCARRAY(pr_colormapP->map[0], MAXCOLORS);
+    MALLOCARRAY(pr_colormapP->map[1], MAXCOLORS);
+    MALLOCARRAY(pr_colormapP->map[2], MAXCOLORS);
+    if ( pr_colormapP->map[0] == NULL || 
+         pr_colormapP->map[1] == NULL ||
+         pr_colormapP->map[2] == NULL )
+        pm_error( "out of memory" );
+
+    return pr_colormapP;
+}
+
+
+
+static colormap_t*
+make_pr_colormap(colorhist_vector const chv,
+                 int              const colors) {
+
+    colormap_t* pr_colormapP;
+    int i;
+
+    pr_colormapP = alloc_pr_colormap( );
+
+    for ( i = 0; i < colors; ++i )
+    {
+        pr_colormapP->map[0][i] = PPM_GETR( chv[i].color );
+        pr_colormapP->map[1][i] = PPM_GETG( chv[i].color );
+        pr_colormapP->map[2][i] = PPM_GETB( chv[i].color );
+    }
+    for ( ; i < MAXCOLORS; ++i )
+        pr_colormapP->map[0][i] = pr_colormapP->map[1][i] =
+            pr_colormapP->map[2][i] = 0;
+
+    return pr_colormapP;
+}
+
+
+
+static colormap_t *
+make_gray_pr_colormap(void) {
+
+    colormap_t* pr_colormapP;
+    int i;
+
+    pr_colormapP = alloc_pr_colormap( );
+
+    for ( i = 0; i < MAXCOLORS; ++i )
+    {
+        pr_colormapP->map[0][i] = i;
+        pr_colormapP->map[1][i] = i;
+        pr_colormapP->map[2][i] = i;
+    }
+
+    return pr_colormapP;
+}
+
+
 
 int
-main( argc, argv )
-    int argc;
-    char* argv[];
-{
+main(int argc, char ** argv) {
+
     FILE* ifp;
     xel** xels;
     xel* xelrow;
@@ -247,64 +311,3 @@ main( argc, argv )
     exit( 0 );
 }
 
-static colormap_t*
-make_pr_colormap( chv, colors )
-    colorhist_vector chv;
-    int colors;
-{
-    colormap_t* pr_colormapP;
-    int i;
-
-    pr_colormapP = alloc_pr_colormap( );
-
-    for ( i = 0; i < colors; ++i )
-    {
-        pr_colormapP->map[0][i] = PPM_GETR( chv[i].color );
-        pr_colormapP->map[1][i] = PPM_GETG( chv[i].color );
-        pr_colormapP->map[2][i] = PPM_GETB( chv[i].color );
-    }
-    for ( ; i < MAXCOLORS; ++i )
-        pr_colormapP->map[0][i] = pr_colormapP->map[1][i] =
-            pr_colormapP->map[2][i] = 0;
-
-    return pr_colormapP;
-}
-
-static colormap_t*
-make_gray_pr_colormap( )
-{
-    colormap_t* pr_colormapP;
-    int i;
-
-    pr_colormapP = alloc_pr_colormap( );
-
-    for ( i = 0; i < MAXCOLORS; ++i )
-    {
-        pr_colormapP->map[0][i] = i;
-        pr_colormapP->map[1][i] = i;
-        pr_colormapP->map[2][i] = i;
-    }
-
-    return pr_colormapP;
-}
-
-static colormap_t*
-alloc_pr_colormap( )
-{
-    colormap_t* pr_colormapP;
-
-    MALLOCVAR(pr_colormapP);
-    if ( pr_colormapP == NULL )
-        pm_error( "out of memory" );
-    pr_colormapP->type = RMT_EQUAL_RGB;
-    pr_colormapP->length = MAXCOLORS;
-    MALLOCARRAY(pr_colormapP->map[0], MAXCOLORS);
-    MALLOCARRAY(pr_colormapP->map[1], MAXCOLORS);
-    MALLOCARRAY(pr_colormapP->map[2], MAXCOLORS);
-    if ( pr_colormapP->map[0] == NULL || 
-         pr_colormapP->map[1] == NULL ||
-         pr_colormapP->map[2] == NULL )
-        pm_error( "out of memory" );
-
-    return pr_colormapP;
-}
diff --git a/converter/other/pnmtorle.c b/converter/other/pnmtorle.c
index b7834e89..8908c356 100644
--- a/converter/other/pnmtorle.c
+++ b/converter/other/pnmtorle.c
@@ -59,8 +59,8 @@ static gray    maxval;
  *                                        Read the pnm image file header.
  */
 static void 
-read_pnm_header()
-{
+read_pnm_header(void) {
+
     pnm_readpnminit(fp, &width, &height, &maxval, &format);
     switch (format) {
     case PBM_FORMAT:
@@ -87,12 +87,12 @@ read_pnm_header()
     if (do_alpha)
         VPRINTF(stderr, "Computing alpha channel...\n");
 }
-/*-----------------------------------------------------------------------------
- *                                             Write the rle image file header.
- */
+
+
+
 static void 
-write_rle_header()
-{
+write_rle_header(void) {
+
     hdr.xmin    = 0;
     hdr.xmax    = width-1;
     hdr.ymin    = 0;
@@ -120,101 +120,106 @@ write_rle_header()
     }
     rle_put_setup(&hdr);
 }
-/*-----------------------------------------------------------------------------
- *                                      Write the rle data portion of the file.
- */
+
+
+
 static void 
-write_rle_data()
-{
-    register int     x;
-    register int     scan;
-    register xel     *xelrow, *pP;
-    rle_pixel        ***scanlines, **scanline;
-/*
- * Allocate some memory.
- */
-    /*xelrow = pnm_allowcrow(width);*/
-    xelrow = (xel*) pm_allocrow( width, sizeof(xel) );
+write_rle_data(void) {
+
+    unsigned int scan;
+    xel * xelrow;
+    rle_pixel *** scanlines;
+
+    MALLOCARRAY(xelrow, width);
     MALLOCARRAY(scanlines, height);
-    RLE_CHECK_ALLOC( hdr.cmd, scanlines, "scanline pointers" );
 
-    for ( scan = 0; scan < height; scan++ )
-        RLE_CHECK_ALLOC( hdr.cmd, (rle_row_alloc(&hdr, &scanlines[scan]) >= 0),
-                         "pixel memory" );
-/*
- * Loop through the pnm files image window, read data and flip vertically.
- */
+    RLE_CHECK_ALLOC(hdr.cmd, scanlines, "scanline pointers");
+
+    for (scan = 0; scan < height; ++scan) {
+        int rc;
+        rc = rle_row_alloc(&hdr, &scanlines[scan]);
+        RLE_CHECK_ALLOC(hdr.cmd, rc >= 0, "pixel memory");
+    }
+    /* Loop through the pnm files image window, read data and flip vertically.
+     */
     switch (format) {
     case PBM_FORMAT:
-    case RPBM_FORMAT:
-        for (scan = 0; scan < height; scan++) {
-            scanline = scanlines[height - scan - 1];
+    case RPBM_FORMAT: {
+        unsigned int scan;
+        for (scan = 0; scan < height; ++scan) {
+            rle_pixel ** const scanline = scanlines[height - scan - 1];
+            unsigned int col;
             pnm_readpnmrow(fp, xelrow, width, maxval, format);
-            for (x = 0, pP = xelrow; x < width; x++, pP++) {
-                scanline[RLE_RED][x]   = (PNM_GET1(*pP) ? 255 : 0);
-                if (do_alpha) {
-                    scanline[RLE_ALPHA][x] = scanline[RLE_RED][x];
-                }
+            for (col = 0; col < width; ++col) {
+                scanline[RLE_RED][col] = PNM_GET1(xelrow[col]) ? 255 : 0;
+                if (do_alpha)
+                    scanline[RLE_ALPHA][col] = scanline[RLE_RED][col];
             }
         }
-        break;
+    } break;
     case PGM_FORMAT:
-    case RPGM_FORMAT:
-        for (scan = 0; scan < height; scan++) {
-            scanline = scanlines[height - scan - 1];
+    case RPGM_FORMAT: {
+        unsigned int scan;
+        for (scan = 0; scan < height; ++scan) {
+            rle_pixel ** const scanline = scanlines[height - scan - 1];
+            unsigned int col;
             pnm_readpnmrow(fp, xelrow, width, maxval, format);
-            for (x = 0, pP = xelrow; x < width; x++, pP++) {
-                scanline[RLE_RED][x]   = PNM_GET1(*pP);
-                if (do_alpha) {
-                    scanline[RLE_ALPHA][x] = (scanline[RLE_RED][x] ? 255 : 0);
-                }
+            for (col = 0; col < width; ++col) {
+                scanline[RLE_RED][col] = PNM_GET1(xelrow[col]);
+                if (do_alpha)
+                    scanline[RLE_ALPHA][col] =
+                        scanline[RLE_RED][col] ? 255 : 0;
             }
         }
-        break;
+    } break;
     case PPM_FORMAT:
-    case RPPM_FORMAT:
+    case RPPM_FORMAT: {
+        unsigned int scan;
         for (scan = 0; scan < height; scan++) {
-            scanline = scanlines[height - scan - 1];
+            rle_pixel ** const scanline = scanlines[height - scan - 1];
+            unsigned int col;
             pnm_readpnmrow(fp, xelrow, width, maxval, format);
-            for (x = 0, pP = xelrow; x < width; x++, pP++) {
-                scanline[RLE_RED][x]   = PPM_GETR(*pP);
-                scanline[RLE_GREEN][x] = PPM_GETG(*pP);
-                scanline[RLE_BLUE][x]  = PPM_GETB(*pP);
-                if (do_alpha) {
-                    scanline[RLE_ALPHA][x] = (scanline[RLE_RED][x] ||
-                                              scanline[RLE_GREEN][x] ||
-                                              scanline[RLE_BLUE][x] ? 255 : 0);
-                }
+            for (col = 0; col < width; ++col) {
+                scanline[RLE_RED][col]   = PPM_GETR(xelrow[col]);
+                scanline[RLE_GREEN][col] = PPM_GETG(xelrow[col]);
+                scanline[RLE_BLUE][col]  = PPM_GETB(xelrow[col]);
+                if (do_alpha)
+                    scanline[RLE_ALPHA][col] =
+                        (scanline[RLE_RED][col] ||
+                         scanline[RLE_GREEN][col] ||
+                         scanline[RLE_BLUE][col] ? 255 : 0);
             }
         }
-        break;
+        } break;
     }
-/*
- * Write out data in URT order (bottom to top).
- */
-    for ( scan = 0; scan < height; scan++ ) {
+    /* Write out data in URT order (bottom to top). */
+    for (scan = 0; scan < height; ++scan)
         rle_putrow(scanlines[scan], width, &hdr);
-        rle_row_free( &hdr, scanlines[scan] );
-    }
-    free( scanlines );
+
+    for (scan = 0; scan < height; ++scan)
+        rle_row_free(&hdr, scanlines[scan]);
+    free(scanlines);
+    free(xelrow);
 
     VPRINTF(stderr, "Done -- write eof to RLE data.\n");
     rle_puteof(&hdr);
 }
 
+
+
 int
-main(argc, argv)
-    int argc;
-    char **argv;
-{
-    char     *pnmname = NULL, *outname = NULL;
-    int      oflag, c;
+main(int argc, char **  argv) {
+
+    const char * pnmname;
+    const char * outname;
+    int oflag;
 
     pnm_init(&argc, argv);
 
-/*
- * Get those options.
- */
+    pnmname = NULL;  /* initial value */
+    outname = NULL;  /* initial value */
+
+    /* Get those options. */
     if (!scanargs(argc,argv,
                   "% v%- h%- a%- o%-outfile!s pnmfile%s\n(\
 \tConvert a PNM file to URT RLE format.\n\
@@ -228,37 +233,32 @@ main(argc, argv)
                   &pnmname))
         exit(-1);
 
-    hdr = *rle_hdr_init( (rle_hdr *)NULL );
-    rle_names( &hdr, cmd_name( argv ), outname, 0 );
-/*
- * Open the file.
- */
+    hdr = *rle_hdr_init(NULL);
+    rle_names(&hdr, cmd_name(argv), outname, 0);
+
+    /* Open the file. */
     if (pnmname == NULL) {
-        fp = stdin;
-    }
-    else {
+        fp = pm_openr("-");
+    } else {
         fp = pm_openr(pnmname);
     }
 
     hdr.rle_file = rle_open_f( hdr.cmd, outname, "wb" );
-    while ( (c = getc( fp )) != EOF ) {
-        ungetc( c, fp );
-/*
- * Read the PPM file header.
- */
+
+    if (header)
         read_pnm_header();
-        if (header)
-            break;
-/*
- * Write the rle file header.
- */
-        rle_addhist(argv, (rle_hdr *)NULL, &hdr);
-        write_rle_header();
-/*
- * Write the rle file data.
- */
-        write_rle_data();
+    else {
+        int eof;
+        for (eof = 0; !eof; ) {
+            read_pnm_header();
+            rle_addhist(argv, NULL, &hdr);
+            write_rle_header();
+            write_rle_data();
+            
+            pnm_nextimage(fp, &eof);
+        }
     }
-    fclose(fp);
+    pm_close(fp);
+
     return 0;
 }
diff --git a/converter/other/pnmtotiffcmyk.c b/converter/other/pnmtotiffcmyk.c
index 0fda6b08..2e6ae935 100644
--- a/converter/other/pnmtotiffcmyk.c
+++ b/converter/other/pnmtotiffcmyk.c
@@ -542,10 +542,6 @@ tiffOpen( Out* out, Root *r ) {
   short photometric = PHOTOMETRIC_SEPARATED ; /* ie cmyk */
   int bytesperrow = r->nCols ;
 
-  /* if i don't set stdout non-blocking on my machine then the read
-     that is called inside TIFFFdOpen hangs until the users types ^D.
-     this is also true for pnmtotiff */
-  fcntl( 1, F_SETFL, O_NONBLOCK ) ;
   t->tiff = TIFFFdOpen( 1, "Standard Output", "w" ) ;
   if ( ! t->tiff ) {
     fprintf( stderr, "cannot open tiff stream to standard output\n" ) ;
@@ -794,7 +790,7 @@ standardOpt( Conv *conv, Root *r, int *argn, int argc, char **argv ) {
   }
   /* handle the special case of -1 (no removal) */
   if ( oldn == *argn && pm_keymatch( argv[*argn], "-gammap", 7 ) &&
-       *argn + 1 < argc && STREQ(argv[*argn + 1], "-1") ) {
+       *argn + 1 < argc && streq(argv[*argn + 1], "-1") ) {
     p->remove = 0 ;
     *argn = (*argn) + 2 ;
   } 
diff --git a/converter/other/pnmtoxwd.c b/converter/other/pnmtoxwd.c
index 32fb8a7b..b6439d28 100644
--- a/converter/other/pnmtoxwd.c
+++ b/converter/other/pnmtoxwd.c
@@ -11,6 +11,8 @@
 */
 
 #include <string.h>
+
+#include "pm_c_util.h"
 #include "pnm.h"
 #include "shhopt.h"
 #include "mallocvar.h"
@@ -471,7 +473,7 @@ main(int argc, char * argv[]) {
         }
     }
 
-    if (STREQ(cmdline.inputFilespec, "-"))
+    if (streq(cmdline.inputFilespec, "-"))
         dumpname = "stdin";
     else {
         if (strlen(cmdline.inputFilespec) > XWDVAL_MAX - sizeof(h11) - 1)
diff --git a/converter/other/pstopnm.c b/converter/other/pstopnm.c
index a31c3f64..ba1470b0 100644
--- a/converter/other/pstopnm.c
+++ b/converter/other/pstopnm.c
@@ -26,6 +26,7 @@
 #include <sys/wait.h>  
 #include <sys/stat.h>
 
+#include "pm_c_util.h"
 #include "pnm.h"
 #include "shhopt.h"
 #include "nstring.h"
@@ -196,8 +197,9 @@ parseCommandLine(int argc, char ** argv,
 
 
 static void
-add_ps_to_filespec(const char orig_filespec[], char ** const new_filespec_p,
-                   const int verbose) {
+addPsToFilespec(char          const orig_filespec[],
+                const char ** const new_filespec_p,
+                bool          const verbose) {
 /*----------------------------------------------------------------------------
    If orig_filespec[] does not name an existing file, but the same
    name with ".ps" added to the end does, return the name with the .ps
@@ -288,13 +290,13 @@ computeSizeResBlind(unsigned int   const xmax,
 
 
 static void
-compute_size_res(struct cmdlineInfo const cmdline, 
-                 enum orientation   const orientation, 
-                 struct box         const bordered_box,
-                 unsigned int *     const xsizeP, 
-                 unsigned int *     const ysizeP,
-                 unsigned int *     const xresP, 
-                 unsigned int *     const yresP) {
+computeSizeRes(struct cmdlineInfo const cmdline, 
+               enum orientation   const orientation, 
+               struct box         const bordered_box,
+               unsigned int *     const xsizeP, 
+               unsigned int *     const ysizeP,
+               unsigned int *     const xresP, 
+               unsigned int *     const yresP) {
 /*----------------------------------------------------------------------------
   Figure out how big the output image should be (return as
   *xsizeP and *ysizeP) and what output device resolution Ghostscript
@@ -353,7 +355,8 @@ compute_size_res(struct cmdlineInfo const cmdline,
 enum postscript_language {COMMON_POSTSCRIPT, ENCAPSULATED_POSTSCRIPT};
 
 static enum postscript_language
-language_declaration(const char input_filespec[], int const verbose) {
+languageDeclaration(char const input_filespec[],
+                    bool const verbose) {
 /*----------------------------------------------------------------------------
   Return the Postscript language in which the file declares it is written.
   (Except that if the file is on Standard Input or doesn't validly declare
@@ -361,7 +364,7 @@ language_declaration(const char input_filespec[], int const verbose) {
 -----------------------------------------------------------------------------*/
     enum postscript_language language;
 
-    if (STREQ(input_filespec, "-"))
+    if (streq(input_filespec, "-"))
         /* Can't read stdin, because we need it to remain positioned for the 
            Ghostscript interpreter to read it.
         */
@@ -395,9 +398,9 @@ language_declaration(const char input_filespec[], int const verbose) {
 
 
 static struct box
-compute_box_to_extract(struct box const cmdline_extract_box,
-                       char       const input_filespec[],
-                       bool       const verbose) {
+computeBoxToExtract(struct box const cmdline_extract_box,
+                    char       const input_filespec[],
+                    bool       const verbose) {
 
     struct box retval;
 
@@ -410,7 +413,7 @@ compute_box_to_extract(struct box const cmdline_extract_box,
         */
         struct box ps_bb;  /* Box described by %%BoundingBox stmt in input */
 
-        if (STREQ(input_filespec, "-"))
+        if (streq(input_filespec, "-"))
             /* Can't read stdin, because we need it to remain
                positioned for the Ghostscript interpreter to read it.  
             */
@@ -466,8 +469,8 @@ compute_box_to_extract(struct box const cmdline_extract_box,
 
 
 static enum orientation
-compute_orientation(struct cmdlineInfo const cmdline, 
-                    struct box         const extract_box) {
+computeOrientation(struct cmdlineInfo const cmdline, 
+                   struct box         const extract_box) {
 
     enum orientation retval;
     unsigned int const input_width  = extract_box.urx - extract_box.llx;
@@ -511,9 +514,10 @@ compute_orientation(struct cmdlineInfo const cmdline,
 
 
 static struct box
-add_borders(const struct box input_box, 
-            const float xborder_scale, float yborder_scale,
-            const int verbose) {
+addBorders(struct box const input_box, 
+           float      const xborder_scale,
+           float      const yborder_scale,
+           bool       const verbose) {
 /*----------------------------------------------------------------------------
    Return a box which is 'input_box' plus some borders.
 
@@ -543,9 +547,12 @@ add_borders(const struct box input_box,
 
 
 static const char *
-compute_pstrans(const struct box box, const enum orientation orientation,
-                const int xsize, const int ysize, 
-                const int xres, const int yres) {
+computePstrans(struct box       const box,
+               enum orientation const orientation,
+               int              const xsize,
+               int              const ysize, 
+               int              const xres,
+               int              const yres) {
 
     const char * retval;
 
@@ -570,13 +577,13 @@ compute_pstrans(const struct box box, const enum orientation orientation,
 
 
 static const char *
-compute_outfile_arg(const struct cmdlineInfo cmdline) {
+computeOutfileArg(struct cmdlineInfo const cmdline) {
 
     const char *retval;  /* malloc'ed */
 
     if (cmdline.goto_stdout)
         retval = strdup("-");
-    else if (STREQ(cmdline.input_filespec, "-"))
+    else if (streq(cmdline.input_filespec, "-"))
         retval = strdup("-");
     else {
         char * basename;
@@ -584,7 +591,7 @@ compute_outfile_arg(const struct cmdlineInfo cmdline) {
         
         basename  = strdup(cmdline.input_filespec);
         if (strlen(basename) > 3 && 
-            STREQ(basename+strlen(basename)-3, ".ps")) 
+            streq(basename+strlen(basename)-3, ".ps")) 
             /* The input filespec ends in ".ps".  Chop it off. */
             basename[strlen(basename)-3] = '\0';
 
@@ -605,7 +612,8 @@ compute_outfile_arg(const struct cmdlineInfo cmdline) {
 
 
 static const char *
-compute_gs_device(const int format_type, const int forceplain) {
+computeGsDevice(int  const format_type,
+                bool const forceplain) {
 
     const char * basetype;
     const char * retval;
@@ -673,19 +681,22 @@ findGhostscriptProg(const char ** const retvalP) {
 
 
 static void
-execGhostscript(int const inputPipeFd,
-                const char ghostscript_device[],
-                const char outfile_arg[], 
-                int const xsize, int const ysize, 
-                int const xres, int const yres,
-                const char input_filespec[], int const verbose) {
+execGhostscript(int  const inputPipeFd,
+                char const ghostscript_device[],
+                char const outfile_arg[], 
+                int  const xsize,
+                int  const ysize, 
+                int  const xres,
+                int  const yres,
+                char const input_filespec[],
+                bool const verbose) {
     
-    const char *arg0;
-    const char *ghostscriptProg;
-    const char *deviceopt;
-    const char *outfileopt;
-    const char *gopt;
-    const char *ropt;
+    const char * arg0;
+    const char * ghostscriptProg;
+    const char * deviceopt;
+    const char * outfileopt;
+    const char * gopt;
+    const char * ropt;
     int rc;
 
     findGhostscriptProg(&ghostscriptProg);
@@ -723,15 +734,17 @@ execGhostscript(int const inputPipeFd,
 
 
 
-
 static void
-execute_ghostscript(const char pstrans[], const char ghostscript_device[],
-                    const char outfile_arg[], 
-                    const int xsize, const int ysize, 
-                    const int xres, const int yres,
-                    const char input_filespec[], 
-                    const enum postscript_language language,
-                    const int verbose) {
+executeGhostscript(char                     const pstrans[],
+                   char                     const ghostscript_device[],
+                   char                     const outfile_arg[], 
+                   int                      const xsize,
+                   int                      const ysize, 
+                   int                      const xres,
+                   int                      const yres,
+                   char                     const input_filespec[], 
+                   enum postscript_language const language,
+                   bool                     const verbose) {
 
     int gs_exit;  /* wait4 exit code from Ghostscript */
     FILE *gs;  /* Pipe to Ghostscript's standard input */
@@ -835,10 +848,10 @@ execute_ghostscript(const char pstrans[], const char ghostscript_device[],
 
 
 int
-main(int argc, char **argv) {
+main(int argc, char ** argv) {
 
     struct cmdlineInfo cmdline;
-    char *input_filespec;  /* malloc'ed */
+    const char * input_filespec;  /* malloc'ed */
         /* The file specification of our Postscript input file */
     unsigned int xres, yres;    /* Resolution in pixels per inch */
     unsigned int xsize, ysize;  /* output image size in pixels */
@@ -851,43 +864,42 @@ main(int argc, char **argv) {
 
     enum postscript_language language;
     enum orientation orientation;
-    const char *ghostscript_device;
-    const char *outfile_arg;
-    const char *pstrans;
+    const char * ghostscript_device;
+    const char * outfile_arg;
+    const char * pstrans;
 
     pnm_init(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
-    add_ps_to_filespec(cmdline.input_filespec, &input_filespec,
-                       cmdline.verbose);
+    addPsToFilespec(cmdline.input_filespec, &input_filespec, cmdline.verbose);
 
-    extract_box = compute_box_to_extract(cmdline.extract_box, input_filespec, 
-                                         cmdline.verbose);
+    extract_box = computeBoxToExtract(cmdline.extract_box, input_filespec, 
+                                      cmdline.verbose);
 
-    language = language_declaration(input_filespec, cmdline.verbose);
+    language = languageDeclaration(input_filespec, cmdline.verbose);
     
-    orientation = compute_orientation(cmdline, extract_box);
+    orientation = computeOrientation(cmdline, extract_box);
 
-    bordered_box = add_borders(extract_box, cmdline.xborder, cmdline.yborder,
-                               cmdline.verbose);
+    bordered_box = addBorders(extract_box, cmdline.xborder, cmdline.yborder,
+                              cmdline.verbose);
 
-    compute_size_res(cmdline, orientation, bordered_box, 
-                     &xsize, &ysize, &xres, &yres);
+    computeSizeRes(cmdline, orientation, bordered_box, 
+                   &xsize, &ysize, &xres, &yres);
     
-    pstrans = compute_pstrans(bordered_box, orientation,
-                              xsize, ysize, xres, yres);
+    pstrans = computePstrans(bordered_box, orientation,
+                             xsize, ysize, xres, yres);
 
-    outfile_arg = compute_outfile_arg(cmdline);
+    outfile_arg = computeOutfileArg(cmdline);
 
     ghostscript_device = 
-        compute_gs_device(cmdline.format_type, cmdline.forceplain);
+        computeGsDevice(cmdline.format_type, cmdline.forceplain);
     
     pm_message("Writing %s file", ghostscript_device);
     
-    execute_ghostscript(pstrans, ghostscript_device, outfile_arg, 
-                        xsize, ysize, xres, yres, input_filespec,
-                        language, cmdline.verbose);
+    executeGhostscript(pstrans, ghostscript_device, outfile_arg, 
+                       xsize, ysize, xres, yres, input_filespec,
+                       language, cmdline.verbose);
 
     strfree(ghostscript_device);
     strfree(outfile_arg);
diff --git a/converter/other/rast.h b/converter/other/rast.h
index 1854e495..e79896ea 100644
--- a/converter/other/rast.h
+++ b/converter/other/rast.h
@@ -97,15 +97,33 @@ typedef struct {
 
 /* And the routine definitions. */
 
-struct pixrect* mem_create ARGS(( int w, int h, int depth ));
-void mem_free ARGS(( struct pixrect* p ));
+struct pixrect *
+mem_create(int const w,
+           int const h,
+           int const depth);
 
-int pr_dump ARGS(( struct pixrect* p, FILE* out, colormap_t* colormap, int type, int copy_flag ));
+void
+mem_free(struct pixrect * const p);
 
-int pr_load_header ARGS(( FILE* in, struct rasterfile* hP ));
+int
+pr_dump(struct pixrect * const p,
+        FILE *           const out,
+        colormap_t *     const colormap,
+        int              const type,
+        int              const copy_flag);
 
-int pr_load_colormap ARGS(( FILE* in, struct rasterfile* hP, colormap_t* colormap ));
+int
+pr_load_header(FILE *              const in,
+               struct rasterfile * const hP);
 
-struct pixrect* pr_load_image ARGS(( FILE* in, struct rasterfile* hP, colormap_t* colormap ));
+int
+pr_load_colormap(FILE *              const in,
+                 struct rasterfile * const hP,
+                 colormap_t *        const colormap);
+
+struct pixrect *
+pr_load_image(FILE *              const in,
+              struct rasterfile * const hP,
+              colormap_t *        const colormap);
 
 #endif
diff --git a/converter/other/rlatopam.c b/converter/other/rlatopam.c
index ae1326ca..703c4820 100644
--- a/converter/other/rlatopam.c
+++ b/converter/other/rlatopam.c
@@ -16,6 +16,7 @@
 #include <string.h>
 #include <errno.h>
 
+#include "pm_c_util.h"
 #include "shhopt.h"
 #include "mallocvar.h"
 #include "pam.h"
diff --git a/converter/other/rletopnm.c b/converter/other/rletopnm.c
index aaa86388..83ada51b 100644
--- a/converter/other/rletopnm.c
+++ b/converter/other/rletopnm.c
@@ -46,6 +46,7 @@
 #define NO_DECLARE_MALLOC
 #include <rle.h>
 
+#include "pm_c_util.h"
 #include "pnm.h"
 #include "shhopt.h"
 #include "mallocvar.h"
@@ -124,7 +125,7 @@ parseCommandLine(int argc, char ** argv,
     if (argc - 1 == 0)
         cmdlineP->input_filename = NULL;  /* he wants stdin */
     else if (argc - 1 == 1) {
-        if (STREQ(argv[1], "-"))
+        if (streq(argv[1], "-"))
             cmdlineP->input_filename = NULL;  /* he wants stdin */
         else 
             cmdlineP->input_filename = strdup(argv[1]);
@@ -133,7 +134,7 @@ parseCommandLine(int argc, char ** argv,
                  "is the input file specification");
 
     if (cmdlineP->alpha_filename && 
-        STREQ(cmdlineP->alpha_filename, "-"))
+        streq(cmdlineP->alpha_filename, "-"))
         cmdlineP->alpha_stdout = TRUE;
     else 
         cmdlineP->alpha_stdout = FALSE;
diff --git a/converter/other/svgtopam.c b/converter/other/svgtopam.c
index 76e122db..c7eac8e6 100644
--- a/converter/other/svgtopam.c
+++ b/converter/other/svgtopam.c
@@ -13,6 +13,17 @@
   By Bryan Henderson, San Jose, California.  May 2006
 
   Contributed to the public domain.
+
+==============================================================================
+
+  Implementation notes:
+
+   A look at Libxml2 Subversion source code change history says the type
+   'xmlReaderTypes' was added (in <libxml/xmlreader.h>) in 2.5.9.  But a MacOS
+   10.3.9 user reports in April 2007 that he has 2.6.16 installed and it
+   doesn't have xmlReaderTypes.  Another MacOS user reported that in December
+   2008.  Apparently that OS has a broken libxml2 installation.
+   
 ============================================================================*/
 
 #define _BSD_SOURCE  /* Make sure strdup() is in <string.h> */
@@ -205,7 +216,16 @@ makePoint(unsigned int const x,
     return p;
 }
 
+static ppmd_point
+makePpmdPoint(point const arg) {
+
+    ppmd_point p;
 
+    p.x = arg.x;
+    p.y = arg.y;
+
+    return p;
+}
 
 typedef enum {
     PATH_MOVETO,
@@ -437,12 +457,12 @@ outlineObject(path *           const pathP,
                     pm_message("Doing cubic spline to (%u, %u)",
                                dest.x, dest.y);
                 /* We need to write ppmd_spline4() */
-                ppmd_spline4(NULL, 0, 0, 0,
-                             currentPos.x, currentPos.y,
-                             ctl1.x, ctl1.y,
-                             ctl2.x, ctl2.y,
-                             dest.x, dest.y,
-                             ppmd_fill_drawproc, fillObjP);
+                ppmd_spline4p(NULL, 0, 0, 0,
+                              makePpmdPoint(currentPos),
+                              makePpmdPoint(dest),
+                              makePpmdPoint(ctl1),
+                              makePpmdPoint(ctl2),
+                              ppmd_fill_drawprocp, fillObjP);
                 currentPos = dest;
             } break;
             }
@@ -562,6 +582,8 @@ static void
 processSubPathNode(xmlTextReaderPtr const xmlReaderP,
                    bool *           const endOfPathP) {
 
+    /* See comment above about xmlReaderTypes not being defined */
+
     xmlReaderTypes const nodeType  = xmlTextReaderNodeType(xmlReaderP);
 
     *endOfPathP = FALSE;  /* initial assumption */
diff --git a/converter/other/tiff.c b/converter/other/tiff.c
index a498571b..90d50710 100644
--- a/converter/other/tiff.c
+++ b/converter/other/tiff.c
@@ -6,6 +6,8 @@
 
 ============================================================================*/
 
+#define _BSD_SOURCE    /* Make sure strcaseeq() is in nstring.h */
+
 #include <string.h>
 
 #ifdef VMS
@@ -40,7 +42,7 @@ number(const char * const value,
         /* It's not a numeric string, so it must be an enumerated value name */
         unsigned int i;
         for (i = 0; tagvallist[i].name; ++i) {
-            if (STRCASEEQ(value, tagvallist[i].name))
+            if (strcaseeq(value, tagvallist[i].name))
                 return tagvallist[i].value;
         }
         pm_error("'%s' is neither a number nor a valid value name", value);
@@ -460,7 +462,7 @@ tagDefFind(const char * const name) {
     for (i = 0;
          i < ARRAY_SIZE(tagDefinitions) && tagDefinitions[i].name;
          ++i) {
-        if (STRCASEEQ(tagDefinitions[i].name, name))
+        if (strcaseeq(tagDefinitions[i].name, name))
             return &tagDefinitions[i];
     }
 
diff --git a/converter/other/tifftopnm.c b/converter/other/tifftopnm.c
index 5969a49a..6665c7fd 100644
--- a/converter/other/tifftopnm.c
+++ b/converter/other/tifftopnm.c
@@ -49,11 +49,16 @@
 #define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
+#include <assert.h>
 #include <string.h>
-#include "pnm.h"
+#include <stdio.h>
+#include <sys/wait.h>
+
+#include "pm_c_util.h"
 #include "shhopt.h"
 #include "mallocvar.h"
 #include "nstring.h"
+#include "pnm.h"
 
 #ifdef VMS
 #ifdef SYSV
@@ -90,13 +95,14 @@ struct cmdlineInfo {
     bool alphaStdout;
     unsigned int respectfillorder;   /* -respectfillorder option */
     unsigned int byrow;
+    unsigned int orientraw;
     unsigned int verbose;
 };
 
 
 
 static void
-parseCommandLine(int argc, char ** argv,
+parseCommandLine(int argc, const char ** const argv,
                  struct cmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that many of the strings that this function returns in the
@@ -122,12 +128,14 @@ parseCommandLine(int argc, char ** argv,
             OPT_FLAG,   NULL, &cmdlineP->respectfillorder,     0);
     OPTENT3(0,   "byrow",   
             OPT_FLAG,   NULL, &cmdlineP->byrow,                0);
+    OPTENT3(0,   "orientraw",   
+            OPT_FLAG,   NULL, &cmdlineP->orientraw,            0);
     OPTENT3('h', "headerdump", 
             OPT_FLAG,   NULL, &cmdlineP->headerdump,           0);
     OPTENT3(0,   "alphaout",   
             OPT_STRING, &cmdlineP->alphaFilename, &alphaSpec,  0);
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
 
     if (argc - 1 == 0)
         cmdlineP->inputFilename = strdup("-");  /* he wants stdin */
@@ -138,7 +146,7 @@ parseCommandLine(int argc, char ** argv,
                  "is the input file name");
 
     if (alphaSpec) {
-        if (STREQ(cmdlineP->alphaFilename, "-"))
+        if (streq(cmdlineP->alphaFilename, "-"))
             cmdlineP->alphaStdout = TRUE;
         else
             cmdlineP->alphaStdout = FALSE;
@@ -150,16 +158,109 @@ parseCommandLine(int argc, char ** argv,
 
 
 
+static void
+getBps(TIFF *           const tif,
+       unsigned short * const bpsP) {
+
+    unsigned short tiffBps;
+    unsigned short bps;
+    int rc;
+
+    rc = TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &tiffBps);
+    bps = (rc == 0) ? 1 : tiffBps;
+
+    if (bps < 1 || (bps > 8 && bps != 16 && bps != 32))
+        pm_error("This program can process Tiff images with only "
+                 "1-8 or 16 bits per sample.  The input Tiff image "
+                 "has %hu bits per sample.", bps);
+    else
+        *bpsP = bps;
+}
+
+
+
+struct tiffDirInfo {
+    /* 'width' and 'height' are the dimensions of the raster matrix in
+       the TIFF stream -- what the TIFF spec calls the image.  The
+       dimensions of the actual visual image represented may be the
+       reverse, because the raster can represent the visual image in
+       various orientations, as described by 'orientation'.
+    */
+    unsigned int   width;
+    unsigned int   height;
+    unsigned short bps;
+    unsigned short spp;
+    unsigned short photomet;
+    unsigned short planarconfig;
+    unsigned short fillorder;
+    unsigned short orientation;
+};
+
+
+
+static void
+tiffToImageDim(unsigned int   const tiffCols,
+               unsigned int   const tiffRows,
+               unsigned short const orientation,
+               unsigned int * const imageColsP,
+               unsigned int * const imageRowsP) {
+
+    switch (orientation) {
+    case ORIENTATION_TOPLEFT:
+    case ORIENTATION_TOPRIGHT:
+    case ORIENTATION_BOTRIGHT:
+    case ORIENTATION_BOTLEFT:
+        *imageColsP = tiffCols;
+        *imageRowsP = tiffRows;
+        break;
+    case ORIENTATION_LEFTTOP:
+    case ORIENTATION_RIGHTTOP:
+    case ORIENTATION_RIGHTBOT:
+    case ORIENTATION_LEFTBOT:
+        *imageColsP = tiffRows;
+        *imageRowsP = tiffCols;
+        break;
+    default:
+        pm_error("Invalid value for orientation tag in TIFF directory: %u",
+                 orientation);
+    }
+}
+
+static void
+getTiffDimensions(TIFF *         const tiffP,
+                  unsigned int * const colsP,
+                  unsigned int * const rowsP) {
+/*----------------------------------------------------------------------------
+   Return the dimensions of the image represented by *tiffP.  Not the
+   dimensions of the internal raster matrix -- the dimensions of the
+   actual visual image.
+-----------------------------------------------------------------------------*/
+    int ok;
+
+    unsigned int width, length;
+    unsigned short tiffOrientation;
+    unsigned short orientation;
+    int present;
+
+    ok = TIFFGetField(tiffP, TIFFTAG_IMAGEWIDTH, &width);
+    if (!ok)
+        pm_error("Input Tiff file is invalid.  It has no IMAGEWIDTH tag.");
+    ok = TIFFGetField(tiffP, TIFFTAG_IMAGELENGTH, &length);
+    if (!ok)
+        pm_error("Input Tiff file is invalid.  It has no IMAGELENGTH tag.");
+
+    present = TIFFGetField(tiffP, TIFFTAG_ORIENTATION, &tiffOrientation);
+    orientation = present ? tiffOrientation : ORIENTATION_TOPLEFT;
+
+    tiffToImageDim(width, length, orientation, colsP, rowsP);
+}
+
+
+
 static void 
-read_directory(TIFF * const tif,
-               unsigned short * const bps_p,
-               unsigned short * const spp_p,
-               unsigned short * const photomet_p,
-               unsigned short * const planarconfig_p,
-               unsigned short * const fillorder_p,
-               unsigned int *   const cols_p,
-               unsigned int *   const rows_p,
-               bool             const headerdump) {
+readDirectory(TIFF *               const tiffP,
+              bool                 const headerdump,
+              struct tiffDirInfo * const headerP) {
 /*----------------------------------------------------------------------------
    Read various values of TIFF tags from the TIFF directory, and
    default them if not in there and make guesses where values are
@@ -173,68 +274,71 @@ read_directory(TIFF * const tif,
    invalid values to our caller.
 -----------------------------------------------------------------------------*/
     int rc;
-    unsigned short tiff_bps;
-    unsigned short tiff_spp;
+    unsigned short tiffSpp;
 
     if (headerdump)
-        TIFFPrintDirectory(tif, stderr, TIFFPRINT_NONE);
+        TIFFPrintDirectory(tiffP, stderr, TIFFPRINT_NONE);
 
-    rc = TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &tiff_bps);
-    *bps_p = (rc == 0) ? 1 : tiff_bps;
+    getBps(tiffP, &headerP->bps);
 
-    if (*bps_p < 1 || (*bps_p > 8 && *bps_p != 16 && *bps_p != 32))
-        pm_error("This program can process Tiff images with only "
-                 "1-8 or 16 bits per sample.  The input Tiff image "
-                 "has %d bits per sample.", *bps_p);
+    rc = TIFFGetFieldDefaulted(tiffP, TIFFTAG_FILLORDER, &headerP->fillorder);
+    rc = TIFFGetField(tiffP, TIFFTAG_SAMPLESPERPIXEL, &tiffSpp);
+    headerP->spp = (rc == 0) ? 1 : tiffSpp;
 
-    rc = TIFFGetFieldDefaulted(tif, TIFFTAG_FILLORDER, fillorder_p);
-    rc = TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &tiff_spp);
-    *spp_p = (rc == 0) ? 1: tiff_spp;
-
-    rc = TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, photomet_p);
+    rc = TIFFGetField(tiffP, TIFFTAG_PHOTOMETRIC, &headerP->photomet);
     if (rc == 0)
         pm_error("PHOTOMETRIC tag is not in Tiff file.  "
                  "TIFFGetField() of it failed.\n"
                  "This means the input is not valid Tiff.");
 
-    if (*spp_p > 1) {
-        rc = TIFFGetField(tif, TIFFTAG_PLANARCONFIG, planarconfig_p);
+    if (headerP->spp > 1) {
+        rc = TIFFGetField(tiffP, TIFFTAG_PLANARCONFIG, &headerP->planarconfig);
         if (rc == 0)
             pm_error("PLANARCONFIG tag is not in Tiff file, though it "
                      "has more than one sample per pixel.  "
                      "TIFFGetField() of it failed.  This means the input "
                      "is not valid Tiff.");
-    } else {
-        *planarconfig_p = PLANARCONFIG_CONTIG;
-    }
+    } else
+        headerP->planarconfig = PLANARCONFIG_CONTIG;
 
-    switch(*planarconfig_p) {
+    switch (headerP->planarconfig) {
     case PLANARCONFIG_CONTIG:
         break;
     case PLANARCONFIG_SEPARATE:
-        if (*photomet_p != PHOTOMETRIC_RGB && 
-            *photomet_p != PHOTOMETRIC_SEPARATED)
+        if (headerP->photomet != PHOTOMETRIC_RGB && 
+            headerP->photomet != PHOTOMETRIC_SEPARATED)
             pm_error("This program can handle separate planes only "
-                     "with RGB (PHOTOMETRIC tag = %d) or SEPARATED "
-                     "(PHOTOMETRIC tag = %d) data.  The input Tiff file " 
-                     "has PHOTOMETRIC tag = %d.",
-                     PHOTOMETRIC_RGB, PHOTOMETRIC_SEPARATED, *photomet_p);
+                     "with RGB (PHOTOMETRIC tag = %u) or SEPARATED "
+                     "(PHOTOMETRIC tag = %u) data.  The input Tiff file " 
+                     "has PHOTOMETRIC tag = %hu.",
+                     PHOTOMETRIC_RGB, PHOTOMETRIC_SEPARATED,
+                     headerP->photomet);
         break;
     default:
-        pm_error("Unrecognized PLANARCONFIG tag value in Tiff input: %d.\n",
-                 *planarconfig_p);
+        pm_error("Unrecognized PLANARCONFIG tag value in Tiff input: %u.\n",
+                 headerP->planarconfig);
     }
 
-    rc = TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, cols_p);
+    rc = TIFFGetField(tiffP, TIFFTAG_IMAGEWIDTH, &headerP->width);
     if (rc == 0)
         pm_error("Input Tiff file is invalid.  It has no IMAGEWIDTH tag.");
-    rc = TIFFGetField( tif, TIFFTAG_IMAGELENGTH, rows_p );
+    rc = TIFFGetField(tiffP, TIFFTAG_IMAGELENGTH, &headerP->height);
     if (rc == 0)
         pm_error("Input Tiff file is invalid.  It has no IMAGELENGTH tag.");
 
+    {
+        unsigned short tiffOrientation;
+        int present;
+        present = TIFFGetField(tiffP, TIFFTAG_ORIENTATION, &tiffOrientation);
+        headerP->orientation =
+            present ? tiffOrientation : ORIENTATION_TOPLEFT;
+    }
     if (headerdump) {
-        pm_message( "%dx%dx%d image", *cols_p, *rows_p, *bps_p * *spp_p );
-        pm_message( "%d bits/sample, %d samples/pixel", *bps_p, *spp_p );
+        pm_message("%ux%ux%u raster matrix, oriented %u",
+                   headerP->width, headerP->height,
+                   headerP->bps * headerP->spp, headerP->orientation);
+        pm_message("%hu bits/sample, %hu samples/pixel",
+                   headerP->bps, headerP->spp);
     }
 }
 
@@ -260,17 +364,18 @@ readscanline(TIFF *         const tif,
    a Tiff scanline.
 -----------------------------------------------------------------------------*/
     int rc;
-    const unsigned int bpsmask = (1 << bps) - 1;
+    unsigned int const bpsmask = (1 << bps) - 1;
       /* A mask for taking the lowest 'bps' bits of a number */
 
     /* The TIFFReadScanline man page doesn't tell the format of its
        'buf' return value, but it is exactly the same format as the 'buf'
        input to TIFFWriteScanline.  The man page for that doesn't say 
-       anything either, but the source code for Pnmtotiff contains a
+       anything either, but the source code for Pamtotiff contains a
        specification.
     */
 
     rc = TIFFReadScanline(tif, scanbuf, row, plane);
+
     if (rc < 0)
         pm_error( "Unable to read row %d, plane %d of input Tiff image.  "
                   "TIFFReadScanline() failed.",
@@ -291,7 +396,7 @@ readscanline(TIFF *         const tif,
 
         for (sample = 0, bitsleft=8, inP=scanbuf; 
              sample < cols*spp; 
-             sample++) {
+             ++sample) {
             if (bitsleft == 0) {
                 ++inP; 
                 bitsleft = 8;
@@ -322,7 +427,7 @@ readscanline(TIFF *         const tif,
            bytes of each sample appear in a TIFF file, which is
            contrary to the TIFF spec.  
         */
-        uint16 * const scanbuf16 = (uint16 *) scanbuf;
+        const uint16 * const scanbuf16 = (const uint16 *) scanbuf;
         unsigned int sample;
 
         for (sample = 0; sample < cols*spp; ++sample)
@@ -547,6 +652,442 @@ analyzeImageType(TIFF *             const tif,
 
 
 
+typedef struct {
+    FILE *       imageoutFileP;
+        /* The stream to which we write the PNM image.  Null for none. */
+    FILE *       alphaFileP;
+        /* The stream to which we write the alpha channel.  Null for none. */
+    unsigned int inCols;
+        /* Width of each row that gets passed to this object */
+    unsigned int inRows;
+        /* Number of rows that get passed to this object */
+    unsigned int outCols;
+        /* Width of each row this object writes out to the file */
+    unsigned int outRows;
+        /* Number of rows this object writes out */
+    xelval       maxval;
+        /* Maxval of the output image */
+    int          format;
+        /* Format of the output image */
+    gray         alphaMaxval;
+        /* Maxval of the alpha channel */
+    bool         flipping;
+        /* We're passing rows through a Pamflip process, rather than writing
+           them directly to *imageoutFileP, *alphaFileP.
+        */
+    FILE *       imagePipeP;
+        /* Stream hooked up to pipe that goes to a Pamflip process.
+           Meaningful only when 'flipping' is true.
+        */
+    FILE *       alphaPipeP;
+        /* Stream hooked up to pipe that goes to a Pamflip process.
+           Meaningful only when 'flipping' is true.
+        */
+    pid_t        imageFlipPid;
+        /* Process ID of the Pamflip process.
+           Meaningful only when 'flipping' is true.
+        */
+    pid_t        alphaFlipPid;
+        /* Process ID of the Pamflip process.
+           Meaningful only when 'flipping' is true.
+        */
+} pnmOut;
+
+
+
+static const char *
+xformNeeded(unsigned short const tiffOrientation) {
+/*----------------------------------------------------------------------------
+   Return the value of the Pamflip -xform option that causes Pamflip
+   to change a raster from orienation 'tiffOrientation' to Row 0 top,
+   Column 0 left.
+-----------------------------------------------------------------------------*/
+    switch (tiffOrientation) {
+    case ORIENTATION_TOPLEFT:  return "";
+    case ORIENTATION_TOPRIGHT: return "leftright";
+    case ORIENTATION_BOTRIGHT: return "topbottom,leftright";
+    case ORIENTATION_BOTLEFT:  return "topbottom";
+    case ORIENTATION_LEFTTOP:  return "transpose";
+    case ORIENTATION_RIGHTTOP: return "transpose,leftright";
+    case ORIENTATION_RIGHTBOT: return "transpose,topbottom,leftright";
+    case ORIENTATION_LEFTBOT:  return "transpose,topbottom";
+    default:
+        pm_error("Invalid value for orientation tag in TIFF directory: %u",
+                 tiffOrientation);
+        return "";
+    }
+}
+
+
+
+/* File descriptors array indices for use with pipe() */
+#define PIPE_READ 0
+#define PIPE_WRITE 1
+
+static void
+spawnWithInputPipe(const char *  const shellCmd,
+                   FILE **       const pipePP,
+                   pid_t *       const pidP,
+                   const char ** const errorP) {
+
+    int fd[2];
+    int rc;
+
+    rc = pipe(fd);
+
+    if (rc != 0)
+        asprintfN(errorP, "Failed to create pipe for process input.  "
+                  "Errno=%d (%s)", errno, strerror(errno));
+    else {
+        int rc;
+
+        rc = fork();
+
+        if (rc < 0) {
+            asprintfN(errorP, "Failed to fork a process.  errno=%d (%s)",
+                      errno, strerror(errno));
+        } else if (rc == 0) {
+            /* This is the child */
+            int rc;
+            close(fd[PIPE_WRITE]);
+            close(STDIN_FILENO);
+            dup2(fd[PIPE_READ], STDIN_FILENO);
+
+            rc = system(shellCmd);
+
+            exit(rc);
+        } else {
+            /* Parent */
+            pid_t const childPid = rc;
+
+            close(fd[PIPE_READ]);
+
+            *pidP   = childPid;
+            *pipePP = fdopen(fd[PIPE_WRITE], "w");
+
+            if (*pipePP == NULL)
+                asprintfN(errorP,"Unable to create stream from pipe.  "
+                          "fdopen() fails with errno=%d (%s)",
+                          errno, strerror(errno));
+            else
+                *errorP = NULL;
+        }
+    }
+}
+
+                  
+
+static void
+createFlipProcess(FILE *         const outFileP,
+                  unsigned short const orientation,
+                  bool           const verbose,
+                  FILE **        const inPipePP,
+                  pid_t *        const pidP) {
+/*----------------------------------------------------------------------------
+   Create a process that runs the program Pamflip and writes its output
+   to *imageoutFileP.
+
+   The process takes it input from a pipe that we create.  We return as
+   *inPipePP a file stream connected to the other end of that pipe.
+
+   I.e. Caller will write a Netpbm file stream to **inPipePP and a flipped
+   version of it will go to *outFileP.
+
+   The flipping it does turns the input from orientation 'orientation'
+   to Netpbm orientation, i.e. raster row 0 top, raster column 0 left,
+   where the raster stream is divided into rows, with row 0 being first
+   in the stream, and column 0 being first within each row.
+
+   Caller must close *inPipePP when he is done.
+-----------------------------------------------------------------------------*/
+    const char * pamflipCmd;
+    const char * error;
+
+    /* Hooking up the process to the output stream is kind of tricky
+       because the stream (FILE *) is an entity local to this process.
+       We just assume that nothing in this process actually touches
+       the stream so that having the process write to the underlying
+       file descriptor is equivalent to writing to the stream.
+    */
+
+    asprintfN(&pamflipCmd, "pamflip -xform=%s >&%u",
+              xformNeeded(orientation), fileno(outFileP));
+
+    if (verbose)
+        pm_message("Reorienting raster with shell command '%s'", pamflipCmd);
+
+    spawnWithInputPipe(pamflipCmd, inPipePP, pidP, &error);
+
+    if (error) {
+        pm_error("Shell command '%s', to reorient the TIFF "
+                 "raster, failed.  %s.  To work around this, you can use "
+                 "the -orientraw option.", pamflipCmd, error);
+
+        strfree(error);
+    }
+}
+
+
+
+static void
+setupFlipper(pnmOut *       const pnmOutP,
+             unsigned short const orientation,
+             bool           const flipIfNeeded,
+             bool           const orientraw,
+             bool           const verbose,
+             bool *         const flipOkP,
+             bool *         const noflipOkP) {
+/*----------------------------------------------------------------------------
+   Set up the Pamflip processes to flip the raster, where needed.
+
+   Whether we need a Pamflip process is a complex decision.  For
+   reasons of efficiency and robustness, we don't want one unless
+   flipping is actually required.  Flipping is not required if the
+   TIFF image is already oriented like a PNM.  It also isn't required
+   if the user explicitly asks for raw orientation.  Finally, it's not
+   required when the user is using the TIFF library whole-image
+   conversion services, because they do the flipping and thus the
+   'pnmOut' object will see properly oriented pixels as input.
+
+   'flipIfNeeded' says to set up the flipping pipe if 'orientation'
+   indicates something other than standard PNM orientation.
+
+   'orientation' is the orientation of the TIFF raster.
+
+   'orientraw' says the final output should be in the same
+   orientation, 'orientation', not the proper PNM orientation.
+
+   *flipOkP means that as we set up the pnmOut object,
+   it is OK if Caller flips the raster on his own.  *noflipOk means
+   is is OK if Caller does not flip the raster on his own.  Note
+   that they are both true if the raster is already in the PNM
+   orientation, because then flipping is idempotent.
+-----------------------------------------------------------------------------*/
+
+    if (orientation == ORIENTATION_TOPLEFT) {
+        /* Ah, the easy case.  Flipping and not flipping are identical,
+           so none of the other parameters matter.  Just write directly
+           to the output file and let Caller flip or not flip as he
+           wishes.
+        */
+        pnmOutP->flipping = FALSE;
+        *flipOkP   = TRUE;
+        *noflipOkP = TRUE;
+    } else {
+        if (orientraw) {
+            /* Raster is not to be flipped, so go directly to file,
+               and tell Caller not to flip it either.
+            */
+            pnmOutP->flipping = FALSE;
+            *flipOkP   = FALSE;
+            *noflipOkP = TRUE;
+        } else {            
+            if (flipIfNeeded) {
+                if (verbose)
+                    pm_message("Transforming raster with Pamflip");
+
+                if (pnmOutP->alphaFileP)
+                    createFlipProcess(pnmOutP->alphaFileP, orientation,
+                                      verbose,
+                                      &pnmOutP->alphaPipeP,
+                                      &pnmOutP->alphaFlipPid);
+                if (pnmOutP->imageoutFileP)
+                    createFlipProcess(pnmOutP->imageoutFileP, orientation,
+                                      verbose,
+                                      &pnmOutP->imagePipeP,
+                                      &pnmOutP->imageFlipPid);
+                
+                /* The stream will flip it, so Caller must not: */
+                pnmOutP->flipping = TRUE;
+                *flipOkP   = FALSE;
+                *noflipOkP = TRUE;
+            } else {
+                /* It needs flipping, but Caller doesn't want us to do it.
+                   So Caller must do it:
+                */
+                pnmOutP->flipping = FALSE;
+                *flipOkP   = TRUE;
+                *noflipOkP = FALSE;
+            }
+        }
+    }
+}
+
+
+
+static void
+computeOutputDimensions(unsigned int       const tiffCols,
+                        unsigned int       const tiffRows,
+                        unsigned short     const orientation,
+                        bool               const orientraw,
+                        unsigned int *     const colsP,
+                        unsigned int *     const rowsP,
+                        bool               const verbose) {
+/*----------------------------------------------------------------------------
+   Compute the dimensions of the image.  We're talking about the
+   actual image, not what the TIFF spec calls the image.  What the
+   TIFF spec calls the image is matrix of pixels within the TIFF file.
+   That matrix can represent the actual image in various ways.
+   E.g. the first row of the matrix might be the right edge of the
+   image.
+
+   'tiffCols' and 'tiffRows' are the images of the TIFF matrix and
+   'orientation' is the orientation of that matrix.
+
+   'orientraw' says we want to output the matrix in its natural
+   orientation, not the true image.
+-----------------------------------------------------------------------------*/
+    if (orientraw) {
+        *colsP = tiffCols;
+        *rowsP = tiffRows;
+    } else
+        tiffToImageDim(tiffCols, tiffRows, orientation, colsP, rowsP);
+
+    if (verbose)
+        pm_message("Generating %uw x %uh PNM image", *colsP, *rowsP);
+}
+
+
+
+static void
+pnmOut_init(FILE *         const imageoutFileP,
+            FILE *         const alphaFileP,
+            unsigned int   const cols,
+            unsigned int   const rows,
+            unsigned short const orientation,
+            xelval         const maxval,
+            int            const format,
+            gray           const alphaMaxval,
+            bool           const flipIfNeeded,
+            bool           const orientraw,
+            bool           const verbose,
+            bool *         const flipOkP,
+            bool *         const noflipOkP,
+            pnmOut *       const pnmOutP) {
+/*----------------------------------------------------------------------------
+   'cols' and 'rows' are the dimensions of the raster matrix which is
+   oriented according to 'orientation' with respect to the image.
+
+   If the user flips the data before giving it to the pnmOut object,
+   pnmOut may see the inverse dimensions; if the pnmOut object flips the
+   data, pnmOut get 'cols' x 'rows' data, but its output file may be
+   'rows x cols'.
+
+   Because the pnmOut object must be set up to receive flipped or not
+   flipped input, we have *flipOkP and *noflipOkP outputs that tell
+   Caller whether he has to flip or not.  In the unique case that
+   the TIFF matrix is already oriented the way the output PNM file needs
+   to be, flipping is idempotent, so both *flipOkP and *noflipOkP are
+   true.
+-----------------------------------------------------------------------------*/
+    pnmOutP->imageoutFileP = imageoutFileP;
+    pnmOutP->alphaFileP    = alphaFileP;
+    pnmOutP->maxval        = maxval;
+    pnmOutP->format        = format;
+    pnmOutP->alphaMaxval   = alphaMaxval;
+
+    setupFlipper(pnmOutP, orientation, flipIfNeeded, orientraw, verbose,
+                 flipOkP, noflipOkP);
+
+    computeOutputDimensions(cols, rows, orientation, orientraw,
+                            &pnmOutP->outCols, &pnmOutP->outRows, verbose);
+
+    if (pnmOutP->flipping) {
+        pnmOutP->inCols = cols;         /* Caller won't flip */
+        pnmOutP->inRows = rows;
+    } else {
+        pnmOutP->inCols = pnmOutP->outCols;  /* Caller will flip */
+        pnmOutP->inRows = pnmOutP->outRows;
+    }    
+    if (pnmOutP->flipping) {
+        if (pnmOutP->imagePipeP != NULL) 
+            pnm_writepnminit(pnmOutP->imagePipeP,
+                             pnmOutP->inCols, pnmOutP->inRows,
+                             pnmOutP->maxval, pnmOutP->format, 0);
+        if (pnmOutP->alphaPipeP != NULL) 
+            pgm_writepgminit(pnmOutP->alphaPipeP,
+                             pnmOutP->inCols, pnmOutP->inRows,
+                             pnmOutP->alphaMaxval, 0);
+    } else {
+        if (imageoutFileP != NULL) 
+            pnm_writepnminit(pnmOutP->imageoutFileP,
+                             pnmOutP->outCols, pnmOutP->outRows,
+                             pnmOutP->maxval, pnmOutP->format, 0);
+        if (alphaFileP != NULL) 
+            pgm_writepgminit(pnmOutP->alphaFileP,
+                             pnmOutP->outCols, pnmOutP->outRows,
+                             pnmOutP->alphaMaxval, 0);
+    }
+}
+
+
+
+static void
+pnmOut_term(pnmOut * const pnmOutP,
+            bool     const verbose) {
+
+    if (pnmOutP->flipping) {
+        /* Closing the pipes also causes the Pamflip processes to terminate
+           and they consequently flush their output to pnmOutP->imageoutFileP
+           and pnmOutP->alphaFileP and close those file descriptors.
+
+           We wait for the processes to exit before returning so that we
+           know everything is flushed, so the invoker of Tifftopnm is free
+           to use its output.
+        */
+        if (verbose)
+            pm_message("Flushing data through Pamflip process, "
+                       "waiting for Pamflip to terminate");
+
+        if (pnmOutP->imagePipeP) {
+            int status;
+            fclose(pnmOutP->imagePipeP);
+            waitpid(pnmOutP->imageFlipPid, &status, 0);
+        }
+        if (pnmOutP->alphaPipeP) {
+            int status;
+            fclose(pnmOutP->alphaPipeP);
+            waitpid(pnmOutP->alphaFlipPid, &status, 0);
+        }
+    } else {
+        if (pnmOutP->imageoutFileP)
+            fflush(pnmOutP->imageoutFileP);
+        if (pnmOutP->alphaFileP)
+            fflush(pnmOutP->alphaFileP);
+    }
+}
+
+
+
+static void
+pnmOut_writeRow(pnmOut *     const pnmOutP,
+                unsigned int const cols,
+                const xel *  const imageRow,
+                const gray * const alphaRow) {
+
+    assert(cols == pnmOutP->inCols);
+
+    if (pnmOutP->flipping) {
+        if (pnmOutP->imagePipeP != NULL) 
+            pnm_writepnmrow(pnmOutP->imagePipeP, (xel *)imageRow,
+                            pnmOutP->inCols, pnmOutP->maxval,
+                            pnmOutP->format, 0);
+        if (pnmOutP->alphaPipeP != NULL) 
+            pgm_writepgmrow(pnmOutP->alphaPipeP, alphaRow,
+                            pnmOutP->inCols, pnmOutP->alphaMaxval, 0);
+    } else {
+        if (pnmOutP->imageoutFileP != NULL) 
+            pnm_writepnmrow(pnmOutP->imageoutFileP, (xel *)imageRow,
+                            pnmOutP->outCols, pnmOutP->maxval,
+                            pnmOutP->format, 0);
+        if (pnmOutP->alphaFileP != NULL) 
+            pgm_writepgmrow(pnmOutP->alphaFileP, alphaRow,
+                            pnmOutP->outCols, pnmOutP->alphaMaxval, 0);
+    }
+}
+
+
+
 static void
 convertRow(unsigned int   const samplebuf[], 
            xel                  xelrow[], 
@@ -706,19 +1247,18 @@ convertMultiPlaneRow(TIFF *         const tif,
 
 
 static void
-convertRasterByRows(FILE *         const imageoutFile, 
-                    FILE *         const alphaFile,
+convertRasterByRows(pnmOut *       const pnmOutP,
                     unsigned int   const cols, 
                     unsigned int   const rows,
                     xelval         const maxval,
-                    int            const format, 
                     TIFF *         const tif,
                     unsigned short const photomet, 
                     unsigned short const planarconfig,
                     unsigned short const bps,
                     unsigned short const spp,
                     unsigned short const fillorder,
-                    xel                  colormap[]) {
+                    xel            const colormap[],
+                    bool           const verbose) {
 /*----------------------------------------------------------------------------
    With the TIFF header all processed (and relevant information from it in 
    our arguments), write out the TIFF raster to the file images *imageoutFile
@@ -735,27 +1275,30 @@ convertRasterByRows(FILE *         const imageoutFile,
         /* Same info as 'scanbuf' above, but with each raster column (sample)
            represented as single array element, so it's easy to work with.
         */
-    xel* xelrow;
+    xel * xelrow;
         /* The ppm-format row of the image row we are presently converting */
-    gray* alpharow;
+    gray * alpharow;
         /* The pgm-format row representing the alpha values for the image 
            row we are presently converting.
         */
 
     int row;
 
+    if (verbose)
+        pm_message("Converting row by row ...");
+
     scanbuf = (unsigned char *) malloc(TIFFScanlineSize(tif));
     if (scanbuf == NULL)
         pm_error("can't allocate memory for scanline buffer");
 
     MALLOCARRAY(samplebuf, cols * spp);
     if (samplebuf == NULL)
-        pm_error ("can't allocate memory for row buffer");
+        pm_error("can't allocate memory for row buffer");
 
     xelrow = pnm_allocrow(cols);
     alpharow = pgm_allocrow(cols);
 
-    for ( row = 0; row < rows; ++row ) {
+    for (row = 0; row < rows; ++row) {
         /* Read one row of samples into samplebuf[] */
 
         if (planarconfig == PLANARCONFIG_CONTIG) {
@@ -769,12 +1312,8 @@ convertRasterByRows(FILE *         const imageoutFile,
             convertMultiPlaneRow(tif, xelrow, alpharow, cols, maxval, row,
                                  photomet, bps, spp, fillorder,
                                  scanbuf, samplebuf);
-            
-        if (imageoutFile != NULL) 
-            pnm_writepnmrow( imageoutFile, 
-                             xelrow, cols, (xelval) maxval, format, 0 );
-        if (alphaFile != NULL) 
-            pgm_writepgmrow( alphaFile, alpharow, cols, (gray) maxval, 0);
+
+        pnmOut_writeRow(pnmOutP, cols, xelrow, alpharow);
     }
     pgm_freerow(alpharow);
     pnm_freerow(xelrow);
@@ -785,38 +1324,81 @@ convertRasterByRows(FILE *         const imageoutFile,
 
 
 
+static void
+warnBrokenTiffLibrary(TIFF * const tiffP) {
+
+/* TIFF library bug:
+
+   In every version of the TIFF library we've seen, TIFFRGBAImageGet()
+   fails when the raster orientation (per the TIFF_ORIENTATION tag)
+   requires a transposition, e.g. ORIENTATION_LEFTBOT.  It simply omits
+   the transposition part, so e.g. it treats ORIENTATION_LEFTBOT as
+   ORIENTATION_BOTLEFT.  And because we provide a raster buffer dimensioned
+   for the properly transposed image, the result is somewhat of a mess.
+   
+   We have found no documentation of the TIFF library that suggests
+   this behavior is as designed, so it's probably not a good idea to
+   work around it; it might be fixed somewhere.
+
+   The user can of course work around just by using -byrow and therefore
+   not using TIFFRGBAImageGet().
+
+   There is some evidence of an interface in the TIFF library that
+   lets you request that TIFFRGBAImageGet() produce a raster in the
+   same orientation as the one in the TIFF image.
+   (tiff.req_orientation).  We could conceivably use that and then do
+   a Pamflip to get the proper orientation, but that somewhat defeats
+   the philosophy of using TIFFRGBAImageGet(), so I would like to wait
+   until there's a good practical reason to do it.
+*/
+
+    unsigned short tiffOrientation;
+    int present;
+    present = TIFFGetField(tiffP, TIFFTAG_ORIENTATION, &tiffOrientation);
+    if (present) {
+        switch (tiffOrientation) {
+        case ORIENTATION_LEFTTOP:
+        case ORIENTATION_RIGHTTOP:
+        case ORIENTATION_RIGHTBOT:
+        case ORIENTATION_LEFTBOT:
+            pm_message("WARNING: This TIFF image has an orientation that "
+                       "most TIFF libraries converts incorrectly.  "
+                       "Use -byrow to circumvent.");
+            break;
+        }
+    }
+}
+
+
 
 static void 
 convertTiffRaster(uint32 *        const raster, 
                   unsigned int    const cols,
                   unsigned int    const rows,
                   xelval          const maxval,
-                  int             const format,
-                  FILE *          const imageoutFile,
-                  FILE *          const alphaFile) {
+                  pnmOut *        const pnmOutP) {
 /*----------------------------------------------------------------------------
    Convert the raster 'raster' from the format generated by the TIFF library
    to PPM (plus PGM alpha mask where applicable) and output it to
-   the files *imageoutFile and *alphaFile in format 'format' with maxval
-   'maxval'.  The raster is 'cols' wide by 'rows' high.
+   the object *pnmOutP.  The raster is 'cols' wide by 'rows' high.
 -----------------------------------------------------------------------------*/
-    xel* xelrow;
+    xel * xelrow;
         /* The ppm-format row of the image row we are
            presently converting 
         */
-    gray* alpharow;
+    gray * alpharow;
         /* The pgm-format row representing the alpha values
            for the image row we are presently converting.  
         */
-    int row;
+    unsigned int row;
 
     xelrow = pnm_allocrow(cols);
     alpharow = pgm_allocrow(cols);
 
     for (row = 0; row < rows; ++row) {
-        uint32* rp;  
+        uint32 * rp;  
             /* Address of pixel in 'raster' we are presently converting */
-        int col;
+        unsigned int col;
 
         /* Start at beginning of row: */
         rp = raster + (rows - row - 1) * cols;
@@ -830,11 +1412,7 @@ convertTiffRaster(uint32 *        const raster,
                        TIFFGetB(tiffPixel) * maxval / 255);
             alpharow[col] = TIFFGetA(tiffPixel) * maxval / 255 ;
         }
-        
-        if (imageoutFile != NULL) 
-            pnm_writepnmrow(imageoutFile, xelrow, cols, maxval, format, 0);
-        if (alphaFile != NULL) 
-            pgm_writepgmrow(alphaFile, alpharow, cols, maxval, 0);
+        pnmOut_writeRow(pnmOutP, cols, xelrow, alpharow);
     }
     
     pgm_freerow(alpharow);
@@ -847,45 +1425,54 @@ enum convertDisp {CONV_DONE, CONV_OOM, CONV_UNABLE, CONV_FAILED,
                   CONV_NOTATTEMPTED};
 
 static void
-convertRasterInMemory(FILE *         const imageoutFile, 
-                      FILE *         const alphaFile,
-                      unsigned int   const cols, 
-                      unsigned int   const rows,
+convertRasterInMemory(pnmOut *       const pnmOutP,
                       xelval         const maxval,
-                      int            const format, 
                       TIFF *         const tif,
                       unsigned short const photomet, 
                       unsigned short const planarconfig,
                       unsigned short const bps,
                       unsigned short const spp,
                       unsigned short const fillorder,
-                      xel                  colormap[],
+                      xel            const colormap[],
+                      bool           const verbose,
                       enum convertDisp * const statusP) {
 /*----------------------------------------------------------------------------
-   With the TIFF header all processed (and relevant information from it in 
-   our arguments), write out the TIFF raster to the file images *imageoutFile
-   and *alphaFile.
+   With the TIFF header all processed (and relevant information from
+   it in our arguments), write out the TIFF raster to the file images
+   *imageoutFileP and *alphaFileP.
 
    Do this by reading the entire TIFF image into memory at once and formatting
    it with the TIFF library's TIFFRGBAImageGet().
 
+   'cols' and 'rows' are the dimensions of the actual image, not of the
+   TIFF raster matrix; ('tif' knows the TIFF raster matrix dimensions).
+
    Return *statusP == CONV_OOM iff we are unable to proceed because we cannot
    get memory to store the entire raster.  This means Caller may still be able
    to do the conversion using a row-by-row strategy.  Like typical Netpbm
    programs, we simply abort the program if we are unable to allocate
    memory for other things.
 -----------------------------------------------------------------------------*/
+    unsigned int cols, rows;  /* Dimensions of output image */
+
+    if (verbose)
+        pm_message("Converting in memory ...");
+
+    warnBrokenTiffLibrary(tif);
+
+    getTiffDimensions(tif, &cols, &rows);
+
     if (rows == 0 || cols == 0) 
         *statusP = CONV_DONE;
     else {
-        char emsg[1024] ;
+        char emsg[1024];
         int ok;
         ok = TIFFRGBAImageOK(tif, emsg);
         if (!ok) {
             pm_message(emsg);
             *statusP = CONV_UNABLE;
         } else {
-            uint32* raster ;
+            uint32 * raster;
 
             /* Note that TIFFRGBAImageGet() converts any bits per sample
                to 8.  Maxval of the raster it returns is always 255.
@@ -897,10 +1484,10 @@ convertRasterInMemory(FILE *         const imageoutFile,
                 *statusP = CONV_OOM;
             } else {
                 int const stopOnErrorFalse = FALSE;
-                TIFFRGBAImage img ;
+                TIFFRGBAImage img;
                 int ok;
                 
-                ok = TIFFRGBAImageBegin(&img, tif, stopOnErrorFalse, emsg) ;
+                ok = TIFFRGBAImageBegin(&img, tif, stopOnErrorFalse, emsg);
                 if (!ok) {
                     pm_message(emsg);
                     *statusP = CONV_FAILED;
@@ -913,8 +1500,7 @@ convertRasterInMemory(FILE *         const imageoutFile,
                         *statusP = CONV_FAILED;
                     } else {
                         *statusP = CONV_DONE;
-                        convertTiffRaster(raster, cols, rows, maxval, format, 
-                                          imageoutFile, alphaFile);
+                        convertTiffRaster(raster, cols, rows, maxval, pnmOutP);
                     }
                 } 
                 free(raster);
@@ -926,62 +1512,86 @@ convertRasterInMemory(FILE *         const imageoutFile,
 
 
 static void
+convertRaster(pnmOut *           const pnmOutP,
+              TIFF *             const tifP,
+              struct tiffDirInfo const tiffDir,
+              xelval             const maxval,
+              unsigned short     const fillorder,
+              const xel *        const colormap,
+              bool               const byrow,
+              bool               const flipOk,
+              bool               const noflipOk,
+              bool               const verbose) {
+
+    enum convertDisp status;
+    if (byrow || !flipOk)
+        status = CONV_NOTATTEMPTED;
+    else {
+        convertRasterInMemory(
+            pnmOutP, maxval,
+            tifP, tiffDir.photomet, tiffDir.planarconfig, 
+            tiffDir.bps, tiffDir.spp, fillorder,
+            colormap, verbose, &status);
+    }
+    if (status == CONV_DONE) {
+        if (tiffDir.bps > 8)
+            pm_message("actual resolution has been reduced to 24 bits "
+                       "per pixel in the conversion.  You can get the "
+                       "full %u bits that are in the TIFF with the "
+                       "-byrow option.", tiffDir.bps);
+    } else {
+        if (status != CONV_NOTATTEMPTED) {
+            pm_message("In-memory conversion failed; "
+                       "using more primitive row-by-row conversion.");
+
+            if (!noflipOk)
+                pm_error("TIFF raster is in nonstandard orientation, "
+                         "and we already committed to in-memory "
+                         "conversion.  To avoid this failure, "
+                         "use -byrow .");
+        }            
+        convertRasterByRows(
+            pnmOutP, tiffDir.width, tiffDir.height, maxval,
+            tifP, tiffDir.photomet, tiffDir.planarconfig,
+            tiffDir.bps, tiffDir.spp, fillorder, colormap, verbose);
+    }
+}
+
+
+
+static void
 convertImage(TIFF *             const tifP,
-             FILE *             const alphaFile, 
-             FILE *             const imageoutFile,
+             FILE *             const alphaFileP,
+             FILE *             const imageoutFileP,
              struct cmdlineInfo const cmdline) {
 
-    unsigned int cols, rows;
+    struct tiffDirInfo tiffDir;
     int format;
     xelval maxval;
-    unsigned short bps, spp;
-
     xel colormap[MAXCOLORS];
-    unsigned short photomet, planarconfig, fillorderTag;
     unsigned short fillorder;
+    bool flipOk, noflipOk;
+    pnmOut pnmOut;
 
-    read_directory(tifP, &bps, &spp, &photomet, &planarconfig, &fillorderTag,
-                   &cols, &rows, 
-                   cmdline.headerdump);
-
+    readDirectory(tifP, cmdline.headerdump, &tiffDir);
 
-    computeFillorder(fillorderTag, &fillorder, cmdline.respectfillorder);
+    computeFillorder(tiffDir.fillorder, &fillorder, cmdline.respectfillorder);
 
-    analyzeImageType(tifP, bps, spp, photomet, 
+    analyzeImageType(tifP, tiffDir.bps, tiffDir.spp, tiffDir.photomet, 
                      &maxval, &format, colormap, cmdline.headerdump, cmdline);
 
-    if (imageoutFile != NULL) 
-        pnm_writepnminit( imageoutFile, 
-                          cols, rows, (xelval) maxval, format, 0 );
-    if (alphaFile != NULL) 
-        pgm_writepgminit( alphaFile, cols, rows, (gray) maxval, 0 );
+    pnmOut_init(imageoutFileP, alphaFileP, tiffDir.width, tiffDir.height,
+                tiffDir.orientation, maxval, format, maxval,
+                cmdline.byrow, cmdline.orientraw,
+                cmdline.verbose,
+                &flipOk, &noflipOk,
+                &pnmOut);
 
-    {
-        enum convertDisp status;
-        if (cmdline.byrow)
-            status = CONV_NOTATTEMPTED;
-        else {
-            convertRasterInMemory(
-                imageoutFile, alphaFile, cols, rows, maxval, format, 
-                tifP, photomet, planarconfig, bps, spp, fillorder,
-                colormap, &status);
-        }
-        if (status == CONV_DONE) {
-            if (bps > 8)
-                pm_message("actual resolution has been reduced to 24 bits "
-                           "per pixel in the conversion.  You can get the "
-                           "full %u bits that are in the TIFF with the "
-                           "-byrow option.", bps);
-        } else {
-            if (status != CONV_NOTATTEMPTED)
-                pm_message("In-memory conversion failed; "
-                           "using more primitive row-by-row conversion.");
-            
-            convertRasterByRows(
-                imageoutFile, alphaFile, cols, rows, maxval, format, 
-                tifP, photomet, planarconfig, bps, spp, fillorder, colormap);
-        }
-    }
+    convertRaster(&pnmOut, tifP, tiffDir, maxval,
+                  fillorder, colormap, cmdline.byrow, flipOk, noflipOk,
+                  cmdline.verbose);
+
+    pnmOut_term(&pnmOut, cmdline.verbose);
 }
 
 
@@ -1013,18 +1623,18 @@ convertIt(TIFF *             const tifP,
 
 
 int
-main(int argc, char * argv[]) {
+main(int argc, const char * argv[]) {
 
     struct cmdlineInfo cmdline;
     TIFF * tif;
     FILE * alphaFile;
     FILE * imageoutFile;
 
-    pnm_init( &argc, argv );
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
-    if (!STREQ(cmdline.inputFilename, "-")) {
+    if (!streq(cmdline.inputFilename, "-")) {
         tif = TIFFOpen(cmdline.inputFilename, "r");
         if (tif == NULL)
             pm_error("error opening TIFF file %s", cmdline.inputFilename);
@@ -1056,7 +1666,6 @@ main(int argc, char * argv[]) {
     strfree(cmdline.inputFilename);
 
     /* If the program failed, it previously aborted with nonzero completion
-       code, via various function calls.
-    */
+       code, via various function calls.  */
     return 0;
 }
diff --git a/converter/other/x11wd.h b/converter/other/x11wd.h
index 711248f5..7161260b 100644
--- a/converter/other/x11wd.h
+++ b/converter/other/x11wd.h
@@ -27,7 +27,7 @@ enum visualclass {StaticGray=0,GrayScale=1,StaticColor=2,PseudoColor=3,
 #define DirectColor 5
 */
 
-typedef uint32n xwdval;
+typedef uint32_t xwdval;
 #define XWDVAL_MAX ((xwdval)(-1))
 #define X11WD_FILE_VERSION 7
 typedef struct {
@@ -67,13 +67,13 @@ typedef struct {
         */
     xwdval window_width;    /* Window width */
     xwdval window_height;   /* Window height */
-    int32n window_x;        /* Window upper left X coordinate */
-    int32n window_y;        /* Window upper left Y coordinate */
+    int32_t window_x;        /* Window upper left X coordinate */
+    int32_t window_y;        /* Window upper left Y coordinate */
     xwdval window_bdrwidth; /* Window border width */
     } X11WDFileHeader;
 
 typedef struct {
-    uint32n num;
+    uint32_t num;
     unsigned short red, green, blue;
     char flags;         /* do_red, do_green, do_blue */
     char pad;
diff --git a/converter/other/xwdtopnm.c b/converter/other/xwdtopnm.c
index 284d0204..2cf1d39b 100644
--- a/converter/other/xwdtopnm.c
+++ b/converter/other/xwdtopnm.c
@@ -14,6 +14,12 @@
 
    The file X11/XWDFile.h from the X Window System is an authority for the
    format of an XWD file.  Netpbm uses its own declaration, though.
+
+   It has been a real challenge trying to reverse engineer the XWD
+   format.  This program is almost always broken as people find XWD images
+   with which it does not work and we update the program in response.
+
+   We consider an XWD file correct if Xwud displays it properly.
 */
 
 
@@ -32,14 +38,22 @@
 #include "x10wd.h"
 #include "x11wd.h"
 
+struct compMask {
+    unsigned long red;
+    unsigned long grn;
+    unsigned long blu;
+};
+
+
 struct cmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    char *input_filename;
+    const char * inputFilename;
     unsigned int verbose;
     unsigned int debug;
     unsigned int headerdump;
+    unsigned int cmapdump;
 };
 
 
@@ -70,23 +84,6 @@ zero_bits(const unsigned long mask) {
 
 
 
-static int
-one_bits(const unsigned long input) {
-/*----------------------------------------------------------------------------
-   Return the number of one bits in the binary representation of 'input'.
------------------------------------------------------------------------------*/
-    int one_bits;
-    unsigned long mask;
-
-    one_bits = 0;   /* initial value */
-    for (mask = 0x00000001; mask != 0x00000000; mask <<= 1)
-        if (input & mask) one_bits++;
-
-    return(one_bits);
-}
-
-
-
 static void
 parseCommandLine(int argc, char ** argv,
                  struct cmdlineInfo * const cmdlineP) {
@@ -110,6 +107,7 @@ parseCommandLine(int argc, char ** argv,
     OPTENT3(0,   "verbose",    OPT_FLAG,   NULL, &cmdlineP->verbose,       0);
     OPTENT3(0,   "debug",      OPT_FLAG,   NULL, &cmdlineP->debug,         0);
     OPTENT3(0,   "headerdump", OPT_FLAG,   NULL, &cmdlineP->headerdump,    0);
+    OPTENT3(0,   "cmapdump",   OPT_FLAG,   NULL, &cmdlineP->cmapdump,      0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
@@ -119,12 +117,12 @@ parseCommandLine(int argc, char ** argv,
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (argc - 1 == 0)
-        cmdlineP->input_filename = NULL;  /* he wants stdin */
+        cmdlineP->inputFilename = NULL;  /* he wants stdin */
     else if (argc - 1 == 1) {
-        if (STREQ(argv[1], "-"))
-            cmdlineP->input_filename = NULL;  /* he wants stdin */
+        if (streq(argv[1], "-"))
+            cmdlineP->inputFilename = NULL;  /* he wants stdin */
         else 
-            cmdlineP->input_filename = strdup(argv[1]);
+            cmdlineP->inputFilename = strdup(argv[1]);
     } else 
         pm_error("Too many arguments.  The only argument accepted\n"
                  "is the input file specification");
@@ -137,16 +135,14 @@ processX10Header(X10WDFileHeader *  const h10P,
                  FILE *             const file,
                  int *              const colsP, 
                  int *              const rowsP, 
-                 int *              const padrightP, 
+                 unsigned int *     const padrightP, 
                  xelval *           const maxvalP, 
                  enum visualclass * const visualclassP, 
                  int *              const formatP, 
                  xel **             const colorsP, 
                  int *              const bits_per_pixelP, 
                  int *              const bits_per_itemP, 
-                 unsigned long *    const red_maskP, 
-                 unsigned long *    const green_maskP, 
-                 unsigned long *    const blue_maskP,
+                 struct compMask *  const compMaskP,
                  enum byteorder *   const byte_orderP,
                  enum byteorder *   const bit_orderP) {
 
@@ -155,9 +151,7 @@ processX10Header(X10WDFileHeader *  const h10P,
     bool grayscale;
     bool byte_swap;
 
-    *maxvalP = 65535;   /* Initial assumption */
-
-    if ( h10P->file_version != X10WD_FILE_VERSION ) {
+    if (h10P->file_version != X10WD_FILE_VERSION) {
         byte_swap = TRUE;
         h10P->header_size     = pm_bs_long(h10P->header_size);
         h10P->file_version    = pm_bs_long(h10P->file_version);
@@ -175,51 +169,53 @@ processX10Header(X10WDFileHeader *  const h10P,
     } else
         byte_swap = FALSE;
 
-    for ( i = 0; i < h10P->header_size - sizeof(*h10P); ++i )
-        if ( getc( file ) == EOF )
-            pm_error( "couldn't read rest of X10 XWD file header" );
+    for (i = 0; i < h10P->header_size - sizeof(*h10P); ++i)
+        if (getc(file) == EOF)
+            pm_error("couldn't read rest of X10 XWD file header");
 
     /* Check whether we can handle this dump. */
-    if ( h10P->window_ncolors > 256 )
-        pm_error( "can't handle X10 window_ncolors > %d", 256 );
-    if ( h10P->pixmap_format != ZFormat && h10P->display_planes != 1 )
-        pm_error(
-            "can't handle X10 pixmap_format %d with planes != 1",
-            h10P->pixmap_format );
+    if (h10P->window_ncolors > 256)
+        pm_error("can't handle X10 window_ncolors > %d", 256);
+    if (h10P->pixmap_format != ZFormat && h10P->display_planes != 1)
+        pm_error("can't handle X10 pixmap_format %d with planes != 1",
+                 h10P->pixmap_format);
 
     grayscale = TRUE;  /* initial assumption */
-    if ( h10P->window_ncolors != 0 ) {
+    if (h10P->window_ncolors != 0) {
         /* Read X10 colormap. */
-        MALLOCARRAY( x10colors, h10P->window_ncolors );
-        if ( x10colors == NULL )
-            pm_error( "out of memory" );
-        for ( i = 0; i < h10P->window_ncolors; ++i ) {
-            if ( fread( &x10colors[i], sizeof(X10Color), 1, file ) != 1 )
-                pm_error( "couldn't read X10 XWD colormap" );
-            if ( byte_swap ) {
+        MALLOCARRAY(x10colors, h10P->window_ncolors);
+        if (x10colors == NULL)
+            pm_error("out of memory");
+        for (i = 0; i < h10P->window_ncolors; ++i) {
+            size_t bytesRead;
+            bytesRead = fread(&x10colors[i], sizeof(X10Color), 1, file);
+            if (bytesRead != 1)
+                pm_error("couldn't read X10 XWD colormap");
+            if (byte_swap) {
                 x10colors[i].red   = pm_bs_short(x10colors[i].red);
                 x10colors[i].green = pm_bs_short(x10colors[i].green);
                 x10colors[i].blue  = pm_bs_short(x10colors[i].blue);
             }
-            if ( x10colors[i].red != x10colors[i].green ||
-                 x10colors[i].green != x10colors[i].blue )
+            if (x10colors[i].red != x10colors[i].green ||
+                x10colors[i].green != x10colors[i].blue)
                 grayscale = FALSE;
         }
     }
 
-    if ( h10P->display_planes == 1 ) {
+    if (h10P->display_planes == 1) {
         *formatP = PBM_TYPE;
         *visualclassP = StaticGray;
         *maxvalP = 1;
-        *colorsP = pnm_allocrow( 2 );
-        PNM_ASSIGN1( (*colorsP)[0], 0 );
-        PNM_ASSIGN1( (*colorsP)[1], *maxvalP );
+        *colorsP = pnm_allocrow(2);
+        PNM_ASSIGN1((*colorsP)[0], 0);
+        PNM_ASSIGN1((*colorsP)[1], *maxvalP);
         *padrightP =
             (((h10P->pixmap_width + 15) / 16) * 16 - h10P->pixmap_width) * 8;
         *bits_per_itemP = 16;
         *bits_per_pixelP = 1;
-    } else if ( h10P->window_ncolors == 0 ) { 
+    } else if (h10P->window_ncolors == 0) { 
         /* Must be grayscale. */
+        unsigned int i;
         *formatP = PGM_TYPE;
         *visualclassP = StaticGray;
         if (h10P->display_planes > sizeof(*maxvalP) * 8 - 1)
@@ -231,23 +227,27 @@ processX10Header(X10WDFileHeader *  const h10P,
             pm_error("XWD header says display_planes = %u, which is too "
                      "large for maximum maxval of %u",
                      h10P->display_planes, PNM_OVERALLMAXVAL);
-        *colorsP = pnm_allocrow( *maxvalP + 1 );
-        for ( i = 0; i <= *maxvalP; ++i )
-            PNM_ASSIGN1( (*colorsP)[i], i );
+        *colorsP = pnm_allocrow(*maxvalP + 1);
+        for (i = 0; i <= *maxvalP; ++i)
+            PNM_ASSIGN1((*colorsP)[i], i);
         *padrightP =
             (((h10P->pixmap_width + 15) / 16) * 16 - h10P->pixmap_width) * 8;
         *bits_per_itemP = 16;
         *bits_per_pixelP = 1;
     } else {
-        *colorsP = pnm_allocrow( h10P->window_ncolors );
+        *maxvalP = 65535;
+
+        *colorsP = pnm_allocrow(h10P->window_ncolors);
         *visualclassP = PseudoColor;
-        if ( grayscale ) {
+        if (grayscale) {
+            unsigned int i;
             *formatP = PGM_TYPE;
-            for ( i = 0; i < h10P->window_ncolors; ++i )
-                PNM_ASSIGN1( (*colorsP)[i], x10colors[i].red );
+            for (i = 0; i < h10P->window_ncolors; ++i)
+                PNM_ASSIGN1((*colorsP)[i], x10colors[i].red);
         } else {
+            unsigned int i;
             *formatP = PPM_TYPE;
-            for ( i = 0; i < h10P->window_ncolors; ++i )
+            for (i = 0; i < h10P->window_ncolors; ++i)
                 PPM_ASSIGN(
                     (*colorsP)[i], x10colors[i].red, x10colors[i].green,
                     x10colors[i].blue);
@@ -308,36 +308,46 @@ fixH11ByteOrder(X11WDFileHeader *  const h11P,
 
 
 static void
-readX11Colormap(FILE *      const file,
-                int         const ncolors, 
-                bool        const byteSwap,
-                X11XColor** const x11colorsP) {
+dumpX11Cmap(unsigned int       const nColors,
+            const X11XColor *  const x11colors) {
+
+    unsigned int i;
+    for (i = 0; i < nColors; ++i)
+        pm_message("Color %u r/g/b = %u/%u/%u", i, 
+                   x11colors[i].red, x11colors[i].green, 
+                   x11colors[i].blue);
+}
+
+
+
+static void
+readX11Colormap(FILE *       const file,
+                unsigned int const nColors, 
+                bool         const byteSwap,
+                bool         const cmapDump,
+                X11XColor**  const x11colorsP) {
                 
     X11XColor * x11colors;
     int rc;
 
     /* Read X11 colormap. */
-    MALLOCARRAY(x11colors, ncolors);
+    MALLOCARRAY(x11colors, nColors);
     if (x11colors == NULL)
         pm_error("out of memory");
-    rc = fread(x11colors, sizeof(x11colors[0]), ncolors, file);
-    if (rc != ncolors)
+    rc = fread(x11colors, sizeof(x11colors[0]), nColors, file);
+    if (rc != nColors)
         pm_error("couldn't read X11 XWD colormap");
     if (byteSwap) {
         unsigned int i;
-        for (i = 0; i < ncolors; ++i) {
+        for (i = 0; i < nColors; ++i) {
             x11colors[i].red   = pm_bs_short(x11colors[i].red);
             x11colors[i].green = pm_bs_short(x11colors[i].green);
             x11colors[i].blue  = pm_bs_short(x11colors[i].blue);
         }
     }
-    if (debug) {
-        unsigned int i;
-        for (i = 0; i < ncolors && i < 8; ++i)
-            pm_message("Color %d r/g/b = %d/%d/%d", i, 
-                       x11colors[i].red, x11colors[i].green, 
-                       x11colors[i].blue);
-    }
+    if (cmapDump)
+        dumpX11Cmap(nColors, x11colors);
+
     *x11colorsP = x11colors;
 }
 
@@ -456,9 +466,7 @@ reverseBits(unsigned long arg,
 
 static void
 computeComponentMasks(X11WDFileHeader * const h11P,
-                      unsigned long *   const redMaskP,
-                      unsigned long *   const grnMaskP,
-                      unsigned long *   const bluMaskP) {
+                      struct compMask * const compMaskP) {
 /*----------------------------------------------------------------------------
    You'd think the component (red, green, blue) masks in the header
    would just be right.  But we've seen a direct color image which has
@@ -470,33 +478,57 @@ computeComponentMasks(X11WDFileHeader * const h11P,
 -----------------------------------------------------------------------------*/
     if (h11P->visual_class == DirectColor &&
         h11P->bits_per_pixel == 24 && h11P->bitmap_bit_order == LSBFirst) {
-        *redMaskP = reverseBits(h11P->red_mask, 24);
-        *grnMaskP = reverseBits(h11P->green_mask, 24);
-        *bluMaskP = reverseBits(h11P->blue_mask, 24);
+        compMaskP->red = reverseBits(h11P->red_mask, 24);
+        compMaskP->grn = reverseBits(h11P->green_mask, 24);
+        compMaskP->blu = reverseBits(h11P->blue_mask, 24);
     } else {
-        *redMaskP = h11P->red_mask;
-        *grnMaskP = h11P->green_mask;
-        *bluMaskP = h11P->blue_mask;
+        compMaskP->red = h11P->red_mask;
+        compMaskP->grn = h11P->green_mask;
+        compMaskP->blu = h11P->blue_mask;
     }
 }
 
 
+/* About TrueColor maxval:
+
+   The X11 spec says that in TrueColor, you use the bits in the raster for a
+   particular color component of a particular pixel to index the server's
+   colormap for that component, which contains 'bits_per_rgb' significant bits
+   of intensity information.  'bits_per_rgb' is in the XWD header, and in
+   practice is normally 8 or 16, usually 8.
+
+   We don't have the server's colormap, so we assume the most ordinary
+   one, a linear-as-possible distribution over the indices.
+
+   That means the maxval is that implied by 'bits_per_rgb' bits and we get
+   the proper sample value by scaling the value from the raster to that
+   maxval.
+
+   We (mostly Julian Bradfield <jcb@inf.ed.ac.uk>) figured this out in Netpbm
+   10.46 (March 2009).  Between ca. 2000 and 10.46, we instead assumed the
+   value in the XWD raster to be the exact brightness value, and chose a
+   maxval that would best allow us to represent that exact value for all
+   three components (e.g. if the XWD had 5 bits for blue, 5 for red, and
+   6 for red, we'd use maxval 31*63=1953).  Before that, the maxval was
+   31 if bits per pixel was 16 and 255 otherwise.
+*/
+
+
 
 static void
 processX11Header(X11WDFileHeader *  const h11P, 
-                 FILE *             const file,
+                 FILE *             const fileP,
+                 bool               const cmapDump,
                  int *              const colsP, 
                  int *              const rowsP, 
-                 int *              const padrightP, 
+                 unsigned int *     const padrightP, 
                  xelval *           const maxvalP, 
                  enum visualclass * const visualclassP, 
                  int *              const formatP, 
                  xel **             const colorsP, 
                  int *              const bits_per_pixelP, 
                  int *              const bits_per_itemP, 
-                 unsigned long *    const red_maskP, 
-                 unsigned long *    const green_maskP, 
-                 unsigned long *    const blue_maskP,
+                 struct compMask *  const compMaskP,
                  enum byteorder *   const byte_orderP,
                  enum byteorder *   const bit_orderP) {
 
@@ -512,38 +544,36 @@ processX11Header(X11WDFileHeader *  const h11P,
         pm_message("Header is different endianness from this machine.");
     
     for (i = 0; i < h11FixedP->header_size - sizeof(*h11FixedP); ++i)
-        if (getc(file) == EOF)
+        if (getc(fileP) == EOF)
             pm_error("couldn't read rest of X11 XWD file header");
 
     /* Check whether we can handle this dump. */
-    if ( h11FixedP->pixmap_depth > 24 )
-        pm_error( "can't handle X11 pixmap_depth > 24" );
-    if ( h11FixedP->bits_per_rgb > 24 )
-        pm_error( "can't handle X11 bits_per_rgb > 24" );
-    if ( h11FixedP->pixmap_format != ZPixmap && h11FixedP->pixmap_depth != 1 )
-        pm_error(
-            "can't handle X11 pixmap_format %d with depth != 1",
-            h11FixedP->pixmap_format );
-    if ( h11FixedP->bitmap_unit != 8 && h11FixedP->bitmap_unit != 16 &&
-         h11FixedP->bitmap_unit != 32 )
-        pm_error(
-            "X11 bitmap_unit (%d) is non-standard - can't handle",
-            h11FixedP->bitmap_unit );
+    if (h11FixedP->pixmap_depth > 24)
+        pm_error( "can't handle X11 pixmap_depth > 24");
+    if (h11FixedP->bits_per_rgb > 24)
+        pm_error("can't handle X11 bits_per_rgb > 24");
+    if (h11FixedP->pixmap_format != ZPixmap && h11FixedP->pixmap_depth != 1)
+        pm_error("can't handle X11 pixmap_format %d with depth != 1",
+                 h11FixedP->pixmap_format);
+    if (h11FixedP->bitmap_unit != 8 && h11FixedP->bitmap_unit != 16 &&
+        h11FixedP->bitmap_unit != 32)
+        pm_error("X11 bitmap_unit (%d) is non-standard - can't handle",
+                 h11FixedP->bitmap_unit);
     /* The following check was added in 10.19 (November 2003) */
-    if ( h11FixedP->bitmap_pad != 8 && h11FixedP->bitmap_pad != 16 &&
-         h11FixedP->bitmap_pad != 32 )
-        pm_error(
-            "X11 bitmap_pad (%d) is non-standard - can't handle",
-            h11FixedP->bitmap_unit );
-
-    if ( h11FixedP->ncolors > 0 ) {
-        readX11Colormap( file, h11FixedP->ncolors, byte_swap, &x11colors );
-        grayscale = colormapAllGray( x11colors, h11FixedP->ncolors );
+    if (h11FixedP->bitmap_pad != 8 && h11FixedP->bitmap_pad != 16 &&
+        h11FixedP->bitmap_pad != 32)
+        pm_error("X11 bitmap_pad (%d) is non-standard - can't handle",
+                 h11FixedP->bitmap_unit);
+
+    if (h11FixedP->ncolors > 0) {
+        readX11Colormap(fileP, h11FixedP->ncolors, byte_swap, cmapDump,
+                        &x11colors);
+        grayscale = colormapAllGray(x11colors, h11FixedP->ncolors);
     } else
         grayscale = TRUE;
 
     *visualclassP = (enum visualclass) h11FixedP->visual_class;
-    if ( *visualclassP == DirectColor ) {
+    if (*visualclassP == DirectColor) {
         unsigned int i;
         *formatP = PPM_TYPE;
         *maxvalP = 65535;
@@ -553,27 +583,23 @@ processX11Header(X11WDFileHeader *  const h11P,
           is composed of 3 separate indices.
         */
 
-        *colorsP = pnm_allocrow( h11FixedP->ncolors );
-        for ( i = 0; i < h11FixedP->ncolors; ++i )
+        *colorsP = pnm_allocrow(h11FixedP->ncolors);
+        for (i = 0; i < h11FixedP->ncolors; ++i)
             PPM_ASSIGN(
                 (*colorsP)[i], x11colors[i].red, x11colors[i].green,
                 x11colors[i].blue);
-    } else if ( *visualclassP == TrueColor ) {
+    } else if (*visualclassP == TrueColor) {
         *formatP = PPM_TYPE;
 
-        *maxvalP = pm_lcm(pm_bitstomaxval(one_bits(h11FixedP->red_mask)),
-                          pm_bitstomaxval(one_bits(h11FixedP->green_mask)),
-                          pm_bitstomaxval(one_bits(h11FixedP->blue_mask)),
-                          PPM_OVERALLMAXVAL
-            );
-    }
-    else if ( *visualclassP == StaticGray && h11FixedP->bits_per_pixel == 1 ) {
+        /* See discussion above about this maxval */
+        *maxvalP = pm_bitstomaxval(h11FixedP->bits_per_rgb);
+    } else if (*visualclassP == StaticGray && h11FixedP->bits_per_pixel == 1) {
         *formatP = PBM_TYPE;
         *maxvalP = 1;
         *colorsP = pnm_allocrow( 2 );
-        PNM_ASSIGN1( (*colorsP)[0], *maxvalP );
-        PNM_ASSIGN1( (*colorsP)[1], 0 );
-    } else if ( *visualclassP == StaticGray ) {
+        PNM_ASSIGN1((*colorsP)[0], *maxvalP);
+        PNM_ASSIGN1((*colorsP)[1], 0);
+    } else if (*visualclassP == StaticGray) {
         unsigned int i;
         *formatP = PGM_TYPE;
         if (h11FixedP->bits_per_pixel > sizeof(*maxvalP) * 8 - 1)
@@ -585,20 +611,20 @@ processX11Header(X11WDFileHeader *  const h11P,
             pm_error("XWD header says bits_per_pixel = %u, which is too "
                      "large for maximum maxval of %u",
                      h11FixedP->bits_per_pixel, PNM_OVERALLMAXVAL);
-        *colorsP = pnm_allocrow( *maxvalP + 1 );
-        for ( i = 0; i <= *maxvalP; ++i )
-            PNM_ASSIGN1( (*colorsP)[i], i );
+        *colorsP = pnm_allocrow(*maxvalP + 1);
+        for (i = 0; i <= *maxvalP; ++i)
+            PNM_ASSIGN1((*colorsP)[i], i);
     } else {
-        *colorsP = pnm_allocrow( h11FixedP->ncolors );
-        if ( grayscale ) {
+        *colorsP = pnm_allocrow(h11FixedP->ncolors);
+        if (grayscale) {
             unsigned int i;
             *formatP = PGM_TYPE;
-            for ( i = 0; i < h11FixedP->ncolors; ++i )
-                PNM_ASSIGN1( (*colorsP)[i], x11colors[i].red );
+            for (i = 0; i < h11FixedP->ncolors; ++i)
+                PNM_ASSIGN1((*colorsP)[i], x11colors[i].red);
         } else {
             unsigned int i;
             *formatP = PPM_TYPE;
-            for ( i = 0; i < h11FixedP->ncolors; ++i )
+            for (i = 0; i < h11FixedP->ncolors; ++i)
                 PPM_ASSIGN(
                     (*colorsP)[i], x11colors[i].red, x11colors[i].green,
                     x11colors[i].blue);
@@ -611,11 +637,12 @@ processX11Header(X11WDFileHeader *  const h11P,
     *padrightP =
         h11FixedP->bytes_per_line * 8 -
         h11FixedP->pixmap_width * h11FixedP->bits_per_pixel;
+
     /* According to X11/XWDFile.h, the item size is 'bitmap_pad' for some
        images and 'bitmap_unit' for others.  This is strange, so there may
        be some subtlety of their definitions that we're missing.
 
-       See comments in getpix() about what an item is.
+       See comments in pixelReader_getpix() about what an item is.
 
        Ben Kelley in January 2002 had a 32 bits-per-pixel xwd file
        from a truecolor 32 bit window on a Hummingbird Exceed X server
@@ -625,25 +652,25 @@ processX11Header(X11WDFileHeader *  const h11P,
        bit-per-pixel direct color window that had bitmap_unit = 32 and
        bitmap_pad = 8.  This was made by Xwd in Red Hat Xfree86 4.3.0-2.
 
-       In March 2007, Darren Frith present an xwd file like this:
+       In March 2007, Darren Frith presented an xwd file like this:
        Header says direct color, bits_per_pixel = 24, bitmap_unit =
        32, bitmap_pad = 8, byte order and bit order LSB first.  The
        bytes in each item are in fact MSB first and the pixels spread
        across the items MSB first.  The raster is consecutive 24 bit
        pixel units, but each row is padded on the right with enough
        bits to make the total line size 32 x width.  Really strange.
-       Xwud, ImageMagick, and Gimp render this image
-       correctly, so it's not broken.
+       The header says the bits within each pixel are one byte red,
+       one byte green, one byte blue.  But they are actually blue,
+       green, red.  Xwud, ImageMagick, and Gimp render this image
+       correctly, so it's not broken.  Created by Xwd of X.org 7.1.1.
 
        Before Netpbm 9.23 (January 2002), we used bitmap_unit as the
        item size always.  Then, until 10.19 (November 2003), we used
        bitmap_pad when pixmap_depth > 1 and pixmap_format == ZPixmap.
        We still don't see any logic in these fields at all, but we
        figure whichever one is greater (assuming both are meaningful)
-       has to be the item size.  
-    */
-    *bits_per_itemP = MAX(h11FixedP->bitmap_pad, h11FixedP->bitmap_unit);
-
+       has to be the item size.  */
+    *bits_per_itemP  = MAX(h11FixedP->bitmap_pad, h11FixedP->bitmap_unit);
     *bits_per_pixelP = h11FixedP->bits_per_pixel;
 
     if (*visualclassP == DirectColor) {
@@ -656,7 +683,7 @@ processX11Header(X11WDFileHeader *  const h11P,
         *byte_orderP = (enum byteorder) h11FixedP->byte_order;
         *bit_orderP  = (enum byteorder) h11FixedP->bitmap_bit_order;
     }
-    computeComponentMasks(h11FixedP, red_maskP, green_maskP, blue_maskP);
+    computeComponentMasks(h11FixedP, compMaskP);
 
     free(h11FixedP);
 } 
@@ -667,29 +694,28 @@ static void
 getinit(FILE *             const ifP, 
         int *              const colsP, 
         int *              const rowsP, 
-        int *              const padrightP, 
+        unsigned int *     const padrightP, 
         xelval *           const maxvalP, 
         enum visualclass * const visualclassP, 
         int *              const formatP, 
         xel **             const colorsP,
         int *              const bits_per_pixelP, 
         int *              const bits_per_itemP, 
-        unsigned long *    const red_maskP, 
-        unsigned long *    const green_maskP,
-        unsigned long *    const blue_maskP,
+        struct compMask *  const compMaskP,
         enum byteorder *   const byte_orderP,
         enum byteorder *   const bit_orderP,
-        bool               const headerDump) {
+        bool               const headerDump,
+        bool               const cmapDump) {
 /*----------------------------------------------------------------------------
    Read the header from the XWD image in input stream 'ifP'.  Leave
    the stream positioned to the beginning of the raster.
 
    Return various fields from the header.
 
-   Return as *padrightP the number of additional pixels of padding are
+   Return as *padrightP the number of additional bits of padding are
    at the end of each line of input.  This says the input stream
-   contains *colsP pixels of image data plus *padrightP pixels of
-   padding.
+   contains *colsP pixels of image data (at *bits_per_pixelP bits each)
+   plus *padrightP bits of padding.
 -----------------------------------------------------------------------------*/
     /* Assume X11 headers are larger than X10 ones. */
     unsigned char header[sizeof(X11WDFileHeader)];
@@ -723,8 +749,7 @@ getinit(FILE *             const ifP,
         processX10Header(h10P, ifP, colsP, rowsP, padrightP, maxvalP, 
                          visualclassP, formatP, 
                          colorsP, bits_per_pixelP, bits_per_itemP, 
-                         red_maskP, green_maskP, blue_maskP, 
-                         byte_orderP, bit_orderP);
+                         compMaskP, byte_orderP, bit_orderP);
     } else if (h11P->file_version == X11WD_FILE_VERSION ||
                pm_bs_long(h11P->file_version) == X11WD_FILE_VERSION) {
         
@@ -742,11 +767,11 @@ getinit(FILE *             const ifP,
         if (headerDump)
             dumpX11Header(h11P);
 
-        processX11Header(h11P, ifP, colsP, rowsP, padrightP, maxvalP, 
+        processX11Header(h11P, ifP, cmapDump,
+                         colsP, rowsP, padrightP, maxvalP, 
                          visualclassP, formatP, 
                          colorsP, bits_per_pixelP, bits_per_itemP, 
-                         red_maskP, green_maskP, blue_maskP, 
-                         byte_orderP, bit_orderP);
+                         compMaskP, byte_orderP, bit_orderP);
     } else
         pm_error("unknown XWD file version: %u", h11P->file_version);
 }
@@ -799,13 +824,10 @@ getinit(FILE *             const ifP,
    one pixel at a time from it.
 
    It consists of a structure of type 'pixelReader' and the
-   getpix() and pixelReaderInit() subroutines.
+   pixelReader_*() subroutines.
 -----------------------------------------------------------------------------*/
 
 typedef struct {
-    /* This structure contains the state of the getpix() reader as it
-       reads across a row in the input image.
-       */
     FILE * fileP;
     unsigned long itemBuffer;
         /* The item buffer.  This contains what's left of the item
@@ -850,12 +872,12 @@ typedef struct {
 
 
 static void
-pixelReaderInit(pixelReader *  const pixelReaderP,
-                FILE *         const fileP,
-                int            const bitsPerPixel,
-                int            const bitsPerItem, 
-                enum byteorder const byteOrder,
-                enum byteorder const bitOrder) {
+pixelReader_init(pixelReader *  const pixelReaderP,
+                 FILE *         const fileP,
+                 int            const bitsPerPixel,
+                 int            const bitsPerItem, 
+                 enum byteorder const byteOrder,
+                 enum byteorder const bitOrder) {
     
     pixelReaderP->fileP           = fileP;
     pixelReaderP->bitsPerPixel    = bitsPerPixel;
@@ -869,6 +891,33 @@ pixelReaderInit(pixelReader *  const pixelReaderP,
 
 
 static void
+pixelReader_term(pixelReader * const pixelReaderP) {
+
+    unsigned int remainingByteCount;
+
+    if (pixelReaderP->nBitsLeft > 0)
+        pm_message("Warning: %u unused bits left in the pixel reader "
+                   "buffer after full image converted.  XWD file may be "
+                   "corrupted or Xwdtopnm may have misinterpreted it",
+                   pixelReaderP->nBitsLeft);
+
+
+    pm_drain(pixelReaderP->fileP, 4096, &remainingByteCount);
+
+    if (remainingByteCount >= 4096)
+        pm_message("Warning: at least 4K additional bytes in XWD input stream "
+                   "after full image converted.  XWD file may be corrupted "
+                   "or Xwdtopnm may have misinterpreted it.");
+    else if (remainingByteCount > 0)
+        pm_message("Warning: %u additional bytes in XWD input stream "
+                   "after full image converted.  XWD file may be corrupted "
+                   "or Xwdtopnm may have misinterpreted it.",
+                   remainingByteCount);
+}
+
+
+
+static void
 readItem(pixelReader * const rdrP) {
 /*----------------------------------------------------------------------------
    Read one item from the XWD raster associated with pixel reader *rdrP.
@@ -940,7 +989,6 @@ static unsigned long const lsbmask[] = {
 };
 
 
-
 static unsigned long
 pixelReader_getbits(pixelReader * const rdrP,
                     unsigned int  const nBits) {
@@ -1005,7 +1053,7 @@ pixelReader_getbits(pixelReader * const rdrP,
 
 
 static unsigned long
-getpix(pixelReader * const rdrP) {
+pixelReader_getpix(pixelReader * const rdrP) {
 /*----------------------------------------------------------------------------
    Get a pixel from the input image.
 
@@ -1056,15 +1104,13 @@ getpix(pixelReader * const rdrP) {
 static void
 reportInfo(int              const cols, 
            int              const rows, 
-           int              const padright, 
+           unsigned int     const padright, 
            xelval           const maxval, 
            enum visualclass const visualclass,
            int              const format, 
            int              const bits_per_pixel,
            int              const bits_per_item, 
-           int              const red_mask, 
-           int              const green_mask, 
-           int              const blue_mask,
+           struct compMask  const compMask,
            enum byteorder   const byte_order, 
            enum byteorder   const bit_order) {
     
@@ -1092,15 +1138,37 @@ reportInfo(int              const cols,
     }
     pm_message("%d rows of %d columns with maxval %d",
                rows, cols, maxval);
-    pm_message("padright=%d.  visualclass = %s.  format=%d (%c%c)",
+    pm_message("padright=%u bits.  visualclass = %s.  format=%d (%c%c)",
                padright, visualclass_name, 
                format, format/256, format%256);
     pm_message("bits_per_pixel=%d; bits_per_item=%d",
                bits_per_pixel, bits_per_item);
     pm_message("byte_order=%s; bit_order=%s",
                byte_order_name, bit_order_name);
-    pm_message("red_mask=0x%.8x; green_mask=0x%.8x; blue_mask=0x%.8x",
-               red_mask, green_mask, blue_mask);
+    pm_message("component mask: red=0x%.8lx; grn=0x%.8lx; blu=0x%.8lx",
+               compMask.red, compMask.grn, compMask.blu);
+}
+
+
+
+static void
+warn16Bit(xelval const maxval) {
+/*----------------------------------------------------------------------------
+   This program is often used by users of X, and those users often use
+   'xv', which doesn't properly interpret PNM files with 16 bit samples.
+   Furthermore, the maxval is often much larger than the user assumes
+   because of PNM's need to use the same maxval for all color components,
+   while XWD often uses different resolution for each.
+
+   Users get really frustrated when Xv displays something other than the
+   original mimage, almost always assuming that means Xwdtopnm converted
+   incorrectly.
+-----------------------------------------------------------------------------*/
+
+    if (pm_maxvaltobits(maxval) > 8)
+        pm_message("WARNING: Producing maxval %u output.  This involves "
+                   "multiple bytes per sample, which some programs, e.g. "
+                   "'xv', can't handle.  See manual.", maxval);
 }
 
 
@@ -1113,36 +1181,34 @@ convertRowSimpleIndex(pixelReader *  const pixelReaderP,
     
     unsigned int col;
     for (col = 0; col < cols; ++col)
-        xelrow[col] = colors[getpix(pixelReaderP)];
+        xelrow[col] = colors[pixelReader_getpix(pixelReaderP)];
 }
 
 
 
 static void
-convertRowDirect(pixelReader *  const pixelReaderP,
-                 int            const cols,
-                 const xel *    const colors,
-                 unsigned long  const red_mask,
-                 unsigned long  const grn_mask,
-                 unsigned long  const blu_mask,
-                 xel *          const xelrow) {
+convertRowDirect(pixelReader *   const pixelReaderP,
+                 int             const cols,
+                 const xel *     const colors,
+                 struct compMask const compMask,
+                 xel *           const xelrow) {
         
     unsigned int col;
 
     for (col = 0; col < cols; ++col) {
         unsigned long pixel;
             /* This is a triplet of indices into the color map, packed
-               into this bit string according to red_mask, etc.
+               into this bit string according to compMask
             */
         unsigned int red_index, grn_index, blu_index;
             /* These are indices into the color map, unpacked from 'pixel'.
              */
             
-        pixel = getpix(pixelReaderP);
+        pixel = pixelReader_getpix(pixelReaderP);
 
-        red_index = (pixel & red_mask) >> zero_bits(red_mask);
-        grn_index = (pixel & grn_mask) >> zero_bits(grn_mask); 
-        blu_index = (pixel & blu_mask) >> zero_bits(blu_mask);
+        red_index = (pixel & compMask.red) >> zero_bits(compMask.red);
+        grn_index = (pixel & compMask.grn) >> zero_bits(compMask.grn); 
+        blu_index = (pixel & compMask.blu) >> zero_bits(compMask.blu);
 
         PPM_ASSIGN(xelrow[col],
                    PPM_GETR(colors[red_index]),
@@ -1155,39 +1221,37 @@ convertRowDirect(pixelReader *  const pixelReaderP,
 
 
 static void
-convertRowTrueColor(pixelReader *  const pixelReaderP,
-                    int                  const cols,
-                    pixval               const maxval,
-                    const xel *          const colors,
-                    unsigned long        const red_mask,
-                    unsigned long        const grn_mask,
-                    unsigned long        const blu_mask,
-                    xel *                const xelrow) {
+convertRowTrueColor(pixelReader *   const pixelReaderP,
+                    int             const cols,
+                    pixval          const maxval,
+                    const xel *     const colors,
+                    struct compMask const compMask,
+                    xel *           const xelrow) {
 
     unsigned int col;
     unsigned int red_shift, grn_shift, blu_shift;
     unsigned int red_maxval, grn_maxval, blu_maxval;
 
-    red_shift = zero_bits(red_mask);
-    grn_shift = zero_bits(grn_mask);
-    blu_shift = zero_bits(blu_mask);
+    red_shift = zero_bits(compMask.red);
+    grn_shift = zero_bits(compMask.grn);
+    blu_shift = zero_bits(compMask.blu);
 
-    red_maxval = red_mask >> red_shift;
-    grn_maxval = grn_mask >> grn_shift;
-    blu_maxval = blu_mask >> blu_shift;
+    red_maxval = compMask.red >> red_shift;
+    grn_maxval = compMask.grn >> grn_shift;
+    blu_maxval = compMask.blu >> blu_shift;
 
     for (col = 0; col < cols; ++col) {
         unsigned long pixel;
 
-        pixel = getpix(pixelReaderP);
+        pixel = pixelReader_getpix(pixelReaderP);
 
         /* The parsing of 'pixel' used to be done with hardcoded layout
            parameters.  See comments at end of this file.
         */
         PPM_ASSIGN(xelrow[col],
-                   ((pixel & red_mask) >> red_shift) * maxval / red_maxval,
-                   ((pixel & grn_mask) >> grn_shift) * maxval / grn_maxval,
-                   ((pixel & blu_mask) >> blu_shift) * maxval / blu_maxval
+                   ((pixel & compMask.red) >> red_shift) * maxval / red_maxval,
+                   ((pixel & compMask.grn) >> grn_shift) * maxval / grn_maxval,
+                   ((pixel & compMask.blu) >> blu_shift) * maxval / blu_maxval
             );
 
     }
@@ -1198,13 +1262,11 @@ convertRowTrueColor(pixelReader *  const pixelReaderP,
 static void
 convertRow(pixelReader *    const pixelReaderP,
            FILE *           const ofP,
-           int              const padright, 
+           unsigned int     const padright, 
            int              const cols, 
            xelval           const maxval,
            int              const format, 
-           unsigned long    const red_mask, 
-           unsigned long    const green_mask, 
-           unsigned long    const blue_mask, 
+           struct compMask  const compMask,
            const xel*       const colors, 
            enum visualclass const visualclass) {
 /*----------------------------------------------------------------------------
@@ -1214,7 +1276,7 @@ convertRow(pixelReader *    const pixelReaderP,
    The row is 'cols' pixels.
 
    After reading the 'cols' pixels, we read and discard an additional
-   'padright' pixels from the input stream, so as to read the entire
+   'padright' bits from the input stream, so as to read the entire
    input line.
 -----------------------------------------------------------------------------*/
     xel* xelrow;
@@ -1228,22 +1290,19 @@ convertRow(pixelReader *    const pixelReaderP,
         convertRowSimpleIndex(pixelReaderP, cols, colors, xelrow);
         break;
     case DirectColor: 
-        convertRowDirect(pixelReaderP, cols, colors,
-                         red_mask, green_mask, blue_mask,
-                         xelrow);
+        convertRowDirect(pixelReaderP, cols, colors, compMask, xelrow);
         
         break;
     case TrueColor: 
         convertRowTrueColor(pixelReaderP, cols, maxval, colors,
-                            red_mask, green_mask, blue_mask,
-                            xelrow);
+                            compMask, xelrow);
         break;
             
     default:
         pm_error("unknown visual class");
     }
     pixelReader_getbits(pixelReaderP, padright);
-
+    
     pnm_writepnmrow(ofP, xelrow, cols, maxval, format, 0);
     pnm_freerow(xelrow);
 }
@@ -1278,15 +1337,17 @@ main(int argc, char *argv[]) {
 
     struct cmdlineInfo cmdline;
     FILE * ifP;
-    int rows, cols, format, padright;
+    int rows, cols, format;
+    unsigned int padright;
+        /* Number of bits of padding on the right of each row */
     unsigned int row;
-    int bits_per_pixel;
-    int bits_per_item;
-    unsigned long red_mask, green_mask, blue_mask;
+    int bitsPerPixel;
+    int bitsPerItem;
+    struct compMask compMask;
     xelval maxval;
     enum visualclass visualclass;
-    enum byteorder byte_order, bit_order;
-    xel *colors;  /* the color map */
+    enum byteorder byteOrder, bitOrder;
+    xel * colors;  /* the color map */
     pixelReader pixelReader;
 
     pnm_init(&argc, argv);
@@ -1296,24 +1357,25 @@ main(int argc, char *argv[]) {
     debug = cmdline.debug;
     verbose = cmdline.verbose;
 
-    if (cmdline.input_filename != NULL) 
-        ifP = pm_openr(cmdline.input_filename);
+    if (cmdline.inputFilename != NULL) 
+        ifP = pm_openr(cmdline.inputFilename);
     else
         ifP = stdin;
 
     getinit(ifP, &cols, &rows, &padright, &maxval, &visualclass, &format, 
-            &colors, &bits_per_pixel, &bits_per_item, 
-            &red_mask, &green_mask, &blue_mask, &byte_order, &bit_order,
-            cmdline.headerdump);
+            &colors, &bitsPerPixel, &bitsPerItem, 
+            &compMask, &byteOrder, &bitOrder,
+            cmdline.headerdump, cmdline.cmapdump);
+
+    warn16Bit(maxval);
     
     if (verbose) 
         reportInfo(cols, rows, padright, maxval, visualclass,
-                   format, bits_per_pixel, bits_per_item,
-                   red_mask, green_mask, blue_mask, 
-                   byte_order, bit_order);
+                   format, bitsPerPixel, bitsPerItem, compMask,
+                   byteOrder, bitOrder);
 
-    pixelReaderInit(&pixelReader, ifP, bits_per_pixel, bits_per_item,
-                    byte_order, bit_order);
+    pixelReader_init(&pixelReader, ifP, bitsPerPixel, bitsPerItem,
+                     byteOrder, bitOrder);
 
     pnm_writepnminit(stdout, cols, rows, maxval, format, 0);
 
@@ -1321,9 +1383,11 @@ main(int argc, char *argv[]) {
 
     for (row = 0; row < rows; ++row) {
         convertRow(&pixelReader, stdout,
-                   padright, cols, maxval, format,
-                   red_mask, green_mask, blue_mask, colors, visualclass);
+                   padright, cols, maxval, format, compMask,
+                   colors, visualclass);
     }
+
+    pixelReader_term(&pixelReader);
     
     pm_close(ifP);
     pm_close(stdout);
@@ -1332,6 +1396,7 @@ main(int argc, char *argv[]) {
 }
 
 
+
 /*
    This used to be the way we parsed a direct/true color pixel.  I'm 
    keeping it here in case we find out some application needs it this way.
diff --git a/converter/pbm/Makefile b/converter/pbm/Makefile
index 318b8e9e..c859b105 100644
--- a/converter/pbm/Makefile
+++ b/converter/pbm/Makefile
@@ -5,7 +5,7 @@ endif
 SUBDIR = converter/pbm
 VPATH=.:$(SRCDIR)/$(SUBDIR)
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
 PORTBINARIES =	atktopbm brushtopbm cmuwmtopbm ddbugtopbm g3topbm escp2topbm \
 		icontopbm macptopbm mdatopbm mgrtopbm mrftopbm \
@@ -17,7 +17,7 @@ PORTBINARIES =	atktopbm brushtopbm cmuwmtopbm ddbugtopbm g3topbm escp2topbm \
 		pbmtomacp pbmtomatrixorbital pbmtomda pbmtomgr pbmtomrf \
 		pbmtonokia \
 		pbmtopi3 pbmtoplot pbmtopsg3 pbmtoptx pbmtowbmp \
-		pbmtox10bm pbmtoxbm pbmtoybm pbmtozinc \
+		pbmtoxbm pbmtoybm pbmtozinc \
 		pi3topbm pktopbm \
 		wbmptopbm xbmtopbm ybmtopbm	
 
@@ -29,7 +29,7 @@ endif
 #in libm?
 MATHBINARIES =	pbmtopk
 BINARIES =	$(PORTBINARIES) $(MATHBINARIES)
-SCRIPTS =
+SCRIPTS =       pbmtox10bm
 
 OBJECTS = $(BINARIES:%=%.o)
 
@@ -41,7 +41,7 @@ SUBDIRS=pbmtoppa
 .PHONY: all
 all: $(BINARIES) $(SUBDIRS:%=%/all)
 
-include $(SRCDIR)/Makefile.common
+include $(SRCDIR)/common.mk
 
 ifneq ($(LEX)x,x)
 thinkjettopbm.c1:%.c1:%.l
diff --git a/converter/pbm/atktopbm.c b/converter/pbm/atktopbm.c
index c4a81808..62664999 100644
--- a/converter/pbm/atktopbm.c
+++ b/converter/pbm/atktopbm.c
@@ -273,7 +273,7 @@ ReadATKRaster(FILE * const file,
 
     /* read the keyword */
     fscanf(file, " %5s", keyword);
-    if (!STREQ(keyword, "bits"))
+    if (!streq(keyword, "bits"))
       pm_error ("keyword is not 'bits'!");
 
     fscanf(file, " %d %d %d ", &objectid, &width, &height);
diff --git a/converter/pbm/cmuwmtopbm.c b/converter/pbm/cmuwmtopbm.c
index 53c64aaa..eabff40c 100644
--- a/converter/pbm/cmuwmtopbm.c
+++ b/converter/pbm/cmuwmtopbm.c
@@ -1,4 +1,4 @@
-/* cmuwmtopbm.c - read a CMU window manager bitmap and produce a portable bitmap
+/* cmuwmtopbm.c - read a CMU window manager bitmap and produce a PBM image.
 **
 ** Copyright (C) 1989 by Jef Poskanzer.
 **
@@ -10,105 +10,106 @@
 ** implied warranty.
 */
 
+/* 2006.10 (afu)
+   Changed bitrow from plain to raw, read function from getc() to fread(),
+   write function from pbm_writepbmrow() to pbm_writepbmrow_packed().
+   Retired bitwise transformation functions.
+
+   This program does not check the pad bits at the end of each row.
+*/
+
+
 #include "pbm.h"
 #include "cmuwm.h"
 
-static void getinit ARGS(( FILE* file, int* colsP, int* rowsP, short* depthP, int* padrightP ));
-static bit getbit ARGS(( FILE* file ));
 
-int
-main( argc, argv )
-    int argc;
-    char* argv[];
-    {
-    FILE* ifp;
-    bit* bitrow;
-    register bit* bP;
-    int rows, cols, padright, row, col;
-    short depth;
 
+static void
+readCmuwmHeader(FILE *         const ifP,
+                unsigned int * const colsP,
+                unsigned int * const rowsP,
+                unsigned int * const depthP) {
 
-    pbm_init( &argc, argv );
+    const char * const initReadError =
+        "CMU window manager header EOF / read error";
 
-    if ( argc > 2 )
-	pm_usage( "[cmuwmfile]" );
+    long l;
+    short s;
+    int rc;
+
+    rc = pm_readbiglong(ifP, &l);
+    if (rc == -1 )
+        pm_error(initReadError);
+    if ((uint32_t)l != CMUWM_MAGIC)
+        pm_error("bad magic number in CMU window manager file");
+    rc = pm_readbiglong(ifP, &l);
+    if (rc == -1)
+        pm_error(initReadError);
+    *colsP = l;
+    rc = pm_readbiglong(ifP, &l);
+    if (rc == -1 )
+        pm_error(initReadError);
+    *rowsP = l;
+    rc = pm_readbigshort(ifP, &s);
+    if (rc == -1)
+        pm_error(initReadError);
+    *depthP = s;
+}
 
-    if ( argc == 2 )
-	ifp = pm_openr( argv[1] );
-    else
-	ifp = stdin;
-
-    getinit( ifp, &cols, &rows, &depth, &padright );
-    if ( depth != 1 )
-	pm_error(
-	    "CMU window manager file has depth of %d, must be 1",
-	    (int) depth );
-
-    pbm_writepbminit( stdout, cols, rows, 0 );
-    bitrow = pbm_allocrow( cols );
-
-    for ( row = 0; row < rows; ++row )
-	{
-	/* Get data. */
-        for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
-	    *bP = getbit( ifp );
-	/* Discard line padding */
-        for ( col = 0; col < padright; ++col )
-	    (void) getbit( ifp );
-	pbm_writepbmrow( stdout, bitrow, cols, 0 );
-	}
-
-    pm_close( ifp );
-    pm_close( stdout );
-
-    exit( 0 );
-    }
 
-static int item, bitsperitem, bitshift;
 
-static void
-getinit( file, colsP, rowsP, depthP, padrightP )
-    FILE* file;
-    int* colsP;
-    int* rowsP;
-    short* depthP;
-    int* padrightP;
-    {
-    long l;
+int
+main(int     argc,
+     char * argv[]) {
 
-    if ( pm_readbiglong( file, &l ) == -1 )
-	pm_error( "EOF / read error" );
-    if ( (uint32_t)l != CMUWM_MAGIC )
-	pm_error( "bad magic number in CMU window manager file" );
-    if ( pm_readbiglong( file, &l ) == -1 )
-	pm_error( "EOF / read error" );
-    *colsP = (int) l;
-    if ( pm_readbiglong( file, &l ) == -1 )
-	pm_error( "EOF / read error" );
-    *rowsP = (int) l;
-    if ( pm_readbigshort( file, depthP ) == -1 )
-	pm_error( "EOF / read error" );
-    *padrightP = ( ( *colsP + 7 ) / 8 ) * 8 - *colsP;
-
-    bitsperitem = 0;
-    }
+    FILE * ifP;
+    unsigned char * bitrow;
+    unsigned int rows, cols, depth;
+    unsigned int row;
+
+    const char * inputFileName;
 
-static bit
-getbit( file )
-    FILE* file;
-    {
-    bit b;
-
-    if ( bitsperitem == 0 )
-	{
-	item = getc( file );
-	if ( item == EOF )
-	    pm_error( "EOF / read error" );
-	bitsperitem = 8;
-	bitshift = 7;
-	}
-    b = ( ( item >> bitshift) & 1 ) ? PBM_WHITE : PBM_BLACK;
-    --bitsperitem;
-    --bitshift;
-    return b;
+    pbm_init(&argc, argv);
+
+    if (argc-1 > 1)
+        pm_error("Too many arguments (%u).  "
+                 "Only argument is optional input file", argc-1);
+    if (argc-1 == 1)
+        inputFileName = argv[1];
+    else
+        inputFileName = "-";
+    
+    ifP = pm_openr(inputFileName);
+
+    readCmuwmHeader(ifP, &cols, &rows, &depth);
+    if (depth != 1)
+        pm_error("CMU window manager file has depth of %u, must be 1", depth);
+
+    pbm_writepbminit(stdout, cols, rows, 0);
+    bitrow = pbm_allocrow_packed(cols);
+
+    for (row = 0; row < rows; ++row) {
+        unsigned int const bytesPerRow = pbm_packed_bytes(cols);
+        unsigned int byteSeq;
+        size_t bytesRead;
+
+        bytesRead = fread(bitrow, 1, bytesPerRow, ifP);
+        if (bytesRead != bytesPerRow)
+            pm_error("CWU window manager bitmap EOF / read error");
+            
+        /* Invert all bits in row - raster formats are similar.
+           CMUWM Black:0 White:1  End of row padded with 1
+           PBM   Black:1 White:0  End preferably padded with 0
+        */
+   
+        for (byteSeq = 0; byteSeq < bytesPerRow; ++byteSeq)
+            bitrow[byteSeq] = ~bitrow[byteSeq];
+                
+        pbm_writepbmrow_packed(stdout, bitrow, cols, 0);
     }
+
+    pm_close(ifP);
+    pm_close(stdout);
+
+    return 0;
+}
diff --git a/converter/pbm/g3topbm.c b/converter/pbm/g3topbm.c
index 1eefee96..7bb95c92 100644
--- a/converter/pbm/g3topbm.c
+++ b/converter/pbm/g3topbm.c
@@ -18,12 +18,19 @@
   contributing their work to the public domain.
 ===========================================================================*/
 
+#define _BSD_SOURCE   /* Make nstring.h define strcaseeq() */
+
+#include "pm_c_util.h"
 #include "pbm.h"
 #include "shhopt.h"
 #include "nstring.h"
 #include "mallocvar.h"
 #include "g3.h"
 #include "bitreverse.h"
+#include "bitarith.h"
+
+#define LEFTBITS pm_byteLeftBits
+#define RIGHTBITS pm_byteRightBits
 
 #define MAXCOLS 10800
 #define MAXROWS 14400   /* this allows up to two pages of image */
@@ -100,15 +107,15 @@ parseCommandLine(int argc, char ** const argv,
         if (cmdlineP->expectedLineSize < 1)
             pm_error("-width must be at least 1");
     } else if (paper_sizeSpec) {
-        if (STRCASEEQ(paperSize, "A6"))
+        if (strcaseeq(paperSize, "A6"))
             cmdlineP->expectedLineSize = 864;
-        else if (STRCASEEQ(paperSize, "A5"))
+        else if (strcaseeq(paperSize, "A5"))
             cmdlineP->expectedLineSize = 1216;
-        else if (STRCASEEQ(paperSize, "A4"))
+        else if (strcaseeq(paperSize, "A4"))
             cmdlineP->expectedLineSize = 1728;
-        else if (STRCASEEQ(paperSize, "B4"))
+        else if (strcaseeq(paperSize, "B4"))
             cmdlineP->expectedLineSize = 2048;
-        else if (STRCASEEQ(paperSize, "A3"))
+        else if (strcaseeq(paperSize, "A3"))
             cmdlineP->expectedLineSize = 2432;
         else
             pm_error("Unrecognized value for -paper_size '%s'.  "
@@ -295,12 +302,12 @@ buildHashes(g3TableEntry * (*whashP)[HASHSIZE],
 
 
 static void
-makeRowWhite(bit *        const bitrow,
-             unsigned int const cols) {
+makeRowWhite(unsigned char * const packedBitrow,
+             unsigned int    const cols) {
 
-    unsigned int col;
-    for (col = 0; col < MAXCOLS; ++col)
-        bitrow[col] = PBM_WHITE;
+    unsigned int colByte;
+    for (colByte = 0; colByte < pbm_packed_bytes(cols); ++colByte)
+        packedBitrow[colByte] = PBM_WHITE * 0xff;
 }
 
 
@@ -333,16 +340,45 @@ g3code(unsigned int const curcode,
 
 
 
-enum g3tableId {TERMWHITE, TERMBLACK, MKUPWHITE, MKUPBLACK};
+static void
+writeBlackBitSpan(unsigned char * const packedBitrow,
+                  int             const cols,
+                  int             const offset) {
+/*----------------------------------------------------------------------------
+   Write black (="1") bits into packedBitrow[], starting at 'offset',
+   length 'cols'.
+-----------------------------------------------------------------------------*/
+    unsigned char * const dest = & packedBitrow[offset/8];
+    unsigned int const rs  = offset % 8;
+    unsigned int const trs = (cols + rs) % 8;
+    unsigned int const colBytes = pbm_packed_bytes(cols + rs);
+    unsigned int const last = colBytes - 1;
+
+    unsigned char const origHead = dest[0];
+    unsigned char const origEnd =  0x00;
+
+    unsigned int i;
 
+    for( i = 0; i < colBytes; ++i)
+        dest[i] = PBM_BLACK * 0xff;
 
+    if (rs > 0)
+        dest[0] = LEFTBITS(origHead, rs) | RIGHTBITS(dest[0], 8-rs);
+
+    if (trs > 0)
+        dest[last] = LEFTBITS(dest[last], trs) | RIGHTBITS(origEnd, 8-trs);
+}
+
+
+
+enum g3tableId {TERMWHITE, TERMBLACK, MKUPWHITE, MKUPBLACK};
 
 static void
-processG3Code(g3TableEntry * const teP,
-              bit *          const bitrow,
-              unsigned int * const colP,
-              bit *          const colorP,
-              unsigned int * const countP) {
+processG3Code(const g3TableEntry * const teP,
+              unsigned char *      const packedBitrow,
+              unsigned int *       const colP,
+              bit *                const colorP,
+              unsigned int *       const countP) {
               
     enum g3tableId const teId =
         (teP > mtable ? 2 : 0) + (teP - ttable) % 2;
@@ -366,14 +402,10 @@ processG3Code(g3TableEntry * const teP,
         runLengthSoFar = MIN(*countP + teCount, MAXCOLS - col);
 
         if (runLengthSoFar > 0) {
-            if (*colorP == PBM_WHITE) {
-                /* Row was initialized to white, so we just skip */
-                col += runLengthSoFar;
-            } else {
-                unsigned int i;
-                for (i = 0; i < runLengthSoFar; ++i)
-                    bitrow[col++] = PBM_BLACK;
-            }
+            if (*colorP == PBM_BLACK)
+                writeBlackBitSpan(packedBitrow, runLengthSoFar, col);
+            /* else : Row was initialized to white, so we just skip */
+            col += runLengthSoFar;
         }
         *colorP = !*colorP;
         *countP = 0;
@@ -399,7 +431,7 @@ formatBadCodeException(const char ** const exceptionP,
     asprintfN(exceptionP,
         "bad code word at Column %u.  "
         "No prefix of the %u bits 0x%x matches any recognized "
-        "code word and no code words longer than 12 bits are "
+        "code word and no code words longer than 13 bits are "
         "defined.  ",
         col, curlen, curcode);
 }
@@ -408,13 +440,13 @@ formatBadCodeException(const char ** const exceptionP,
 
 static void
 readFaxRow(struct bitStream * const bitStreamP,
-           bit *              const bitrow,
+           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 
-  bitrow[].  Return the length of the line, in pixels, as *lineLengthP.
+  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,
   advance the input stream past the next EOL mark, and put a text
@@ -438,11 +470,9 @@ readFaxRow(struct bitStream * const bitStreamP,
         /* Number of consecutive pixels of the same color */
     bit currentColor;
         /* The color of the current run of pixels */
-    g3TableEntry * te;
-        /* Address of structure that describes the current G3 code */
     bool done;
 
-    makeRowWhite(bitrow, MAXCOLS);  /* initialize row */
+    makeRowWhite(packedBitrow, MAXCOLS);  /* initialize row */
 
     col = 0;
     curlen = 0;
@@ -483,10 +513,14 @@ readFaxRow(struct bitStream * const bitStreamP,
                     formatBadCodeException(exceptionP, col, curlen, curcode);
                     done = TRUE;
                 } else if (curcode != 0) {
-                    te = g3code(curcode, curlen, currentColor);
-                    
-                    if (te) {
-                        processG3Code(te, bitrow, &col, &currentColor, &count);
+                    const g3TableEntry * const teP =
+                        g3code(curcode, curlen, currentColor);
+                        /* Address of structure that describes the 
+                           current G3 code
+                        */
+                    if (teP) {
+                        processG3Code(teP, packedBitrow,
+                                      &col, &currentColor, &count);
                         
                         curcode = 0;
                         curlen = 0;
@@ -504,9 +538,9 @@ readFaxRow(struct bitStream * const bitStreamP,
 
 
 static void
-freeBits(bit **       const bits,
-         unsigned int const rows,
-         bool         const stretched) {
+freeBits(unsigned char ** const packedBits,
+         unsigned int     const rows,
+         bool             const stretched) {
 
     unsigned int row;
 
@@ -516,9 +550,9 @@ freeBits(bit **       const bits,
                free it twice.
             */
         } else 
-            pbm_freerow(bits[row]);
+            pbm_freerow_packed(packedBits[row]);
     }
-    free(bits);
+    free(packedBits);
 }
 
 
@@ -623,8 +657,8 @@ analyzeLineSize(lineSizeAnalyzer * const analyzerP,
 
 
 /* An empty line means EOF.  An ancient comment in the code said there
-   is supposed to 6 EOL marks in a row to indicate EOF, but the code
-   checked for 3 and considered 2 in row just to mean a zero length
+   is supposed to be 6 EOL marks in a row to indicate EOF, but the code
+   checked for 3 and considered 2 in a row just to mean a zero length
    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
@@ -636,17 +670,17 @@ readFax(struct bitStream * const bitStreamP,
         bool               const stretch,
         unsigned int       const expectedLineSize,
         bool               const tolerateErrors,
-        bit ***            const bitsP,
+        unsigned char ***  const packedBitsP,
         unsigned int *     const colsP,
         unsigned int *     const rowsP) {
 
     lineSizeAnalyzer lineSizeAnalyzer;
-    bit ** bits;
+    unsigned char ** packedBits;
     const char * error;
     bool eof;
     unsigned int row;
     
-    MALLOCARRAY_NOFAIL(bits, MAXROWS);
+    MALLOCARRAY_NOFAIL(packedBits, MAXROWS);
 
     initializeLineSizeAnalyzer(&lineSizeAnalyzer,
                                expectedLineSize, tolerateErrors);
@@ -664,8 +698,9 @@ readFax(struct bitStream * const bitStreamP,
         else {
             const char * exception;
 
-            bits[row] = pbm_allocrow(MAXCOLS);
-            readFaxRow(bitStreamP, bits[row], &lineSize, &exception, &error);
+            packedBits[row] = pbm_allocrow_packed(MAXCOLS);
+            readFaxRow(bitStreamP, packedBits[row],
+                       &lineSize, &exception, &error);
 
             handleRowException(exception, error, row, tolerateErrors);
 
@@ -683,16 +718,16 @@ readFax(struct bitStream * const bitStreamP,
                                       "program can handle at most %u rows "
                                       "after stretching", MAXROWS);
                         else
-                            bits[row] = bits[row-1];
+                            packedBits[row] = packedBits[row-1];
                     }
                     ++row;
                 }
             }
         }
     }
-    *rowsP  = row;
-    *colsP  = lineSizeAnalyzer.maxLineSize;
-    *bitsP  = bits;
+    *rowsP        = row;
+    *colsP        = lineSizeAnalyzer.maxLineSize;
+    *packedBitsP  = packedBits;
 }
 
 
@@ -704,7 +739,8 @@ main(int argc, char * argv[]) {
     FILE * ifP;
     struct bitStream bitStream;
     unsigned int rows, cols;
-    bit ** bits;
+    unsigned char ** packedBits;
+    int row;
 
     pbm_init(&argc, argv);
 
@@ -726,14 +762,17 @@ main(int argc, char * argv[]) {
 
     readFax(&bitStream, cmdline.stretch, cmdline.expectedLineSize,
             !cmdline.stop_error, 
-            &bits, &cols, &rows);
+            &packedBits, &cols, &rows);
 
     pm_close(ifP);
 
-    pbm_writepbm(stdout, bits, cols, rows, 0);
+    pbm_writepbminit(stdout, cols, rows, 0);
+    for (row = 0; row < rows; ++row)
+        pbm_writepbmrow_packed(stdout, packedBits[row], cols, 0);
+
     pm_close(stdout);
 
-    freeBits(bits, rows, cmdline.stretch);
+    freeBits(packedBits, rows, cmdline.stretch);
 
     return 0;
 }
diff --git a/converter/pbm/icontopbm.c b/converter/pbm/icontopbm.c
index d6dba8ae..a0d1bd2b 100644
--- a/converter/pbm/icontopbm.c
+++ b/converter/pbm/icontopbm.c
@@ -46,35 +46,35 @@ ReadIconFile(FILE *                const file,
         }
         variable[i] = '\0';
 
-        if ( STREQ( variable, "*/" )&& gotsome )
+        if ( streq( variable, "*/" )&& gotsome )
             break;
 
         if ( fscanf( file, "%d", &value ) != 1 )
             continue;
 
-        if ( STREQ( variable, "Width" ) )
+        if ( streq( variable, "Width" ) )
         {
             *widthP = value;
             gotsome = 1;
         }
-        else if ( STREQ( variable, "Height" ) )
+        else if ( streq( variable, "Height" ) )
         {
             *heightP = value;
             gotsome = 1;
         }
-        else if ( STREQ( variable, "Depth" )  )
+        else if ( streq( variable, "Depth" )  )
         {
             if ( value != 1 )
                 pm_error( "invalid depth" );
             gotsome = 1;
         }
-        else if ( STREQ( variable, "Format_version" ) )
+        else if ( streq( variable, "Format_version" ) )
         {
             if ( value != 1 )
                 pm_error( "invalid Format_version" );
             gotsome = 1;
         }
-        else if ( STREQ( variable, "Valid_bits_per_item" ) )
+        else if ( streq( variable, "Valid_bits_per_item" ) )
         {
             if ( value != 16 )
                 pm_error( "invalid Valid_bits_per_item" );
diff --git a/converter/pbm/mgrtopbm.c b/converter/pbm/mgrtopbm.c
index cea4be48..9f7004a1 100644
--- a/converter/pbm/mgrtopbm.c
+++ b/converter/pbm/mgrtopbm.c
@@ -1,13 +1,10 @@
-/* mgrtopbm.c - read a MGR bitmap and produce a portable bitmap
-**
-** 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.
+/* mgrtopbm.c - read a MGR bitmap and produce a PBM image.
+
+   Copyright information is at end of file.
+
+   You can find MGR and some MGR format test images at
+   ftp://sunsite.unc.edu/pub/Linux/apps/MGR/!INDEX.html
+
 */
 
 #include <string.h>
@@ -17,29 +14,29 @@
 #include "mgr.h"
 
 
-static unsigned char item;
-static int bitsperitem, bitshift;
 
 static void
-getinit(FILE * const file, 
-        int *  const colsP, 
-        int *  const rowsP, 
-        int *  const depthP, 
-        int *  const padrightP,
-        int *  const bitsperitemP) {
-
+readMgrHeader(FILE *          const ifP, 
+              unsigned int *  const colsP, 
+              unsigned int *  const rowsP, 
+              unsigned int *  const depthP, 
+              unsigned int *  const padrightP ) {
+    
     struct b_header head;
-    int pad;
+    unsigned int pad;
+    size_t bytesRead;
 
-    if (fread(&head, sizeof(struct old_b_header), 1, file ) != 1)
+    bytesRead = fread(&head, sizeof(struct old_b_header), 1, ifP);
+    if (bytesRead != 1)
         pm_error("Unable to read 1st byte of file.  "
                  "fread() returns errno %d (%s)",
                  errno, strerror(errno));
     if (head.magic[0] == 'y' && head.magic[1] == 'z') { 
         /* new style bitmap */
-        if (fread(&head.depth, 
-                  sizeof(head) - sizeof(struct old_b_header), 1, file) 
-             != 1 )
+        size_t bytesRead;
+        bytesRead = fread(&head.depth, 
+                          sizeof(head) - sizeof(struct old_b_header), 1, ifP);
+        if (bytesRead != 1 )
             pm_error("Unable to read header after 1st byte.  "
                      "fread() returns errno %d (%s)",
                      errno, strerror(errno));
@@ -60,7 +57,7 @@ getinit(FILE * const file,
     } else {
         pm_error("bad magic chars in MGR file: '%c%c'",
                  head.magic[0], head.magic[1] );
-        pad = -1;  /* should never reach here */
+        pad = 0;  /* should never reach here */
     }
 
     if (head.h_wide < ' ' || head.l_wide < ' ')
@@ -71,75 +68,79 @@ getinit(FILE * const file,
     *colsP = (((int)head.h_wide - ' ') << 6) + ((int)head.l_wide - ' ');
     *rowsP = (((int)head.h_high - ' ') << 6) + ((int) head.l_high - ' ');
     *padrightP = ( ( *colsP + pad - 1 ) / pad ) * pad - *colsP;
-    
-    *bitsperitemP = 8;
 }
 
 
 
-static bit
-getbit( file )
-    FILE* file;
-    {
-    bit b;
-
-    if ( bitsperitem == 8 )
-	{
-	item = getc( file );
-	bitsperitem = 0;
-	bitshift = 7;
-	}
-    bitsperitem++;
-    b = ( ( item >> bitshift) & 1 ) ? PBM_BLACK : PBM_WHITE;
-    bitshift--;
-    return b;
-    }
-
+int
+main(int    argc,
+     char * argv[]) {
+
+    FILE * ifP;
+    unsigned char * bitrow;
+    unsigned int rows, cols, depth;
+    unsigned int padright;
+    unsigned int row;
+    unsigned int itemCount;
+    const char * inputFileName;
+
+    pbm_init(&argc, argv);
+
+    if (argc-1 > 1)
+        pm_error("Too many arguments (%u).  "
+                 "Only argument is optional input file", argc-1);
+    if (argc-1 == 1)
+        inputFileName = argv[1];
+    else
+        inputFileName = "-";
+    
+    ifP = pm_openr(inputFileName);
 
+    readMgrHeader(ifP, &cols, &rows, &depth, &padright);
+    if (depth != 1)
+        pm_error("MGR file has depth of %u, must be 1", depth);
 
-int
-main( argc, argv )
-    int argc;
-    char* argv[];
-    {
-    FILE* ifp;
-    bit* bitrow;
-    register bit* bP;
-    int rows, cols, depth, padright, row, col;
+    pbm_writepbminit(stdout, cols, rows, 0);
 
+    bitrow = pbm_allocrow_packed(cols + padright);
+    
+    itemCount = (cols + padright ) / 8;
+
+    for (row = 0; row < rows; ++row) {
+        /* The raster formats are nearly identical.
+           MGR may have rows padded to 16 or 32 bit boundaries.
+        */
+        size_t bytesRead;
+        bytesRead = fread(bitrow, 1, itemCount, ifP);
+        if (bytesRead < itemCount)
+            pm_error("fread() failed to read mgr bitmap data");
+
+        pbm_writepbmrow_packed(stdout, bitrow, cols, 0);
+    }
+    pm_close(ifP);
+    pm_close(stdout);
+    return 0;
+}
 
-    pbm_init( &argc, argv );
 
-    if ( argc > 2 )
-	pm_usage( "[mgrfile]" );
 
-    if ( argc == 2 )
-	ifp = pm_openr( argv[1] );
-    else
-	ifp = stdin;
-
-    getinit( ifp, &cols, &rows, &depth, &padright, &bitsperitem );
-    if ( depth != 1 )
-	pm_error( "MGR file has depth of %d, must be 1", depth );
-
-    pbm_writepbminit( stdout, cols, rows, 0 );
-    bitrow = pbm_allocrow( cols );
-
-    for ( row = 0; row < rows; row++ )
-	{
-	/* Get data, bit-reversed within each byte. */
-        for ( col = 0, bP = bitrow; col < cols; col++, bP++ )
-	    *bP = getbit( ifp );
-	/* Discard line padding */
-        for ( col = 0; col < padright; col ++ )
-	    (void) getbit( ifp );
-	pbm_writepbmrow( stdout, bitrow, cols, 0 );
-	}
-
-    pm_close( ifp );
-    pm_close( stdout );
-
-    exit( 0 );
-    }
+/* 2006.10 (afu)
+   Changed bitrow from plain to raw, read function from getc() to fread(),
+   write function from pbm_writepbmrow() to pbm_writepbmrow_packed().
+   Retired bitwise transformation functions.
+   
+   NOT tested for old-style format files.  Only one zz file in mgrsrc-0.69 .
+  
+*/
 
 
+/*
+** 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.
+*/
diff --git a/converter/pbm/mrftopbm.c b/converter/pbm/mrftopbm.c
index 9534f3a5..51281028 100644
--- a/converter/pbm/mrftopbm.c
+++ b/converter/pbm/mrftopbm.c
@@ -41,34 +41,50 @@ bit_input(FILE * const in) {
 
 
 static void 
-doSquare(FILE *          const in,
-          unsigned char * const image,
-          int             const ox,
-          int             const oy,
-          int             const w,
-          int             const size) {
-
-    if (size == 1 || bit_input(in)) { 
+doSquare(FILE *          const ifP,
+         unsigned char * const image,
+         unsigned int    const ulCol,
+         unsigned int    const ulRow,
+         unsigned int    const imageWidth,
+         unsigned int    const size) {
+/*----------------------------------------------------------------------------
+   Do a square of side 'size', whose upper left corner is at (ulCol, ulRow).
+   The contents of that square are next in file *ifP, in MRF format.
+
+   Return the pixel values of the square in the corresponding position of
+   image[], which is a concatenation of rows 'imageWidth' pixels wide, one
+   byte per pixel.
+-----------------------------------------------------------------------------*/
+    if (size == 1 || bit_input(ifP)) { 
         /* It's all black or all white.  Next bit says which. */
 
-        unsigned int const c = bit_input(in);
+        unsigned int const c = bit_input(ifP);
 
-        unsigned int y;
+        unsigned int rowOfSquare;
 
-        for (y = 0; y < size; ++y) {
-            unsigned int x;
-            for (x = 0; x < size; ++x)
-                image[(oy+y)*w+ox+x] = c;
+        for (rowOfSquare = 0; rowOfSquare < size; ++rowOfSquare) {
+            unsigned int colOfSquare;
+            for (colOfSquare = 0; colOfSquare < size; ++colOfSquare) {
+                unsigned int rowOfImage = ulRow + rowOfSquare;
+                unsigned int colOfImage = ulCol + colOfSquare;
+
+                image[rowOfImage * imageWidth + colOfImage] = c;
+            }
         }
     } else {
-        /* not all one color, so recurse. */
-
-        int halfsize = size >> 1;
-
-        doSquare(in, image, ox,          oy,          w, halfsize);
-        doSquare(in, image, ox+halfsize, oy,          w, halfsize);
-        doSquare(in, image, ox,          oy+halfsize, w, halfsize);
-        doSquare(in, image, ox+halfsize, oy+halfsize, w, halfsize);
+        /* Square is not all one color, so recurse.  Do each of the four
+           quadrants of this square individually.
+        */
+        unsigned int const quadSize = size/2;
+
+        doSquare(ifP, image, ulCol,            ulRow,
+                 imageWidth, quadSize);
+        doSquare(ifP, image, ulCol + quadSize, ulRow,
+                 imageWidth, quadSize);
+        doSquare(ifP, image, ulCol,            ulRow + quadSize,
+                 imageWidth, quadSize);
+        doSquare(ifP, image, ulCol + quadSize, ulRow + quadSize,
+                 imageWidth, quadSize);
     }
 }
 
@@ -80,7 +96,7 @@ writeOutput(FILE *                const ofP,
             int                   const rows,
             const unsigned char * const image) {
             
-    /* w64 is units-of-64-bits width, h64 same for height */
+    /* w64 is units-of-64-bits width */
     unsigned int const w64 = (cols+63)/64;
 
     bit * bitrow;
@@ -145,7 +161,7 @@ readMrfImage(FILE *           const ifP,
         pm_error("Ridiculously large, unprocessable image: %u cols x %u rows",
                  cols, rows);
 
-    image = calloc(w64*h64*64*64,1);
+    image = calloc(w64*h64*64*64, 1);
     if (image == NULL)
         pm_error("Unable to get memory for raster");
                  
@@ -179,7 +195,7 @@ main(int argc, char *argv[]) {
 
     expandAll = FALSE;  /* initial assumption */
 
-    if (argc-1 >= 1 && STREQ(argv[1], "-a")) {
+    if (argc-1 >= 1 && streq(argv[1], "-a")) {
         expandAll = TRUE;
         argc--,argv++;
     }
diff --git a/converter/pbm/pbmto4425.c b/converter/pbm/pbmto4425.c
index 605b12d5..1d97ac6a 100644
--- a/converter/pbm/pbmto4425.c
+++ b/converter/pbm/pbmto4425.c
@@ -145,7 +145,7 @@ main(int argc, char * argv[]) {
         pbmfile = argv[argn];
     }
 
-    if(STREQ(pbmfile, "-"))
+    if(streq(pbmfile, "-"))
     {
         pbmfp = stdin;
     }
diff --git a/converter/pbm/pbmtocmuwm.c b/converter/pbm/pbmtocmuwm.c
index 64d7af40..773d988b 100644
--- a/converter/pbm/pbmtocmuwm.c
+++ b/converter/pbm/pbmtocmuwm.c
@@ -1,4 +1,4 @@
-/* pbmtocmuwm.c - read a portable bitmap and produce a CMU window manager bitmap
+/* pbmtocmuwm.c - read a PBM image and produce a CMU window manager bitmap
 **
 ** Copyright (C) 1989 by Jef Poskanzer.
 **
@@ -10,108 +10,95 @@
 ** implied warranty.
 */
 
-#include "pbm.h"
-#include "cmuwm.h"
+/* 2006.10 (afu)
+   Changed bitrow from plain to raw, read function from pbm_readpbmrow() to
+   pbm_readpbmrow_packed(), write function from putc() to fwrite().
 
-static void putinit ARGS(( int rows, int cols ));
-static void putbit ARGS(( bit b ));
-static void putrest ARGS(( void ));
-static void putitem ARGS(( void ));
-
-int
-main( argc, argv )
-    int argc;
-    char* argv[];
-    {
-    FILE* ifp;
-    bit* bitrow;
-    register bit* bP;
-    int rows, cols, format, padright, row, col;
+   Retired bitwise transformation functions.
+*/
 
+#include "pbm.h"
+#include "cmuwm.h"
 
-    pbm_init( &argc, argv );
+static void
+putinit(unsigned int const rows,
+        unsigned int const cols) {
 
-    if ( argc > 2 )
-	pm_usage( "[pbmfile]" );
+    const char * const initWriteError =
+        "CMU window manager header write error";
 
-    if ( argc == 2 )
-	ifp = pm_openr( argv[1] );
-    else
-	ifp = stdin;
+    int rc;
 
-    pbm_readpbminit( ifp, &cols, &rows, &format );
-    bitrow = pbm_allocrow( cols );
-    
-    /* Round cols up to the nearest multiple of 8. */
-    padright = ( ( cols + 7 ) / 8 ) * 8 - cols;
+    rc = pm_writebiglong(stdout, CMUWM_MAGIC);
+    if (rc == -1)
+        pm_error(initWriteError);
+    rc = pm_writebiglong(stdout, cols);
+    if (rc == -1)
+        pm_error(initWriteError);
+    rc = pm_writebiglong(stdout, rows);
+    if (rc == -1)
+        pm_error(initWriteError);
+    rc = pm_writebigshort(stdout, (short) 1);
+    if (rc == -1)
+        pm_error(initWriteError);
+}
 
-    putinit( rows, cols );
-    for ( row = 0; row < rows; row++ )
-	{
-	pbm_readpbmrow( ifp, bitrow, cols, format );
-        for ( col = 0, bP = bitrow; col < cols; col++, bP++ )
-	    putbit( *bP );
-	for ( col = 0; col < padright; col++ )
-	    putbit( 0 );
-        }
 
-    pm_close( ifp );
 
-    putrest( );
+int
+main(int argc,
+     char * argv[]) {
 
-    exit( 0 );
-    }
+    FILE * ifP;
+    unsigned char * bitrow;
+    int rows, cols;
+    int format;
+    unsigned int row;
+    const char * inputFileName;
 
-static unsigned char item;
-static int bitsperitem, bitshift;
+    pbm_init(&argc, argv);
 
-static void
-putinit( rows, cols )
-    int rows, cols;
-    {
-    if ( pm_writebiglong( stdout, CMUWM_MAGIC ) == -1 )
-	pm_error( "write error" );
-    if ( pm_writebiglong( stdout, cols ) == -1 )
-	pm_error( "write error" );
-    if ( pm_writebiglong( stdout, rows ) == -1 )
-	pm_error( "write error" );
-    if ( pm_writebigshort( stdout, (short) 1 ) == -1 )
-	pm_error( "write error" );
-
-    item = 0;
-    bitsperitem = 0;
-    bitshift = 7;
-    }
+    if (argc-1 > 1)
+        pm_error("Too many arguments (%u).  "
+                 "Only argument is optional input file", argc-1);
+    if (argc-1 == 1)
+        inputFileName = argv[1];
+    else
+        inputFileName = "-";
+    
+    ifP = pm_openr(inputFileName);
 
-#if __STDC__
-static void
-putbit( bit b )
-#else /*__STDC__*/
-static void
-putbit( b )
-    bit b;
-#endif /*__STDC__*/
-    {
-    if ( bitsperitem == 8 )
-	putitem( );
-    if ( b == PBM_WHITE )
-	item += 1 << bitshift;
-    bitsperitem++;
-    bitshift--;
-    }
+    pbm_readpbminit(ifP, &cols, &rows, &format);
+    bitrow = pbm_allocrow_packed(cols);
 
-static void
-putrest( )
-    {
-    if ( bitsperitem > 0 )
-	putitem( );
+    putinit(rows, cols);
+    
+    /* Convert PBM raster data to CMUWM and write */ 
+    for (row = 0; row < rows; ++row) {
+        unsigned int const bytesPerRow = pbm_packed_bytes(cols);
+        unsigned char const padding = 
+            (cols % 8 == 0) ? 0x00 : ((unsigned char) ~0 >> (cols % 8));
+
+        unsigned int i;
+        size_t bytesWritten;
+
+        pbm_readpbmrow_packed(ifP, bitrow, cols, format);
+
+        /* Invert all bits in row - raster formats are similar.
+           PBM   Black:1 White:0  "Don't care" bits at end of row
+           CMUWM Black:0 White:1  End of row padded with 1
+        */
+
+        for (i = 0; i < bytesPerRow; ++i)
+            bitrow[i] = ~bitrow[i];
+
+        bitrow[bytesPerRow-1] |= padding;  /* Set row end pad bits */
+        
+        bytesWritten = fwrite(bitrow, 1, bytesPerRow, stdout);
+        if (bytesWritten != bytesPerRow)
+            pm_error("fwrite() failed to write CMU window manager bitmap");
     }
 
-static void
-putitem( )
-    {
-    (void) putc( item, stdout );
-    item = 0;
-    bitsperitem = 0;
-    bitshift = 7;
-    }
+    pm_close(ifP);
+    return 0;
+}
diff --git a/converter/pbm/pbmtodjvurle.c b/converter/pbm/pbmtodjvurle.c
index dbe96f31..83e99ec4 100644
--- a/converter/pbm/pbmtodjvurle.c
+++ b/converter/pbm/pbmtodjvurle.c
@@ -50,91 +50,97 @@ writebyte(FILE *        const ofP,
 
 /* Write a run length to the RLE file. */
 static void 
-write_rle (FILE *rlefile, uint32n tally)
-{
-  do {
-    /* Output a single run. */
-    if (tally < 192) {
-      /* Single-byte runs */
-      writebyte (rlefile, tally);
-      tally >>= 8;
+write_rle(FILE *   const rlefile,
+          uint32_t const tallyArg) {
+
+    uint32_t remainingTally;
+
+    remainingTally = tallyArg;  /* initial value */
+
+    do {
+        /* Output a single run. */
+        if (remainingTally < 192) {
+            /* Single-byte runs */
+            writebyte (rlefile, remainingTally);
+            remainingTally >>= 8;
+        }
+        else {
+            /* Two-byte runs */
+            writebyte (rlefile, ((remainingTally>>8) & 0x3F) + 0xC0);
+            writebyte (rlefile, remainingTally & 0xFF);
+            remainingTally >>= 14;
+        }
+
+        /* Very large runs need to be split into smaller runs.  We
+           therefore need to toggle back to the same color we had for the
+           previous smaller run.
+        */
+        if (remainingTally > 0)
+            writebyte (rlefile, 0);
     }
-    else {
-      /* Two-byte runs */
-      writebyte (rlefile, ((tally>>8)&0x3F) + 0xC0);
-      writebyte (rlefile, tally&0xFF);
-      tally >>= 14;
-    }
-
-    /* Very large runs need to be split into smaller runs.  We
-     * therefore need to toggle back to the same color we had for the
-     * previous smaller run. */
-    if (tally > 0)
-      writebyte (rlefile, 0);
-  }
-  while (tally > 0);
+    while (remainingTally > 0);
 }
 
 
 
 int 
-main (int argc, char *argv[])
-{
-  FILE * const rlefile = stdout;    /* Generated Bitonal RLE file */
-
-  FILE *pbmfile;             /* PBM file to convert */
-  int numcols, numrows;      /* Width and height in pixels of the PBM file */
-  int format;                /* Original image type before conversion to PBM */
-  bit *pbmrow;               /* One row of the PBM file */
-  uint32n pixeltally = 0;    /* Run length of the current color */
-  int row, col;              /* Row and column loop variables */
-  const char * pbmfilename;  /* Name of input file */
-
-  /* Parse the command line. */
-  pbm_init (&argc, argv);
-
-  if (argc-1 < 1)
-      pbmfilename = "-";
-  else if (argc-1 == 1)
-      pbmfilename = argv[1];
-  else
-      pm_error("Program takes at most 1 argument -- the input file name.  "
-               "You specified %d", argc-1);
-
-  pbmfile = pm_openr(pbmfilename);
-
-  /* Write an RLE header. */
-  pbm_readpbminit (pbmfile, &numcols, &numrows, &format);
-  fprintf (rlefile, "R4\n");
-  fprintf (rlefile, "%d %d\n", numcols, numrows);
-
-  /* Write the RLE data. */
-  pbmrow = pbm_allocrow (numcols);
-  for (row=0; row<numrows; row++) {
-    bit prevpixel;        /* Previous pixel seen */
-
-    pbm_readpbmrow (pbmfile, pbmrow, numcols, format);
-    prevpixel = PBM_WHITE;   /* Bitonal RLE rows always start with white */
-    pixeltally = 0;
-    for (col=0; col<numcols; col++) {
-      bit newpixel = pbmrow[col];      /* Current pixel color */
-
-      if (newpixel == prevpixel)
-        pixeltally++;
-      else {
-        write_rle (rlefile, pixeltally);
-        pixeltally = 1;
-        prevpixel = newpixel;
-      }
+main (int argc, const char * argv[]) {
+
+    FILE * const rlefile = stdout; /* Generated Bitonal RLE file */
+
+    FILE * pbmfile;          /* PBM file to convert */
+    int numcols, numrows;    /* Width and height in pixels of the PBM file */
+    int format;              /* Original image type before conversion to PBM */
+    bit * pbmrow;            /* One row of the PBM file */
+    unsigned int row;
+    const char * pbmfilename;  /* Name of input file */
+
+    pm_proginit(&argc, argv);
+
+    if (argc-1 < 1)
+        pbmfilename = "-";
+    else if (argc-1 == 1)
+        pbmfilename = argv[1];
+    else
+        pm_error("Program takes at most 1 argument -- the input file name.  "
+                 "You specified %d", argc-1);
+
+    pbmfile = pm_openr(pbmfilename);
+
+    /* Write an RLE header. */
+    pbm_readpbminit(pbmfile, &numcols, &numrows, &format);
+    fprintf(rlefile, "R4\n");
+    fprintf(rlefile, "%d %d\n", numcols, numrows);
+
+    /* Write the RLE data. */
+    pbmrow = pbm_allocrow(numcols);
+    for (row = 0; row < numrows; ++row) {
+        unsigned int col;
+        uint32_t pixeltally;   /* Run length of the current color */
+        bit prevpixel;         /* Previous pixel seen */
+
+        pbm_readpbmrow(pbmfile, pbmrow, numcols, format);
+        prevpixel = PBM_WHITE;   /* Bitonal RLE rows always start with white */
+        pixeltally = 0;
+        for (col = 0; col < numcols; ++col) {
+            bit newpixel = pbmrow[col];      /* Current pixel color */
+
+            if (newpixel == prevpixel)
+                ++pixeltally;
+            else {
+                write_rle(rlefile, pixeltally);
+                pixeltally = 1;
+                prevpixel = newpixel;
+            }
+        }
+        write_rle(rlefile, pixeltally);
     }
-    write_rle (rlefile, pixeltally);
-  }
-
-  /* Finish up cleanly. */
-  pbm_freerow (pbmrow);
-  if (rlefile != stdout)
-    pm_close (rlefile);
-  if (pbmfile != stdin)
-    pm_close (pbmfile);
-  exit (0);
+
+    pbm_freerow(pbmrow);
+    if (rlefile != stdout)
+        pm_close(rlefile);
+    if (pbmfile != stdin)
+        pm_close(pbmfile);
+
+    return 0;
 }
diff --git a/converter/pbm/pbmtoepsi.c b/converter/pbm/pbmtoepsi.c
index 81e03fbe..5eccc298 100644
--- a/converter/pbm/pbmtoepsi.c
+++ b/converter/pbm/pbmtoepsi.c
@@ -16,6 +16,7 @@
 ** implied warranty.
 */
 
+#include "pm_c_util.h"
 #include "pbm.h"
 #include "shhopt.h"
 
diff --git a/converter/pbm/pbmtoepson.c b/converter/pbm/pbmtoepson.c
index 1d1b1608..86185d15 100644
--- a/converter/pbm/pbmtoepson.c
+++ b/converter/pbm/pbmtoepson.c
@@ -16,8 +16,9 @@
 #include <stdio.h>
 #include <string.h>
 
-#include "shhopt.h"
+#include "pm_c_util.h"
 #include "mallocvar.h"
+#include "shhopt.h"
 
 #include "pbm.h"
 
diff --git a/converter/pbm/pbmtoescp2.c b/converter/pbm/pbmtoescp2.c
index 9992fec9..e280b3df 100644
--- a/converter/pbm/pbmtoescp2.c
+++ b/converter/pbm/pbmtoescp2.c
@@ -16,6 +16,7 @@
 
 #include <string.h>
 
+#include "pm_c_util.h"
 #include "pbm.h"
 #include "shhopt.h"
 
diff --git a/converter/pbm/pbmtog3.c b/converter/pbm/pbmtog3.c
index 77ea545a..c0dd8c64 100644
--- a/converter/pbm/pbmtog3.c
+++ b/converter/pbm/pbmtog3.c
@@ -18,12 +18,14 @@
 
 #include <assert.h>
 
-#include "pbm.h"
+#include "pm_c_util.h"
 #include "shhopt.h"
 #include "mallocvar.h"
 #include "bitreverse.h"
 #include "wordaccess.h"
+#include "wordintclz.h"
 #include "g3.h"
+#include "pbm.h"
 
 #define TC_MC 64
 
@@ -55,7 +57,7 @@ struct bitString {
 
 struct outStream {
     struct bitString buffer;
-    
+
     bool reverseBits;
 };
 
@@ -109,6 +111,8 @@ parseCommandLine(int argc, char ** const argv,
     optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
+    free(option_def);
+
     if (argc-1 == 0) 
         cmdlineP->inputFileName = "-";
     else if (argc-1 != 1)
@@ -142,7 +146,7 @@ makeBs(wordint      const bits,
     return retval;
 }
 
-    
+
 
 static __inline__ void
 putbits(struct bitString const newBits) {
@@ -153,7 +157,7 @@ putbits(struct bitString const newBits) {
    Flush the buffer to stdout as necessary to make room.
 
    'newBits' must be shorter than a whole word.
-   
+
    N.B. the definition of struct bitString requires upper bits to be zero.
 -----------------------------------------------------------------------------*/
     unsigned int const spaceLeft = 
@@ -182,11 +186,11 @@ putbits(struct bitString const newBits) {
                        | (newBits.intBuffer >> nextBufBitCount));
         if (out.reverseBits)
             reversebuffer(outbytes, sizeof(outbytes));
-            
+
         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;
     }
@@ -237,7 +241,7 @@ putcode2(int const 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)
@@ -299,7 +303,7 @@ puteol(void) {
         puts("EOL");
     else {
         struct bitString const eol = {12, 1};
-            
+
         putbits(eol);
     }
 }
@@ -341,7 +345,7 @@ convertRowToRunLengths(unsigned char * const bitrow,
     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 */
@@ -411,7 +415,7 @@ main(int    argc,
            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;
@@ -423,7 +427,7 @@ main(int    argc,
     pbm_init(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
-     
+
     ifP = pm_openr(cmdline.inputFileName);
 
     pbm_readpbminit(ifP, &cols, &rows, &format);
@@ -447,9 +451,9 @@ main(int    argc,
         unsigned int i;
 
         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++])
@@ -459,6 +463,8 @@ main(int    argc,
     }
 
     free(milepost);
+    pbm_freerow_packed(bitrow);
+
     {
         unsigned int i;  
         for( i = 0; i < 6; ++i)
@@ -467,7 +473,7 @@ main(int    argc,
     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, 
diff --git a/converter/pbm/pbmtogem.c b/converter/pbm/pbmtogem.c
index 59f2b9cf..cefbdc95 100644
--- a/converter/pbm/pbmtogem.c
+++ b/converter/pbm/pbmtogem.c
@@ -129,14 +129,8 @@ putinit (rows, cols)
   linerepeat = -1;
 }
 
-#if __STDC__
 static void
 putbit( bit b )
-#else /*__STDC__*/
-static void
-putbit( b )
-    bit b;
-#endif /*__STDC__*/
     {
     if ( bitsperitem == 8 )
 	putitem( );
diff --git a/converter/pbm/pbmtogo.c b/converter/pbm/pbmtogo.c
index 18f3ab6c..23b2ee9a 100644
--- a/converter/pbm/pbmtogo.c
+++ b/converter/pbm/pbmtogo.c
@@ -1,11 +1,11 @@
 /* pbmtogo.c - read a PBM image and produce a GraphOn terminal raster file
-**	
-**	Rev 1.1 was based on pbmtolj.c
+**      
+**      Rev 1.1 was based on pbmtolj.c
 **
-**	Bo Thide', Swedish Institute of Space Physics, bt@irfu.se
-**				   
+**      Bo Thide', Swedish Institute of Space Physics, bt@irfu.se
+**                                 
 **
-** $Log:	pbmtogo.c,v $
+** $Log:        pbmtogo.c,v $
  * Revision 1.5  89/11/25  00:24:12  00:24:12  root (Bo Thide)
  * Bug found: The byte after 64 repeated bytes sometimes lost. Fixed.
  * 
@@ -35,63 +35,125 @@
 ** implied warranty.
 */
 
+#include <assert.h>
 #include <stdio.h>
 #include <string.h>
 
 #include "pm_c_util.h"
 #include "pbm.h"
 
-#define BUFSIZE 132	/* GraphOn has 132 byte/1056 bit wide raster lines */
-#define REPEAT_CURRENT_LINE_MASK	0x00 
-#define SKIP_AND_PLOT_MASK		0x40 
-#define REPEAT_PLOT_MASK		0x80 
-#define PLOT_ARBITRARY_DATA_MASK	0xc0 
+#define GRAPHON_WIDTH 1056 /* GraphOn has 1056 bit wide raster lines */
+#define GRAPHON_WIDTH_BYTES (GRAPHON_WIDTH / 8)
+#define REPEAT_CURRENT_LINE_MASK        0x00 
+#define SKIP_AND_PLOT_MASK              0x40 
+#define REPEAT_PLOT_MASK                0x80 
+#define PLOT_ARBITRARY_DATA_MASK        0xc0 
 #define MAX_REPEAT 64
 
-static unsigned char *scanlineptr;		/* Pointer to current scan line byte */
+static unsigned char * scanlineptr;
+    /* Pointer to current scan line byte */
 
-static void putinit ARGS(( void ));
-static void putbit ARGS(( bit b ));
-static void putrest ARGS(( void ));
-static void putitem ARGS(( void ));
+static int item, bitsperitem, bitshift;
 
-int
-main( argc, argv )
-     int argc;
-     char* argv[];
-{
-    FILE* ifp;
-    bit* bitrow;
-    register bit* bP;
-    int argn, rows, cols, format, rucols, padright, row, col;
-    int nbyte, bytesperrow, ecount, ucount, nout, i, linerepeat;
-    int	olditem;
-    unsigned char oldscanline[BUFSIZE];
-    unsigned char newscanline[BUFSIZE];
-    unsigned char diff[BUFSIZE];
-    unsigned char buffer[BUFSIZE];
-    unsigned char outbuffer[2*(BUFSIZE+1)];	/* Worst case.  Should malloc */
-    const char* usage = "[-c] [pbmfile]";
+static void
+putinit(void) {
+
+    /* Enter graphics window */
+    printf("\0331");
+
+    /* Erase graphics window */
+    printf("\033\014");
+
+    /* Set graphics window in raster mode */
+    printf("\033r");
+
+    /* Select standard Tek coding **/
+    printf("\033[=11l");
+
+    bitsperitem = 1;
+    item = 0;
+    bitshift = 7;
+}
+
+
+
+static void
+putitem(void) {
 
+    *scanlineptr++ = item;
+    bitsperitem = 0;
+    item = 0;
+}
+
+
+
+static void
+putbit(bit const b) {
+
+    if (b == PBM_BLACK)
+        item += 1 << bitshift;
+
+    bitshift--;
+
+    if (bitsperitem == 8)
+    {
+        putitem();
+        bitshift = 7;
+    }
+    bitsperitem++;
+}
+
+
+
+static void
+putrest(void) {
 
-    pbm_init( &argc, argv );
+    if (bitsperitem > 1)
+        putitem();
 
-    argn = 2;
+    /* end raster downloading */
+    printf("\033\134");
 
-    /* Check for flags. */
-    if (argc > argn + 1)
-      pm_usage(usage);
+    /* Exit raster mode */
+    printf("\033t");
 
-    if (argc == argn)
-      ifp = pm_openr( argv[argn-1] );
+    /* Exit graphics window
+       printf("\0332"); */
+}
+
+
+
+int
+main(int           argc,
+     const char ** argv) {
+
+    FILE * ifP;
+    bit * bitrow;
+    bit * bP;
+    int rows, cols, format, rucols, padright, row, col;
+    int nbyte, bytesperrow, ecount, ucount, nout, i, linerepeat;
+    int olditem;
+    unsigned char oldscanline[GRAPHON_WIDTH_BYTES];
+    unsigned char newscanline[GRAPHON_WIDTH_BYTES];
+    unsigned char diff[GRAPHON_WIDTH_BYTES];
+    unsigned char buffer[GRAPHON_WIDTH_BYTES];
+    unsigned char outbuffer[2*(GRAPHON_WIDTH_BYTES+1)];     /* Worst case. */
+
+    pm_proginit(&argc, argv);
+
+    if (argc-1 == 0)
+      ifP = stdin;
+    else if (argc-1 == 1)
+      ifP = pm_openr(argv[1]);
     else
-      ifp = stdin;
+        pm_error("There is at most one argument: input file name.  "
+                 "You specified %u", argc-1);
 
-    pbm_readpbminit(ifp, &cols, &rows, &format);
+    pbm_readpbminit(ifP, &cols, &rows, &format);
 
-    if (cols > 1056)
+    if (cols > GRAPHON_WIDTH)
         pm_error("Image is wider (%u pixels) than a Graphon terminal "
-                 "(%u pixels)", cols, 1056);
+                 "(%u pixels)", cols, GRAPHON_WIDTH);
 
     bitrow = pbm_allocrow(cols);
 
@@ -101,7 +163,7 @@ main( argc, argv )
     rucols = rucols * 8;
     padright = rucols - cols;
 
-    for (i = 0; i < BUFSIZE; ++i )
+    for (i = 0; i < GRAPHON_WIDTH_BYTES; ++i )
       buffer[i] = oldscanline[i] = 0;
     putinit();
 
@@ -113,12 +175,14 @@ main( argc, argv )
     for (row = 0; row < rows; row++) {
         /* Store scan line data in the new scan line vector */
         scanlineptr = newscanline;
-        pbm_readpbmrow(ifp, bitrow, cols, format);
+        pbm_readpbmrow(ifP, bitrow, cols, format);
         /* Transfer raster graphics */
         for (col = 0, bP = bitrow; col < cols; col++, bP++)
           putbit(*bP);
         for (col = 0; col < padright; col++)
           putbit(0);
+
+        assert(bytesperrow <= GRAPHON_WIDTH_BYTES);
         
         /* XOR data from the new scan line with data from old scan line */
         for (i = 0; i < bytesperrow; i++)
@@ -219,7 +283,7 @@ main( argc, argv )
             fflush(stdout);
 
             /* Output the plot data */
-            write(1, outbuffer, nout);
+            fwrite(outbuffer, 1, nout, stdout);
 
             /* Reset the counters */
             linerepeat = 0;
@@ -230,7 +294,7 @@ main( argc, argv )
                   putchar(linerepeat);
                   printf("%d/", nout+1);
                   fflush(stdout);
-                  write(1, outbuffer, nout);
+                  fwrite(outbuffer, 1, nout, stdout);
                   linerepeat = 0;
               }
         }
@@ -240,75 +304,11 @@ main( argc, argv )
           oldscanline[i] = newscanline[i];
     }
     putchar(linerepeat);        /* For the last line(s) to be plotted */
-    pm_close(ifp);
+    pm_close(ifP);
     putrest();
-    exit(0);
-}
-
-
-
-static int item, bitsperitem, bitshift;
-
-static void
-putinit()
-{
-  /* Enter graphics window */
-  printf("\0331");
-
-  /* Erase graphics window */
-  printf("\033\014");
-
-  /* Set graphics window in raster mode */
-  printf("\033r");
-
-  /* Select standard Tek coding **/
-  printf("\033[=11l");
 
-  bitsperitem = 1;
-  item = 0;
-  bitshift = 7;
-}
-
-#if __STDC__
-static void
-putbit(bit b)
-#else /*__STDC__*/
-static void
-putbit(b)
-bit b;
-#endif /*__STDC__*/
-{
-  if (b == PBM_BLACK)
-    item += 1 << bitshift;
-  bitshift--;
-  if (bitsperitem == 8)
-  {
-    putitem();
-    bitshift = 7;
-  }
-  bitsperitem++;
+    return 0;
 }
 
-static void
-putrest()
-{
-  if (bitsperitem > 1)
-      putitem();
-
-  /* end raster downloading */
-  printf("\033\134");
 
-  /* Exit raster mode */
-  printf("\033t");
 
-  /* Exit graphics window
-  printf("\0332"); */
-}
-
-static void
-putitem()
-  {
-  *scanlineptr++ = item;
-  bitsperitem = 0;
-  item = 0;
-  }
diff --git a/converter/pbm/pbmtoibm23xx.c b/converter/pbm/pbmtoibm23xx.c
index 973f7de0..a83e260d 100644
--- a/converter/pbm/pbmtoibm23xx.c
+++ b/converter/pbm/pbmtoibm23xx.c
@@ -46,6 +46,7 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include "pm_c_util.h"
 #include "pbm.h"
 #include "shhopt.h"
 #include "mallocvar.h"
diff --git a/converter/pbm/pbmtoicon.c b/converter/pbm/pbmtoicon.c
index 0e21c202..d5fefb76 100644
--- a/converter/pbm/pbmtoicon.c
+++ b/converter/pbm/pbmtoicon.c
@@ -1,4 +1,4 @@
-/* pbmtoicon.c - read a portable bitmap and produce a Sun icon file
+/* pbmtoicon.c - read a PBM image and produce a Sun icon file
 **
 ** Copyright (C) 1988 by Jef Poskanzer.
 **
@@ -10,125 +10,177 @@
 ** implied warranty.
 */
 
+/* 2006.10 (afu)
+   Changed bitrow from plain to raw, read function from pbm_readpbmrow() to
+   pbm_readpbmrow_packed.  Applied wordint to scoop up 16 bit output items.
+   putitem changed to better express the output format.
+   Retired bitwise transformation functions.
+*/
+
+#include "wordaccess.h"
 #include "pbm.h"
 
-static void putinit ARGS(( void ));
-static void putbit ARGS(( bit b ));
-static void putrest ARGS(( void ));
-static void putitem ARGS(( void ));
+static unsigned short int itemBuff[8];
+static unsigned int itemCnt;    /* takes values 0 to 8 */
+FILE * putFp;
 
-int
-main( argc, argv )
-    int argc;
-    char* argv[];
-    {
-    FILE* ifp;
-    bit* bitrow;
-    register bit* bP;
-    int rows, cols, format, pad, padleft, padright, row, col;
 
 
-    pbm_init( &argc, argv );
+static void
+putinit(FILE * const ofP) {
+    putFp = ofP;
+    itemCnt = 0;
+}
 
-    if ( argc > 2 )
-	pm_usage( "[pbmfile]" );
 
-    if ( argc == 2 )
-	ifp = pm_openr( argv[1] );
-    else
-	ifp = stdin;
 
-    pbm_readpbminit( ifp, &cols, &rows, &format );
-    bitrow = pbm_allocrow( cols );
+static void
+putitem(wordint const item) {
+
+    if (itemCnt == 8 ) {
+        /* Buffer is full.  Write out one line. */
+        int rc;
     
-    /* Round cols up to the nearest multiple of 16. */
-    pad = ( ( cols + 15 ) / 16 ) * 16 - cols;
-    padleft = pad / 2;
-    padright = pad - padleft;
-
-    printf( "/* Format_version=1, Width=%d, Height=%d", cols + pad, rows );
-    printf( ", Depth=1, Valid_bits_per_item=16\n */\n" );
-
-    putinit( );
-    for ( row = 0; row < rows; ++row )
-	{
-	pbm_readpbmrow( ifp, bitrow, cols, format );
-	for ( col = 0; col < padleft; ++col )
-	    putbit( 0 );
-        for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
-	    putbit( *bP );
-	for ( col = 0; col < padright; ++col )
-	    putbit( 0 );
-        }
+        rc = fprintf(putFp,
+                     "\t0x%04x,0x%04x,0x%04x,0x%04x,"
+                     "0x%04x,0x%04x,0x%04x,0x%04x,\n",
+                     itemBuff[0],itemBuff[1],itemBuff[2],itemBuff[3],
+                     itemBuff[4],itemBuff[5],itemBuff[6],itemBuff[7]);
+        if (rc < 0)        
+           pm_error("fprintf() failed to write Icon bitmap");
+           
+        itemCnt = 0;
+    }
+    itemBuff[itemCnt++] = item & 0xffff;  /* Only lower 16 bits are used */
+}
 
-    pm_close( ifp );
 
-    putrest( );
 
-    exit( 0 );
-    }
+static void
+putterm(void) {
 
-static int item, bitsperitem, bitshift, itemsperline, firstitem;
+    unsigned int i;
 
-static void
-putinit( )
-    {
-    itemsperline = 0;
-    bitsperitem = 0;
-    item = 0;
-    bitshift = 15;
-    firstitem = 1;
+    for (i = 0; i < itemCnt; ++i) {
+        int rc;
+        rc = fprintf(putFp, "%s0x%04x%c", i == 0  ? "\t" : "", itemBuff[i],
+                     i == itemCnt - 1 ? '\n' : ',');
+        if (rc < 0)        
+            pm_error("fprintf() failed to write Icon bitmap");
     }
+}     
+
+
 
-#if __STDC__
-static void
-putbit( bit b )
-#else /*__STDC__*/
 static void
-putbit( b )
-bit b;
-#endif /*__STDC__*/
-    {
-    if ( bitsperitem == 16 )
-	putitem( );
-    ++bitsperitem;
-    if ( b == PBM_BLACK )
-	item += 1 << bitshift;
-    --bitshift;
-    }
+writeIconHeader(FILE *       const ofP,
+                unsigned int const width,
+                unsigned int const height) {
+
+    int rc;
+
+    rc = fprintf(ofP,
+                 "/* Format_version=1, Width=%u, Height=%u", width, height);
+    if (rc < 0)
+        pm_error("fprintf() failed to write Icon header");
+        
+    rc = fprintf(ofP, ", Depth=1, Valid_bits_per_item=16\n */\n");
+    if (rc < 0)
+        pm_error("fprintf() failed to write Icon header");
+}
+
+
 
 static void
-putrest( )
-    {
-    if ( bitsperitem > 0 )
-	putitem( );
-    putchar( '\n' );
+writeIcon(FILE *       const ifP,
+          unsigned int const cols,
+          unsigned int const rows,
+          int          const format,
+          FILE *       const ofP) {
+
+    unsigned int const wordintSize = sizeof(wordint) * 8;
+        /* wordintSize is usually 32 or 64 bits.  Must be at least 24. */
+    unsigned int const items = (cols + 15) / 16;
+    unsigned int const bitrowBytes = pbm_packed_bytes(cols);
+    unsigned int const pad = items * 16 - cols;
+    /* 'padleft' is added to the output.  'padbyte' is for cleaning
+       the input
+    */
+    unsigned int const padleft = pad / 2;
+    unsigned int const padbyte = bitrowBytes * 8 - cols;
+    unsigned int const shift   = (wordintSize - 24) + padleft;
+    
+    unsigned char * bitbuffer;
+    unsigned char * bitrow;
+    unsigned int row;
+
+    bitbuffer = pbm_allocrow_packed(cols + wordintSize);
+    bitrow = &bitbuffer[1];
+    bitbuffer[0] = 0;
+    bitrow[bitrowBytes] = 0;
+    
+    writeIconHeader(ofP, cols + pad, rows);
+
+    putinit(ofP);
+
+    for (row = 0; row < rows; ++row) {
+        unsigned int itemSeq;
+        pbm_readpbmrow_packed(ifP, bitrow, cols, format);
+
+        /* Clear post-data junk in final partial byte */
+        if (padbyte > 0) {
+            bitrow[bitrowBytes-1] >>= padbyte;
+            bitrow[bitrowBytes-1] <<= padbyte;
+        }
+        
+        for (itemSeq = 0; itemSeq < items; ++itemSeq) {
+            /* Scoop up bits, shift-align, send to format & print function.
+    
+               An item is 16 bits, typically spread over 3 bytes due to
+               left-padding.  We use wordint here to scoop up 4 (or more)
+               consecutive bytes.  An item always resides within the higher
+               24 bits of each scoop.  It is essential to use wordint
+               (or rather the wordaccess function bytesToWordInt() ); 
+               simple long, uint_32t, etc. do not work for they are not
+               shift-tolerant.
+            */
+            
+            wordint const scoop = bytesToWordint(&bitbuffer[itemSeq*2]);
+            putitem (scoop >> shift);
+        }
     }
+    putterm();    
+}
 
-static void
-putitem( )
-    {
-    const char* hexits = "0123456789abcdef";
 
-    if ( firstitem )
-	firstitem = 0;
+
+int
+main(int argc,
+     char * argv[]) {
+
+    FILE * ifP;
+    int rows, cols;
+    int format;
+    const char * inputFileName;
+
+    pbm_init(&argc, argv);
+
+    if (argc-1 > 1)
+        pm_error("Too many arguments (%u).  "
+                 "Only argument is optional input file", argc-1);
+    if (argc-1 == 1)
+        inputFileName = argv[1];
     else
-	putchar( ',' );
-    if ( itemsperline == 8 )
-	{
-	putchar( '\n' );
-	itemsperline = 0;
-	}
-    if ( itemsperline == 0 )
-	putchar( '\t' );
-    putchar( '0' );
-    putchar( 'x' );
-    putchar( hexits[item >> 12] );
-    putchar( hexits[( item >> 8 ) & 15] );
-    putchar( hexits[( item >> 4 ) & 15] );
-    putchar( hexits[item & 15] );
-    ++itemsperline;
-    bitsperitem = 0;
-    item = 0;
-    bitshift = 15;
-    }
+        inputFileName = "-";
+    
+    ifP = pm_openr(inputFileName);
+
+    pbm_readpbminit(ifP, &cols, &rows, &format);
+
+    writeIcon(ifP, cols, rows, format, stdout);
+
+    pm_close(ifP);
+
+    return 0;
+}
+
diff --git a/converter/pbm/pbmtolj.c b/converter/pbm/pbmtolj.c
index e8373050..be28f635 100644
--- a/converter/pbm/pbmtolj.c
+++ b/converter/pbm/pbmtolj.c
@@ -27,6 +27,7 @@
 ** implied warranty.
 */
 
+#include "pm_c_util.h"
 #include "pbm.h"
 #include "shhopt.h"
 #include "mallocvar.h"
diff --git a/converter/pbm/pbmtoln03.c b/converter/pbm/pbmtoln03.c
index 07c8629f..f7cf53c7 100644
--- a/converter/pbm/pbmtoln03.c
+++ b/converter/pbm/pbmtoln03.c
@@ -219,13 +219,8 @@ main (int argc, char **argv) {
    if( argn != argc )
       pm_usage(usage);
 
-
-   /* Initialize pbm file */
    pbm_readpbminit (input, &width, &height, &format) ;
 
-   if (format != PBM_FORMAT && format != RPBM_FORMAT)
-      pm_error ("input not in PBM format") ;
-
 /*
  * In explanation of the sequence below:
  *      <ESC>[!p        DECSTR  soft terminal reset
diff --git a/converter/pbm/pbmtolps.c b/converter/pbm/pbmtolps.c
index 13a14e2b..5adef4c8 100644
--- a/converter/pbm/pbmtolps.c
+++ b/converter/pbm/pbmtolps.c
@@ -112,7 +112,7 @@ main(int argc, char ** argv) {
 	pbm_init(&argc, argv);
 
     i = 1;
-    if (i < argc && STREQ(argv[i], "-dpi")) {
+    if (i < argc && streq(argv[i], "-dpi")) {
         if (i == argc - 1)
             pm_usage(usage);
         sscanf(argv[i + 1], "%f", &dpi);
diff --git a/converter/pbm/pbmtomgr.c b/converter/pbm/pbmtomgr.c
index acf689e1..d12e6635 100644
--- a/converter/pbm/pbmtomgr.c
+++ b/converter/pbm/pbmtomgr.c
@@ -1,124 +1,127 @@
-/* pbmtomgr.c - read a portable bitmap and produce a MGR bitmap
-**
-** 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.
+/* pbmtomgr.c - read a PBM image and produce a MGR bitmap
+
+   Copyright information is at end of file.
+
+   You can find MGR and some MGR format test images at
+   ftp://sunsite.unc.edu/pub/Linux/apps/MGR/!INDEX.html
 */
 
+#include <assert.h>
 #include "pbm.h"
 #include "mgr.h"
 
-static void putinit ARGS(( int rows, int cols ));
-static void putbit ARGS(( bit b ));
-static void putrest ARGS(( void ));
-static void putitem ARGS(( void ));
 
-int
-main( argc, argv )
-    int argc;
-    char* argv[];
-    {
-    FILE* ifp;
-    bit* bitrow;
-    register bit* bP;
-    int rows, cols, format, padright, row, col;
 
+static void
+putinit(unsigned int const rows,
+        unsigned int const cols) {
 
-    pbm_init( &argc, argv );
+    struct b_header head;
+    size_t writtenCount;
+
+    /* Because of argument restrictions: maximum dimensions: */
+    assert((rows & 0xfff) == rows);
+    assert((cols & 0xfff) == cols);
+
+    head.magic[0] = 'y';
+    head.magic[1] = 'z';
+    head.h_wide = ((cols >> 6) & 0x3f) + ' ';
+    head.l_wide = (cols & 0x3f) + ' ';
+    head.h_high = (( rows >> 6) & 0x3f) + ' ';
+    head.l_high = (rows & 0x3f) + ' ';
+    head.depth = (1 & 0x3f) + ' ';
+    head._reserved = ' ';
+    writtenCount = fwrite(&head, sizeof(head), 1, stdout);
+    if (writtenCount != 1)
+        pm_error("fwrite() failed to write the MGR header.");    
+}
 
-    if ( argc > 2 )
-	pm_usage( "[pbmfile]" );
 
-    if ( argc == 2 )
-	ifp = pm_openr( argv[1] );
+
+int
+main(int argc,
+     char * argv[]) {
+
+    FILE * ifP;
+    unsigned char * bitrow;
+    int rows;
+    int cols;
+    int format;
+    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);
+
+    if (argc-1 > 1)
+        pm_error("Too many arguments (%u).  "
+                 "Only argument is optional input file", argc-1);
+    if (argc-1 == 1)
+        inputFileName = argv[1];
     else
-	ifp = stdin;
-
-    pbm_readpbminit( ifp, &cols, &rows, &format );
-    if (cols > 4095)
-        pm_error("Image width too large: %u (max: 4095)", cols);
-    if (rows > 4095)
-        pm_error("Image heigth too large: %u (max: 4095)", cols);
-    bitrow = pbm_allocrow( cols );
+        inputFileName = "-";
     
-    /* Round cols up to the nearest multiple of 8. */
-    padright = ( ( cols + 7 ) / 8 ) * 8 - cols;
-
-    putinit( rows, cols );
-    for ( row = 0; row < rows; ++row )
-	{
-	pbm_readpbmrow( ifp, bitrow, cols, format );
-        for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
-	    putbit( *bP );
-	for ( col = 0; col < padright; ++col )
-	    putbit( 0 );
-        }
+    ifP = pm_openr(inputFileName);
 
-    pm_close( ifp );
+    pbm_readpbminit(ifP, &cols, &rows, &format);
+    if (cols > maxDimension)
+        pm_error("Image width too large: %u (max: %u)", cols, maxDimension);
+    if (rows > maxDimension)
+        pm_error("Image height too large: %u (max: %u)", rows, maxDimension);
+    
+    bitrow = pbm_allocrow_packed(cols);
+    bytesPerRow = pbm_packed_bytes(cols);
+    padright = bytesPerRow * 8 - cols;
 
-    putrest( );
+    putinit(rows, cols);
+    
+    for (row = 0; row < rows; ++row) {
+        /* The raster formats are identical. 
+           The row end pad bits are set to 0 in mgr.
+        */
+        size_t bytesWritten;
+
+        pbm_readpbmrow_packed(ifP, bitrow, cols, format);
+        
+        if (padright > 0) {
+            bitrow[bytesPerRow-1] >>= padright;
+            bitrow[bytesPerRow-1] <<= padright;
+        }
 
-    exit( 0 );
+        bytesWritten = fwrite(bitrow, 1, bytesPerRow, stdout);
+        if (bytesWritten != bytesPerRow )
+            pm_error("fwrite() failed to write MGR bitmap "
+                     "to Standard Output.");    
     }
+    pm_close(ifP);
+    return 0;
+}
 
-static unsigned char item;
-static int bitsperitem, bitshift;
 
-static void
-putinit( rows, cols )
-    int rows, cols;
-    {
-    struct b_header head;
 
-    head.magic[0] = 'y';
-    head.magic[1] = 'z';
-    head.h_wide = ( ( cols >> 6 ) & 0x3f ) + ' ';
-    head.l_wide = ( cols & 0x3f ) + ' ';
-    head.h_high = ( ( rows >> 6 ) & 0x3f ) + ' ';
-    head.l_high = ( rows & 0x3f ) + ' ';
-    head.depth = ( 1 & 0x3f ) + ' ';
-    head._reserved = ' ';
-    fwrite( &head, sizeof(head), 1, stdout );
-
-    item = 0;
-    bitsperitem = 0;
-    bitshift = 7;
-    }
+/* 2006.10 (afu)
+   Changed bitrow from plain to raw, read function from pbm_readpbmrow() to
+   pbm_readpbmrow_packed(), write function from putc() to fwrite().
 
-#if __STDC__
-static void
-putbit( bit b )
-#else /*__STDC__*/
-static void
-putbit( b )
-    bit b;
-#endif /*__STDC__*/
-    {
-    if ( bitsperitem == 8 )
-	putitem( );
-    ++bitsperitem;
-    if ( b == PBM_BLACK )
-	item += 1 << bitshift;
-    --bitshift;
-    }
-
-static void
-putrest( )
-    {
-    if ( bitsperitem > 0 )
-	putitem( );
-    }
+   Retired bitwise transformation functions.
+   
+   Produces only new style bitmap (8 bit padding.)  See mgrtopbm.c .
+*/
 
-static void
-putitem( )
-    {
-    fwrite( &item, sizeof(item), 1, stdout );
-    item = 0;
-    bitsperitem = 0;
-    bitshift = 7;
-    }
+/*
+** 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.
+*/
diff --git a/converter/pbm/pbmtomrf.c b/converter/pbm/pbmtomrf.c
index c93c88aa..e7b7fcc9 100644
--- a/converter/pbm/pbmtomrf.c
+++ b/converter/pbm/pbmtomrf.c
@@ -13,83 +13,139 @@
 #include "pm_c_util.h"
 #include "pbm.h"
 
-static int bitbox;
-static int bitsleft;
 
-static FILE *bit_out;
+
+typedef struct bitOut {
+    int bitbox;
+    int bitsleft;
+    FILE * fileP;
+} bitOut;
+
 
 
 static void 
-bit_init(FILE * const out) {
-    bitbox = 0; 
-    bitsleft = 8;
-    bit_out = out;
+bit_init(struct bitOut * const bitOutP,
+         FILE *          const ofP) {
+
+    bitOutP->bitbox = 0; 
+    bitOutP->bitsleft = 8;
+    bitOutP->fileP = ofP;
 }
 
 
 
 static void 
-bit_output(int const bit) {
-    --bitsleft;
-    bitbox |= (bit << bitsleft);
-    if (bitsleft == 0) {
-        fputc(bitbox, bit_out);
-        bitbox = 0;
-        bitsleft = 8;
+bit_output(struct bitOut * const bitOutP,
+           int             const bit) {
+
+    --bitOutP->bitsleft;
+    bitOutP->bitbox |= (bit << bitOutP->bitsleft);
+    if (bitOutP->bitsleft == 0) {
+        fputc(bitOutP->bitbox, bitOutP->fileP);
+        bitOutP->bitbox = 0;
+        bitOutP->bitsleft = 8;
     }
 }
 
 
 
 static void 
-bit_flush(void) {
+bit_flush(struct bitOut * const bitOutP) {
     /* there are never 0 bits left outside of bit_output, but
      * if 8 bits are left here there's nothing to flush, so
      * only do it if bitsleft!=8.
      */
-    if (bitsleft != 8) {
-        bitsleft = 1;
-        bit_output(0);    /* yes, really. This will always work. */
+    if (bitOutP->bitsleft != 8) {
+        bitOutP->bitsleft = 1;
+        bit_output(bitOutP, 0);    /* yes, really. This will always work. */
     }
 }
 
 
 
-static void 
-doSquare(unsigned char * const image,
-         int             const ox,
-         int             const oy,
-         int             const w,
-         int             const size) {
-
-    unsigned int y;
+static void
+determineBlackWhiteOrMix(const unsigned char * const image,
+                         unsigned int          const ulCol,
+                         unsigned int          const ulRow,
+                         unsigned int          const imageWidth,
+                         unsigned int          const size,
+                         bool *                const oneColorP,
+                         int *                 const colorP) {
+/*----------------------------------------------------------------------------
+   Determine whether a square within 'image' is all white, all black,
+   or a mix.
+-----------------------------------------------------------------------------*/
+    unsigned int rowOfSquare;
     unsigned int t;
 
-    /* check square to see if it's all black or all white. */
+    for (rowOfSquare = 0, t = 0; rowOfSquare < size; ++rowOfSquare) {
+        unsigned int colOfSquare;
+        for (colOfSquare = 0; colOfSquare < size; ++colOfSquare) {
+            unsigned int rowOfImage = ulRow + rowOfSquare;
+            unsigned int colOfImage = ulCol + colOfSquare;
 
-    t = 0;
-    for (y = 0; y < size; ++y) {
-        unsigned int x;
-        for (x = 0; x < size; ++x)
-            t += image[(oy+y)*w + ox + x];
+            t += image[rowOfImage * imageWidth + colOfImage];
+        }
     }        
     /* if the total's 0, it's black. if it's size*size, it's white. */
-    if (t == 0 || t == size*size) {
-        if (size != 1)     /* (it's implicit when size is 1, of course) */
-            bit_output(1);  /* all same color */
-        bit_output(t?1:0);
-        return;
-    }
-    
-    /* otherwise, if our square is greater than 1x1, we need to recurse. */
-    if(size > 1) {
-        int halfsize = size >> 1;
-
-        bit_output(0);    /* not all same */
-        doSquare(image, ox,          oy,          w, halfsize);
-        doSquare(image, ox+halfsize, oy,          w, halfsize);
-        doSquare(image, ox,          oy+halfsize, w, halfsize);
-        doSquare(image, ox+halfsize, oy+halfsize, w, halfsize);
+    if (t == 0) {
+        *oneColorP = TRUE;
+        *colorP = 0;
+    } else if (t == SQR(size)) {
+        *oneColorP = TRUE;
+        *colorP = 1;
+    } else
+        *oneColorP = FALSE;
+}
+
+
+
+static void 
+doSquare(bitOut *              const bitOutP,
+         const unsigned char * const image,
+         unsigned int          const ulCol,
+         unsigned int          const ulRow,
+         unsigned int          const imageWidth,
+         unsigned int          const size) {
+/*----------------------------------------------------------------------------
+   Do a square of side 'size', whose upper left corner is at (ulCol, ulRow).
+   This is a square within 'image', which is a concatenation of rows
+   'imageWidth' pixels wide, one byte per pixel.
+
+   Write the pixel values out to the bit stream *bitOutP, in MRF format.
+-----------------------------------------------------------------------------*/
+    if (size == 1) {
+        /* The fact that it is all one color is implied because the square is
+           just one pixel; no bit goes in MRF output to state that.
+        */
+        bit_output(bitOutP, image[ulRow * imageWidth + ulCol] ? 1 : 0);
+    } else {
+        bool oneColor;
+        int color;
+
+        determineBlackWhiteOrMix(image, ulCol, ulRow, imageWidth, size,
+                                 &oneColor, &color);
+
+        if (oneColor) {
+            bit_output(bitOutP, 1);  /* all same color */
+            bit_output(bitOutP, color);
+        } else {
+            /* Square is not all the same color, so recurse.  Do each
+               of the four quadrants of this square individually.
+            */
+            unsigned int const quadSize = size/2;
+
+            bit_output(bitOutP, 0);    /* not all same color */
+
+            doSquare(bitOutP, image, ulCol,            ulRow,
+                     imageWidth, quadSize);
+            doSquare(bitOutP, image, ulCol + quadSize, ulRow,
+                     imageWidth, quadSize);
+            doSquare(bitOutP, image, ulCol,            ulRow + quadSize,
+                     imageWidth, quadSize);
+            doSquare(bitOutP, image, ulCol + quadSize, ulRow + quadSize,
+                     imageWidth, quadSize);
+        }
     }
 }
     
@@ -243,7 +299,7 @@ readPbmImage(FILE *           const ifP,
         pm_error("Ridiculously large, unprocessable image: %u cols x %u rows",
                  cols, rows);
 
-    image = calloc(w64*h64*64*64,1);
+    image = calloc(w64*h64*64*64, 1);
     if (image == NULL)
         pm_error("Unable to get memory for raster");
                  
@@ -276,6 +332,8 @@ outputMrf(FILE *          const ofP,
     unsigned int const w64 = (cols + 63) / 64;
     unsigned int const h64 = (rows + 63) / 64;
 
+    bitOut bitOut;
+
     unsigned int row;
 
     fprintf(ofP, "MRF1");
@@ -285,14 +343,14 @@ outputMrf(FILE *          const ofP,
     
     /* now recursively check squares. */
 
-    bit_init(ofP);
+    bit_init(&bitOut, ofP);
 
     for (row = 0; row < h64; ++row) {
         unsigned int col;
         for (col = 0; col < w64; ++col)
-            doSquare(image, col*64, row*64, w64*64, 64);
+            doSquare(&bitOut, image, col*64, row*64, w64*64, 64);
     }
-    bit_flush();
+    bit_flush(&bitOut);
 }
 
 
diff --git a/converter/pbm/pbmtonokia.c b/converter/pbm/pbmtonokia.c
index 214958c5..b8057393 100644
--- a/converter/pbm/pbmtonokia.c
+++ b/converter/pbm/pbmtonokia.c
@@ -1,231 +1,527 @@
-/* pbmtonokia.c - convert a portable bitmap to Nokia Smart Messaging
+/* pbmtonokia.c - convert a PBM image to Nokia Smart Messaging
    Formats (NOL, NGG, HEX)
 
-** Copyright (C)2001 OMS Open Media System GmbH, Tim Rühsen
-** <tim.ruehsen@openmediasystem.de>.
-**
-** Permission to use, copy, modify, and distribute this software and its
-** documentation for any purpose and without fee is hereby granted, provided
-** that the above copyright notice appear in all copies and that both that
-** copyright notice and this permission notice appear in supporting
-** documentation.  This software is provided "as is" without express or
-** implied warranty.
+   Copyright information is at end of file.
+*/
 
-History
-  07.06.2001 Created
-  20.11.2001 Handle Picture Messages
-             new option -txt to embed text into Picture Messages
-             new option -net to specify operator network code for 
-                Nokia Operator Logos
+#define _BSD_SOURCE    /* Make sure strcaseeq() is in nstring.h */
+#include <string.h>
+#include <assert.h>
 
-Notes:
-  - limited to rows <= 255 and columns <= 255
-  - limited to b/w graphics, not animated
+#include "pm_c_util.h"
+#include "nstring.h"
+#include "mallocvar.h"
+#include "shhopt.h"
+#include "pbm.h"
 
-Testing:
-  Testing was done with SwissCom SMSC (Switzerland) and IC3S SMSC (Germany).
-  The data was send with EMI/UCP protocol over TCP/IP.
+enum outputFormat {
+    FMT_HEX_NOL,
+    FMT_HEX_NGG,
+    FMT_HEX_NPM,
+    FMT_NOL,
+    FMT_NGG,
+    FMT_NPM
+};
 
-  - 7.6.2001: tested with Nokia 3210: 72x14 Operator Logo
-  - 7.6.2001: tested with Nokia 6210: 72x14 Operator Logo and 
-              72x14 Group Graphic
 
-Todo:
-  - more testing
-  - sendsms compatibility ?
-  - are -fmt NOL and -fmt NGG working ok?  */
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * inputFileName;  /* Filename of input files */
+    int outputFormat;
+    const char * networkCode;
+    const char * txt;  /* NULL means unspecified */
+};
 
-#define _BSD_SOURCE    /* Make sure strcasecmp() is in string.h */
-#include <string.h>
 
-#include "nstring.h"
-#include "pbm.h"
 
-#define FMT_HEX_NOL   1
-#define FMT_HEX_NGG   2
-#define FMT_HEX_NPM   3
-#define FMT_NOL       4
-#define FMT_NGG       5
-
-static void 
-usage(char *myname)
-{
-    pm_message("Copyright (C)2001 OMS GmbH");
-    pm_message("Contact: Tim Ruehsen <tim.ruehsen@openmediasystem.de>\n");
-    pm_usage("[options] [pbmfile]\n"
-             "  Options:\n"
-             "    -fmt <HEX_NOL|HEX_NGG|HEX_NPM|NOL|NGG>  "
-             "Output format (default=HEX_NOL)\n"
-             "    -net <network code>                     "
-             "Network code for NOL operator logos\n"
-             "    -txt <text message>                     "
-             "Text for NPM picture messages\n");
-
-    exit(1);
+static const char *
+uppercase(const char * const subject) {
+
+    char * buffer;
+
+    buffer = malloc(strlen(subject) + 1);
+
+    if (buffer == NULL)
+        pm_error("Out of memory allocating buffer for uppercasing a "
+                 "%u-character string", strlen(subject));
+    else {
+        unsigned int i;
+
+        i = 0;
+        while (subject[i]) {
+            buffer[i] = TOUPPER(subject[i]);
+            ++i;
+        }
+        buffer[i] = '\0';
+    }
+    return buffer;
+}
+
+
+
+static void
+parseCommandLine(int argc, 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 option_def_index;
+    unsigned int fmtSpec, netSpec, txtSpec;
+    const char * fmtOpt;
+    const char * netOpt;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "fmt",     OPT_STRING, &fmtOpt, 
+            &fmtSpec, 0);
+    OPTENT3(0, "net",     OPT_STRING, &netOpt,
+            &netSpec, 0);
+    OPTENT3(0, "txt",     OPT_STRING, &cmdlineP->txt,
+            &txtSpec, 0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (fmtSpec) {
+        if (strcaseeq(fmtOpt, "HEX_NOL"))
+            cmdlineP->outputFormat = FMT_HEX_NOL;
+        else if (strcaseeq(fmtOpt, "HEX_NGG"))
+            cmdlineP->outputFormat = FMT_HEX_NGG;
+        else if (strcaseeq(fmtOpt, "HEX_NPM"))
+            cmdlineP->outputFormat = FMT_HEX_NPM;
+        else if (strcaseeq(fmtOpt, "NOL"))
+            cmdlineP->outputFormat = FMT_NOL;
+        else if (strcaseeq(fmtOpt, "NGG"))
+            cmdlineP->outputFormat = FMT_NGG;
+        else if (strcaseeq(fmtOpt, "NPM"))
+            cmdlineP->outputFormat = FMT_NPM;
+        else
+            pm_error("-fmt option must be HEX_NGG, HEX_NOL, HEX_NPM, "
+                     "NGG, NOL or NPM.  You specified '%s'", fmtOpt);
+    } else
+        cmdlineP->outputFormat = FMT_HEX_NOL;
+
+    if (netSpec) {
+        if (strlen(netOpt) != 6)
+            pm_error("-net option must be 6 hex digits long.  "
+                     "You specified %u characters", strlen(netOpt));
+        else if (!strishex(netOpt))
+            pm_error("-net option must be hexadecimal.  You specified '%s'",
+                     netOpt);
+        else
+            cmdlineP->networkCode = uppercase(netOpt);
+    } else
+        cmdlineP->networkCode = strdup("62F210");  /* German D1 net */
+
+    if (!txtSpec)
+        cmdlineP->txt = NULL;
+    else if (strlen(cmdlineP->txt) > 120)
+        pm_error("Text message is longer (%u characters) than "
+                 "the 120 characters allowed by the format.",
+                 strlen(cmdlineP->txt));
+
+    if (argc-1 == 0) 
+        cmdlineP->inputFileName = "-";
+    else if (argc-1 != 1)
+        pm_error("Program takes zero or one argument (filename).  You "
+                 "specified %u", argc-1);
+    else
+        cmdlineP->inputFileName = argv[1];
 }
 
-int 
-main(int argc, char *argv[])
-{
-    FILE    *fp;
-    bit    **image;
-    unsigned int    c;
-    int    argpos, output=FMT_HEX_NOL, rows, cols, row, col, p, it, len;
-    char    header[32], *myname;
-    char    network_code[6+1];
-    char    *text=NULL;
-
-    if ((myname=strrchr(argv[0],'/'))!=NULL) myname++; else myname=argv[0];
 
-    pbm_init(&argc, argv);
 
-    strcpy(network_code, "62F210"); /* default is German D1 net */
-
-    for(argpos=1;argpos<argc;argpos++) {
-        if (argv[argpos][0]=='-') {
-            if (argv[argpos][1]=='-') {
-                if (argc>argpos+1 && ISDIGIT(argv[argpos+1][0]))
-                    {argpos++;break;}
-            } else if (STREQ(argv[argpos],"-fmt") && argc>argpos+1) {
-                ++argpos;
-                if (!strcasecmp(argv[argpos],"HEX_NOL")) output=FMT_HEX_NOL;
-                else if (!strcasecmp(argv[argpos],"HEX_NGG")) 
-                    output=FMT_HEX_NGG;
-                else if (!strcasecmp(argv[argpos],"HEX_NPM")) 
-                    output=FMT_HEX_NPM;
-                else if (!strcasecmp(argv[argpos],"NOL")) output=FMT_NOL;
-                else if (!strcasecmp(argv[argpos],"NGG")) output=FMT_NGG;
-                else usage(myname);
-            } else if (STREQ(argv[argpos],"-net") && argc>argpos+1) {
-                char * const network_code_arg=argv[++argpos];
-                unsigned int it;
-                len=strlen(network_code_arg);
-                if (len!=6) 
-                    pm_error("Network code must be 6 hex-digits long");
-                for (it=0;it<strlen(network_code_arg);it++) {
-                    if (!ISXDIGIT(network_code_arg[it])) 
-                        pm_error("Network code must contain hex-digits only");
-                    network_code[it]=TOUPPER(network_code_arg[it]);
-                }
-                network_code[it] = '\0';
-            } else if (STREQ(argv[argpos],"-txt") && argc>argpos+1) {
-                text=argv[++argpos];
+static void
+freeCmdline(struct cmdlineInfo const cmdline) {
+
+    strfree(cmdline.networkCode);
+}
+
+
+
+static void
+validateSize(unsigned int const cols,
+             unsigned int const rows){
+
+    if (cols > 255)
+        pm_error("This program cannot handle files with more than 255 "
+                 "columns");
+    if (rows > 255)
+        pm_error("This program cannot handle files with more than 255 "
+                 "rows");
+}
+
+
+
+
+static void
+convertToHexNol(bit **       const image,
+                unsigned int const cols,
+                unsigned int const rows,
+                const char * const networkCode,
+                FILE *       const ofP) {
+
+    unsigned int row;
+
+    /* header */
+    fprintf(ofP, "06050415820000%s00%02X%02X01", networkCode, cols, rows);
+    
+    /* image */
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+        unsigned int p;
+        unsigned int c;
+
+        c = 0;
+
+        for (p = 0, col = 0; col < cols; ++col) {
+            if (image[row][col] == PBM_BLACK)
+                c |= 0x80 >> p;
+            if (++p == 8) {
+                fprintf(ofP, "%02X",c);
+                p = c = 0;
             }
-            else usage(myname);
-        } else break;
+        }
+        if (p > 0)
+            fprintf(ofP, "%02X", c);
     }
+}
 
-    if (argpos==argc) {
-        image = pbm_readpbm(stdin, &cols, &rows);
-    } else {
-        fp=pm_openr(argv[argpos]);
-        image = pbm_readpbm(fp, &cols, &rows);
-        pm_close(fp);
-    }
 
-    memset(header,0,sizeof(header));
 
-    switch (output) {
-    case FMT_HEX_NOL:
-        /* header */
-        printf("06050415820000%s00%02X%02X01",network_code,cols,rows);
-
-        /* image */
-        for (row=0;row<rows;row++) {
-            for (p=c=col=0;col<cols;col++) {
-                if (image[row][col]==PBM_BLACK) c|=0x80>>p;
-                if (++p==8) {
-                    printf("%02X",c);
-                    p=c=0;
-                }
+static void
+convertToHexNgg(bit **       const image,
+                unsigned int const cols,
+                unsigned int const rows,
+                FILE *       const ofP) {
+
+    unsigned int row;
+
+    /* header */
+    fprintf(ofP, "0605041583000000%02X%02X01", cols, rows);
+
+    /* 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) {
+            if (image[row][col] == PBM_BLACK)
+                c |= 0x80 >> p;
+            if (++p == 8) {
+                fprintf(ofP, "%02X", c);
+                p = c = 0;
             }
-            if (p) printf("%02X",c);
         }
-        break;
-    case FMT_HEX_NGG:
-        /* header */
-        printf("0605041583000000%02X%02X01",cols,rows);
-
-        /* image */
-        for (row=0;row<rows;row++) {
-            for (p=c=col=0;col<cols;col++) {
-                if (image[row][col]==PBM_BLACK) c|=0x80>>p;
-                if (++p==8) {
-                    printf("%02X",c);
-                    p=c=0;
-                }
+        if (p > 0)
+            fprintf(ofP, "%02X", c);
+    }
+}
+
+
+
+
+static void
+convertToHexNpm(bit **       const image,
+                unsigned int const cols,
+                unsigned int const rows,
+                const char * const text,
+                FILE *       const ofP) {
+
+    unsigned int row;
+    
+    /* header */
+    fprintf(ofP, "060504158A0000");
+
+    /* text */
+    if (text) {
+        size_t const len = strlen(text);
+
+        unsigned int it;
+
+        fprintf(ofP, "00%04X", len);
+
+        for (it = 0; it < len; ++it)
+            fprintf(ofP, "%02X", text[it]);
+    }
+
+    /* image */
+    fprintf(ofP, "02%04X00%02X%02X01", (cols * rows) / 8 + 4, cols, rows);
+
+    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) {
+            if (image[row][col] == PBM_BLACK)
+                c |= 0x80 >> p;
+            if (++p == 8) {
+                fprintf(ofP, "%02X", c);
+                p = c = 0;
             }
-            if (p) printf("%02X",c);
         }
-        break;
-    case FMT_HEX_NPM:
-        /* header */
-        printf("060504158A0000");
+        if (p > 0)
+            fprintf(ofP, "%02X", c);
+    }
+}
+
+
+
+static void
+convertToNol(bit **       const image,
+             unsigned int const cols,
+             unsigned int const rows,
+             FILE *       const ofP) {
+
+    unsigned int row;
+    char header[32];
+    unsigned int it;
+    
+    /* header - this is a hack */
+
+    header[ 0] = 'N';
+    header[ 1] = 'O';
+    header[ 2] = 'L';
+    header[ 3] = 0;
+    header[ 4] = 1;
+    header[ 5] = 0;
+    header[ 6] = 4;
+    header[ 7] = 1;
+    header[ 8] = 1;
+    header[ 9] = 0;
+    header[10] = cols;
+    header[11] = 0;
+    header[12] = rows;
+    header[13] = 0;
+    header[14] = 1;
+    header[15] = 0;
+    header[16] = 1;
+    header[17] = 0;
+    header[18] = 0x53;
+    header[19] = 0;
 
-        /* text */
-        if (text!=NULL) {
-            printf("00%04X",(len=strlen(text)));
-            for (it=0;it<len;it++) printf("%02X",text[it]);
+    fwrite(header, 20, 1, ofP);
+    
+    /* 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) {
+            char const output = image[row][col] == PBM_BLACK ? '1' : '0';
+
+            putc(output, ofP);
         }
+    }
 
-        /* image */
-        printf("02%04X00%02X%02X01",(cols*rows)/8+4,cols,rows);
-        for (row=0;row<rows;row++) {
-            for (p=c=col=0;col<cols;col++) {
-                if (image[row][col]==PBM_BLACK) c|=0x80>>p;
-                if (++p==8) {
-                    printf("%02X",c);
-                    p=c=0;
-                }
-            }
-            if (p) printf("%02X",c);
+    /* padding (to keep gnokii happy) */
+    for (it = 0; it < 8 - cols * rows % 8; ++it)
+        putc('0', ofP);
+}
+
+
+
+
+static void
+convertToNgg(bit **       const image,
+             unsigned int const cols,
+             unsigned int const rows,
+             FILE *       const ofP) {
+
+    unsigned int row;
+    char    header[32];
+    unsigned int it;
+
+    /* header - this is a hack */
+
+    header[ 0] = 'N';
+    header[ 1] = 'G';
+    header[ 2] = 'G';
+    header[ 3] = 0;
+    header[ 4] = 1;
+    header[ 5] = 0;
+    header[ 6] = cols;
+    header[ 7] = 0;
+    header[ 8] = rows;
+    header[ 9] = 0;
+    header[10] = 1;
+    header[11] = 0;
+    header[12] = 1;
+    header[13] = 0;
+    header[14] = 0x4a;
+    header[15] = 0;
+
+    fwrite(header, 16, 1, ofP);
+    
+    /* 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) {
+            char const output = image[row][col] == PBM_BLACK ? '1' : '0';
+
+            putc(output, ofP);
         }
-        break;
-    case FMT_NOL:
-        /* header - this is a hack */
-        header[0]='N';
-        header[1]='O';
-        header[2]='L';
-        header[4]=header[7]=header[8]=header[14]=header[16]=1;
-        header[6]=4;
-        header[10]=cols;
-        header[12]=rows;
-        header[18]=0x53;
-        fwrite(header,20,1,stdout);
-
-        /* image */
-        for (row=0;row<rows;row++) {
-            for (p=c=col=0;col<cols;col++) {
-                if (image[row][col]==PBM_BLACK) putchar('1');
-                else putchar('0');
+    }
+
+    /* padding (to keep gnokii happy) */
+    for (it = 0; it < 8 - cols * rows % 8; ++it)
+        putc('0', ofP);
+}
+
+
+
+static void
+convertToNpm(bit **       const image,
+             unsigned int const cols,
+             unsigned int const rows,
+             const char * const text,
+             FILE *       const ofP) {
+
+    unsigned int row;
+    char header[132];
+    size_t len;
+
+    if (text) 
+        len = strlen(text);
+    else
+        len = 0;
+
+    /* header and optional text */
+
+    header[       0] = 'N';
+    header[       1] = 'P';
+    header[       2] = 'M';
+    header[       3] = 0;
+    header[       4] = len;
+    header[       5] = 0;
+    memcpy(&header[5], text, len);
+    header[ 6 + len] = cols;
+    header[ 7 + len] = rows;
+    header[ 8 + len] = 1;
+    header[ 9 + len] = 1;
+    header[10 + len] = 0; /* unknown */
+
+    assert(10 + len < sizeof(header));
+
+    fwrite(header, 11 + len, 1, ofP);
+    
+    /* image: stream of bits, each row padded to a byte boundary
+       inspired by gnokii/common/gsm-filesystems.c
+     */
+    for (row = 0; row < rows; row++) {
+        unsigned int byteNumber;
+        int bitNumber;
+        char buffer[32];  /* picture messages are (always?) 72 x 28 */
+        unsigned int col;
+
+        byteNumber = 0;
+        bitNumber = 7;
+
+        memset(buffer, 0, sizeof(buffer));
+
+        for (col = 0; col < cols; ++col) {
+            if (image[row][col] == PBM_BLACK)
+                buffer[byteNumber] |= (1 << bitNumber);
+            --bitNumber;
+            if (bitNumber < 0 && col < (cols - 1)) {
+                bitNumber = 7;
+                ++byteNumber;
             }
         }
+        fwrite(buffer, byteNumber + 1, 1, ofP);
+    }
+}
+
+
+
+int 
+main(int    argc,
+     char * argv[]) {
+
+    struct cmdlineInfo cmdline;
+    FILE  * ifP;
+    bit ** bits;
+    int rows, cols;
+
+    pbm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFileName);
+    bits = pbm_readpbm(ifP, &cols, &rows);
+    pm_close(ifP);
+
+    validateSize(cols, rows);
+
+    switch (cmdline.outputFormat) {
+    case FMT_HEX_NGG:
+        convertToHexNgg(bits, cols, rows, stdout);
+        break;
+    case FMT_HEX_NOL:
+        convertToHexNol(bits, cols, rows, cmdline.networkCode, stdout);
+        break;
+    case FMT_HEX_NPM:
+        convertToHexNpm(bits, cols, rows, cmdline.txt, stdout);
         break;
     case FMT_NGG:
-        /* header - this is a hack */
-        header[0]='N';
-        header[1]='G';
-        header[2]='G';
-        header[4]=header[10]=header[12]=1;
-        header[6]=cols;
-        header[8]=rows;
-        header[14]=0x4a;
-        fwrite(header,16,1,stdout);
-
-        /* image */
-        for (row=0;row<rows;row++) {
-            for (p=c=col=0;col<cols;col++) {
-                if (image[row][col]==PBM_BLACK) putchar('1');
-                else putchar('0');
-            }
-        }
+        convertToNgg(bits, cols, rows, stdout);
+        break;
+    case FMT_NOL:
+        convertToNol(bits, cols, rows, stdout);
+        break;
+    case FMT_NPM:
+        convertToNpm(bits, cols, rows, cmdline.txt, stdout);
         break;
-    default:
-        pm_error("Output format %d not implemented!\n"
-                 "Contact Tim Ruehsen <tim.ruehsen@openmediasystem.de>\n",
-                 output);
-        return 1;
     }
+
+freeCmdline(cmdline);
+
     return 0;
 }
 
+
+
+/* Copyright (C)2001 OMS Open Media System GmbH, Tim Rühsen
+** <tim.ruehsen@openmediasystem.de>.
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+
+  Created 2001.06.07
+
+Notes:
+  - limited to rows <= 255 and columns <= 255
+  - limited to b/w graphics, not animated
+
+Testing:
+  Testing was done with SwissCom SMSC (Switzerland) and IC3S SMSC (Germany).
+  The data was send with EMI/UCP protocol over TCP/IP.
+
+  - 7.6.2001: tested with Nokia 3210: 72x14 Operator Logo
+  - 7.6.2001: tested with Nokia 6210: 72x14 Operator Logo and 
+              72x14 Group Graphic
+*/
diff --git a/converter/pbm/pbmtopi3.c b/converter/pbm/pbmtopi3.c
index 8d178f61..1dbf1a71 100644
--- a/converter/pbm/pbmtopi3.c
+++ b/converter/pbm/pbmtopi3.c
@@ -90,14 +90,8 @@ putinit( )
     bitshift = 7;
     }
 
-#if __STDC__
 static void
 putbit( bit b )
-#else /*__STDC__*/
-static void
-putbit( b )
-    bit b;
-#endif /*__STDC__*/
     {
     if (bitsperitem == 8)
 	putitem( );
diff --git a/converter/pbm/pbmtopk.c b/converter/pbm/pbmtopk.c
index 08aff49a..fc94f855 100644
--- a/converter/pbm/pbmtopk.c
+++ b/converter/pbm/pbmtopk.c
@@ -172,7 +172,7 @@ pbmtopk_add_suffix(char * const name,
     char *slash = strrchr(name, '/');
     char *dot = strrchr(name, '.');
 
-    if ((dot && slash ? dot < slash : !dot) && !STREQ(name, "-"))
+    if ((dot && slash ? dot < slash : !dot) && !streq(name, "-"))
         strcat(name, suffix);
 }
 
diff --git a/converter/pbm/pbmtoppa/Makefile b/converter/pbm/pbmtoppa/Makefile
index ba15adaa..5f205230 100644
--- a/converter/pbm/pbmtoppa/Makefile
+++ b/converter/pbm/pbmtoppa/Makefile
@@ -5,7 +5,7 @@ endif
 SUBDIR = converter/pbm/pbmtoppa
 VPATH=.:$(SRCDIR)/$(SUBDIR)
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
 all: pbmtoppa
 
@@ -16,7 +16,7 @@ MERGEBINARIES = $(BINARIES)
 OBJECTS = pbmtoppa.o ppa.o pbm.o cutswath.o
 MERGE_OBJECTS = pbmtoppa.o2 ppa.o pbm.o cutswath.o
 
-include $(SRCDIR)/Makefile.common
+include $(SRCDIR)/common.mk
 
 pbmtoppa: $(OBJECTS) $(NETPBMLIB) $(LIBOPT)
 	$(LD) -o pbmtoppa $(OBJECTS) \
diff --git a/converter/pbm/pbmtopsg3.c b/converter/pbm/pbmtopsg3.c
index 68b265f0..54d0a0a0 100644
--- a/converter/pbm/pbmtopsg3.c
+++ b/converter/pbm/pbmtopsg3.c
@@ -26,6 +26,7 @@
 #include <unistd.h>
 #include <ctype.h>
 
+#include "pm_c_util.h"
 #include "pbm.h"
 #include "shhopt.h"
 
diff --git a/converter/pbm/pbmtoptx.c b/converter/pbm/pbmtoptx.c
index 5031efcb..8cd60326 100644
--- a/converter/pbm/pbmtoptx.c
+++ b/converter/pbm/pbmtoptx.c
@@ -67,14 +67,8 @@ putinit( )
     bitshift = 0;
     }
 
-#if __STDC__
 static void
 putbit( bit b )
-#else /*__STDC__*/
-static void
-putbit( b )
-    bit b;
-#endif /*__STDC__*/
     {
     if ( bitsperitem == 6 )
 	putitem( );
diff --git a/converter/pbm/pbmtox10bm b/converter/pbm/pbmtox10bm
new file mode 100644
index 00000000..9a1a7286
--- /dev/null
+++ b/converter/pbm/pbmtox10bm
@@ -0,0 +1,45 @@
+#! /usr/bin/perl
+
+#============================================================================
+#  This is a compatibility interface to Pbmtoxbm.
+#
+#  It exists so existing programs and procedures that rely on Pbmtox10bm
+#  syntax continue to work.  You should not make new use of Pbmtox10bm and
+#  if you modify an old use, you should upgrade it to use Pbmtoxbm.
+#
+#  Pbmtoxbm with the -x10 option is backward compatible with Pbmtox10bm.
+#============================================================================
+
+use strict;
+use File::Basename;
+use Cwd 'abs_path';
+
+my $infile;
+
+foreach (@ARGV) {
+    if (/^-/) {
+        # It's an option.  But Pbmtox10bm didn't have any options.
+        print(STDERR "Invalid option '$_'\n");
+        exit(10);
+    } else {
+        # It's a parameter
+        if (defined($infile)) {
+            print(STDERR
+                  "You may specify at most one non-option parameter.\n");
+            exit(10);
+        } else {
+            $infile = $_;
+        }
+    }
+}
+
+my $infileParm = defined($infile) ? $infile : "-";
+
+# We want to get Pbmtoxbm from the same directory we came from if
+# it's there.  Frequently, the directory containing Netpbm programs is
+# not in the PATH and we were invoked by absolute path.
+
+my $my_directory = abs_path(dirname($0));
+$ENV{"PATH"} = $my_directory . ":" . $ENV{"PATH"};
+
+exec('pbmtoxbm', '-x10', $infileParm);
diff --git a/converter/pbm/pbmtox10bm.c b/converter/pbm/pbmtox10bm.c
deleted file mode 100644
index ef31fb9b..00000000
--- a/converter/pbm/pbmtox10bm.c
+++ /dev/null
@@ -1,120 +0,0 @@
-/* pbmtox10bm.c - read a portable bitmap and produce an X10 bitmap file
-**
-** Copyright (C) 1988 by Jef Poskanzer.
-**
-** Permission to use, copy, modify, and distribute this software and its
-** documentation for any purpose and without fee is hereby granted, provided
-** that the above copyright notice appear in all copies and that both that
-** copyright notice and this permission notice appear in supporting
-** documentation.  This software is provided "as is" without express or
-** implied warranty.
-*/
-
-#include <string.h>
-
-#include "nstring.h"
-#include "pbm.h"
-
-int
-main(int argc, char * argv[]) {
-
-    FILE* ifp;
-    bit* bitrow;
-    bit * bP;
-    int rows, cols, format, padright, row;
-    int col;
-    char name[100];
-    char* cp;
-    int itemsperline;
-    int bitsperitem;
-    int item;
-    int firstitem;
-    const char* const hexchar = "0123456789abcdef";
-
-
-    pbm_init( &argc, argv );
-
-    if ( argc > 2 )
-        pm_usage( "[pbmfile]" );
-
-    if ( argc == 2 )
-	{
-        ifp = pm_openr( argv[1] );
-        strcpy( name, argv[1] );
-        if (STREQ( name, "-" ))
-            strcpy( name, "noname" );
-
-        if ( ( cp = strchr( name, '.' ) ) != 0 )
-            *cp = '\0';
-	}
-    else
-	{
-        ifp = stdin;
-        strcpy( name, "noname" );
-	}
-
-    pbm_readpbminit( ifp, &cols, &rows, &format );
-    bitrow = pbm_allocrow( cols );
-
-    /* Compute padding to round cols up to the nearest multiple of 16. */
-    padright = ( ( cols + 15 ) / 16 ) * 16 - cols;
-
-    printf( "#define %s_width %d\n", name, cols );
-    printf( "#define %s_height %d\n", name, rows );
-    printf( "static short %s_bits[] = {\n", name );
-
-    itemsperline = 0;
-    bitsperitem = 0;
-    item = 0;
-    firstitem = 1;
-
-#define PUTITEM \
-    { \
-        if ( firstitem ) \
-	        firstitem = 0; \
-        else \
-	        putchar( ',' ); \
-    if ( itemsperline == 11 ) \
-	{ \
-	    putchar( '\n' ); \
-	    itemsperline = 0; \
-	} \
-    if ( itemsperline == 0 ) \
-	    putchar( ' ' ); \
-    ++itemsperline; \
-    putchar('0'); \
-    putchar('x'); \
-    putchar(hexchar[item >> 12]); \
-    putchar(hexchar[(item >> 8) & 15]); \
-    putchar(hexchar[(item >> 4) & 15]); \
-    putchar(hexchar[item & 15]); \
-    bitsperitem = 0; \
-    item = 0; \
-    }
-
-#define PUTBIT(b) \
-    { \
-    if ( bitsperitem == 16 ) \
-	    PUTITEM; \
-    if ( (b) == PBM_BLACK ) \
-	    item += 1 << bitsperitem; \
-    ++bitsperitem; \
-    }
-
-    for ( row = 0; row < rows; ++row )
-	{
-        pbm_readpbmrow( ifp, bitrow, cols, format );
-        for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
-            PUTBIT(*bP);
-        for ( col = 0; col < padright; ++col )
-            PUTBIT(0);
-    }
-
-    pm_close( ifp );
-    
-    if ( bitsperitem > 0 )
-        PUTITEM;
-    printf( "};\n" );
-
-    return 0;
-}
diff --git a/converter/pbm/pbmtoxbm.c b/converter/pbm/pbmtoxbm.c
index 96830a0c..340642ce 100644
--- a/converter/pbm/pbmtoxbm.c
+++ b/converter/pbm/pbmtoxbm.c
@@ -1,4 +1,4 @@
-/* pbmtoxbm.c - read a portable bitmap and produce an X11 bitmap file
+/* pbmtoxbm.c - read a PBM image and produce an X11/X10 bitmap file
 **
 ** Copyright (C) 1988 by Jef Poskanzer.
 **
@@ -10,17 +10,114 @@
 ** implied warranty.
 */
 
+/* 2006.10 (afu)   
+   Changed bitrow from plain to raw, read function from pbm_readpbmrow() to
+   pbm_readpbmrow_packed().  Retired bitwise transformation functions.
+ 
+   Output function putitem rewritten to handle both X10 and X11.
+
+   Added -name option.  There is no check for the string thus given.
+
+*/
+
 #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 "pm_c_util.h"
 #include "pbm.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+#include "bitreverse.h"
 #include "nstring.h"
 
 
+enum xbmVersion { X10, X11 };
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char *    inputFileName;
+    const char *    name;
+    enum xbmVersion xbmVersion;
+};
+
+static void
+parseCommandLine(int                 argc, 
+                 char **             argv,
+                 struct cmdlineInfo *cmdlineP ) {
+/*----------------------------------------------------------------------------
+   Parse program command line described in Unix standard form by argc
+   and argv.  Return the information in the options as *cmdlineP.  
+
+   If command line is internally inconsistent (invalid options, etc.),
+   issue error message to stderr and abort program.
+
+   Note that the strings we return are stored in the storage that
+   was passed to us as the argv array.  We also trash *argv.
+-----------------------------------------------------------------------------*/
+    optEntry *option_def;
+    /* Instructions to optParseOptions3 on how to parse our options. */
+
+    optStruct3 opt;
+    unsigned int option_def_index;
+    unsigned int x10, x11, nameSpec;
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+
+    OPTENT3(0, "name", OPT_STRING, &cmdlineP->name, &nameSpec, 0);
+    OPTENT3(0, "x10" , OPT_FLAG,   NULL, &x10, 0);
+    OPTENT3(0, "x11" , OPT_FLAG,   NULL, &x11, 0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    optParseOptions3( &argc, argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (!nameSpec)
+        cmdlineP->name = NULL;
+    else if (strlen(cmdlineP->name) > 56)
+        pm_error("Image name too long: %d chars. (max 56)",
+                 strlen(cmdlineP->name));
+    else if (!ISALPHA(cmdlineP->name[0]) && cmdlineP->name[0] !='_')
+        pm_error("Image name '%s' starts with non-alphabet character.",
+                  cmdlineP->name);
+    else {
+        unsigned int i;
+        for (i = 0 ; i < strlen(cmdlineP->name); ++i)
+            if (!ISALNUM(cmdlineP->name[i]) && cmdlineP->name[i] != '_')
+                pm_error("Image name '%s' contains invalid character (%c).",
+                         cmdlineP->name, cmdlineP->name[i]);
+    }
+    
+    if (x10 && x11)
+        pm_error("You can't specify both -x10 and -x11");
+    else if (x10)
+        cmdlineP->xbmVersion = X10;
+    else 
+        cmdlineP->xbmVersion = X11;
+        
+    if (argc-1 < 1) 
+        cmdlineP->inputFileName = "-";
+    else {
+        cmdlineP->inputFileName = argv[1];
+        
+        if (argc-1 > 1)
+            pm_error("Program takes zero or one argument (filename).  You "
+                     "specified %u", argc-1);
+    }
+}
+
+
+
 static void
-generateName(char const filenameArg[], const char ** const nameP) {
+generateName(char          const filenameArg[],
+             const char ** const nameP) {
 /*----------------------------------------------------------------------------
    Generate a name for the image to put in the bitmap file.  Derive it from
    the filename argument filenameArg[] and return it as a null-terminated
@@ -34,7 +131,7 @@ generateName(char const filenameArg[], const char ** const nameP) {
    "noname".  Also, if the argument is null or ends in a slash, we
    return "noname".
 -----------------------------------------------------------------------------*/
-    if (STREQ(filenameArg, "-"))
+    if (streq(filenameArg, "-"))
         *nameP = strdup("noname");
     else {
         int nameIndex, argIndex;
@@ -67,99 +164,248 @@ generateName(char const filenameArg[], const char ** const nameP) {
 
 
 
-int
-main(int argc, char * argv[]) {
+static unsigned short int itemBuff[22];
+static int itemCnt;    /* takes values 0 to 15 (x11) or 21 (x10) */
+static enum xbmVersion itemVersion;
 
-    FILE* ifp;
-    bit* bitrow;
-    int rows, cols, format;
-    int padright;
-    int row;
-    const char * inputFilename;
-    const char *name;
-    int itemsperline;
-    int bitsperitem;
-    int item;
-    int firstitem;
-    const char hexchar[] = "0123456789abcdef";
 
-    pbm_init(&argc, argv);
 
-    if (argc-1 > 1)
-        pm_error("Too many arguments (%d).  The only valid argument is an "
-                 "input file name.", argc-1);
-    else if (argc-1 == 1) 
-        inputFilename = argv[1];
-    else
-        inputFilename = "-";
+static void
+putitemX10(unsigned char const item) {
 
-    generateName(inputFilename, &name);
-    ifp = pm_openr(inputFilename);
-    
-    pbm_readpbminit(ifp, &cols, &rows, &format);
-    bitrow = pbm_allocrow(cols);
-    
-    /* Compute padding to round cols up to the nearest multiple of 8. */
-    padright = ((cols + 7)/8) * 8 - cols;
-
-    printf("#define %s_width %d\n", name, cols);
-    printf("#define %s_height %d\n", name, rows);
-    printf("static char %s_bits[] = {\n", name);
-
-    itemsperline = 0;
-    bitsperitem = 0;
-    item = 0;
-    firstitem = 1;
-
-#define PUTITEM \
-    { \
-    if ( firstitem ) \
-        firstitem = 0; \
-    else \
-        putchar( ',' ); \
-    if ( itemsperline == 15 ) \
-        { \
-        putchar( '\n' ); \
-        itemsperline = 0; \
-        } \
-    if ( itemsperline == 0 ) \
-        putchar( ' ' ); \
-    ++itemsperline; \
-    putchar('0'); \
-    putchar('x'); \
-    putchar(hexchar[item >> 4]); \
-    putchar(hexchar[item & 15]); \
-    bitsperitem = 0; \
-    item = 0; \
+    if (itemCnt == 22) {
+        /* Buffer is full.  Write out one line. */
+        int rc;
+        rc = printf(" 0x%02x%02x,0x%02x%02x,0x%02x%02x,0x%02x%02x,"
+                    "0x%02x%02x,0x%02x%02x,0x%02x%02x,0x%02x%02x,"
+                    "0x%02x%02x,0x%02x%02x,0x%02x%02x,\n",
+                    itemBuff[ 1], itemBuff[ 0], itemBuff[ 3], itemBuff[ 2],
+                    itemBuff[ 5], itemBuff[ 4], itemBuff[ 7], itemBuff[ 6],
+                    itemBuff[ 9], itemBuff[ 8], itemBuff[11], itemBuff[10],
+                    itemBuff[13], itemBuff[12], itemBuff[15], itemBuff[14],
+                    itemBuff[17], itemBuff[16], itemBuff[19], itemBuff[18],
+                    itemBuff[21], itemBuff[20]
+            );
+
+        if (rc < 0)        
+            pm_error("Error writing X10 bitmap raster item.  "
+                     "printf() failed with errno %d (%s)",
+                     errno, strerror(errno));
+        
+        itemCnt = 0;
     }
+    itemBuff[itemCnt++] = bitreverse[item];
+}
+
 
-#define PUTBIT(b) \
-    { \
-    if ( bitsperitem == 8 ) \
-        PUTITEM; \
-    if ( (b) == PBM_BLACK ) \
-        item += 1 << bitsperitem; \
-    ++bitsperitem; \
+
+static void
+putitemX11(unsigned char const item) {
+
+    if (itemCnt == 15 ) {
+        /* Buffer is full.  Write out one line. */
+        int rc;
+        rc = printf(" 0x%02x,0x%02x,0x%02x,0x%02x,"
+                    "0x%02x,0x%02x,0x%02x,0x%02x,"
+                    "0x%02x,0x%02x,0x%02x,0x%02x,"
+                    "0x%02x,0x%02x,0x%02x,\n",
+                    itemBuff[0], itemBuff[1], itemBuff[2], itemBuff[3],
+                    itemBuff[4], itemBuff[5], itemBuff[6], itemBuff[7],
+                    itemBuff[8], itemBuff[9], itemBuff[10],itemBuff[11],
+                    itemBuff[12],itemBuff[13],itemBuff[14]
+            );
+        if (rc < 0)        
+            pm_error("Error writing X11 bitmap raster item.  "
+                     "printf() failed with errno %d (%s)",
+                     errno, strerror(errno));
+        
+        itemCnt = 0;
     }
+    itemBuff[itemCnt++] = bitreverse[item];
+}
 
-    for (row = 0; row < rows; ++row) {
-        int col;
-        pbm_readpbmrow(ifp, bitrow, cols, format);
-        for (col = 0; col < cols; ++col)
-            PUTBIT(bitrow[col]);
-        for (col = 0; col < padright; ++col)
-            PUTBIT(0);
+
+
+static void
+putitem(unsigned char const item) {
+
+    switch (itemVersion) {
+    case X10: putitemX10(item); break;
+    case X11: putitemX11(item); break;
     }
+}
+
+
+
+static void
+puttermX10(void) {
+
+    unsigned int i;
+
+    for (i = 0; i < itemCnt; i += 2) {
+        int rc;
+
+        rc = printf("%s0x%02x%02x%s",
+                    (i == 0) ? " " : "",
+                    itemBuff[i+1],
+                    itemBuff[i], 
+                    (i == itemCnt - 2) ? "" : ",");
+        if (rc < 0)        
+            pm_error("Error writing end of X10 bitmap raster.  "
+                     "printf() failed with errno %d (%s)",
+                     errno, strerror(errno));
+    }
+}
+
+
+
+static void
+puttermX11(void) {
+
+    unsigned int i;
 
-    pm_close(ifp);
+    for (i = 0; i < itemCnt; ++i) {
+        int rc;
 
-    if (bitsperitem > 0)
-        PUTITEM;
-    printf("};\n");
+        rc = printf("%s0x%02x%s",
+                    (i == 0)  ? " " : "",
+                    itemBuff[i],
+                    (i == itemCnt - 1) ? "" : ",");
+
+        if (rc < 0)        
+            pm_error("Error writing end of X11 bitmap raster.  "
+                     "printf() failed with errno %d (%s)",
+                     errno, strerror(errno));
+    }
+}
+
+
+
+static void
+putinit(enum xbmVersion const xbmVersion) {
+
+    itemCnt = 0;
+    itemVersion = xbmVersion;
+}
+
+
+
+static void
+putterm(void) {
+
+    switch (itemVersion) {
+    case X10: puttermX10(); break;
+    case X11: puttermX11(); break;
+    }
+
+    {
+        int rc;
+
+        rc = printf("};\n");
+
+        if (rc < 0)        
+            pm_error("Error writing end of X11 bitmap raster.  "
+                     "printf() failed with errno %d (%s)",
+                     errno, strerror(errno));
+    }
+}
+
+
+
+static void
+writeXbmHeader(enum xbmVersion const xbmVersion,
+               const char *    const name,
+               unsigned int    const width,
+               unsigned int    const height,
+               FILE *          const ofP) {
+
+    printf("#define %s_width %d\n", name, width);
+    printf("#define %s_height %d\n", name, height);
+    printf("static %s %s_bits[] = {\n",
+           xbmVersion == X10 ? "short" : "char",
+           name);
+}
+
+
+
+static void
+convertRaster(FILE *          const ifP,
+              unsigned int    const cols,
+              unsigned int    const rows,
+              int             const format,
+              FILE *          const ofP,
+              enum xbmVersion const xbmVersion) {
+              
+    unsigned int const bitsPerUnit = xbmVersion == X10 ? 16 : 8;   
+    unsigned int const padright =
+        ((cols + bitsPerUnit - 1 ) / bitsPerUnit) * bitsPerUnit - cols;
+        /* Amount of padding to round cols up to the nearest multiple of 
+           8 (if x11) or 16 (if x10).
+        */
+    unsigned int const bitrowBytes = (cols + padright) / 8;
+
+    unsigned char * bitrow;
+    unsigned int row;
+
+    putinit(xbmVersion);
+
+    bitrow = pbm_allocrow_packed(cols + padright);
+    
+    for (row = 0; row < rows; ++row) {
+        int const bitrowInBytes = pbm_packed_bytes(cols);
+        int const padrightIn    = bitrowInBytes * 8 - cols;
+
+        unsigned int i;
+
+        pbm_readpbmrow_packed(ifP, bitrow, cols, format);
+
+        if (padrightIn > 0) {
+            bitrow[bitrowInBytes - 1] >>= padrightIn;
+            bitrow[bitrowInBytes - 1] <<= padrightIn;
+        }
+
+        if (padright >= 8)
+            bitrow[bitrowBytes-1] = 0x00;
+
+        for (i = 0; i < bitrowBytes; ++i)
+            putitem(bitrow[i]);
+    }
+
+    putterm();
 
     pbm_freerow(bitrow);
+}
+
+
+
+int
+main(int    argc,
+     char * argv[]) {
+
+    struct cmdlineInfo cmdline; 
+    FILE * ifP;
+    int rows, cols, format;
+    const char * name;
+
+    pbm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+    if (cmdline.name == NULL) 
+        generateName(cmdline.inputFileName, &name);
+    else
+        name = strdup(cmdline.name);
+
+    ifP = pm_openr(cmdline.inputFileName);
+    
+    pbm_readpbminit(ifP, &cols, &rows, &format);
+    
+    writeXbmHeader(cmdline.xbmVersion, name, cols, rows, stdout);
+
+    convertRaster(ifP, cols, rows, format, stdout, cmdline.xbmVersion);
 
     strfree(name);
+    pm_close(ifP);
 
-    exit(0);
+    return 0;
 }
+
diff --git a/converter/pbm/pbmtoybm.c b/converter/pbm/pbmtoybm.c
index 1d2be3d9..508e8e92 100644
--- a/converter/pbm/pbmtoybm.c
+++ b/converter/pbm/pbmtoybm.c
@@ -80,14 +80,8 @@ putinit( cols, rows )
     bitshift = 0;
     }
 
-#if __STDC__
 static void
 putbit( bit b )
-#else /*__STDC__*/
-static void
-putbit( b )
-    bit b;
-#endif /*__STDC__*/
     {
     if ( bitsperitem == 16 )
 	putitem( );
diff --git a/converter/pbm/pbmtozinc.c b/converter/pbm/pbmtozinc.c
index d39b71bc..2df39f0d 100644
--- a/converter/pbm/pbmtozinc.c
+++ b/converter/pbm/pbmtozinc.c
@@ -49,7 +49,7 @@ main(int argc, char * argv[]) {
 	{
         ifp = pm_openr( argv[1] );
         strcpy( name, argv[1] );
-        if ( STREQ( name, "-" ) )
+        if ( streq( name, "-" ) )
             strcpy( name, "noname" );
 
         if ( ( cp = strchr( name, '.' ) ) != 0 )
diff --git a/converter/pbm/pktopbm.c b/converter/pbm/pktopbm.c
index af49e9c7..712f339f 100644
--- a/converter/pbm/pktopbm.c
+++ b/converter/pbm/pktopbm.c
@@ -50,7 +50,7 @@ pktopbm_add_suffix(char *       const name,
     char * const slash = strrchr(name, '/');
     char * const dot   = strrchr(name, '.');
     
-    if ((dot && slash ? dot < slash : !dot) && !STREQ(name, "-"))
+    if ((dot && slash ? dot < slash : !dot) && !streq(name, "-"))
         strcat(name, suffix);
 }
 
diff --git a/converter/pbm/thinkjettopbm.l b/converter/pbm/thinkjettopbm.l
index a66ae07e..71501596 100644
--- a/converter/pbm/thinkjettopbm.l
+++ b/converter/pbm/thinkjettopbm.l
@@ -37,6 +37,7 @@
 #include <string.h>
 #include <errno.h>
 #include <unistd.h>
+#include "pm_c_util.h"
 #include "pbm.h"
 #include "shhopt.h"
 
diff --git a/converter/pbm/xbmtopbm.c b/converter/pbm/xbmtopbm.c
index 7779a9b5..9505ba67 100644
--- a/converter/pbm/xbmtopbm.c
+++ b/converter/pbm/xbmtopbm.c
@@ -1,4 +1,4 @@
-/* xbmtopbm.c - read an X bitmap file and produce a portable bitmap
+/* xbmtopbm.c - read an X bitmap file and produce a PBM image
 **
 ** Copyright (C) 1988 by Jef Poskanzer.
 **
@@ -10,246 +10,382 @@
 ** implied warranty.
 */
 
+
+#include <assert.h>
 #include <string.h>
 
+#include "pm_c_util.h"
+#include "mallocvar.h"
 #include "nstring.h"
 #include "pbm.h"
+#include "bitreverse.h"
 
-#define TRUE 1
-#define FALSE 0
 
-static void ReadBitmapFile ARGS(( FILE* stream, int* widthP, int* heightP, char** dataP ));
 
-int
-main( argc, argv )
-    int argc;
-    char* argv[];
-    {
-    FILE* ifp;
-    bit* bitrow;
-    register bit* bP;
-    int rows, cols, row, col, charcount;
-    char* data;
-    char mask;
-
-    pbm_init( &argc, argv );
-
-    if ( argc > 2 )
-        pm_usage( "[bitmapfile]" );
-    
-    if ( argc == 2 )
-        ifp = pm_openr( argv[1] );
-    else
-        ifp = stdin;
-
-    ReadBitmapFile( ifp, &cols, &rows, &data );
-
-    pm_close( ifp );
-
-    pbm_writepbminit( stdout, cols, rows, 0 );
-    bitrow = pbm_allocrow( cols );
-
-    for ( row = 0; row < rows; ++row )
-        {
-        charcount = 0;
-        mask = 1;
-        for ( col = 0, bP = bitrow; col < cols; ++col, ++bP )
-            {
-            if ( charcount >= 8 )
-                {
-                ++data;
-                charcount = 0;
-                mask = 1;
-                }
-            *bP = ( *data & mask ) ? PBM_BLACK : PBM_WHITE;
-            ++charcount;
-            mask = mask << 1;
-            }
-        ++data;
-        pbm_writepbmrow( stdout, bitrow, cols, 0 );
-        }
+#define MAX_LINE 500
+
+static unsigned int hexTable[256];
+    /* Hexadecimal ASCII translation table.  Constant */
+
+static void
+initHexTable(void) {
+
+    unsigned int i;
+
+    for (i = 0; i < 256; ++i)
+        hexTable[i] = 256;
+
+    hexTable['0'] =  0;
+    hexTable['1'] =  1;
+    hexTable['2'] =  2;
+    hexTable['3'] =  3;
+    hexTable['4'] =  4;
+    hexTable['5'] =  5;
+    hexTable['6'] =  6;
+    hexTable['7'] =  7;
+    hexTable['8'] =  8;
+    hexTable['9'] =  9;
+    hexTable['A'] = 10;
+    hexTable['B'] = 11;
+    hexTable['C'] = 12;
+    hexTable['D'] = 13;
+    hexTable['E'] = 14;
+    hexTable['F'] = 15;
+    hexTable['a'] = 10;
+    hexTable['b'] = 11;
+    hexTable['c'] = 12;
+    hexTable['d'] = 13;
+    hexTable['e'] = 14;
+    hexTable['f'] = 15;
+}
 
-    pm_close( stdout );
-    exit( 0 );
-    }
 
-#define MAX_LINE 500
 
 static void
-ReadBitmapFile( stream, widthP, heightP, dataP )
-     FILE* stream;
-     int* widthP;
-     int* heightP;
-     char** dataP;
-{
-  char line[MAX_LINE], name_and_type[MAX_LINE];
-  char* ptr;
-  char* t;
-  int version10, raster_length, v;
-  register int bytes, bytes_per_line, padding;
-  register int c1, c2, value1, value2;
-  int hex_table[256];
-  int found_declaration;
-  /* In scanning through the bitmap file, we have found the first
-     line of the C declaration of the array (the "static char ..."
-     or whatever line)
-     */
-  int eof;
-  /* We've encountered end of file while searching file */
-
-  *widthP = *heightP = -1;
-
-  found_declaration = FALSE;    /* Haven't found it yet; haven't even looked*/
-  eof = FALSE;                  /* Haven't encountered end of file yet */
-
-  while (!found_declaration && !eof) {
-    if ( fgets( line, MAX_LINE, stream ) == NULL )
-      eof = TRUE;
-    else {
-      if ( strlen( line ) == MAX_LINE - 1 )
-        pm_error( "line too long" );
-
-      if ( sscanf( line, "#define %s %d", name_and_type, &v ) == 2 ) {
-        if ( ( t = strrchr( name_and_type, '_' ) ) == NULL )
-          t = name_and_type;
+parseWidthHeightLine(const char *   const line,
+                     bool *         const gotWidthP,
+                     unsigned int * const widthP,
+                     bool *         const gotHeightP,
+                     unsigned int * const heightP) {
+
+    int rc;
+    char nameAndType[MAX_LINE];
+    unsigned int value;
+
+    rc = sscanf(line, "#define %s %u", nameAndType, &value);
+    if (rc == 2) {
+        const char * underscorePos = strrchr(nameAndType, '_');
+        const char * type;
+        if (underscorePos)
+            type = underscorePos + 1;
         else
-          ++t;
-        if ( STREQ( "width", t ) )
-          *widthP = v;
-        else if ( STREQ( "height", t ) )
-          *heightP = v;
-        continue;
-      }
+            type = nameAndType;
+        if (streq(type, "width")) {
+            *gotWidthP = TRUE;
+            *widthP = value;
+        } else if (streq(type, "height")) {
+            *gotHeightP = TRUE;
+            *heightP = value;
+        }
+    }
+}
+
+
+
+static void
+parseDeclaration(const char * const line,
+                 bool *       const isDeclarationP,
+                 bool *       const version10P) {
+/*----------------------------------------------------------------------------
+   Parse the XBM file line 'line' as the first line of the data structure
+   declaration, i.e. the one that looks like this:
+
+      static unsigned char myImage = {
+
+   Return as *isDeclarationP whether the line actually is such a line,
+   and if so, return as nameAndType what the variable name ('myImage'
+   in the example) is and as *version10P whether it's of the type used
+   by X10 as opposed to X11.
+-----------------------------------------------------------------------------*/
+    char nameAndType[MAX_LINE];
+    int rc;
         
-      if ( sscanf( line, "static short %s = {", name_and_type ) == 1 ) {
-        version10 = TRUE;
-        found_declaration = TRUE;
-      }
-      else if ( sscanf( line, "static char %s = {", name_and_type ) == 1 ) {
-        version10 = FALSE;
-        found_declaration = TRUE;
-      }
-      else if (sscanf(line, 
-                      "static unsigned char %s = {", name_and_type ) == 1 ) {
-        version10 = FALSE;
-        found_declaration = TRUE;
-      }
+    rc = sscanf(line, "static short %s = {", nameAndType);
+    if (rc == 1) {
+        *version10P     = TRUE;
+        *isDeclarationP = TRUE;
+    } else {
+        int rc;
+        rc = sscanf(line, "static char %s = {", nameAndType);
+        if (rc == 1) {
+            *version10P     = FALSE;
+            *isDeclarationP = TRUE;
+        } else {
+            int rc;
+            rc = sscanf(line, "static unsigned char %s = {", nameAndType);
+            if (rc == 1) {
+                *version10P     = FALSE;
+                *isDeclarationP = TRUE;
+            } else
+                *isDeclarationP = FALSE;
+        }
     }
-  }
- 
-  if (!found_declaration) 
-    pm_error("Unable to find a line in the file containing the start "
-             "of C array declaration (\"static char\" or whatever)");
-
-  if ( *widthP == -1 )
-    pm_error( "invalid width" );
-  if ( *heightP == -1 )
-    pm_error( "invalid height" );
-
-  padding = 0;
-  if ( ((*widthP % 16) >= 1) && ((*widthP % 16) <= 8) && version10 )
-    padding = 1;
-
-  bytes_per_line = (*widthP+7)/8 + padding;
+}
+
+
+
+static void
+getXbmHeader(FILE *         const ifP,
+             unsigned int * const widthP,
+             unsigned int * const heightP,
+             bool *         const version10P) {
+
+    bool foundDeclaration;
+        /* In scanning through the bitmap file, we have found the first
+           line of the C declaration of the array (the "static char ..."
+           or whatever line)
+        */
+    bool gotWidth, gotHeight;
+        /* We found the line in the bitmap file that gives the width
+           or height, respectively, of the image (and have set
+           *widthP or *heightP to the value in it).
+        */
+
+    bool eof;
+        /* We've encountered end of file while searching file */
+
+    gotWidth = FALSE;
+    gotHeight = FALSE;
+    foundDeclaration = FALSE;    /* Haven't found it yet; haven't even looked*/
+    eof = FALSE;                 /* Haven't encountered end of file yet */
+
+    while (!foundDeclaration && !eof) {
+        char * rc;
+        char line[MAX_LINE];
+
+        rc = fgets(line, MAX_LINE, ifP);
+        if (rc == NULL)
+            eof = TRUE;
+        else {
+            if (strlen(line) == MAX_LINE - 1)
+                pm_error("A line in the input file is %u characters long.  "
+                         "%u is the maximum we can handle",
+                         strlen(line), MAX_LINE-1);
+
+            parseWidthHeightLine(line, &gotWidth, widthP, &gotHeight, heightP);
+
+            parseDeclaration(line, &foundDeclaration, version10P);
+        }
+    }
+
+    if (!foundDeclaration) 
+        pm_error("Unable to find a line in the file containing the start "
+                 "of C array declaration (\"static char\" or whatever)");
+
+    if (!gotWidth)
+        pm_error("Unable to find the #define statement that gives the "
+                 "width of the image, before the data structure "
+                 "declaration.");
+    if (!gotHeight)
+        pm_error("Unable to find the #define statement that gives the "
+                 "height of the image, before the data structure "
+                 "declaration.");
+}
+
+
+
+static void
+getHexByte(FILE *         const ifP,
+           unsigned int * const valueP) {
+
+    int c1, c2;
+    unsigned int value;
+
+    c1 = getc(ifP);
+    c2 = getc(ifP);
+    if (c1 == EOF || c2 == EOF)
+        pm_error("EOF / read error");
+
+    assert(c1 >= 0); assert(c1 < 256);
+    assert(c2 >= 0); assert(c2 < 256);
     
-  raster_length =  bytes_per_line * *heightP;
-  *dataP = (char*) malloc( raster_length );
-  if ( *dataP == (char*) 0 )
-    pm_error( "out of memory" );
-
-  /* Initialize hex_table. */
-  for ( c1 = 0; c1 < 256; ++c1 )
-    hex_table[c1] = 256;
-  hex_table['0'] = 0;
-  hex_table['1'] = 1;
-  hex_table['2'] = 2;
-  hex_table['3'] = 3;
-  hex_table['4'] = 4;
-  hex_table['5'] = 5;
-  hex_table['6'] = 6;
-  hex_table['7'] = 7;
-  hex_table['8'] = 8;
-  hex_table['9'] = 9;
-  hex_table['A'] = 10;
-  hex_table['B'] = 11;
-  hex_table['C'] = 12;
-  hex_table['D'] = 13;
-  hex_table['E'] = 14;
-  hex_table['F'] = 15;
-  hex_table['a'] = 10;
-  hex_table['b'] = 11;
-  hex_table['c'] = 12;
-  hex_table['d'] = 13;
-  hex_table['e'] = 14;
-  hex_table['f'] = 15;
-
-  if ( version10 )
-    for ( bytes = 0, ptr = *dataP; bytes < raster_length; bytes += 2 ) {
-      while ( ( c1 = getc( stream ) ) != 'x' )
-        if ( c1 == EOF )
-          pm_error( "EOF / read error" );
-      c1 = getc( stream );
-      c2 = getc( stream );
-      if ( c1 == EOF || c2 == EOF )
-        pm_error( "EOF / read error" );
-      value1 = ( hex_table[c1] << 4 ) + hex_table[c2];
-      if ( value1 >= 256 )
-        pm_error( "syntax error" );
-      c1 = getc( stream );
-      c2 = getc( stream );
-      if ( c1 == EOF || c2 == EOF )
-        pm_error( "EOF / read error" );
-      value2 = ( hex_table[c1] << 4 ) + hex_table[c2];
-      if ( value2 >= 256 )
-        pm_error( "syntax error" );
-      *ptr++ = value2;
-      if ( ( ! padding ) || ( ( bytes + 2 ) % bytes_per_line ) )
-        *ptr++ = value1;
+    value = (hexTable[c1] << 4) + hexTable[c2];
+    if (value >= 256)
+        pm_error("Invalid XBM input.  What should be a two digit "
+                 "hexadecimal cipher is instead '%c%c'", c1, c2);
+
+    *valueP = value;
+}
+
+
+                     
+static void
+readX10Raster(FILE *          const ifP,
+              unsigned int    const rasterLength,
+              unsigned char * const data,
+              unsigned int    const bytesPerLine,
+              bool            const mustPad) {
+
+    unsigned int bytesDone;
+    unsigned char * p;
+
+    for (bytesDone = 0, p = &data[0];
+         bytesDone < rasterLength;
+         bytesDone += 2) {
+
+        unsigned int value1;
+        unsigned int value2;
+
+        while (getc(ifP) != 'x') {}  /* Read up through the 'x' in 0x1234 */
+
+        getHexByte(ifP, &value1);  /* Read first two hex digits */
+        getHexByte(ifP, &value2);  /* Read last two hex digits */
+
+        *p++ = value2;
+
+        if (!mustPad || ((bytesDone + 2) % bytesPerLine))
+            *p++ = value1;
     }
-  else
-    for ( bytes = 0, ptr = *dataP; bytes < raster_length; ++bytes ) {
-      /*
-       ** Skip until digit is found.
-       */
-      for ( ; ; )
-        {
-          c1 = getc( stream );
-          if ( c1 == EOF )
-            pm_error( "EOF / read error" );
-          value1 = hex_table[c1];
-          if ( value1 != 256 )
-            break;
-        }
-      /*
-       ** Loop on digits.
-       */
-      for ( ; ; ) {
-        c2 = getc( stream );
-        if ( c2 == EOF )
-          pm_error( "EOF / read error" );
-        value2 = hex_table[c2];
-        if ( value2 != 256 ) {
-          value1 = (value1 << 4) | value2;
-          if ( value1 >= 256 )
-            pm_error( "syntax error" );
+}
+
+
+
+static void
+readX11Raster(FILE * const ifP,
+              unsigned int const rasterLength,
+              unsigned char * data) {
+
+    unsigned int i;
+
+    for (i = 0; i < rasterLength; ++i) {
+        unsigned int value;
+        int c;
+
+        /* Read up through the 'x' in 0x12 */
+        while ((c = getc(ifP))) {
+            if (c == EOF)
+                pm_error("EOF where 0x expected");
+            else if (toupper(c) == 'X')
+                break;
         }
-        else if ( c2 == 'x' || c2 == 'X' )
-          if ( value1 == 0 )
-            continue;
-          else pm_error( "syntax error" );
-        else break;
-      }
-      *ptr++ = value1;
+
+        getHexByte(ifP, &value);  /* Read the two hex digits */
+
+        assert(value < 256);
+
+        data[i] = value;
     }
 }
 
 
-/*  CHANGE HISTORY:
 
-  99.09.08 bryanh    Recognize "static unsigned char" declaration.
+static void
+readBitmapFile(FILE *           const ifP,
+               unsigned int *   const widthP,
+               unsigned int *   const heightP,
+               unsigned char ** const dataP) {
+
+    bool version10;
+    unsigned int rasterLength;
+    unsigned int width, height;
+    unsigned char * data;
 
+    unsigned int bytesPerLine;
+    bool mustPad;
 
+    getXbmHeader(ifP, &width, &height, &version10);
 
+    *widthP = width;
+    *heightP = height;
 
+    mustPad = (width % 16 >= 1 && width % 16 <= 8 && version10);
+
+    bytesPerLine = (width + 7) / 8 + (mustPad ? 1 : 0);
+    
+    rasterLength = bytesPerLine * height;
+
+    MALLOCARRAY(data, rasterLength);
+    if (data == NULL)
+        pm_error("Unable to allocate memory for the %u-byte raster",
+                 rasterLength);
+
+    if (version10)
+        readX10Raster(ifP, rasterLength, data, bytesPerLine, mustPad);
+    else
+        readX11Raster(ifP, rasterLength, data);
+
+    *dataP = data;
+}
+
+
+
+int
+main(int    argc,
+     char * argv[]) {
+
+    FILE * ifP;
+    bit * bitrow;
+    unsigned int rows, cols;
+    unsigned int row;
+    unsigned char * data;
+    const char * inputFileName;
+    unsigned char * p;
+        /* Cursor in raster data data[] */
+    
+    initHexTable();
+
+    pbm_init(&argc, argv);
+
+    if (argc-1 > 1)
+        pm_error("The only possible argument is the input file name.  "
+                 "You specified %u arguments", argc-1);
+    
+    if (argc-1 > 0)
+        inputFileName = argv[1];
+    else
+        inputFileName = "-";
+
+    ifP = pm_openr(inputFileName);
+
+    readBitmapFile(ifP, &cols, &rows, &data);
+
+    pm_close(ifP);
+
+    pbm_writepbminit(stdout, cols, rows, 0);
+    bitrow = pbm_allocrow_packed(cols);
+
+    p = &data[0];  /* Start at beginning of raster */
+
+    for (row = 0; row < rows; ++row) {
+        unsigned int const bytesPerRow = pbm_packed_bytes(cols);
+        unsigned int i;
+        
+        for (i = 0; i < bytesPerRow; ++i)
+            bitrow[i] = bitreverse[*p++];
+            
+        if (cols % 8 > 0) {
+            bitrow[bytesPerRow-1] >>= 8 - cols % 8;
+            bitrow[bytesPerRow-1] <<= 8 - cols % 8;
+        }
+            
+        pbm_writepbmrow_packed(stdout, bitrow, cols, 0);
+    }
+
+    pbm_freerow(bitrow);
+    free(data);
+    pm_close(stdout);
+
+    return 0;
+}
+
+/*  CHANGE HISTORY:
+
+  99.09.08 bryanh    Recognize "static unsigned char" declaration.
+
+  06.10 (afu)
+   Changed bitrow from plain to raw, write function from pbm_writepbmrow()
+   to pbm_writepbmrow_packed().
+   Retired bitwise transformation functions.
 
 */
+
diff --git a/converter/pgm/Makefile b/converter/pgm/Makefile
index f562fe92..b109683b 100644
--- a/converter/pgm/Makefile
+++ b/converter/pgm/Makefile
@@ -5,7 +5,7 @@ endif
 SUBDIR = converter/pgm
 VPATH=.:$(SRCDIR)/$(SUBDIR)
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
 PORTBINARIES =	asciitopgm bioradtopgm fstopgm hipstopgm \
 		lispmtopgm pgmtofs pgmtolispm pgmtopgm \
@@ -20,4 +20,4 @@ MERGE_OBJECTS = $(MERGEBINARIES:%=%.o2)
 .PHONY: all
 all: $(BINARIES)
 
-include $(SRCDIR)/Makefile.common
+include $(SRCDIR)/common.mk
diff --git a/converter/pgm/asciitopgm.c b/converter/pgm/asciitopgm.c
index 42f6e7db..a3a5bd48 100644
--- a/converter/pgm/asciitopgm.c
+++ b/converter/pgm/asciitopgm.c
@@ -68,7 +68,7 @@ main( argc, argv )
 
     if ( argv[argn][0] == '-' )
     {
-        if ( STREQ( argv[argn], "-d" ) )
+        if ( streq( argv[argn], "-d" ) )
         {
             if ( argc == argn + 1 )
                 pm_usage( usage );
diff --git a/converter/pgm/hipstopgm.c b/converter/pgm/hipstopgm.c
index 826a8511..2f5956e1 100644
--- a/converter/pgm/hipstopgm.c
+++ b/converter/pgm/hipstopgm.c
@@ -98,7 +98,7 @@ read_hips_header( fd, hP )
 	{
         read_line( fd, buf, 5000 );
 	}
-    while ( !STREQ( buf, ".\n" ) );
+    while ( !streq( buf, ".\n" ) );
 }
 
 
diff --git a/converter/pgm/lispmtopgm.c b/converter/pgm/lispmtopgm.c
index 7b98ef00..40dd3fb4 100644
--- a/converter/pgm/lispmtopgm.c
+++ b/converter/pgm/lispmtopgm.c
@@ -89,7 +89,7 @@ getinit( file, colsP, rowsP, depthP, padrightP )
     for ( i = 0; i < sizeof(magic)-1; ++i )
         magic[i] = getc( file );
     magic[i]='\0';
-    if (!STREQ(LISPM_MAGIC, magic))
+    if (!streq(LISPM_MAGIC, magic))
         pm_error( "bad id string in Lispm file" );
     
     if ( pm_readlittleshort( file, colsP ) == -1 )
diff --git a/converter/pgm/pgmtofs.c b/converter/pgm/pgmtofs.c
index b34d77c4..53a2e7b3 100644
--- a/converter/pgm/pgmtofs.c
+++ b/converter/pgm/pgmtofs.c
@@ -128,14 +128,8 @@ putitem( )
     bitshift = 8 - bitspersample;
 }
 
-#if __STDC__
 static void
 putgray( gray g )
-#else /*__STDC__*/
-    static void
-putgray( g )
-    gray g;
-#endif /*__STDC__*/
 {
     if ( bitsperitem == 8 )
         putitem( );
diff --git a/converter/pgm/pgmtolispm.c b/converter/pgm/pgmtolispm.c
index 02f2fd1e..abb85494 100644
--- a/converter/pgm/pgmtolispm.c
+++ b/converter/pgm/pgmtolispm.c
@@ -115,14 +115,8 @@ depth_to_word_size (depth)	/* Lispm architecture specific - if a bitmap is writt
 
 
 
-#if __STDC__
 static void
 putval( gray b )
-#else /*__STDC__*/
-static void
-putval( b )
-gray b;
-#endif /*__STDC__*/
     {
     if ( bitsperitem == 32 )
 	putitem( );
diff --git a/converter/pgm/pgmtopgm.c b/converter/pgm/pgmtopgm.c
index c65e98e0..250bb4dc 100644
--- a/converter/pgm/pgmtopgm.c
+++ b/converter/pgm/pgmtopgm.c
@@ -19,7 +19,7 @@ main(int argc, char *argv[]) {
     int rows, cols;
     gray maxval;
     int row;
-    gray* grayrow;
+    gray * grayrow;
     
     pgm_init(&argc, argv);
     
@@ -32,7 +32,7 @@ main(int argc, char *argv[]) {
 
     grayrow = pgm_allocrow(cols);
 
-    for (row = 0; row < rows; row++) {
+    for (row = 0; row < rows; ++row) {
         pgm_readpgmrow(stdin, grayrow, cols, maxval, format);
         pgm_writepgmrow(stdout, grayrow, cols, maxval, 0);
     }
diff --git a/converter/pgm/rawtopgm.c b/converter/pgm/rawtopgm.c
index c91c6178..0180a02c 100644
--- a/converter/pgm/rawtopgm.c
+++ b/converter/pgm/rawtopgm.c
@@ -11,6 +11,8 @@
 */
 
 #include <math.h>
+
+#include "pm_c_util.h"
 #include "pgm.h"
 #include "shhopt.h"
 
@@ -18,13 +20,13 @@ struct cmdline_info {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    const char *input_filespec;  /* Filespec of input file */
+    const char * inputFileName;
     unsigned int headerskip;
     float rowskip;
     int bottomfirst;  /* the -bottomfirst/-bt option */
     int autosize;  /* User wants us to figure out the size */
-    int width;
-    int height;
+    unsigned int width;
+    unsigned int height;
     int bpp;
       /* bytes per pixel in input format.  1 or 2 */
     int littleendian;
@@ -74,21 +76,21 @@ parse_command_line(int argc, char ** argv,
         /* Uses and sets argc, argv, and some of *cmdline_p and others. */
 
     if (argc-1 == 0) {
-        cmdline_p->input_filespec = "-";
+        cmdline_p->inputFileName = "-";
         cmdline_p->autosize = TRUE;
     } else if (argc-1 == 1) {
-        cmdline_p->input_filespec = argv[1];
+        cmdline_p->inputFileName = argv[1];
         cmdline_p->autosize = TRUE;
     } else if (argc-1 == 2) {
-        cmdline_p->input_filespec = "-";
+        cmdline_p->inputFileName = "-";
         cmdline_p->autosize = FALSE;
-        cmdline_p->width = atoi(argv[1]);
-        cmdline_p->height = atoi(argv[2]);
+        cmdline_p->width = pm_parse_width(argv[1]);
+        cmdline_p->height = pm_parse_height(argv[2]);
     } else if (argc-1 == 3) {
-        cmdline_p->input_filespec = argv[3];
+        cmdline_p->inputFileName = argv[3];
         cmdline_p->autosize = FALSE;
-        cmdline_p->width = atoi(argv[1]);
-        cmdline_p->height = atoi(argv[2]);
+        cmdline_p->width = pm_parse_width(argv[1]);
+        cmdline_p->height = pm_parse_height(argv[2]);
     } else
         pm_error("Program takes zero, one, two, or three arguments.  You "
                  "specified %d", argc-1);
@@ -107,11 +109,6 @@ parse_command_line(int argc, char ** argv,
         pm_error("Maxval must be less than 65536.  You specified %d.",
                  cmdline_p->maxval);
 
-    if (cmdline_p->width <= 0) 
-        pm_error("Width must be a positive number.");
-    if (cmdline_p->height <= 0) 
-        pm_error("Height must be a positive number.");
-
     if (cmdline_p->rowskip && cmdline_p->autosize)
         pm_error("If you specify -rowskip, you must also give the image "
                  "dimensions.");
@@ -221,7 +218,7 @@ main(int argc, char *argv[] ) {
 
     parse_command_line(argc, argv, &cmdline);
 
-    ifp = pm_openr(cmdline.input_filespec);
+    ifp = pm_openr(cmdline.inputFileName);
 
     if (cmdline.autosize || cmdline.bottomfirst) {
         buf = pm_read_unknown_size( ifp, &nread );
diff --git a/converter/pgm/sbigtopgm.c b/converter/pgm/sbigtopgm.c
index 228b71b3..dd4e921a 100644
--- a/converter/pgm/sbigtopgm.c
+++ b/converter/pgm/sbigtopgm.c
@@ -142,7 +142,7 @@ int main(argc, argv)
 	    cols = atoi(hdr + 6);
         } else if (strncmp(hdr, "sat_level=", 10) == 0) {
 	    maxval = atoi(hdr + 10);
-        } else if (STREQ(hdr, "end")) {
+        } else if (streq(hdr, "end")) {
 	    break;
 	}
 	hdr = cp + 1;
diff --git a/converter/ppm/411toppm.c b/converter/ppm/411toppm.c
index a5b25ac1..6ece4c4b 100644
--- a/converter/ppm/411toppm.c
+++ b/converter/ppm/411toppm.c
@@ -58,6 +58,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#include "pm_c_util.h"
 #include "ppm.h"
 #include "shhopt.h"
 #include "mallocvar.h"
diff --git a/converter/ppm/Makefile b/converter/ppm/Makefile
index 31ca826b..adc3a400 100644
--- a/converter/ppm/Makefile
+++ b/converter/ppm/Makefile
@@ -5,7 +5,7 @@ endif
 SUBDIR = converter/ppm
 VPATH=.:$(SRCDIR)/$(SUBDIR)
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
 SUBDIRS = hpcdtoppm ppmtompeg
 
@@ -42,7 +42,7 @@ MERGE_OBJECTS = $(BINARIES:%=%.o2)
 .PHONY: all
 all: $(BINARIES) $(SUBDIRS:%=%/all)
 
-include $(SRCDIR)/Makefile.common
+include $(SRCDIR)/common.mk
 
 ppmtobmp.o ppmtobmp.o2: bmp.h
 
diff --git a/converter/ppm/eyuvtoppm.c b/converter/ppm/eyuvtoppm.c
index dcbb9547..33d57409 100644
--- a/converter/ppm/eyuvtoppm.c
+++ b/converter/ppm/eyuvtoppm.c
@@ -34,6 +34,8 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
+
+#include "pm_c_util.h"
 #include "ppm.h"
 #include "shhopt.h"
 #include "mallocvar.h"
diff --git a/converter/ppm/hpcdtoppm/Makefile b/converter/ppm/hpcdtoppm/Makefile
index 9a7c67d0..ddf79ee5 100644
--- a/converter/ppm/hpcdtoppm/Makefile
+++ b/converter/ppm/hpcdtoppm/Makefile
@@ -5,21 +5,21 @@ endif
 SUBDIR = converter/ppm/hpcdtoppm
 VPATH=.:$(SRCDIR)/$(SUBDIR)
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
 all: hpcdtoppm
 
 SCRIPTS = hpcdtoppm pcdovtoppm
 MERGE_OBJECTS =
 
-include $(SRCDIR)/Makefile.common
+include $(SRCDIR)/common.mk
 
 install: 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
+	$(SYMLINK) pcdindex$(EXE) pcdovtoppm$(EXE)
 
 
 FORCE:
diff --git a/converter/ppm/ilbmtoppm.c b/converter/ppm/ilbmtoppm.c
index 5001741a..5ceb70b6 100644
--- a/converter/ppm/ilbmtoppm.c
+++ b/converter/ppm/ilbmtoppm.c
@@ -1,4 +1,4 @@
-/* ilbmtoppm.c - read an IFF ILBM file and produce a portable pixmap
+/* ilbmtoppm.c - read an IFF ILBM file and produce a PPM
 **
 ** Copyright (C) 1989 by Jef Poskanzer.
 **
@@ -43,11 +43,10 @@
 #include <string.h>
 
 #include "pm_c_util.h"
-#include "ppm.h"
-#include "ilbm.h"
 #include "mallocvar.h"
-
-/*#define DEBUG*/
+#include "intcode.h"
+#include "ilbm.h"
+#include "ppm.h"
 
 typedef struct {
     int reg;            /* color register to change */
@@ -764,10 +763,15 @@ multi_free(cmap)
  ****************************************************************************/
 
 static void
-check_cmap(bmhd, cmap)
-    BitMapHeader *bmhd;
-    ColorMap *cmap;
-{
+prepareCmap(const BitMapHeader * const bmhd,
+            ColorMap *           const cmap) {
+/*----------------------------------------------------------------------------
+   This is a really ugly subroutine that 1) analyzes a colormap and its
+   context (returning the analysis in global variables); and 2) modifies that
+   color map, because it's really one type of data structure as input and
+   another as output.
+
+-----------------------------------------------------------------------------*/
     pixval colmaxval = 0;
     int shifted = 1;
     int i, r, g, b;
@@ -990,6 +994,8 @@ std_to_ppm(FILE *         const ifp,
            ColorMap *     const cmap, 
            long           const viewportmodes);
 
+
+
 static void
 ham_to_ppm(FILE *         const ifp, 
            long           const chunksize, 
@@ -1255,7 +1261,7 @@ dcol_to_ppm(FILE *         const ifP,
     unsigned int const greenplanes = dcol->g;
     unsigned int const blueplanes  = dcol->b;
     
-    int col, row, i;
+    int col, row;
     rawtype *Rrow, *Grow, *Brow;
     pixval maxval, redmaxval, greenmaxval, bluemaxval;
     pixval *redtable, *greentable, *bluetable;
@@ -1301,13 +1307,15 @@ dcol_to_ppm(FILE *         const ifP,
     MALLOCARRAY_NOFAIL(greentable, greenmaxval +1);
     MALLOCARRAY_NOFAIL(bluetable,  bluemaxval  +1);
 
-    for( i = 0; i <= redmaxval; i++ )
-        redtable[i] = (i * maxval + redmaxval/2)/redmaxval;
-    for( i = 0; i <= greenmaxval; i++ )
-        greentable[i] = (i * maxval + greenmaxval/2)/greenmaxval;
-    for( i = 0; i <= bluemaxval; i++ )
-        bluetable[i] = (i * maxval + bluemaxval/2)/bluemaxval;
-
+    {
+        unsigned int i;
+        for (i = 0; i <= redmaxval; ++i)
+            redtable[i] = ROUNDDIV(i * maxval, redmaxval);
+        for (i = 0; i <= greenmaxval; ++i)
+            greentable[i] = ROUNDDIV(i * maxval, greenmaxval);
+        for (i = 0; i <= bluemaxval; ++i)
+            bluetable[i] = ROUNDDIV(i * maxval, bluemaxval);
+    }
     if( transpName ) {
         MALLOCVAR_NOFAIL(transpColor);
         *transpColor = ppm_parsecolor(transpName, maxval);
@@ -1343,16 +1351,10 @@ dcol_to_ppm(FILE *         const ifP,
 
 
 static void
-cmap_to_ppm(cmap)
-    ColorMap *cmap;
-{
-    ppm_colorrowtomapfile(stdout, cmap->color, cmap->ncolors, MAXCOLVAL);
-#if 0
-    int i;
-    ppm_writeppminit(stdout, cmap->ncolors, 1, MAXCOLVAL, 1);
-    for( i = 0; i < cmap->ncolors; i++ )
-        ppm_writeppmrow(stdout, &(cmap->color[i]), 1, MAXCOLVAL, 1);
-#endif
+cmapToPpm(FILE *     const ofP,
+            ColorMap * const cmapP) {
+
+    ppm_colorrowtomapfile(ofP, cmapP->color, cmapP->ncolors, MAXCOLVAL);
 }
 
 
@@ -1657,39 +1659,41 @@ PCHG_DecompHuff(src, dest, tree, origsize)
 }
 
 
+
 static void
-PCHG_Decompress(PCHG, CompHdr, compdata, compsize, comptree, data)
-    PCHGHeader *PCHG;
-    PCHGCompHeader *CompHdr;
-    unsigned char *compdata;
-    unsigned long compsize;
-    unsigned char *comptree;
-    unsigned char *data;
-{
-    short *hufftree;
-    unsigned long huffsize, i;
-    unsigned long treesize = CompHdr->CompInfoSize;
+PCHG_Decompress(PCHGHeader *     const PCHG,
+                PCHGCompHeader * const CompHdr,
+                unsigned char *  const compdata,
+                unsigned long    const compsize,
+                unsigned char *  const comptree,
+                unsigned char *  const data) {
 
-    switch( PCHG->Compression ) {
-        case PCHG_COMP_HUFFMAN:
+    switch(PCHG->Compression) {
+    case PCHG_COMP_HUFFMAN: {
+        unsigned long const treesize = CompHdr->CompInfoSize;
+        unsigned long const huffsize = treesize / 2;
+        const bigend16 * const bigendComptree = (const void *)comptree;
 
-#ifdef DEBUG
-            pm_message("PCHG Huffman compression");
-#endif
-            /* turn big-endian 2-byte shorts into native format */
-            huffsize = treesize/2;
-            MALLOCARRAY_NOFAIL(hufftree, huffsize);
-            for( i = 0; i < huffsize; i++ ) {
-                hufftree[i] = (short)BIG_WORD(comptree);
-                comptree += 2;
-            }
+        short * hufftree;
+        unsigned long i;
 
-            /* decompress the change structure data */
-            PCHG_DecompHuff(compdata, data, &hufftree[huffsize-1], 
-                            CompHdr->OriginalDataSize);
+        /* Convert big-endian 2-byte shorts to C shorts */
 
-            free(hufftree);
-            break;
+        MALLOCARRAY(hufftree, huffsize);
+
+        if (!hufftree)
+            pm_error("Couldn't get memory for %lu-byte Huffman tree",
+                     huffsize);
+
+        for (i = 0; i < huffsize; ++i)
+            hufftree[i] = pm_uintFromBigend16(bigendComptree[i]);
+
+        /* decompress the change structure data */
+        PCHG_DecompHuff(compdata, data, &hufftree[huffsize-1], 
+                        CompHdr->OriginalDataSize);
+        
+        free(hufftree);
+    } break;
         default:
             pm_error("unknown PCHG compression type %d", PCHG->Compression);
     }
@@ -1798,94 +1802,119 @@ fail2:
 }
 
 
+
 static void
-PCHG_ConvertBig(PCHG, cmap, mask, datasize)
-    PCHGHeader *PCHG;
-    ColorMap *cmap;
-    unsigned char *mask;
-    unsigned long datasize;
-{
-    unsigned char *data;
+PCHG_ConvertBig(PCHGHeader *    const PCHG,
+                ColorMap *      const cmap,
+                unsigned char * const maskStart,
+                unsigned long   const datasize) {
+
+    unsigned char * data;
     unsigned char thismask;
-    int bits, row, i, changes, masklen, reg;
-    unsigned long totalchanges = 0;
-    int changedlines = PCHG->ChangedLines;
+    int bits;
+    unsigned int row;
+    int changes;
+    int masklen;
+    int reg;
+    unsigned long totalchanges;
+    int changedlines;
+    unsigned long dataRemaining;
+    unsigned char * mask;
+
+    mask = maskStart;  /* initial value */
+    dataRemaining = datasize;  /* initial value */
+    changedlines = PCHG->ChangedLines;  /* initial value */
+    totalchanges = 0;  /* initial value */
 
     masklen = 4 * MaskLongWords(PCHG->LineCount);
-    data = mask + masklen; datasize -= masklen;
+    data = mask + masklen; dataRemaining -= masklen;
 
-    bits = 0;
-    for( row = PCHG->StartLine; changedlines && row < 0; row++ ) {
-        if( bits == 0 ) {
-            if( masklen == 0 ) goto fail2;
+    for (row = PCHG->StartLine, bits = 0; changedlines && row < 0; ++row) {
+        if (bits == 0) {
+            if (masklen == 0)
+                pm_error("insufficient data in line mask");
             thismask = *mask++;
             --masklen;
             bits = 8;
         }
-        if( thismask & (1<<7) ) {
-            if( datasize < 2 ) goto fail;
-            changes = BIG_WORD(data); data += 2; datasize -= 2;
+        if (thismask & (1<<7)) {
+            unsigned int i;
+
+            if (dataRemaining < 2)
+                pm_error("insufficient data in BigLineChanges structures");
+
+            changes = BIG_WORD(data); data += 2; dataRemaining -= 2;
+
+            for (i = 0; i < changes; ++i) {
+                if (totalchanges >= PCHG->TotalChanges)
+                    pm_error("insufficient data in BigLineChanges structures");
+
+                if (dataRemaining < 6)
+                    pm_error("insufficient data in BigLineChanges structures");
 
-            for( i = 0; i < changes; i++ ) {
-                if( totalchanges >= PCHG->TotalChanges ) goto fail;
-                if( datasize < 6 ) goto fail;
                 reg = BIG_WORD(data); data += 2;
                 cmap->mp_init[reg - PCHG->MinReg].reg = reg;
                 ++data; /* skip alpha */
                 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++;
-                datasize -= 6;
+                dataRemaining -= 6;
                 ++totalchanges;
             }
             --changedlines;
         }
         thismask <<= 1;
-        bits--;
+        --bits;
     }
 
-    for( row = PCHG->StartLine; changedlines && row < cmap->mp_rows; row++ ) {
-        if( bits == 0 ) {
-            if( masklen == 0 ) goto fail2;
+    for (row = PCHG->StartLine; changedlines && row < cmap->mp_rows; ++row) {
+        if (bits == 0) {
+            if (masklen == 0)
+                pm_error("insufficient data in line mask");
+
             thismask = *mask++;
             --masklen;
             bits = 8;
         }
-        if( thismask & (1<<7) ) {
-            if( datasize < 2 ) goto fail;
-            changes = BIG_WORD(data); data += 2; datasize -= 2;
+        if (thismask & (1<<7)) {
+            unsigned int i;
+
+            if (dataRemaining < 2)
+                pm_error("insufficient data in BigLineChanges structures");
+
+            changes = BIG_WORD(data); data += 2; dataRemaining -= 2;
 
             MALLOCARRAY_NOFAIL(cmap->mp_change[row], changes + 1);
-            for( i = 0; i < changes; i++ ) {
-                if( totalchanges >= PCHG->TotalChanges ) goto fail;
-                if( datasize < 6 ) goto fail;
+            for (i = 0; i < changes; ++i) {
+                if (totalchanges >= PCHG->TotalChanges)
+                    pm_error("insufficient data in BigLineChanges structures");
+
+                if (dataRemaining < 6)
+                    pm_error("insufficient data in BigLineChanges structures");
+
                 reg = BIG_WORD(data); data += 2;
                 cmap->mp_change[row][i].reg = reg;
                 ++data; /* skip alpha */
                 cmap->mp_change[row][i].r = *data++;
                 cmap->mp_change[row][i].b = *data++;    /* yes, RBG */
                 cmap->mp_change[row][i].g = *data++;
-                datasize -= 6;
+                dataRemaining -= 6;
                 ++totalchanges;
             }
             cmap->mp_change[row][changes].reg = MP_REG_END;
             --changedlines;
         }
         thismask <<= 1;
-        bits--;
+        --bits;
     }
-    if( totalchanges != PCHG->TotalChanges )
+    if (totalchanges != PCHG->TotalChanges)
         pm_message("warning - got %ld change structures, "
                    "chunk header reports %ld", 
                    totalchanges, PCHG->TotalChanges);
-    return;
-fail:
-    pm_error("insufficient data in BigLineChanges structures");
-fail2:
-    pm_error("insufficient data in line mask");
 }
 
 
+
 static void
 read_pchg(FILE *     const ifp,
           IFF_ID     const iffid,
@@ -1939,7 +1968,7 @@ read_pchg(FILE *     const ifp,
             read_bytes(ifp, treesize, comptree, iffid, &remainingChunksize);
 
             compsize = remainingChunksize;
-            MALLOCARRAY_NOFAIL(compdata, remainingChunksize);
+            MALLOCARRAY_NOFAIL(compdata, compsize);
             read_bytes(ifp, compsize, compdata, iffid, &remainingChunksize);
 
             datasize = CompHdr.OriginalDataSize;
@@ -2033,7 +2062,7 @@ process_body( FILE *          const ifp,
         pm_error("%s chunk without %s chunk", 
                  ID2string(ID_BODY), ID2string(ID_BMHD));
 
-    check_cmap(bmhdP, cmap);
+    prepareCmap(bmhdP, cmap);
 
     pixelrow = ppm_allocrow(bmhdP->w);
     if( maskfile ) {
@@ -2081,21 +2110,21 @@ process_body( FILE *          const ifp,
 
 
 static void 
-process_chunk(FILE *          const ifp,
-              long            const formsize,
-              IFF_ID          const ignorelist[],
-              unsigned int    const ignorecount,
-              int             const fakeviewport,
-              int             const viewportmask,
-              int             const isdeepopt,
-              bool            const cmaponly,
-              bool *          const bodyChunkProcessedP,
-              bool *          const endchunkP,
-              BitMapHeader ** const bmhdP,
-              ColorMap *      const cmap,
-              DirectColor **  const dcolP,
-              int *           const viewportmodesP,
-              long *          const bytesReadP
+processChunk(FILE *          const ifP,
+             long            const formsize,
+             IFF_ID          const ignorelist[],
+             unsigned int    const ignorecount,
+             int             const fakeviewport,
+             int             const viewportmask,
+             int             const isdeepopt,
+             bool            const cmaponly,
+             bool *          const bodyChunkProcessedP,
+             bool *          const endchunkP,
+             BitMapHeader ** const bmhdP,
+             ColorMap *      const cmap,
+             DirectColor **  const dcolP,
+             int *           const viewportmodesP,
+             long *          const bytesReadP
     ) {
 
     IFF_ID iffid;
@@ -2104,129 +2133,168 @@ process_chunk(FILE *          const ifp,
 
     bytesread = 0;
 
-    iffid = get_big_long(ifp, ID_FORM, NULL);
-    chunksize = get_big_long(ifp, iffid, NULL);
+    iffid = get_big_long(ifP, ID_FORM, NULL);
+    chunksize = get_big_long(ifP, iffid, NULL);
     bytesread += 8;
 
     if (debug)
-        pm_message("reading %s chunk: %ld bytes", 
-                   ID2string(iffid), chunksize);
+        pm_message("reading %s chunk: %ld bytes", ID2string(iffid), chunksize);
 
-    if( ignored_iffid(iffid, ignorelist, ignorecount) ) {
+    if (ignored_iffid(iffid, ignorelist, ignorecount)) {
         pm_message("ignoring %s chunk", ID2string(iffid));
-        skip_chunk(ifp, iffid, chunksize);
-    } else if( iffid == ID_END ) {
+        skip_chunk(ifP, iffid, chunksize);
+    } 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 
         */
-        if( chunksize != 0 ) {
+        if (chunksize != 0 ) {
             pm_message("warning - non-0 %s chunk", ID2string(iffid));
-            skip_chunk(ifp, iffid, chunksize);
+            skip_chunk(ifP, iffid, chunksize);
         }
-        if( formsize != 0xffffffff )
+        if (formsize != 0xffffffff)
             pm_message("warning - %s chunk with FORM size 0x%08lx "
                        "(should be 0x%08x)",
                        ID2string(iffid), formsize, 0xffffffff);
         *endchunkP = 1;
-    } else if( *bodyChunkProcessedP ) {
+    } else if (*bodyChunkProcessedP) {
         pm_message("%s chunk found after %s chunk - skipping", 
                    ID2string(iffid), ID2string(ID_BODY));
-        skip_chunk(ifp, iffid, chunksize);
+        skip_chunk(ifP, iffid, chunksize);
     } else
-        switch( iffid ) {
+        switch (iffid) {
         case ID_BMHD:
-            *bmhdP = read_bmhd(ifp, iffid, chunksize);
+            *bmhdP = read_bmhd(ifP, iffid, chunksize);
             break;
         case ID_CMAP:
-            read_cmap(ifp, iffid, chunksize, cmap);
+            read_cmap(ifP, iffid, chunksize, cmap);
             break;
         case ID_CMYK:
-            read_cmyk(ifp, iffid, chunksize, cmap);
+            read_cmyk(ifP, iffid, chunksize, cmap);
             break;
         case ID_CLUT:
-            read_clut(ifp, iffid, chunksize, cmap);
+            read_clut(ifP, iffid, chunksize, cmap);
             break;
         case ID_CAMG:
-            if( chunksize != CAMGChunkSize )
+            if (chunksize != CAMGChunkSize)
                 pm_error("%s chunk size mismatch", ID2string(iffid));
-            *viewportmodesP = get_big_long(ifp, ID_CAMG, NULL);
+            *viewportmodesP = get_big_long(ifP, ID_CAMG, NULL);
             *viewportmodesP &= viewportmask;      /* -isnotham/-isnotehb */
             break;
         case ID_PCHG:
-            read_pchg(ifp, iffid, chunksize, cmap);
+            read_pchg(ifP, iffid, chunksize, cmap);
             break;
         case ID_CTBL:
         case ID_SHAM:
-            read_4bit_mp(ifp, iffid, chunksize, cmap);
+            read_4bit_mp(ifP, iffid, chunksize, cmap);
             break;
         case ID_DCOL:
-            if( chunksize != DirectColorSize )
+            if (chunksize != DirectColorSize)
                 pm_error("%s chunk size mismatch", ID2string(iffid));
             MALLOCVAR_NOFAIL(*dcolP);
-            (*dcolP)->r = get_byte(ifp, iffid, NULL);
-            (*dcolP)->g = get_byte(ifp, iffid, NULL);
-            (*dcolP)->b = get_byte(ifp, iffid, NULL);
-            (void)get_byte(ifp, iffid, NULL);       /* skip pad byte */
+            (*dcolP)->r = get_byte(ifP, iffid, NULL);
+            (*dcolP)->g = get_byte(ifP, iffid, NULL);
+            (*dcolP)->b = get_byte(ifP, iffid, NULL);
+            get_byte(ifP, iffid, NULL);       /* skip pad byte */
             break;
         case ID_BODY: 
-            if( cmaponly || (*bmhdP && (*bmhdP)->nPlanes == 0 )) {
-                skip_chunk(ifp, ID_BODY,  chunksize);
-                return;
-            }
-    
-            process_body(ifp, chunksize, *bmhdP, cmap, 
-                         maskfile, fakeviewport, isdeepopt, *dcolP,
-                         viewportmodesP);
-
-            *bodyChunkProcessedP = TRUE;
+            if (cmaponly || (*bmhdP && (*bmhdP)->nPlanes == 0))
+                skip_chunk(ifP, ID_BODY,  chunksize);
+            else {
+                process_body(ifP, chunksize, *bmhdP, cmap, 
+                             maskfile, fakeviewport, isdeepopt, *dcolP,
+                             viewportmodesP);
 
-            break;
+                *bodyChunkProcessedP = TRUE;
+            } break;
         case ID_GRAB:   case ID_DEST:   case ID_SPRT:   case ID_CRNG:
         case ID_CCRT:   case ID_DYCP:   case ID_DPPV:   case ID_DRNG:
         case ID_EPSF:   case ID_JUNK:   case ID_CNAM:   case ID_PRVW:
         case ID_TINY:   case ID_DPPS:
-            skip_chunk(ifp, iffid, chunksize);
+            skip_chunk(ifP, iffid, chunksize);
             break;
         case ID_copy:   case ID_AUTH:   case ID_NAME:   case ID_ANNO:
         case ID_TEXT:   case ID_FVER:
-            if( verbose )
-                display_chunk(ifp, iffid, chunksize);
+            if (verbose)
+                display_chunk(ifP, iffid, chunksize);
             else
-                skip_chunk(ifp, iffid, chunksize);
+                skip_chunk(ifP, iffid, chunksize);
             break;
-        case ID_DPI:
-        {
+        case ID_DPI: {
             int x, y;
 
-            x = get_big_short(ifp, ID_DPI, NULL);
-            y = get_big_short(ifp, ID_DPI, NULL);
-            if( verbose )
+            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", 
                            ID2string(ID_DPI), x, y);
-        }
-        break;
+        } break;
         default:
             pm_message("unknown chunk type %s - skipping", 
                        ID2string(iffid));
-            skip_chunk(ifp, iffid, chunksize);
+            skip_chunk(ifP, iffid, chunksize);
             break;
         }
 
     bytesread += chunksize;
 
-    if( ODD(chunksize) ) {
-        (void) get_byte(ifp, iffid, NULL);
+    if (ODD(chunksize)) {
+        get_byte(ifP, iffid, NULL);
         bytesread += 1;
     } 
     *bytesReadP = bytesread;
 }
 
 
+
+static void
+maybeWriteColorMap(FILE *               const ofP,
+                   const BitMapHeader * const bmhdP,
+                   ColorMap *           const cmapP,
+                   bool                 const bodyChunkProcessed,
+                   bool                 const cmaponly) {
+/*----------------------------------------------------------------------------
+   Write to file *ofP the color map *cmapP as a PPM, if appropriate.
+
+   The logic (not just here -- in the program as a whole) for deciding whether
+   to write the image or the colormap is quite twisted.  If I thought anyone
+   was actually using this program, I would take the time to straighten it
+   out.
+
+   What's really sick about this subroutine is that in choosing to write
+   a color map, it has to know that Caller hasn't already written
+   the image.  Huge modularity violation.
+-----------------------------------------------------------------------------*/
+    if (cmaponly) {
+        if (HAS_COLORMAP(cmapP)) {
+            prepareCmap(bmhdP, cmapP);
+            cmapToPpm(ofP, cmapP);
+        } else
+            pm_error("You specified -cmaponly, but the ILBM "
+                     "has no colormap");
+    } else if (bmhdP && bmhdP->nPlanes == 0) {
+        if (HAS_COLORMAP(cmapP)) {
+            prepareCmap(bmhdP, cmapP);
+            cmapToPpm(ofP, cmapP);
+        } else
+            pm_error("ILBM has neither a color map nor color planes");
+    } else if (!bodyChunkProcessed) {
+        if (HAS_COLORMAP(cmapP)) {
+            pm_message("input is a colormap file");
+            prepareCmap(bmhdP, cmapP);
+            cmapToPpm(ofP, cmapP);
+        } else
+            pm_error("ILBM has neither %s or %s chunk", 
+                     ID2string(ID_BODY), ID2string(ID_CMAP));
+    }
+}
+
+
+
 int
 main(int argc, char *argv[]) {
 
-    FILE *ifp;
+    FILE * ifP;
     int argn;
     short cmaponly = 0, isdeepopt = 0;
     bool endchunk;
@@ -2300,25 +2368,25 @@ main(int argc, char *argv[]) {
     }    
 
     if( argn < argc ) {
-        ifp = pm_openr( argv[argn] );
+        ifP = pm_openr( argv[argn] );
         argn++;
     } else
-        ifp = stdin;
+        ifP = stdin;
 
     if( argn != argc )
         pm_usage(usage);
 
     /* Read in the ILBM file. */
 
-    firstIffid = get_big_long(ifp, ID_FORM, NULL);
-    if( firstIffid != ID_FORM )
+    firstIffid = get_big_long(ifP, ID_FORM, NULL);
+    if (firstIffid != ID_FORM)
         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 && 
-        typeid != ID_PBM )
-        pm_error( "input is not an ILBM, RGBN, RGB8 or PBM "
-                  "type FORM 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 && 
+        typeid != ID_PBM)
+        pm_error("input is not an ILBM, RGBN, RGB8 or PBM "
+                 "type FORM IFF file");
     bytesread = 4;  /* FORM and formsize do not count */
 
     cmap = alloc_cmap();
@@ -2326,55 +2394,41 @@ main(int argc, char *argv[]) {
     /* Main loop, parsing the IFF FORM. */
     bodyChunkProcessed = FALSE;
     endchunk = FALSE;
-    while( !endchunk && formsize-bytesread >= 8 ) {
-        long bytes_read_for_chunk;
+    while (!endchunk && formsize-bytesread >= 8) {
+        long bytesReadForChunk;
 
-        process_chunk(ifp, formsize, ignorelist, ignorecount,
-                      fakeviewport, viewportmask,
-                      isdeepopt, cmaponly,
-                      &bodyChunkProcessed,
-                      &endchunk, &bmhdP, cmap, &dcol,
-                      &viewportmodes, &bytes_read_for_chunk);
+        processChunk(ifP, formsize, ignorelist, ignorecount,
+                     fakeviewport, viewportmask,
+                     isdeepopt, cmaponly,
+                     &bodyChunkProcessed,
+                     &endchunk, &bmhdP, cmap, &dcol,
+                     &viewportmodes, &bytesReadForChunk);
 
-        bytesread += bytes_read_for_chunk;
+        bytesread += bytesReadForChunk;
     }
 
-    if( maskfile ) {
+    if (maskfile) {
         pm_close(maskfile);
-        if( !wrotemask )
+        if (!wrotemask)
             remove(maskname);
         pbm_freerow(maskrow);
     }
 
-    if( cmaponly || (bmhdP && bmhdP->nPlanes == 0 )) {
-        if( HAS_COLORMAP(cmap) ) {
-            check_cmap(bmhdP, cmap);
-            cmap_to_ppm(cmap);
-        } else
-            pm_error("no colormap");
-    } else if( !bodyChunkProcessed ) {
-        if( HAS_COLORMAP(cmap) ) {
-            pm_message("input is a colormap file");
-            check_cmap(bmhdP, cmap);
-            cmap_to_ppm(cmap);
-        } else
-            pm_error("no %s or %s chunk found", 
-                     ID2string(ID_BODY), ID2string(ID_CMAP));
-    }
+    maybeWriteColorMap(stdout, bmhdP, cmap, bodyChunkProcessed, cmaponly);
 
     {
         unsigned int skipped;
         
-        for( skipped = 0; fgetc(ifp) != EOF; ++skipped )
-            bytesread++;
+        for (skipped = 0; fgetc(ifP) != EOF; ++skipped)
+            ++bytesread;
 
-        if( skipped > 0 )
+        if (skipped > 0)
             pm_message("skipped %u extraneous byte%s after last chunk",
                        skipped, (skipped == 1 ? "" : "s"));
     }
-    pm_close(ifp);
+    pm_close(ifP);
 
-    if( !endchunk && bytesread != formsize ) {
+    if (!endchunk && bytesread != formsize) {
         pm_message("warning - file length/FORM size field mismatch "
                    "(%ld != %ld+8)",
                    bytesread+8 /* FORM+size */, formsize);
diff --git a/converter/ppm/mitsu.h b/converter/ppm/mitsu.h
index 8676a39d..bca4fbdf 100644
--- a/converter/ppm/mitsu.h
+++ b/converter/ppm/mitsu.h
@@ -1,7 +1,6 @@
 #ifndef MITSU_H_INCLUDED
 #define MITSU_H_INCLUDED
 
-/* static char SCCSid[] = "@(#)mitsu.h\t\t1.3\t(SPZ)\t3/11/92\n"; */
 #define MAXLUTCOL   255
 
 #define A4_MAXCOLS  1184
diff --git a/converter/ppm/pcxtoppm.c b/converter/ppm/pcxtoppm.c
index 9f403538..25a81531 100644
--- a/converter/ppm/pcxtoppm.c
+++ b/converter/ppm/pcxtoppm.c
@@ -31,6 +31,7 @@
  *  - moved check of 16-color-palette into pcx_16col_to_ppm(),
  *    now checks if it contains only a single color
  */
+#include "pm_c_util.h"
 #include "mallocvar.h"
 #include "shhopt.h"
 #include "ppm.h"
diff --git a/converter/ppm/picttoppm.c b/converter/ppm/picttoppm.c
index d52963fc..9a7d8e7c 100644
--- a/converter/ppm/picttoppm.c
+++ b/converter/ppm/picttoppm.c
@@ -94,16 +94,16 @@ struct rgbPlanes {
     word * blu;
 };
 
+struct canvas {
+    struct rgbPlanes planes;
+};
+
 typedef void (*transfer_func) (struct RGBColor* src, struct RGBColor* dst);
 
-static const char* stage;
+static const char * stage;
 static struct Rect picFrame;
-static word* red;
-static word* green;
-static word* blue;
 static word rowlen;
 static word collen;
-static longword planelen;
 static int verbose;
 static int fullres;
 static int recognize_comment;
@@ -142,13 +142,6 @@ static int ps_cent_x;
 static int ps_cent_y;
 static int ps_cent_set;
 
-struct opdef {
-    const char* name;
-    int len;
-    void (*impl) (int);
-    const char* description;
-};
-
 struct raster {
 /*----------------------------------------------------------------------------
    An image raster.  May be either truecolor or paletted.
@@ -178,13 +171,28 @@ struct raster {
 };
 
 
+
 static void
 allocateRaster(struct raster * const rasterP,
                unsigned int    const width,
                unsigned int    const height,
                unsigned int    const bitsPerPixel) {
+/*----------------------------------------------------------------------------
+   Allocate storage for a raster that can contain a 'width' x 'height'
+   pixel rectangle read from a PICT image with 'bitsPerPixel' bits
+   per pixel.
+
+   Make the space large enough to round the number of pixels up to a
+   multiple of 16, because we've seen many images in which the PICT raster
+   does contain that much padding on the right.  I don't know why; I could
+   understand a multiple of 8, since in 1 bpp image, the smallest unit
+   expressable in PICT is 8 pixels.  But why 16?  The images we saw came
+   from Adobe Illustrator 10 in March 2007, supplied by
+   Guillermo Gómez Valcárcel.
+-----------------------------------------------------------------------------*/
+    unsigned int const allocWidth = ROUNDUP(width, 16);
 
-    if (width > UINT_MAX/4)
+    if (width > UINT_MAX/4 - 16)
         pm_error("Width %u pixels too large for arithmetic", width);
 
     rasterP->rowCount = height;
@@ -209,16 +217,16 @@ allocateRaster(struct raster * const rasterP,
            We have yet to see if we can properly interpret the data.
         */
 
-        rasterP->rowSize = width * 4;
+        rasterP->rowSize = allocWidth * 4;
         break;
     case 16:
-        rasterP->rowSize = width * 2;
+        rasterP->rowSize = allocWidth * 2;
         break;
     case 8:
     case 4:
     case 2:
     case 1:
-        rasterP->rowSize = width * 1;
+        rasterP->rowSize = allocWidth * 1;
         break;
     default:
         pm_error("INTERNAL ERROR: impossible bitsPerPixel value in "
@@ -248,13 +256,30 @@ struct blit_info {
     struct raster      srcplane;
     int                pixSize;
     struct Rect        dstRect;
-    struct RGBColor *  color_map;
+    struct RGBColor *  colorMap;
     int                mode;
     struct blit_info * next;
 };
 
-static struct blit_info* blit_list = 0;
-static struct blit_info** last_bl = &blit_list;
+typedef struct {
+    struct blit_info * firstP;
+    struct blit_info ** connectorP;
+    bool unblittableText;
+        /* The image contains text opcodes, and we don't know how to put that
+           in a blit list (I really don't even know what a blit _is_), so
+           the image information here is incomplete.
+        */
+} blitList;
+
+
+typedef void (drawFn)(struct canvas *, blitList *, int);
+
+struct opdef {
+    const char* name;
+    int len;
+    drawFn * impl;
+    const char* description;
+};
 
 #define WORD_LEN (-1)
 
@@ -589,15 +614,25 @@ picComment(word const type,
 
 
 
+static drawFn ShortComment;
+
 static void
-ShortComment(int const version) {
+ShortComment(struct canvas * const canvasP,
+             blitList *      const blitListP,
+             int             const version) {
+
     picComment(read_word(), 0);
 }
 
 
 
+static drawFn LongComment;
+
 static void
-LongComment(int const version) {
+LongComment(struct canvas * const canvasP,
+            blitList *      const blitListP,
+            int             const version) {
+
     word type;
 
     type = read_word();
@@ -606,8 +641,13 @@ LongComment(int const version) {
 
 
 
+static drawFn skip_poly_or_region;
+
 static void
-skip_poly_or_region(int const version) {
+skip_poly_or_region(struct canvas * const canvasP,
+                    blitList *      const blitListP,
+                    int             const version) {
+
     stage = "skipping polygon or region";
     skip(read_word() - 2);
 }
@@ -639,56 +679,98 @@ static struct fontinfo** fontlist_ins = &fontlist;
 
 
 
+static void
+tokenize(char *         const s,
+         const char **  const vec,
+         unsigned int   const vecSize,
+         unsigned int * const nTokenP) {
+
+    unsigned int nToken;
+    char * p;
+
+    p = &s[0];   /* start at beginning of string */
+    nToken = 0;  /* no tokens yet */
+
+    while (*p && nToken < vecSize - 1) {
+        if (ISSPACE(*p))
+            *p++ = '\0';
+        else {
+            vec[nToken++] = p;
+            /* Skip to next non-space character or end */
+            while (*p && !ISSPACE(*p))
+                ++p;
+        }
+    }
+    vec[nToken] = NULL;
+
+    *nTokenP = nToken;
+}
+
+
+
+static void
+parseFontLine(const char **      const token,
+              struct fontinfo ** const fontinfoPP) {
+
+    struct fontinfo * fontinfoP;
+
+    MALLOCVAR(fontinfoP);
+    if (fontinfoP == NULL)
+        pm_error("out of memory for font information");
+    MALLOCARRAY(fontinfoP->filename, strlen(token[3] + 1));
+    if (fontinfoP->filename == NULL)
+        pm_error("out of memory for font information file name");
+
+    fontinfoP->font  = atoi(token[0]);
+    fontinfoP->size  = atoi(token[1]);
+    fontinfoP->style = atoi(token[2]);
+    strcpy(fontinfoP->filename, token[3]);
+    fontinfoP->loaded = 0;
+
+    *fontinfoPP = fontinfoP;
+}
+
+
+
 static int 
 load_fontdir(const char * const dirfile) {
 /*----------------------------------------------------------------------------
    Load the font directory from file named 'dirfile'.  Add its contents
    to the global list of fonts 'fontlist'.
 -----------------------------------------------------------------------------*/
-    FILE* fp;
-    int n, nfont;
-    char* arg[5], line[1024];
-    struct fontinfo* fontinfo;
+    FILE * ifP;
+    unsigned int nFont;
+    char line[1024]; 
 
-    if (!(fp = fopen(dirfile, "rb")))
-        return -1;
-    
-    nfont = 0;
-    while (fgets(line, 1024, fp)) {
-        if ((n = mk_argvn(line, arg, 5)) == 0 || arg[0][0] == '#')
-            continue;
-        if (n != 4)
-            continue;
-        MALLOCVAR(fontinfo);
-        if (fontinfo == NULL)
-            pm_error("out of memory for font information");
-        MALLOCARRAY(fontinfo->filename, strlen(arg[3] + 1));
-        if (fontinfo->filename == NULL)
-            pm_error("out of memory for font information file name");
+    ifP = pm_openr(dirfile);
 
-        fontinfo->font = atoi(arg[0]);
-        fontinfo->size = atoi(arg[1]);
-        fontinfo->style = atoi(arg[2]);
-        strcpy(fontinfo->filename, arg[3]);
-        fontinfo->loaded = 0;
+    nFont = 0;
+    while (fgets(line, 1024, ifP) && nFont < INT_MAX) {
+        const char * token[10];
+        unsigned int nToken;
 
-        fontinfo->next = 0;
-        *fontlist_ins = fontinfo;
-        fontlist_ins = &fontinfo->next;
-        nfont++;
-    }
+        tokenize(line, token, ARRAY_SIZE(token), &nToken);
 
-    return nfont;
-}
+        if (nToken == 0) {
+            /* blank line - ignore */
+        } else if (token[0][0] == '#') {
+            /* comment - ignore */
+        } else if (nToken != 4) {
+            /* Unrecognized format - ignore */
+        } else {
+            struct fontinfo * fontinfoP;
 
+            parseFontLine(token, &fontinfoP);
 
+            fontinfoP->next = 0;
+            *fontlist_ins = fontinfoP;
+            fontlist_ins = &fontinfoP->next;
+            ++nFont;
+        }
+    }
+    pm_close(ifP);
 
-static void
-read_rect(struct Rect * const r) {
-    r->top = read_word();
-    r->left = read_word();
-    r->bottom = read_word();
-    r->right = read_word();
+    return nFont;
 }
 
 
@@ -705,6 +787,26 @@ dumpRect(const char * const label,
 
 
 
+static void
+read_rect(struct Rect * const r) {
+
+    r->top    = read_word();
+    r->left   = read_word();
+    r->bottom = read_word();
+    r->right  = read_word();
+
+    if (r->top > r->bottom || r->right < r->left)
+        dumpRect("Invalid rectangle", *r);
+
+    if (r->top > r->bottom)
+        pm_error("Invalid PICT: a rectangle has a top below its bottom");
+    if (r->right < r->left)
+        pm_error("Invalid PICT: a rectangle has a right edge "
+                 "left of its left edge");
+}
+
+
+
 static int
 rectwidth(const struct Rect * const r) {
     return r->right - r->left;
@@ -720,10 +822,10 @@ rectheight(const struct Rect * const r) {
 
 
 static bool
-rectsamesize(const struct Rect * const r1, 
-             const struct Rect * const r2) {
-    return r1->right - r1->left == r2->right - r2->left &&
-           r1->bottom - r1->top == r2->bottom - r2->top ;
+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 ;
 }
 
 
@@ -753,20 +855,45 @@ rectscale(struct Rect * const r,
 
 
 
-static struct blit_info* 
-add_blit_list(void) {
+static void
+    initBlitList(blitList * const blitListP) {
 
-    struct blit_info * bi;
+    blitListP->firstP          = NULL;
+    blitListP->connectorP      = &blitListP->firstP;
+    blitListP->unblittableText = false;
+}
+
+
+
+static void
+addBlitList(blitList *        const blitListP,
+            struct Rect       const srcRect,
+            struct Rect       const srcBounds,
+            struct raster     const srcplane,
+            int               const pixSize,
+            struct Rect       const dstRect,
+            struct RGBColor * const colorMap,
+            int               const mode) {
+
+    struct blit_info * biP;
     
-    MALLOCVAR(bi);
-    if (bi == NULL)
+    MALLOCVAR(biP);
+    if (biP == NULL)
         pm_error("out of memory for blit list");
-    
-    bi->next = 0;
-    *last_bl = bi;
-    last_bl = &bi->next;
-    
-    return bi;
+    else {
+        biP->srcRect   = srcRect;
+        biP->srcBounds = srcBounds;
+        biP->srcplane  = srcplane;
+        biP->pixSize   = pixSize;
+        biP->dstRect   = dstRect;
+        biP->colorMap  = colorMap;
+        biP->mode      = mode;
+
+        biP->next = NULL;
+
+        *blitListP->connectorP = biP;
+        blitListP->connectorP = &biP->next;
+    }
 }
 
 
@@ -1026,7 +1153,7 @@ static pixval
 redepth(pixval const c,
         pixval const oldMaxval) {
     
-    return (c * PPM_MAXMAXVAL + oldMaxval / 2) / oldMaxval;
+    return ROUNDDIV(c * PPM_MAXMAXVAL, oldMaxval);
 }
 
 
@@ -1245,8 +1372,6 @@ doSameSize(transfer_func           trf,
            struct rgbPlanes  const dst,
            unsigned int      const dstwid) {
 /*----------------------------------------------------------------------------
-   Generalized (but slow) blit.
-
    Transfer pixels from 'src' to 'dst', applying the transfer function
    'trf'.
 
@@ -1419,18 +1544,30 @@ blitIdempotent(unsigned int          const pixSize,
 
 
 static void
-generalBlit(struct Rect       const srcRect, 
-            struct Rect       const srcBounds, 
-            struct raster     const srcplane,
-            int               const pixSize, 
-            struct Rect       const dstRect, 
-            struct Rect       const dstBounds, 
-            int               const dstwid, 
-            struct RGBColor * const color_map, 
-            int               const mode,
-            struct Rect       const clipsrc,
-            struct Rect       const clipdst) {
-    
+doBlit(struct Rect       const srcRect, 
+       struct Rect       const dstRect, 
+       struct Rect       const srcBounds, 
+       struct raster     const srcplane,
+       struct Rect       const dstBounds, 
+       struct rgbPlanes  const canvasPlanes,
+       int               const pixSize, 
+       int               const dstwid, 
+       struct RGBColor * const color_map, 
+       int               const mode) {
+/*----------------------------------------------------------------------------
+   Transfer some pixels from 'srcplane' to 'canvasPlanes', applying the
+   transfer function 'trf'.
+
+   'srcplane' contains the rectangle 'srcBounds' of the image.
+   'canvasPlanes' contains the rectangle 'dstRect' of the image.
+
+   Take the rectangle 'srcRect' of the source image and copy it to the
+   rectangle 'dstRec' of the destination image.
+
+   Each plane of 'canvasPlanes' is one word per pixel and contains actual
+   colors, never a palette index.  It is an array in row-major order
+   with 'dstwid' words per row.
+-----------------------------------------------------------------------------*/
     unsigned char * src;
     struct rgbPlanes dst;
     int dstoff;
@@ -1440,31 +1577,31 @@ generalBlit(struct Rect       const srcRect,
     transfer_func trf;
 
     if (verbose) {
-        dumpRect("copying from:", clipsrc);
-        dumpRect("to:          ", clipdst);
+        dumpRect("copying from:", srcRect);
+        dumpRect("to:          ", dstRect);
         pm_message("a %u x %u area to a %u x %u area",
-                   rectwidth(&clipsrc), rectheight(&clipsrc),
-                   rectwidth(&clipdst), rectheight(&clipdst));
+                   rectwidth(&srcRect), rectheight(&srcRect),
+                   rectwidth(&dstRect), rectheight(&dstRect));
     }
 
     {
         unsigned int const pkpixsize = pixSize == 16 ? 2 : 1;
-        unsigned int const srcRowNumber = clipsrc.top - srcBounds.top;
+        unsigned int const srcRowNumber = srcRect.top - srcBounds.top;
         unsigned int const srcRowOffset =
-            (clipsrc.left - srcBounds.left) * pkpixsize;
+            (srcRect.left - srcBounds.left) * pkpixsize;
         assert(srcRowNumber < srcplane.rowCount);
         assert(srcRowOffset < srcplane.rowSize);
         src = srcplane.bytes + srcRowNumber * srcplane.rowSize + srcRowOffset;
-        xsize = clipsrc.right - clipsrc.left;
-        ysize = clipsrc.bottom - clipsrc.top;
+        xsize = rectwidth(&srcRect);
+        ysize = rectheight(&srcRect);
         srcadd = srcplane.rowSize - xsize * pkpixsize;
     }
 
-    dstoff = (clipdst.top - dstBounds.top) * dstwid +
-        (clipdst.left - dstBounds.left);
-    dst.red = red + dstoff;
-    dst.grn = green + dstoff;
-    dst.blu = blue + dstoff;
+    dstoff = (dstRect.top - dstBounds.top) * dstwid +
+        (dstRect.left - dstBounds.left);
+    dst.red = canvasPlanes.red + dstoff;
+    dst.grn = canvasPlanes.grn + dstoff;
+    dst.blu = canvasPlanes.blu + dstoff;
 
     /* get rid of Text mask mode bit, if (erroneously) set */
     if ((mode & ~64) == 0)
@@ -1472,8 +1609,8 @@ generalBlit(struct Rect       const srcRect,
     else
         trf = transfer(mode & ~64);
 
-    if (!rectsamesize(&clipsrc, &clipdst))
-        doDiffSize(clipsrc, clipdst, pixSize, xsize, ysize,
+    if (!rectsamesize(srcRect, dstRect))
+        doDiffSize(srcRect, dstRect, pixSize, xsize, ysize,
                    trf, color_map, src, srcplane.rowSize, dst, dstwid);
     else {
         if (trf == NULL)
@@ -1491,12 +1628,22 @@ static int
 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 mode) {
+/*----------------------------------------------------------------------------
+   'srcplane' contains the rectangle 'srcBounds' of the image.
+
+   We transfer rectangle 'srcRect' from that.
+
+   if 'blitListP' is non-null, we don't draw anything on 'canvasP'; instead,
+   we add to the list *blitlistP a description of what needs to be drawn.
+-----------------------------------------------------------------------------*/
 
     /* 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,
@@ -1511,9 +1658,7 @@ blit(struct Rect       const srcRect,
         retval = 1;
     else {
         /* Almost got it.  Clip source rect with source bounds.
-           clip dest rect with dest bounds.  If source and
-           destination are not the same size, use Pnmscale
-           to get a nicely sized rectangle.
+           clip dest rect with dest bounds.
         */
         struct Rect clipsrc;
         struct Rect clipdst;
@@ -1521,22 +1666,16 @@ blit(struct Rect       const srcRect,
         rectinter(srcBounds, srcRect, &clipsrc);
         rectinter(dstBounds, dstRect, &clipdst);
 
-        if (fullres) {
-            struct blit_info * bi;
-            bi = add_blit_list();
-            bi->srcRect   = clipsrc;
-            bi->srcBounds = srcBounds;
-            bi->srcplane  = srcplane;
-            bi->pixSize   = pixSize;
-            bi->dstRect   = clipdst;
-            bi->color_map = color_map;
-            bi->mode      = mode;
+        if (blitListP) {
+            addBlitList(blitListP,
+                        clipsrc, srcBounds, srcplane, pixSize,
+                        clipdst, color_map, mode);
 
             retval = 0;
         } else {
-            generalBlit(srcRect, srcBounds, srcplane, pixSize,
-                        dstRect, dstBounds, dstwid, color_map, mode,
-                        clipsrc, clipdst);
+            doBlit(clipsrc, clipdst,
+                   srcBounds, srcplane, dstBounds, canvasP->planes,
+                   pixSize, dstwid, color_map, mode);
 
             retval = 1;
         }
@@ -1553,17 +1692,15 @@ blit(struct Rect       const srcRect,
  */
 
 static void 
-allocPlanes(struct rgbPlanes * const planesP) {
-
-    struct rgbPlanes planes;
+allocPlanes(unsigned int       const width,
+            unsigned int       const height,
+            struct rgbPlanes * const planesP) {
 
-    rowlen = picFrame.right - picFrame.left;
-    collen = picFrame.bottom - picFrame.top;
+    unsigned int const planelen = width * height;
 
-    clip_rect = picFrame;
+    struct rgbPlanes planes;
 
-    planelen = rowlen * collen;
-    MALLOCARRAY(planes.red,  planelen);
+    MALLOCARRAY(planes.red, planelen);
     MALLOCARRAY(planes.grn, planelen);
     MALLOCARRAY(planes.blu, planelen);
     if (planes.red == NULL || planes.grn == NULL || planes.blu == NULL)
@@ -1575,14 +1712,6 @@ allocPlanes(struct rgbPlanes * const planesP) {
     memset(planes.blu, 255, planelen * sizeof(word));
 
     *planesP = planes;
-
-    /* Until we wean this program off of global variables, we have to
-       set these:
-    */
-
-    red   = planes.red;
-    green = planes.grn;
-    blue  = planes.blu;
 }
 
 
@@ -1606,9 +1735,31 @@ compact(word const input) {
 
 
 static void
-do_blits(struct rgbPlanes * const planesP) {
+reportBlitList(blitList * const blitListP) {
+
+    if (verbose) {
+        unsigned int count;
+        struct blit_info * biP;
+
+        for (count = 0, biP = blitListP->firstP; biP; biP = biP->next)
+            ++count;
+
+        pm_message("# blits: %u", count);
+    }
+}
+
+
+
+static void
+doBlitList(struct canvas * const canvasP,
+           blitList *      const blitListP) {
+/*----------------------------------------------------------------------------
+   Do the list of blits *blitListP, drawing on canvas *canvasP.
 
-    struct blit_info* bi;
+   We allocate new plane data structures in *canvasP.  We assume it doesn't
+   have them already.
+-----------------------------------------------------------------------------*/
+    struct blit_info * bi;
     int srcwidth, dstwidth, srcheight, dstheight;
     double  scale, scalelow, scalehigh;
     double  xscale = 1.0;
@@ -1616,9 +1767,11 @@ do_blits(struct rgbPlanes * const planesP) {
     double  lowxscale, highxscale, lowyscale, highyscale;
     int     xscalecalc = 0, yscalecalc = 0;
 
+    reportBlitList(blitListP);
+
     fullres = 0;
 
-    for (bi = blit_list; bi; bi = bi->next) {
+    for (bi = blitListP->firstP; bi; bi = bi->next) {
         srcwidth = rectwidth(&bi->srcRect);
         dstwidth = rectwidth(&bi->dstRect);
         srcheight = rectheight(&bi->srcRect);
@@ -1694,29 +1847,34 @@ do_blits(struct rgbPlanes * const planesP) {
     }
 
     if (xscale != 1.0 || yscale != 1.0) {
-        for (bi = blit_list; bi; bi = bi->next)
-            rectscale(&bi->dstRect, xscale, yscale);
+        struct blit_info * biP;
+        
+        for (biP = blitListP->firstP; biP; biP = biP->next)
+            rectscale(&biP->dstRect, xscale, yscale);
 
-        pm_message("Scaling output by %f in X and %f in Y",
-                   xscale, yscale);
+        pm_message("Scaling output by %f in X and %f in Y", xscale, yscale);
         rectscale(&picFrame, xscale, yscale);
     }
 
-    allocPlanes(planesP);
+    rowlen = picFrame.right  - picFrame.left;
+    collen = picFrame.bottom - picFrame.top;
+
+    allocPlanes(rowlen, collen, &canvasP->planes);
 
-    for (bi = blit_list; bi; bi = bi->next) {
-        blit(bi->srcRect, bi->srcBounds, bi->srcplane,
-             bi->pixSize,
-             bi->dstRect, picFrame, rowlen,
-             bi->color_map,
-             bi->mode);
+    clip_rect = picFrame;
+
+    for (bi = blitListP->firstP; bi; bi = bi->next) {
+        doBlit(bi->srcRect, bi->dstRect,
+               bi->srcBounds, bi->srcplane, picFrame, canvasP->planes,
+               bi->pixSize, rowlen, bi->colorMap, bi->mode);
     }
 }
 
 
 
 static void
-outputPpm(struct rgbPlanes const planes) {
+outputPpm(FILE *           const ofP,
+          struct rgbPlanes const planes) {
 
     unsigned int width;
     unsigned int height;
@@ -1732,7 +1890,7 @@ outputPpm(struct rgbPlanes const planes) {
     width  = picFrame.right  - picFrame.left;
     height = picFrame.bottom - picFrame.top;
 
-    ppm_writeppminit(stdout, width, height, PPM_MAXMAXVAL, 0);
+    ppm_writeppminit(ofP, width, height, PPM_MAXMAXVAL, 0);
     pixelrow = ppm_allocrow(width);
     srcCursor = 0;
     for (row = 0; row < height; ++row) {
@@ -1745,9 +1903,8 @@ outputPpm(struct rgbPlanes const planes) {
                 );
             ++srcCursor;
         }
-        ppm_writeppmrow(stdout, pixelrow, width, PPM_MAXMAXVAL, 0);
+        ppm_writeppmrow(ofP, pixelrow, width, PPM_MAXMAXVAL, 0);
     }
-    pm_close(stdout);
 }
 
 
@@ -1773,8 +1930,13 @@ get_op(int const version) {
 
 
 
+static drawFn Clip;
+
 static void
-Clip(int const version) {
+Clip(struct canvas * const canvasP,
+     blitList *      const blitListP,
+     int             const version) {
+
     word len;
 
     len = read_word();
@@ -1791,8 +1953,13 @@ Clip(int const version) {
 
 
 
+static drawFn OpColor;
+
 static void
-OpColor(int const version) {
+OpColor(struct canvas * const canvasP,
+        blitList *      const blitListP,
+        int             const version) {
+
     op_color.red = read_word();
     op_color.grn = read_word();
     op_color.blu = read_word();
@@ -1806,17 +1973,17 @@ read_pixmap(struct pixMap * const p) {
     stage = "getting pixMap header";
 
     read_rect(&p->Bounds);
-    p->version = read_word();
-    p->packType = read_word();
-    p->packSize = read_long();
-    p->hRes = read_long();
-    p->vRes = read_long();
-    p->pixelType = read_word();
-    p->pixelSize = read_word();
-    p->cmpCount = read_word();
-    p->cmpSize = read_word();
+    p->version    = read_word();
+    p->packType   = read_word();
+    p->packSize   = read_long();
+    p->hRes       = read_long();
+    p->vRes       = read_long();
+    p->pixelType  = read_word();
+    p->pixelSize  = read_word();
+    p->cmpCount   = read_word();
+    p->cmpSize    = read_word();
     p->planeBytes = read_long();
-    p->pmTable = read_long();
+    p->pmTable    = read_long();
     p->pmReserved = read_long();
 
     if (verbose) {
@@ -1824,6 +1991,8 @@ read_pixmap(struct pixMap * const p) {
         pm_message("pixelSize: %d", p->pixelSize);
         pm_message("cmpCount:  %d", p->cmpCount);
         pm_message("cmpSize:   %d", p->cmpSize);
+        if (verbose)
+            dumpRect("Bounds:", p->Bounds);
     }
 
     if (p->pixelType != 0)
@@ -1878,7 +2047,7 @@ read_color_table(void) {
         color_table[val].blu = read_word();
 
         if (verbose > 1)
-            pm_message("%d: [%d,%d,%d]", val,
+            pm_message("Color %3u: [%u,%u,%u]", val,
                 color_table[val].red,
                 color_table[val].grn,
                 color_table[val].blu);
@@ -2084,6 +2253,42 @@ unpackUncompressedBits(FILE *          const ifP,
 
 
 static void
+reportValidateCompressedLineLen(unsigned int const row,
+                                unsigned int const linelen,
+                                unsigned int const rowSize) {
+/*----------------------------------------------------------------------------
+   'row' is a row number in the raster.
+
+   'linelen' is the number of bytes of PICT that the PICT says hold the
+   compressed version of that row.
+
+   'rowSize' is the number of bytes we expect the uncompressed line to
+   be (includes pad pixels on the right).
+-----------------------------------------------------------------------------*/
+    if (verbose > 1)
+        pm_message("Row %u: %u-byte compressed line", row, linelen);
+
+    /* When the line length value is garbage, it often causes the program to
+       try to read beyond EOF.  To make that failure easier to diagnose,
+       we sanity check the line length now.
+    */
+
+    /* In the worst case, a pixel is represented by two bytes: a one byte
+       repeat count of one followed by a one byte pixel value (the byte could
+       be up to 8 pixels) or a one byte block length of one followed by the
+       pixel value.  So expansion factor two.
+    */
+
+    if (linelen > rowSize * 2)
+        pm_error("Invalid PICT: compressed line of %u bytes for Row %u "
+                 "is too big "
+                 "to represent a %u-byte padded row, even with worse case "
+                 "compression.", linelen, row, rowSize);
+}
+
+
+
+static void
 expandRun(unsigned char * const block,
           unsigned int    const blockLimit,
           unsigned int    const bitsPerPixel,
@@ -2099,6 +2304,9 @@ expandRun(unsigned char * const block,
    returned.
 -----------------------------------------------------------------------------*/
     unsigned int const pkpixsize = bitsPerPixel == 16 ? 2 : 1;
+        /* The repetition unit size, in bytes.  The run consists of this many
+           bytes of packed data repeated the specified number of times.
+        */
 
     if (1 + pkpixsize > blockLimit)
         pm_error("PICT run block runs off the end of its line.  "
@@ -2114,14 +2322,28 @@ expandRun(unsigned char * const block,
         assert(block[0] & 0x80);  /* It's a run */
 
         if (verbose > 1)
-            pm_message("Block: run of %u pixels or plane samples", runLength);
-        
+            pm_message("Block: run of %u packed %u-byte units",
+                       runLength, pkpixsize);
+
         unpackBuf(&block[1], pkpixsize, bitsPerPixel,
                   &bytePixels, &expandedByteCount);
+
+        /* I assume in a legal PICT the run never has padding for the
+           case that the run is at the right edge of a row and the
+           remaining columns in the row don't fill whole bytes.
+           E.g. if there are 25 columns left in the row and 1 bit per
+           pixel, we won't see a run of 4 bytes and have to ignore the
+           last 7 pixels.  Instead, we'll see a run of 3 bytes
+           followed by a non-run block for the remaining pixel.
+
+           That is what I saw in a test image.
+        */
         
-        if (expandedByteCount > expandedSize)
+        if (expandedByteCount * runLength > expandedSize)
             pm_error("Invalid PICT image.  It contains a row with more pixels "
-                     "than the width of the image");
+                     "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) {
@@ -2146,9 +2368,23 @@ copyPixelGroup(unsigned char * const block,
                unsigned int *  const rasterBytesGeneratedP) {
 /*----------------------------------------------------------------------------
    Copy a group of pixels (the data says, "take the following N pixels").
+
+   Copy them (unpacked) from block block[] to dest[].
+
+   block[] self-describes its length.  Return that length as
+   *blockLengthP.
+
+   block[] contains at most 'blockLimit' valid array elements, so if
+   the length information in block[] indicates the block is larger
+   than that, the block is corrupt.
+
+   Return the number of pixels placed in dest[] as *rasterBytesGeneratedP.
+
+   The output array dest[] has 'destSize' elements of space.  Ignore
+   any pixels on the right that won't fit in that.
 -----------------------------------------------------------------------------*/
-    unsigned int const pkpixsize = bitsPerPixel == 16 ? 2 : 1;
-    unsigned int const groupLen = block[0] + 1;
+    unsigned int const pkpixsize   = bitsPerPixel == 16 ? 2 : 1;
+    unsigned int const groupLen    = block[0] + 1;
     unsigned int const blockLength = 1 + groupLen * pkpixsize;
 
     if (blockLength > blockLimit)
@@ -2159,22 +2395,35 @@ copyPixelGroup(unsigned char * const block,
         unsigned int i;
         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 individual pixels or plane samples",
-                       groupLen);
+            pm_message("Block: %u explicit packed %u-byte units",
+                       groupLen, pkpixsize);
         
         unpackBuf(&block[1], groupLen * pkpixsize, bitsPerPixel,
                   &bytePixels, &bytePixelLen);
-        
-        for (i = 0; i < MIN(bytePixelLen, destSize); ++i)
+
+        /* It is normal for the above to return more pixels than there
+           are left in the row, because of padding.  E.g. there is one
+           pixel left in the row, at one bit per pixel.  But a block
+           contains full bytes, so it must contain at least 8 pixels.
+           7 of them are padding, which we should ignore.
+
+           BUT: I saw an image in which the block had _two_ data bytes
+           (16 pixels) when only 1 pixel remained in the row.  I don't
+           understand why, but ignoring the 15 extra seemed to work.
+        */
+        rasterBytesGenerated = MIN(bytePixelLen, destSize);
+
+        for (i = 0; i < rasterBytesGenerated; ++i)
             dest[i] = bytePixels[i];
         
         *blockLengthP = blockLength;
-        *rasterBytesGeneratedP = MIN(bytePixelLen, destSize);
+        *rasterBytesGeneratedP = rasterBytesGenerated;
     }
 }
 
@@ -2218,12 +2467,83 @@ static unsigned int const maxPixelBytesPerBlock = 1024;
 
 
 static void
+interpretCompressedLine(unsigned char * const linebuf,
+                        unsigned int    const linelen,
+                        unsigned char * const rowRaster,
+                        unsigned int    const rowSize,
+                        unsigned int    const bitsPerPixel) {
+/*----------------------------------------------------------------------------
+   linebuf[] contains 'linelen' bytes from the PICT image that represents
+   one row of the image, in compressed format.  Return the
+   uncompressed pixels of that row as rowRaster[].
+
+   rowRaster[] has 'rowSize' bytes of space.  Caller ensures that
+   linebuf[] does not contain more pixels than that, unless the PICT
+   image from which it comes is corrupt.
+-----------------------------------------------------------------------------*/
+    unsigned int lineCursor;
+        /* Cursor into linebuf[] -- the compressed data */
+    unsigned int rasterCursor;
+        /* Cursor into rowRaster[] -- the uncompressed data */
+
+    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);
+
+        interpretOneRasterBlock(
+            &linebuf[lineCursor], linelen - lineCursor,
+            bitsPerPixel,
+            &rowRaster[rasterCursor], rowSize - rasterCursor,
+            &blockLength, &rasterBytesGenerated);
+        
+        lineCursor += blockLength;
+        rasterCursor += rasterBytesGenerated;
+        assert(rasterCursor <= rowSize);
+    }
+    if (verbose > 1)
+        pm_message("Got %u pixels for row", rasterCursor);
+}
+
+
+/* There is some confusion about when, in PICT, a line length is one byte and
+  when it is two.  An Apple document says it is two bytes when the number of
+  pixels in the row, padded, is > 250.  Ppmtopict generated PICTs that way
+  until January 2009.  Picttoppm assumed something similar until March 2004:
+  It assumed the line length is two bytes when the number of pixels > 250 _or_
+  bits per pixel > 8.  But in March 2004, Steve Summit did a bunch of
+  experiments on existing PICT files and found that they all worked with the
+  rule "pixels per row > 200 => 2 byte line length" and some did not work
+  with the original rule.
+
+  So in March 2004, Picttoppm changed to pixels per row > 200.  Ppmtopict
+  didn't catch up until January 2009.
+
+  http://developer.apple.com/documentation/mac/QuickDraw/QuickDraw-460.html#HEADING460-0
+
+  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 
+  16 bpp images with 200 pixels per row.
+
+  We have not been able to find an offical spec for PICT.
+
+  Some day, we may have to make a user option for this.
+*/
+
+
+static void
 unpackCompressedBits(FILE *          const ifP,
                      struct raster   const raster,
                      unsigned int    const rowBytes,
                      unsigned int    const bitsPerPixel) {
 /*----------------------------------------------------------------------------
-   Read the raster of on file *ifP and place it in 'raster'.
+   Read the raster on file *ifP and place it in 'raster'.
 
    The data in the file is compressed with run length encoding and
    possibly packed multiple pixels per byte as well.
@@ -2232,11 +2552,13 @@ unpackCompressedBits(FILE *          const ifP,
    "packing" and I don't know what packing is called.  But we don't
    use that confusing terminology in this program, except when talking
    to the user.
-
-   *boundsP describes the rectangle.
 -----------------------------------------------------------------------------*/
     unsigned int const llsize = rowBytes > 200 ? 2 : 1;
-    unsigned int rowOfRect;
+        /* Width in bytes of the field at the beginning of a line that tells
+           how long (in bytes) the line is.  See notes above about this
+           computation.
+        */
+    unsigned int row;
     unsigned char * linebuf;
     unsigned int linebufSize;
 
@@ -2245,20 +2567,17 @@ unpackCompressedBits(FILE *          const ifP,
     if (linebuf == NULL)
         pm_error("can't allocate memory for line buffer");
 
-    for (rowOfRect = 0; rowOfRect < raster.rowCount; ++rowOfRect) {
+    for (row = 0; row < raster.rowCount; ++row) {
         unsigned char * const rowRaster =
-            &raster.bytes[rowOfRect * raster.rowSize];
+            &raster.bytes[row * raster.rowSize];
         unsigned int linelen;
-        unsigned int lineCursor;
-        unsigned int rasterCursor;
 
         if (llsize == 2)
             linelen = read_word();
         else
             linelen = read_byte();
 
-        if (verbose > 1)
-            pm_message("linelen: %u", linelen);
+        reportValidateCompressedLineLen(row, linelen, raster.rowSize);
 
         if (linelen > linebufSize) {
             linebufSize = linelen;
@@ -2268,23 +2587,8 @@ unpackCompressedBits(FILE *          const ifP,
         }
         readBytes(ifP, linelen, linebuf);
 
-        for (lineCursor = 0, rasterCursor = 0; lineCursor < linelen; ) {
-            unsigned int blockLength, rasterBytesGenerated;
-
-            assert(lineCursor <= linelen);
-            
-            interpretOneRasterBlock(
-                &linebuf[lineCursor], linelen - lineCursor,
-                bitsPerPixel,
-                &rowRaster[rasterCursor], raster.rowSize - rasterCursor,
-                &blockLength, &rasterBytesGenerated);
-
-            lineCursor += blockLength;
-            rasterCursor += rasterBytesGenerated;
-            assert(rasterCursor <= raster.rowSize);
-        }
-        if (verbose > 1)
-            pm_message("row %u: got %u", rowOfRect, rasterCursor);
+        interpretCompressedLine(linebuf, linelen, rowRaster, raster.rowSize,
+                                bitsPerPixel);
     }
     free(linebuf);
 }
@@ -2386,21 +2690,42 @@ read_pattern(void) {
 
 /* these 3 do nothing but skip over their data! */
 
+static drawFn BkPixPat;
+
 static void
-BkPixPat(int const version) {
+BkPixPat(struct canvas * const canvasP,
+         blitList *      const blitListP,
+         int             const version) {
+
     read_pattern();
 }
 
+
+
+static drawFn PnPixPat;
+
 static void
-PnPixPat(int const version) {
+PnPixPat(struct canvas * const canvasP,
+         blitList *      const blitListP,
+         int             const version) {
+
     read_pattern();
 }
 
+
+
+static drawFn FillPixPat;
+
 static void
-FillPixPat(int const version) {
+FillPixPat(struct canvas * const canvasP,
+           blitList *      const blitListP,
+           int             const version) {
+
     read_pattern();
 }
 
+
+
 static void
 read_8x8_pattern(struct Pattern * const pat) {
     unsigned char buf[8];
@@ -2424,29 +2749,49 @@ read_8x8_pattern(struct Pattern * const pat) {
 
 
 
+static drawFn BkPat;
+
 static void 
-BkPat(int const version) {
+BkPat(struct canvas * const canvasP,
+      blitList *      const blitListP,
+      int             const version) {
+
     read_8x8_pattern(&bkpat);
 }
 
 
 
+static drawFn PnPat;
+
 static void 
-PnPat(int const version) {
+PnPat(struct canvas * const canvasP,
+      blitList *      const blitListP,
+      int             const version) {
+
     read_8x8_pattern(&pen_pat);
 }
 
 
 
+static drawFn FillPat;
+
 static void 
-FillPat(int const version) {
+FillPat(struct canvas * const canvasP,
+        blitList *      const blitListP,
+        int             const version) {
+
     read_8x8_pattern(&fillpat);
 }
 
 
 
+static drawFn PnSize;
+
 static void 
-PnSize(int const version) {
+PnSize(struct canvas * const canvasP,
+       blitList *      const blitListP,
+       int             const version) {
+
     pen_height = read_word();
     pen_width = read_word();
     if (verbose)
@@ -2455,8 +2800,12 @@ PnSize(int const version) {
 
 
 
+static drawFn PnSize;
+
 static void 
-PnMode(int const version) {
+PnMode(struct canvas * const canvasP,
+       blitList *      const blitListP,
+       int             const version) {
 
     pen_mode = read_word();
 
@@ -2480,8 +2829,13 @@ read_rgb(struct RGBColor * const rgb) {
 
 
 
+static drawFn RGBFgCol;
+
 static void 
-RGBFgCol(int const v) {
+RGBFgCol(struct canvas * const canvasP,
+         blitList *      const blitListP,
+         int             const version) {
+
     read_rgb(&foreground);
     if (verbose)
         pm_message("foreground now [%d,%d,%d]", 
@@ -2490,8 +2844,13 @@ RGBFgCol(int const v) {
 
 
 
+static drawFn RGBBkCol;
+
 static void 
-RGBBkCol(int const v) {
+RGBBkCol(struct canvas * const canvasP,
+         blitList *      const blitListP,
+         int             const version) {
+
     read_rgb(&background);
     if (verbose)
         pm_message("background now [%d,%d,%d]", 
@@ -2503,34 +2862,37 @@ RGBBkCol(int const v) {
 #define PIXEL_INDEX(x,y) ((y) - picFrame.top) * rowlen + (x) - picFrame.left
 
 static void 
-draw_pixel(int                const x, 
-           int                const y, 
+draw_pixel(struct canvas *   const canvasP,
+           int               const x, 
+           int               const y, 
            struct RGBColor * const clr, 
-           transfer_func            trf) {
-
-    int i;
-    struct RGBColor dst;
+           transfer_func           trf) {
 
     if (x < clip_rect.left || x >= clip_rect.right ||
-        y < clip_rect.top || y >= clip_rect.bottom)
-    {
-        return;
-    }
+        y < clip_rect.top || y >= clip_rect.bottom) {
+    } else {
+        unsigned int const i = PIXEL_INDEX(x, y);
+
+        struct RGBColor dst;
+
+        dst.red = canvasP->planes.red[i];
+        dst.grn = canvasP->planes.grn[i];
+        dst.blu = canvasP->planes.blu[i];
+ 
+        (*trf)(clr, &dst);
 
-    i = PIXEL_INDEX(x, y);
-    dst.red = red[i];
-    dst.grn = green[i];
-    dst.blu = blue[i];
-    (*trf)(clr, &dst);
-    red[i] = dst.red;
-    green[i] = dst.grn;
-    blue[i] = dst.blu;
+        canvasP->planes.red[i] = dst.red;
+        canvasP->planes.grn[i] = dst.grn;
+        canvasP->planes.blu[i] = dst.blu;
+    }
 }
 
 
 
 static void 
-draw_pen_rect(struct Rect * const r) {
+draw_pen_rect(struct canvas * const canvasP,
+              struct Rect *   const r) {
+
     int const rowadd = rowlen - (r->right - r->left);
 
     int i;
@@ -2541,16 +2903,18 @@ draw_pen_rect(struct Rect * const r) {
     
     for (y = r->top; y < r->bottom; y++) {
         for (x = r->left; x < r->right; x++) {
-            dst.red = red[i];
-            dst.grn = green[i];
-            dst.blu = blue[i];
+            dst.red = canvasP->planes.red[i];
+            dst.grn = canvasP->planes.grn[i];
+            dst.blu = canvasP->planes.blu[i];
+
             if (pen_pat.pix[(x & 7) + (y & 7) * 8])
                 (*pen_trf)(&black, &dst);
             else
                 (*pen_trf)(&white, &dst);
-            red[i] = dst.red;
-            green[i] = dst.grn;
-            blue[i] = dst.blu;
+
+            canvasP->planes.red[i] = dst.red;
+            canvasP->planes.grn[i] = dst.grn;
+            canvasP->planes.blu[i] = dst.blu;
 
             i++;
         }
@@ -2561,8 +2925,10 @@ draw_pen_rect(struct Rect * const r) {
 
 
 static void 
-draw_pen(int const x, 
-         int const y) {
+draw_pen(struct canvas * const canvasP,
+         int             const x, 
+         int             const y) {
+
     struct Rect penrect;
 
     penrect.left = x;
@@ -2572,7 +2938,7 @@ draw_pen(int const x,
 
     rectinter(penrect, clip_rect, &penrect);
 
-    draw_pen_rect(&penrect);
+    draw_pen_rect(canvasP, &penrect);
 }
 
 /*
@@ -2589,10 +2955,12 @@ draw_pen(int const x,
  * Paul Heckbert    3 Sep 85
  */
 static void 
-scan_line(short const x1, 
-          short const y1, 
-          short const x2, 
-          short const y2) {
+scan_line(struct canvas * const canvasP,
+          short           const x1, 
+          short           const y1, 
+          short           const x2, 
+          short           const y2) {
+
     int d, x, y, ax, ay, sx, sy, dx, dy;
 
     if (!(pen_width == 0 && pen_height == 0)) {
@@ -2605,7 +2973,7 @@ scan_line(short const x1,
         if (ax>ay) {        /* x dominant */
             d = ay-(ax>>1);
             for (;;) {
-                draw_pen(x, y);
+                draw_pen(canvasP, x, y);
                 if (x==x2) return;
                 if ((x > rowlen) && (sx > 0)) return;
                 if (d>=0) {
@@ -2619,7 +2987,7 @@ scan_line(short const x1,
         else {          /* y dominant */
             d = ax-(ay>>1);
             for (;;) {
-                draw_pen(x, y);
+                draw_pen(canvasP, x, y);
                 if (y==y2) return;
                 if ((y > collen) && (sy > 0)) return;
                 if (d>=0) {
@@ -2635,137 +3003,178 @@ scan_line(short const x1,
 
 
 
+static drawFn Line;
+
 static void 
-Line(int const v) {
+Line(struct canvas * const canvasP,
+     blitList *      const blitListP,
+     int             const version) {
+
   struct Point p1;
   read_point(&p1);
   read_point(&current);
   if (verbose)
     pm_message("(%d,%d) to (%d, %d)",
            p1.x,p1.y,current.x,current.y);
-  scan_line(p1.x,p1.y,current.x,current.y);
+  scan_line(canvasP, p1.x,p1.y,current.x,current.y);
 }
 
 
 
+static drawFn LineFrom;
+
 static void 
-LineFrom(int const v) {
-  struct Point p1;
-  read_point(&p1);
-  if (verbose)
-    pm_message("(%d,%d) to (%d, %d)",
-           current.x,current.y,p1.x,p1.y);
+LineFrom(struct canvas * const canvasP,
+         blitList *      const blitListP,
+         int             const version) {
+
+    struct Point p1;
+    read_point(&p1);
+    if (verbose)
+        pm_message("(%d,%d) to (%d, %d)", current.x, current.y, p1.x, p1.y);
 
-  if (!fullres)
-      scan_line(current.x,current.y,p1.x,p1.y);
+    if (!blitListP)
+        scan_line(canvasP, current.x, current.y, p1.x, p1.y);
 
-  current.x = p1.x;
-  current.y = p1.y;
+    current.x = p1.x;
+    current.y = p1.y;
 }
 
 
 
+static drawFn ShortLine;
+
 static void 
-ShortLine(int const v) {
-  struct Point p1;
-  read_point(&p1);
-  read_short_point(&current);
-  if (verbose)
-    pm_message("(%d,%d) delta (%d, %d)",
-           p1.x,p1.y,current.x,current.y);
-  current.x += p1.x;
-  current.y += p1.y;
+ShortLine(struct canvas * const canvasP,
+          blitList *      const blitListP,
+          int             const version) {
 
-  if (!fullres)
-      scan_line(p1.x,p1.y,current.x,current.y);
+    struct Point p1;
+    read_point(&p1);
+    read_short_point(&current);
+    if (verbose)
+        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);
 }
 
 
 
+static drawFn ShortLineFrom;
+
 static void 
-ShortLineFrom(int const v) {
-  struct Point p1;
-  read_short_point(&p1);
-  if (verbose)
-    pm_message("(%d,%d) delta (%d, %d)",
-               current.x,current.y,p1.x,p1.y);
-  p1.x += current.x;
-  p1.y += current.y;
-  if (!fullres)
-      scan_line(current.x,current.y,p1.x,p1.y);
-  current.x = p1.x;
-  current.y = p1.y;
+ShortLineFrom(struct canvas * const canvasP,
+              blitList *      const blitListP,
+              int             const version) {
+
+    struct Point p1;
+    read_short_point(&p1);
+    if (verbose)
+        pm_message("(%d,%d) delta (%d, %d)",
+                   current.x,current.y,p1.x,p1.y);
+    p1.x += current.x;
+    p1.y += current.y;
+    if (!blitListP)
+        scan_line(canvasP, current.x, current.y, p1.x, p1.y);
+    current.x = p1.x;
+    current.y = p1.y;
 }
 
+
+
 static void 
-do_paintRect(struct Rect const prect) {
+do_paintRect(struct canvas * const canvasP,
+             struct Rect     const prect) {
+
     struct Rect rect;
   
-    if (fullres)
-        return;
-
     if (verbose)
         dumpRect("painting", prect);
 
     rectinter(clip_rect, prect, &rect);
 
-    draw_pen_rect(&rect);
+    draw_pen_rect(canvasP, &rect);
 }
 
 
 
+static drawFn paintRect;
+
 static void 
-paintRect(int const v) {
+paintRect(struct canvas * const canvasP,
+          blitList *      const blitListP,
+          int             const version) {
+
     read_rect(&cur_rect);
-    do_paintRect(cur_rect);
+    if (!blitListP)
+        do_paintRect(canvasP, cur_rect);
 }
 
 
 
+static drawFn paintSameRect;
+
 static void 
-paintSameRect(int const v) {
-    do_paintRect(cur_rect);
+paintSameRect(struct canvas * const canvasP,
+              blitList *      const blitListP,
+              int             const version) {
+
+    if (!blitListP)
+        do_paintRect(canvasP, cur_rect);
 }
 
 
 
 static void 
-do_frameRect(struct Rect const rect) {
-    int x, y;
+do_frameRect(struct canvas * const canvasP,
+             struct Rect     const rect) {
 
-    if (fullres)
-        return;
-  
     if (verbose)
         dumpRect("framing", rect);
 
-    if (pen_width == 0 || pen_height == 0)
-        return;
-
-    for (x = rect.left; x <= rect.right - pen_width; x += pen_width) {
-        draw_pen(x, rect.top);
-        draw_pen(x, rect.bottom - pen_height);
-    }
+    if (pen_width > 0 && pen_height > 0) {
+        unsigned int x, y;
 
-    for (y = rect.top; y <= rect.bottom - pen_height ; y += pen_height) {
-        draw_pen(rect.left, y);
-        draw_pen(rect.right - pen_width, y);
+        for (x = rect.left; x <= rect.right - pen_width; x += pen_width) {
+            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);
+        }
     }
 }
 
 
 
+static drawFn frameRect;
+
 static void 
-frameRect(int const v) {
+frameRect(struct canvas * const canvasP,
+          blitList *      const blitListP,
+          int             const version) {
+
     read_rect(&cur_rect);
-    do_frameRect(cur_rect);
+    if (!blitListP)
+        do_frameRect(canvasP, cur_rect);
 }
 
 
 
+static drawFn frameSameRect;
+
 static void 
-frameSameRect(int const v) {
-    do_frameRect(cur_rect);
+frameSameRect(struct canvas * const canvasP,
+              blitList *      const blitListP,
+              int             const version) {
+
+    if (!blitListP)
+        do_frameRect(canvasP, cur_rect);
 }
 
 
@@ -2806,8 +3215,10 @@ poly_sort(int const sort_index, struct Point points[]) {
 /* Watch out for the lack of error checking in the next two functions ... */
 
 static void 
-scan_poly(int          const np, 
-          struct Point       pts[]) {
+scan_poly(struct canvas * const canvasP,
+          int             const np, 
+          struct Point          pts[]) {
+
   int dx,dy,dxabs,dyabs,i,scan_index,j,k,px,py;
   int sdx,sdy,x,y,toggle,old_sdy,sy0;
 
@@ -2860,7 +3271,7 @@ scan_poly(int          const np,
         scan_index++;
       }
       px += sdx;
-      draw_pen(px, py);
+      draw_pen(canvasP, px, py);
     }
       }
     else
@@ -2876,7 +3287,7 @@ scan_poly(int          const np,
         old_sdy = sdy;
         if (sdy != 0) scan_index--;
       }
-      draw_pen(px,py);
+      draw_pen(canvasP, px,py);
       coord[scan_index].x = px;
       coord[scan_index].y = py;
       scan_index++;
@@ -2896,7 +3307,7 @@ scan_poly(int          const np,
     if ((coord[i].y == coord[i+1].y) && (toggle == 0))
       {
     for (j = coord[i].x; j <= coord[i+1].x; j++)
-      draw_pen(j, coord[i].y);
+      draw_pen(canvasP, j, coord[i].y);
     toggle = 1;
       }
     else
@@ -2905,9 +3316,14 @@ scan_poly(int          const np,
 }
   
 
+
+static drawFn paintPoly;
   
 static void 
-paintPoly(int const v) {
+paintPoly(struct canvas * const canvasP,
+          blitList *      const blitListP,
+          int             const version) {
+
   struct Rect bb;
   struct Point pts[100];
   int i, np = (read_word() - 10) >> 2;
@@ -2917,14 +3333,19 @@ paintPoly(int const v) {
     read_point(&pts[i]);
 
   /* scan convert poly ... */
-  if (!fullres)
-      scan_poly(np, pts);
+  if (!blitListP)
+      scan_poly(canvasP, np, pts);
 }
 
 
 
+static drawFn PnLocHFrac;
+
 static void 
-PnLocHFrac(int const version) {
+PnLocHFrac(struct canvas * const canvasP,
+           blitList *      const blitListP,
+           int             const version) {
+
     word frac = read_word();
 
     if (verbose)
@@ -2933,8 +3354,13 @@ PnLocHFrac(int const version) {
 
 
 
+static drawFn TxMode;
+
 static void 
-TxMode(int const version) {
+TxMode(struct canvas * const canvasP,
+       blitList *      const blitListP,
+       int             const version) {
+
     text_mode = read_word();
 
     if (text_mode >= 8 && text_mode < 15)
@@ -2949,8 +3375,13 @@ TxMode(int const version) {
 
 
 
+static drawFn TxFont;
+
 static void 
-TxFont(int const version) {
+TxFont(struct canvas * const canvasP,
+       blitList *      const blitListP,
+       int             const version) {
+
     text_font = read_word();
     if (verbose)
         pm_message("text font %s", const_name(font_name, text_font));
@@ -2958,8 +3389,13 @@ TxFont(int const version) {
 
 
 
+static drawFn TxFace;
+
 static void 
-TxFace(int const version) {
+TxFace(struct canvas * const canvasP,
+       blitList *      const blitListP,
+       int             const version) {
+
     text_face = read_byte();
     if (verbose)
         pm_message("text face %d", text_face);
@@ -2967,8 +3403,13 @@ TxFace(int const version) {
 
 
 
+static drawFn TxSize;
+
 static void 
-TxSize(int const version) {
+TxSize(struct canvas * const canvasP,
+       blitList *      const blitListP,
+       int             const version) {
+
     text_size = read_word();
     if (verbose)
         pm_message("text size %d", text_size);
@@ -2977,8 +3418,11 @@ TxSize(int const version) {
 
 
 static void
-skip_text(void) {
+skip_text(blitList * const blitListP) {
+
     skip(read_byte());
+
+    blitListP->unblittableText = true;
 }
 
 
@@ -2997,6 +3441,7 @@ static struct font*
 get_font(int const font, 
          int const size, 
          int const style) {
+
     int closeness, bestcloseness;
     struct fontinfo* fi, *best;
 
@@ -3058,8 +3503,10 @@ rotate(int * const x,
 
 
 static void
-do_ps_text(word const tx, 
-           word const ty) {
+do_ps_text(struct canvas * const canvasP,
+           word            const tx, 
+           word            const ty) {
+
     int len, width, i, w, h, x, y, rx, ry, o;
     byte str[256], ch;
     struct glyph* glyph;
@@ -3110,9 +3557,9 @@ do_ps_text(word const tx,
                 {
                     o = PIXEL_INDEX(rx, ry);
                     if (glyph->bmap[h * glyph->width + w]) {
-                        red[o] = foreground.red;
-                        green[o] = foreground.grn;
-                        blue[o] = foreground.blu;
+                        canvasP->planes.red[o] = foreground.red;
+                        canvasP->planes.grn[o] = foreground.grn;
+                        canvasP->planes.blu[o] = foreground.blu;
                     }
                 }
             }
@@ -3125,16 +3572,19 @@ do_ps_text(word const tx,
 
 
 static void
-do_text(word const startx, 
-        word const starty) {
-    if (fullres)
-        skip_text();
+do_text(struct canvas *  const canvasP,
+        blitList *       const blitListP,
+        word             const startx, 
+        word             const starty) {
+
+    if (blitListP)
+        skip_text(blitListP);
     else {
         if (!(tfont = get_font(text_font, text_size, text_face)))
             tfont = pbm_defaultfont("bdf");
 
         if (ps_text)
-            do_ps_text(startx, starty);
+            do_ps_text(canvasP, startx, starty);
         else {
             int len;
             word x, y;
@@ -3154,7 +3604,8 @@ do_text(word const startx,
                             struct RGBColor * const colorP = 
                                 glyph->bmap[h * glyph->width + w] ?
                                 &black : &white;
-                            draw_pixel(x + w + glyph->x, dy, colorP, text_trf);
+                            draw_pixel(canvasP,
+                                       x + w + glyph->x, dy, colorP, text_trf);
                         }
                     }
                     x += glyph->xadd;
@@ -3168,34 +3619,56 @@ do_text(word const startx,
 
 
 
+static drawFn LongText;
+
 static void
-LongText(int const version) {
+LongText(struct canvas * const canvasP,
+         blitList *      const blitListP,
+         int             const version) {
+
     struct Point p;
 
     read_point(&p);
-    do_text(p.x, p.y);
+
+    do_text(canvasP, blitListP, p.x, p.y);
 }
 
 
 
+static drawFn DHText;
+
 static void
-DHText(int const version) {
+DHText(struct canvas * const canvasP,
+       blitList *      const blitListP,
+       int             const version) {
+
     current.x += read_byte();
-    do_text(current.x, current.y);
+
+    do_text(canvasP, blitListP, current.x, current.y);
 }
 
 
 
+static drawFn DVText;
+
 static void
-DVText(int const version) {
+DVText(struct canvas * const canvasP,
+       blitList *      const blitListP,
+       int             const version) {
+
     current.y += read_byte();
-    do_text(current.x, current.y);
+
+    do_text(canvasP, blitListP, current.x, current.y);
 }
 
 
 
+static drawFn DHDVText;
+
 static void
-DHDVText(int const version) {
+DHDVText(struct canvas * const canvasP,
+         blitList *      const blitListP,
+         int             const version) {
     byte dh, dv;
 
     dh = read_byte();
@@ -3206,7 +3679,8 @@ DHDVText(int const version) {
 
     current.x += dh;
     current.y += dv;
-    do_text(current.x, current.y);
+
+    do_text(canvasP, blitListP, current.x, current.y);
 }
 
 
@@ -3216,8 +3690,10 @@ DHDVText(int const version) {
  */
 
 static void
-directBits(unsigned int const pictVersion, 
-           bool         const skipRegion) {
+directBits(struct canvas * const canvasP,
+           blitList *      const blitListP,
+           unsigned int    const pictVersion, 
+           bool            const skipRegion) {
 
     struct pixMap   p;
     struct Rect     srcRect;
@@ -3257,11 +3733,11 @@ directBits(unsigned int const pictVersion,
         pm_message("transfer mode = %s", const_name(transfer_name, mode));
 
     if (skipRegion) 
-        skip_poly_or_region(pictVersion);
+        skip_poly_or_region(canvasP, blitListP, pictVersion);
 
     unpackbits(ifp, &p.Bounds, 0, p.pixelSize, &raster);
 
-    blit(srcRect, p.Bounds, raster, p.pixelSize,
+    blit(srcRect, p.Bounds, raster, canvasP, blitListP, p.pixelSize,
          dstRect, picFrame, rowlen, NULL, mode);
 
     freeRaster(raster);
@@ -3272,26 +3748,36 @@ directBits(unsigned int const pictVersion,
 #define SKIP_REGION_TRUE TRUE
 #define SKIP_REGION_FALSE FALSE
 
+static drawFn DirectBitsRect;
+
 static void
-DirectBitsRect(int const version) {
+DirectBitsRect(struct canvas * const canvasP,
+               blitList *      const blitListP,
+               int             const version) {
 
-    directBits(version, SKIP_REGION_FALSE);
+    directBits(canvasP, blitListP, version, SKIP_REGION_FALSE);
 }
 
 
 
+static drawFn DirectBitsRgn;
+
 static void
-DirectBitsRgn(int const version) {
+DirectBitsRgn(struct canvas * const canvasP,
+              blitList *      const blitListP,
+              int             const version) {
 
-    directBits(version, SKIP_REGION_TRUE);
+    directBits(canvasP, blitListP, version, SKIP_REGION_TRUE);
 }
 
 
 
 static void
-do_pixmap(int  const version, 
-          word const rowBytes, 
-          int  const is_region) {
+do_pixmap(struct canvas * const canvasP,
+          blitList *      const blitListP,
+          int             const version, 
+          word            const rowBytes, 
+          int             const is_region) {
 /*----------------------------------------------------------------------------
    Do a paletted image.
 -----------------------------------------------------------------------------*/
@@ -3327,13 +3813,13 @@ do_pixmap(int  const version,
         pm_message("transfer mode = %s", const_name(transfer_name, mode));
 
     if (is_region)
-        skip_poly_or_region(version);
+        skip_poly_or_region(canvasP, blitListP, version);
 
     stage = "unpacking rectangle";
 
     unpackbits(ifp, &p.Bounds, rowBytes, p.pixelSize, &raster);
 
-    blit(srcRect, p.Bounds, raster, 8,
+    blit(srcRect, p.Bounds, raster, canvasP, blitListP, 8,
          dstRect, picFrame, rowlen, color_table, mode);
 
     free(color_table);
@@ -3343,17 +3829,25 @@ do_pixmap(int  const version,
 
 
 static void
-do_bitmap(int const version, 
-          int const rowBytes, 
-          int const is_region) {
+do_bitmap(FILE *          const ifP,
+          struct canvas * const canvasP,
+          blitList *      const blitListP,
+          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.
+
+   Read the raster from file 'ifP'.
 -----------------------------------------------------------------------------*/
     struct Rect Bounds;
     struct Rect srcRect;
     struct Rect dstRect;
     word mode;
     struct raster raster;
+        /* This raster contains padding on the right to make a multiple
+           of 16 pixels per row.
+        */
     static struct RGBColor color_table[] = { 
         {65535L, 65535L, 65535L}, {0, 0, 0} };
 
@@ -3365,13 +3859,13 @@ do_bitmap(int const version,
         pm_message("transfer mode = %s", const_name(transfer_name, mode));
 
     if (is_region)
-        skip_poly_or_region(version);
+        skip_poly_or_region(canvasP, blitListP, version);
 
     stage = "unpacking rectangle";
 
-    unpackbits(ifp, &Bounds, rowBytes, 1, &raster);
+    unpackbits(ifP, &Bounds, rowBytes, 1, &raster);
 
-    blit(srcRect, Bounds, raster, 8,
+    blit(srcRect, Bounds, raster, canvasP, blitListP, 8,
          dstRect, picFrame, rowlen, color_table, mode);
 
     freeRaster(raster);
@@ -3379,8 +3873,12 @@ do_bitmap(int const version,
 
 
 
+static drawFn BitsRect;
+
 static void
-BitsRect(int const version) {
+BitsRect(struct canvas * const canvasP,
+         blitList *      const blitListP,
+         int             const version) {
 
     word rowBytesWord;
     bool pixMap;
@@ -3392,16 +3890,20 @@ BitsRect(int const version) {
     interpretRowBytesWord(rowBytesWord, &pixMap, &rowBytes);
 
     if (pixMap)
-        do_pixmap(version, rowBytes, 0);
+        do_pixmap(canvasP, blitListP, version, rowBytes, 0);
     else
-        do_bitmap(version, rowBytes, 0);
+        do_bitmap(ifp, canvasP, blitListP, version, rowBytes, 0);
 }
 
 
 
-static void
-BitsRegion(int const version) {
+static drawFn BitsRegion;
 
+static void
+BitsRegion(struct canvas * const canvasP,
+           blitList *      const blitListP,
+           int             const version) {
+    
     word rowBytesWord;
     bool pixMap;
     unsigned int rowBytes;
@@ -3412,9 +3914,9 @@ BitsRegion(int const version) {
     interpretRowBytesWord(rowBytesWord, &pixMap, &rowBytes);
 
     if (pixMap)
-        do_pixmap(version, rowBytes, 1);
+        do_pixmap(canvasP, blitListP, version, rowBytes, 1);
     else
-        do_bitmap(version, rowBytes, 1);
+        do_bitmap(ifp, canvasP, blitListP, version, rowBytes, 1);
 }
 
 
@@ -3597,14 +4099,85 @@ static struct opdef const optable[] = {
 
 
 static void
-interpret_pict(void) {
+processOpcode(word const opcode, 
+              struct canvas * const canvasP,
+              blitList *      const blitListP,
+              unsigned int    const version) {
+
+    if (opcode < 0xa2) {
+        stage = optable[opcode].name;
+        if (verbose) {
+            if (streq(stage, "reserved"))
+                pm_message("reserved opcode=0x%x", opcode);
+            else
+                pm_message("Opcode: %s", optable[opcode].name);
+        }
+
+        if (optable[opcode].impl != NULL)
+            (*optable[opcode].impl)(canvasP, blitListP, version);
+        else if (optable[opcode].len >= 0)
+            skip(optable[opcode].len);
+        else {
+            switch (optable[opcode].len) {
+            case WORD_LEN: {
+                word const len = read_word();
+                skip(len);
+                } break;
+            default:
+                pm_error("can't do length %u", optable[opcode].len);
+            }
+        }
+    } else if (opcode == 0xc00) {
+        if (verbose)
+            pm_message("HeaderOp");
+        stage = "HeaderOp";
+        skip(24);
+    } else if (opcode >= 0xa2 && opcode <= 0xaf) {
+        stage = "skipping reserved";
+        if (verbose)
+            pm_message("%s 0x%x", stage, opcode);
+        skip(read_word());
+    } else if (opcode >= 0xb0 && opcode <= 0xcf) {
+        /* just a reserved opcode, no data */
+        if (verbose)
+            pm_message("reserved 0x%x", opcode);
+    } else if (opcode >= 0xd0 && opcode <= 0xfe) {
+        stage = "skipping reserved";
+        if (verbose)
+            pm_message("%s 0x%x", stage, opcode);
+        skip(read_long());
+    } else if (opcode >= 0x100 && opcode <= 0x7fff) {
+        stage = "skipping reserved";
+        if (verbose)
+            pm_message("%s 0x%x", stage, opcode);
+        skip((opcode >> 7) & 255);
+    } else if (opcode >= 0x8000 && opcode <= 0x80ff) {
+        /* just a reserved opcode */
+        if (verbose)
+            pm_message("reserved 0x%x", opcode);
+    } else if (opcode >= 0x8100) {
+        stage = "skipping reserved";
+        if (verbose)
+            pm_message("%s 0x%x", stage, opcode);
+        skip(read_long());
+    } else
+        pm_error("This program does not understand opcode 0x%04x", opcode);
+}
+
+
+
+static void
+interpret_pict(FILE * const ofP) {
+
     byte ch;
     word picSize;
     word opcode;
-    word len;
     unsigned int version;
     int i;
-    struct rgbPlanes planes;
+    struct canvas canvas;
+    blitList blitList;
+
+    initBlitList(&blitList);
 
     for (i = 0; i < 64; i++)
         pen_pat.pix[i] = bkpat.pix[i] = fillpat.pix[i] = 1;
@@ -3618,20 +4191,26 @@ interpret_pict(void) {
     picSize = read_word();
 
     if (verbose)
-        pm_message("picture size = %d (0x%x)", picSize, picSize);
+        pm_message("picture size = %u (0x%x)", picSize, picSize);
 
     stage = "reading picture frame";
     read_rect(&picFrame);
 
     if (verbose) {
         dumpRect("Picture frame:", picFrame);
-        pm_message("Picture size is %d x %d",
-            picFrame.right - picFrame.left,
-            picFrame.bottom - picFrame.top);
+        pm_message("Picture size is %u x %u",
+                   picFrame.right - picFrame.left,
+                   picFrame.bottom - picFrame.top);
     }
 
-    if (!fullres)
-        allocPlanes(&planes);
+    if (!fullres) {
+        rowlen = picFrame.right  - picFrame.left;
+        collen = picFrame.bottom - picFrame.top;
+
+        allocPlanes(rowlen, collen, &canvas.planes);
+
+        clip_rect = picFrame;
+    }
 
     while ((ch = read_byte()) == 0)
         ;
@@ -3657,71 +4236,35 @@ interpret_pict(void) {
     if (verbose)
         pm_message("PICT version %u", version);
 
-    while((opcode = get_op(version)) != 0xff) {
-        if (opcode < 0xa2) {
-            stage = optable[opcode].name;
-            if (verbose) {
-                if (STREQ(stage, "reserved"))
-                    pm_message("reserved opcode=0x%x", opcode);
-                else
-                    pm_message("Opcode: %s", optable[opcode].name);
-            }
-
-            if (optable[opcode].impl != NULL)
-                (*optable[opcode].impl)(version);
-            else if (optable[opcode].len >= 0)
-                skip(optable[opcode].len);
-            else switch (optable[opcode].len) {
-            case WORD_LEN:
-                len = read_word();
-                skip(len);
-                break;
-            default:
-                pm_error("can't do length %u", optable[opcode].len);
-            }
-        } else if (opcode == 0xc00) {
-            if (verbose)
-                pm_message("HeaderOp");
-            stage = "HeaderOp";
-            skip(24);
-        } else if (opcode >= 0xa2 && opcode <= 0xaf) {
-            stage = "skipping reserved";
-            if (verbose)
-                pm_message("%s 0x%x", stage, opcode);
-            skip(read_word());
-        } else if (opcode >= 0xb0 && opcode <= 0xcf) {
-            /* just a reserved opcode, no data */
-            if (verbose)
-                pm_message("reserved 0x%x", opcode);
-        } else if (opcode >= 0xd0 && opcode <= 0xfe) {
-            stage = "skipping reserved";
-            if (verbose)
-                pm_message("%s 0x%x", stage, opcode);
-            skip(read_long());
-        } else if (opcode >= 0x100 && opcode <= 0x7fff) {
-            stage = "skipping reserved";
-            if (verbose)
-                pm_message("%s 0x%x", stage, opcode);
-            skip((opcode >> 7) & 255);
-        } else if (opcode >= 0x8000 && opcode <= 0x80ff) {
-            /* just a reserved opcode */
-            if (verbose)
-                pm_message("reserved 0x%x", opcode);
-        } else if (opcode >= 0x8100) {
-            stage = "skipping reserved";
-            if (verbose)
-                pm_message("%s 0x%x", stage, opcode);
-            skip(read_long());
-        } else
-            pm_error("This program does not understand opcode 0x%04x", opcode);
-    }
+    while((opcode = get_op(version)) != 0xff)
+        processOpcode(opcode, &canvas, fullres ? &blitList : NULL, version);
     
-    if (fullres)
-        do_blits(&planes);
+    if (fullres) {
+        if (blitList.unblittableText)
+            pm_message("Warning: text is omitted from the output because "
+                       "we don't know how to do text with -fullres.");
+        doBlitList(&canvas, &blitList);
+    }
+    outputPpm(ofP, canvas.planes);
+
+    freePlanes(canvas.planes);
+}
+
+
 
-    outputPpm(planes);
+static void
+loadDefaultFontDir(void) {
+/*----------------------------------------------------------------------------
+   Load the fonts from the font directory file "fontdir" (in the current
+   directory), if it exists.
+-----------------------------------------------------------------------------*/
+    struct stat statbuf;
+    int rc;
 
-    freePlanes(planes);
+    rc = stat("fontdir", &statbuf);
+    
+    if (rc == 0)
+        load_fontdir("fontdir");
 }
 
 
@@ -3762,8 +4305,6 @@ main(int argc, char * argv[]) {
         ++argn;
     }
 
-    load_fontdir("fontdir");
-
     if (argn < argc) {
         ifp = pm_openr(argv[argn]);
         ++argn;
@@ -3773,12 +4314,17 @@ main(int argc, char * argv[]) {
     if (argn != argc)
         pm_usage(usage);
 
+    loadDefaultFontDir();
+
     if (header) {
         stage = "Reading 512 byte header";
+        /* Note that the "header" in PICT is entirely comment! */
         skip(512);
     }
 
-    interpret_pict();
+    interpret_pict(stdout);
+
+    pm_close(stdout);
 
     return 0;
 }
diff --git a/converter/ppm/ppmtoarbtxt.c b/converter/ppm/ppmtoarbtxt.c
index fe466ea5..df859c84 100644
--- a/converter/ppm/ppmtoarbtxt.c
+++ b/converter/ppm/ppmtoarbtxt.c
@@ -24,6 +24,10 @@ typedef enum {
     WIDTH, HEIGHT, POSX, POSY
 } SKL_OBJ_TYP;
 
+typedef enum {
+    OBJTYP_ICOLOR, OBJTYP_FCOLOR, OBJTYP_INT, OBJTYP_BDATA
+} SKL_OBJ_CLASS;
+
 /* Maximum size for a format string ("%d" etc.) */
 #define MAXFORMAT 16
 
@@ -158,257 +162,328 @@ save_bin_data(int    const ndat,
 
 
 
+static SKL_OBJ *
+save_icol_data(SKL_OBJ_TYP  const ctyp,
+               const char * const format,
+               int          const icolmin,
+               int          const icolmax) {
 /* Save integer color data in Object */
-static SKL_OBJ *save_icol_data (ctyp,format,icolmin,icolmax)
-SKL_OBJ_TYP ctyp;
-char *format;
-int icolmin, icolmax;
 
-{SKL_OBJ *obj;
+    SKL_OBJ * objP;
 
- obj = (SKL_OBJ *)malloc (sizeof (SKL_OBJ));
- if (obj != NULL)
- {
-   obj->otyp = ctyp;
-   strcpy (obj->odata.icol_data.icformat,format);
-   obj->odata.icol_data.icolmin = icolmin;
-   obj->odata.icol_data.icolmax = icolmax;
- }
- return (obj);
+    MALLOCVAR(objP);
+
+    if (objP) {
+        objP->otyp = ctyp;
+        strcpy(objP->odata.icol_data.icformat, format);
+        objP->odata.icol_data.icolmin = icolmin;
+        objP->odata.icol_data.icolmax = icolmax;
+    }
+    return objP;
 }
 
 
+
+static SKL_OBJ *
+save_fcol_data(SKL_OBJ_TYP  const ctyp,
+               const char * const format,
+               double       const fcolmin,
+               double       const fcolmax) {
 /* Save float color data in Object */
-static SKL_OBJ *save_fcol_data (ctyp,format,fcolmin,fcolmax)
-SKL_OBJ_TYP ctyp;
-char *format;
-double fcolmin, fcolmax;
 
-{SKL_OBJ *obj;
+    SKL_OBJ * objP;
 
- obj = (SKL_OBJ *)malloc (sizeof (SKL_OBJ));
- if (obj != NULL)
- {
-   obj->otyp = ctyp;
-   strcpy (obj->odata.fcol_data.fcformat,format);
-   obj->odata.fcol_data.fcolmin = fcolmin;
-   obj->odata.fcol_data.fcolmax = fcolmax;
- }
- return (obj);
+    MALLOCVAR(objP);
+
+    if (objP) {
+        objP->otyp = ctyp;
+        strcpy(objP->odata.fcol_data.fcformat, format);
+        objP->odata.fcol_data.fcolmin = fcolmin;
+        objP->odata.fcol_data.fcolmax = fcolmax;
+    }
+    return objP;
 }
 
 
+
+static SKL_OBJ *
+save_i_data(SKL_OBJ_TYP  const ctyp,
+            const char * const format) {
+
 /* Save universal data in Object */
-static SKL_OBJ *save_i_data (ctyp,format)
-SKL_OBJ_TYP ctyp;
-char *format;
 
-{SKL_OBJ *obj;
+    SKL_OBJ * objP;
 
- obj = (SKL_OBJ *)malloc (sizeof (SKL_OBJ));
- if (obj != NULL)
- {
-   obj->otyp = ctyp;
-   strcpy (obj->odata.i_data.iformat,format);
- }
- return (obj);
+    MALLOCVAR(objP);
+    if (objP) {
+        objP->otyp = ctyp;
+        strcpy(objP->odata.i_data.iformat, format);
+    }
+    return objP;
 }
 
 
-/* Read skeleton file */
-static int read_skeleton (filename,maxskl,nskl,skl)
-char *filename;
-int maxskl,*nskl;
-SKL_OBJ **skl;
 
-{FILE *sklfile;
- int slen, objlen, chr, n_odata;
- int icolmin,icolmax;
- double fcolmin,fcolmax;
- SKL_OBJ_TYP otyp;
- char line[MAX_LINE_BUF+MAX_OBJ_BUF+16];
- char objstr[MAX_OBJ_BUF],typstr[MAX_OBJ_BUF];
- char formstr[MAX_OBJ_BUF];
- char meta1 = '#', meta2 = '(', meta3 = ')';
+static char const escape = '#';
 
-#define SAVE_BIN(slen,line) \
- { if ((skl[*nskl] = save_bin_data (slen,line)) != NULL) (*nskl)++; \
-   slen = 0; }
 
-#define ADD_STR(slen,line,addlen,addstr) \
- {int count=0; line[slen++] = meta1; line[slen++] = meta2; \
-  while (count++ < addlen) line[slen++] = addstr[count]; }
 
- if ((sklfile = fopen (filename,"r")) == NULL)
-   return (-1);
+static SKL_OBJ_TYP
+interpretObjType(const char * const typstr) {
 
- /* Parse skeleton file */
- *nskl = 0;
+    SKL_OBJ_TYP otyp;
 
- slen = 0;
- while ((chr = getc (sklfile)) != EOF)   /* Up to end of skeleton file */
- {
-   if (*nskl >= maxskl) return (-1);
+    /* Check for integer colors */
+    if      (streq(typstr, "ired")  ) otyp = IRED;
+    else if (streq(typstr, "igreen")) otyp = IGREEN;
+    else if (streq(typstr, "iblue") ) otyp = IBLUE;
+    else if (streq(typstr, "ilum")  ) otyp = ILUM;
+    /* Check for real colors */
+    else if (streq(typstr, "fred")  ) otyp = FRED;
+    else if (streq(typstr, "fgreen")) otyp = FGREEN;
+    else if (streq(typstr, "fblue") ) otyp = FBLUE;
+    else if (streq(typstr, "flum")  ) otyp = FLUM;
+    /* Check for integer data */
+    else if (streq(typstr, "width") ) otyp = WIDTH;
+    else if (streq(typstr, "height")) otyp = HEIGHT;
+    else if (streq(typstr, "posx")  ) otyp = POSX;
+    else if (streq(typstr, "posy")  ) otyp = POSY;
+    else                              otyp = BDATA;
 
-   if (slen+1 >= MAX_LINE_BUF)   /* Buffer finished ? Save as binary object */
-   {
-     SAVE_BIN (slen,line);
-   }
+    return otyp;
+}
 
-   if (chr != meta1)      /* Look for start of replacement string */
-   {
-     line[slen++] = chr;
-     continue;
-   }
 
-   if ((chr = getc (sklfile)) == EOF)
-   {
-     line[slen++] = meta1;
-     break;
-   }
-   if (chr != meta2) /* '(' ? Otherwise no replacement */
-   {
-     line[slen++] = meta1;
-     line[slen++] = chr;
-     continue;
-   }
 
-   objlen = 0;
-   for (;;)   /* Read replacement string up to ')' */
-   {
-     if (objlen == sizeof (objstr)-1) break; /* ')' not found */
-     if ((chr = getc (sklfile)) == EOF) break;
-     if (chr == meta3) break;
-     objstr[objlen++] = chr;
-   }
-   objstr[objlen] = '\0'; /* Now objstr keeps data without metas */
+static SKL_OBJ_CLASS
+objClass(SKL_OBJ_TYP const otyp) {
 
-   if (chr != meta3)    /* Object not found ? */
-   {
-     ADD_STR (slen,line,objlen,objstr);   /* Save what we already read */
-     if (chr == EOF) break;
-     continue;
-   }
+    switch (otyp) {
+    case IRED:
+    case IGREEN:
+    case IBLUE:
+    case ILUM:
+        return OBJTYP_ICOLOR;
 
-   typstr[0] = '\0';           /* Get typ of data */
-   sscanf (objstr,"%s",typstr);
-
-                   /* Check for integer colors */
-   if      (STREQ(typstr,"ired")  ) otyp = IRED;
-   else if (STREQ(typstr,"igreen")) otyp = IGREEN;
-   else if (STREQ(typstr,"iblue") ) otyp = IBLUE;
-   else if (STREQ(typstr,"ilum")  ) otyp = ILUM;
-                   /* Check for real colors */
-   else if (STREQ(typstr,"fred")  ) otyp = FRED;
-   else if (STREQ(typstr,"fgreen")) otyp = FGREEN;
-   else if (STREQ(typstr,"fblue") ) otyp = FBLUE;
-   else if (STREQ(typstr,"flum")  ) otyp = FLUM;
-                   /* Check for integer data */
-   else if (STREQ(typstr,"width") ) otyp = WIDTH;
-   else if (STREQ(typstr,"height")) otyp = HEIGHT;
-   else if (STREQ(typstr,"posx")  ) otyp = POSX;
-   else if (STREQ(typstr,"posy")  ) otyp = POSY;
-   else                                    otyp = BDATA;
-
-   if ((otyp == IRED) || (otyp == IGREEN) || (otyp == IBLUE) || (otyp == ILUM))
-   {
-     n_odata = sscanf (objstr,"%*s%s%d%d",formstr,&icolmin,&icolmax);
-
-     if (n_odata == EOF)  /* No arguments specified ? Use defaults */
-     {
-       strcpy (formstr,"%d"); icolmin = 0; icolmax = 255;
-     }
-     else if (n_odata != 3)  /* Wrong specification */
-     {
-       otyp = BDATA;
-     }
-   }
+    case FRED:
+    case FGREEN:
+    case FBLUE:
+    case FLUM:
+        return OBJTYP_FCOLOR;
 
-   if ((otyp == FRED) || (otyp == FGREEN) || (otyp == FBLUE) || (otyp == FLUM))
-   {
-     n_odata = sscanf (objstr,"%*s%s%lf%lf",formstr,&fcolmin,&fcolmax);
-
-     if (n_odata == EOF)  /* No arguments specified ? Use defaults */
-     {
-       strcpy (formstr,"%f"); fcolmin = 0.0; fcolmax = 1.0;
-     }
-     else if (n_odata != 3)  /* Wrong specification */
-     {
-       otyp = BDATA;
-     }
-   }
+    case WIDTH:
+    case HEIGHT:
+    case POSX:
+    case POSY:
+        return OBJTYP_INT;
+    case BDATA:
+        return OBJTYP_BDATA;
+    }
+    return 999; /* quiet compiler warning */
+}
 
-   if (   (otyp == WIDTH) || (otyp == HEIGHT)
-       || (otyp == POSX) || (otyp == POSY))
-   {
-     n_odata = sscanf (objstr,"%*s%s",formstr);
-
-     if (n_odata == EOF)  /* No arguments specified ? Use defaults */
-     {
-       strcpy (formstr,"%d");
-     }
-     else if (n_odata != 1)  /* Wrong specification */
-     {
-       otyp = BDATA;
-     }
-   }
 
-   if (otyp != BDATA)   /* Got an object definition ? */
-   {
-     if (slen > 0)      /* Save what we already found */
-     {
-       SAVE_BIN (slen,line);
-     }
-   }
 
-   /* Now process the object in objstr */
-   switch (otyp)
-   {
-     case BDATA:   /* Bad object definition ? Save as text */
-       ADD_STR (slen,line,objlen,objstr);
-       break;
+static void
+addImpostorReplacementSeq(char *         const line,
+                          unsigned int   const startCursor,
+                          const char *   const seqContents,
+                          unsigned int   const seqContentsLen,
+                          unsigned int * const newCursorP) {
+/*----------------------------------------------------------------------------
+   Add to line line[], at position 'startCursor', text that looks like a
+   replacement sequence but doesn't have the proper contents (the
+   stuff between the parentheses) to be one.  For example,
 
-     case IRED:
-     case IGREEN:
-     case IBLUE:
-     case ILUM:
-       skl[*nskl] = save_icol_data (otyp,formstr,icolmin,icolmax);
-       if (skl[*nskl] != NULL) (*nskl)++;
-       break;
+     "#(fread x)"
 
-     case FRED:
-     case FGREEN:
-     case FBLUE:
-     case FLUM:
-       skl[*nskl] = save_fcol_data (otyp,formstr,fcolmin,fcolmax);
-       if (skl[*nskl] != NULL) (*nskl)++;
-       break;
+   seqContents[] is the contents; 'seqContentsLen' its length.
 
-     case WIDTH:
-     case HEIGHT:
-     case POSX:
-     case POSY:
-       skl[*nskl] = save_i_data (otyp,formstr);
-       if (skl[*nskl] != NULL) (*nskl)++;
-       break;
-   }
- } /* EOF of skeleton file */
+   Return as *newCursorP where the line[] cursor ends up after adding
+   the sequence.
+-----------------------------------------------------------------------------*/
+    unsigned int cursor;
+    unsigned int i;
 
- if (slen > 0)      /* Drop finishing newline character */
- {
-   if (line[slen-1] == '\n') slen--;
- }
+    cursor = startCursor;
 
- if (slen > 0)      /* Something left ? */
- {
-   SAVE_BIN (slen,line);   /* Save it */
- }
+    line[cursor++] = escape;
+    line[cursor++] = '(';
 
- fclose (sklfile);
- return (0);
+    for (i = 0; i < seqContentsLen; ++i)
+        line[cursor++] = seqContents[i];
+
+    line[cursor++] = ')';
+
+    *newCursorP = cursor;
 }
 
 
+
+static int
+read_skeleton(const char *   const filename,
+              unsigned int   const maxskl,
+              unsigned int * const nsklP,
+              SKL_OBJ **     const skl) {
+/*----------------------------------------------------------------------------
+  Read skeleton file
+-----------------------------------------------------------------------------*/
+    FILE * sklfile;
+    unsigned int slen;
+    unsigned int objlen;
+    int chr;
+    SKL_OBJ_TYP otyp;
+    char line[MAX_LINE_BUF+MAX_OBJ_BUF+16];
+    char objstr[MAX_OBJ_BUF],typstr[MAX_OBJ_BUF];
+    unsigned int nskl;
+
+#define SAVE_BIN(slen,line) \
+    { if (slen > 0 && (skl[nskl] = save_bin_data(slen,line)) != NULL) ++nskl; \
+      slen = 0; }
+
+    sklfile = pm_openr(filename);
+
+    /* Parse skeleton file */
+    nskl = 0;  /* initial value */
+
+    slen = 0;
+    while ((chr = getc (sklfile)) != EOF) {  /* Up to end of skeleton file */
+        if (nskl >= maxskl)
+            return -1;
+
+        if (slen+1 >= MAX_LINE_BUF) {
+            /* Buffer finished.  Save as binary object */
+            SAVE_BIN(slen,line);
+        }
+
+        if (chr != escape) {
+            /* Not a replacement sequence; just a literal character */
+            line[slen++] = chr;
+            continue;
+        }
+
+        chr = getc(sklfile);
+        if (chr == EOF) {
+            /* Not a valid replacement sequence */
+            line[slen++] = escape;
+            break;
+        }
+        if (chr != '(') {
+            /* Not a valid replacement sequence */
+            line[slen++] = escape;
+            line[slen++] = chr;
+            continue;
+        }
+        /* Read replacement string up through ')'.  Put contents of
+           parentheses in objstr[].
+        */
+        for (objlen = 0; objlen < sizeof(objstr)-1; ++objlen) {
+            chr = getc(sklfile);
+            if (chr == EOF) break;
+            if (chr == ')') break;
+            objstr[objlen] = chr;
+        }
+        objstr[objlen] = '\0';
+
+        if (chr != ')') {
+            /* Not valid replacement sequence */
+            unsigned int i;
+            line[slen++] = escape;
+            line[slen++] = chr;
+            for (i = 0; i < objlen; ++i)
+                line[slen++] = objstr[i];
+            if (chr == EOF)
+                break;
+            continue;
+        }
+
+        typstr[0] = '\0';           /* Get typ of data */
+        sscanf(objstr, "%s", typstr);
+
+        otyp = interpretObjType(typstr);
+
+        switch (objClass(otyp)) {
+        case OBJTYP_ICOLOR: {
+            int icolmin, icolmax;
+            char formstr[MAX_OBJ_BUF];
+            int n_odata;
+
+            n_odata = sscanf(objstr, "%*s%s%d%d", formstr, &icolmin, &icolmax);
+
+            if (n_odata == 3) {
+                SAVE_BIN(slen, line);
+                skl[nskl] = save_icol_data(otyp, formstr, icolmin, icolmax);
+                if (skl[nskl] != NULL)
+                    ++nskl;
+            } else if (n_odata == EOF) {
+                /* No arguments specified.  Use defaults */
+                SAVE_BIN(slen, line);
+                skl[nskl] = save_icol_data(otyp, "%d", 0, 255);
+                if (skl[nskl] != NULL)
+                    ++nskl;
+            } else
+                addImpostorReplacementSeq(line, slen, objstr, objlen, &slen);
+        } break;
+        case OBJTYP_FCOLOR: {
+            double fcolmin, fcolmax;
+            char formstr[MAX_OBJ_BUF];
+            int n_odata;
+
+            n_odata = sscanf(objstr, "%*s%s%lf%lf", formstr,
+                             &fcolmin, &fcolmax);
+
+            if (n_odata == 3) {
+                SAVE_BIN(slen, line);
+                skl[nskl] = save_fcol_data(otyp, formstr, fcolmin, fcolmax);
+                if (skl[nskl] != NULL)
+                    ++nskl;
+            } else if (n_odata == EOF) {
+                /* No arguments specified.  Use defaults */
+                SAVE_BIN(slen, line);
+                skl[nskl] = save_fcol_data(otyp, "%f", 0.0, 1.0);
+                if (skl[nskl] != NULL)
+                    ++nskl;
+            } else
+                addImpostorReplacementSeq(line, slen, objstr, objlen, &slen);
+        } break;
+
+        case OBJTYP_INT: {
+            char formstr[MAX_OBJ_BUF];
+            int const n_odata = sscanf(objstr, "%*s%s", formstr);
+
+            if (n_odata == 1) {
+                SAVE_BIN(slen, line);
+                skl[nskl] = save_i_data(otyp, formstr);
+                if (skl[nskl] != NULL)
+                    ++nskl;
+            } else if (n_odata == EOF) {
+                /* No arguments specified.  Use defaults */
+                SAVE_BIN(slen, line);
+                skl[nskl] = save_i_data(otyp, "%d");
+                if (skl[nskl] != NULL)
+                    ++nskl;
+            } else
+                addImpostorReplacementSeq(line, slen, objstr, objlen, &slen);
+        } break;
+        case OBJTYP_BDATA:
+            addImpostorReplacementSeq(line, slen, objstr, objlen, &slen);
+        }
+    } /* EOF of skeleton file */
+
+    if (slen >= 1 && line[slen-1] == '\n')
+        /* Drop finishing newline character */
+        --slen;
+
+    SAVE_BIN(slen, line);  /* Save whatever is left */
+
+    *nsklP = nskl;
+
+    fclose(sklfile);
+    return 0;
+}
+
+
+
 int main( argc, argv )
 int argc;
 char* argv[];
@@ -419,7 +494,7 @@ char* argv[];
  pixval maxval,red,green,blue;
  double dmaxval;
  int argn, rows, cols, format, row;
- int head_nskl,body_nskl,tail_nskl;
+ unsigned int head_nskl,body_nskl,tail_nskl;
  SKL_OBJ *head_skl[MAX_SKL_HEAD_OBJ];
  SKL_OBJ *body_skl[MAX_SKL_BODY_OBJ];
  SKL_OBJ *tail_skl[MAX_SKL_TAIL_OBJ];
diff --git a/converter/ppm/ppmtobmp.c b/converter/ppm/ppmtobmp.c
index 635464dd..c295f70c 100644
--- a/converter/ppm/ppmtobmp.c
+++ b/converter/ppm/ppmtobmp.c
@@ -18,15 +18,24 @@
 
 #include <assert.h>
 #include <string.h>
-#include "bmp.h"
-#include "ppm.h"
+
+#include "pm_c_util.h"
+#include "mallocvar.h"
 #include "shhopt.h"
+#include "bmp.h"
 #include "bitio.h"
+#include "ppm.h"
 
 #define MAXCOLORS 256
 
 enum colortype {TRUECOLOR, PALETTE};
 
+struct rgb {
+    unsigned char red;
+    unsigned char grn;
+    unsigned char blu;
+};
+
 typedef struct {
 /*----------------------------------------------------------------------------
    A color map for a BMP file.
@@ -34,13 +43,13 @@ typedef struct {
     unsigned int count;
         /* Number of colors in the map.  The first 'count' elements of these
            arrays are defined; all others are not.
+
+           At most MAXCOLORS.
         */
     colorhash_table cht;
 
-    /* Indices in the following arrays are the same as in 'cht', above. */
-    unsigned char red[MAXCOLORS];
-    unsigned char grn[MAXCOLORS];
-    unsigned char blu[MAXCOLORS];
+    /* Indices in the following array are the same as in 'cht', above. */
+    struct rgb bmpMap[MAXCOLORS];
 } colorMap;
 
 
@@ -54,7 +63,7 @@ freeColorMap(const colorMap * const colorMapP) {
 
 
 
-static struct cmdline_info {
+struct cmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
@@ -62,63 +71,71 @@ static struct cmdline_info {
     int class;  /* C_WIN or C_OS2 */
     unsigned int bppSpec;
     unsigned int bpp;
-} cmdline;
+    const char * mapfile;
+};
 
 
 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 many of the strings that this function returns in the
    *cmdline_p structure are actually in the supplied argv array.  And
    sometimes, one of these strings is actually just a suffix of an entry
    in argv!
 -----------------------------------------------------------------------------*/
-    optEntry *option_def = malloc(100*sizeof(optEntry));
+    optEntry * option_def;
         /* Instructions to OptParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
-    unsigned int windowsSpec, os2Spec;
+    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, &cmdline_p->bpp, 
-            &cmdline_p->bppSpec,      0);
+    OPTENT3(0,   "bpp",       OPT_UINT, &cmdlineP->bpp, 
+            &cmdlineP->bppSpec,      0);
+    OPTENT3(0,   "mapfile",   OPT_STRING, &cmdlineP->mapfile, 
+            &mapfileSpec,             0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
 
     if (windowsSpec && os2Spec) 
         pm_error("Can't specify both -windows and -os2 options.");
     else if (windowsSpec) 
-        cmdline_p->class = C_WIN;
+        cmdlineP->class = C_WIN;
     else if (os2Spec)
-        cmdline_p->class = C_OS2;
+        cmdlineP->class = C_OS2;
     else 
-        cmdline_p->class = C_WIN;
+        cmdlineP->class = C_WIN;
 
 
-    if (cmdline_p->bppSpec) {
-        if (cmdline_p->bpp != 1 && cmdline_p->bpp != 4 && 
-            cmdline_p->bpp != 8 && cmdline_p->bpp != 24)
-        pm_error("Invalid -bpp value specified: %u.  The only values valid\n"
+    if (cmdlineP->bppSpec) {
+        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",
-                 cmdline_p->bpp);
+                 cmdlineP->bpp);
     }
 
+    if (!mapfileSpec)
+        cmdlineP->mapfile = NULL;
+
     if (argc - 1 == 0)
-        cmdline_p->input_filename = strdup("-");  /* he wants stdin */
+        cmdlineP->input_filename = strdup("-");  /* he wants stdin */
     else if (argc - 1 == 1)
-        cmdline_p->input_filename = strdup(argv[1]);
+        cmdlineP->input_filename = strdup(argv[1]);
     else 
-        pm_error("Too many arguments.  The only argument accepted\n"
+        pm_error("Too many arguments.  The only argument accepted "
                  "is the input file specificaton");
 
 }
@@ -243,7 +260,7 @@ BMPwriteinfoheader(FILE *        const fp,
 
 
 static int
-BMPwritergb(FILE * const fp, 
+BMPwriteRgb(FILE * const fp, 
             int    const class, 
             pixval const R, 
             pixval const G, 
@@ -264,7 +281,7 @@ BMPwritergb(FILE * const fp,
         PutByte(fp, R);
         return 3;
     default:
-        pm_error(er_internal, "BMPwritergb");
+        pm_error(er_internal, "BMPwriteRgb");
     }
     return -1;
 }
@@ -272,101 +289,118 @@ BMPwritergb(FILE * const fp,
 
 
 static int
-BMPwritecolormap(FILE *           const ifP, 
+BMPwriteColormap(FILE *           const ifP, 
                  int              const class, 
                  int              const bpp,
                  const colorMap * const colorMapP) {
 /*----------------------------------------------------------------------------
   Return the number of bytes written, or -1 on error.
 -----------------------------------------------------------------------------*/
-    long const ncolors = (1 << bpp);
+    unsigned int const ncolors = (1 << bpp);
 
-    unsigned int  nbyte;
-    unsigned int  i;
+    unsigned int nbyte;
+    unsigned int i;
 
-    nbyte = 0;
-    for (i = 0; i < colorMapP->count; ++i)
-        nbyte += BMPwritergb(ifP, class,
-                             colorMapP->red[i],
-                             colorMapP->grn[i],
-                             colorMapP->blu[i]);
+    assert(ncolors <= MAXCOLORS);
+    assert(ncolors <= ARRAY_SIZE(colorMapP->bmpMap));
 
+    nbyte = 0;
+    for (i = 0; i < colorMapP->count; ++i) {
+        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;
 }
 
 
 
-static int
-BMPwriterow_palette(FILE *          const fp, 
+static void
+lookupColor(colorhash_table const cht,
+            pixel           const color,
+            unsigned int *  const colorIndexP) {
+
+    int rc;
+
+    rc = ppm_lookupcolor(cht, &color);
+
+    if (rc < 0)
+        pm_error("Color (%u,%u,%u) is not in the provided palette",
+                 PPM_GETR(color), PPM_GETG(color), PPM_GETB(color));
+    else
+        *colorIndexP = rc;
+}
+
+
+
+static void
+bmpWriteRow_palette(FILE *          const fp, 
                     const pixel *   const row, 
-                    unsigned long   const cx, 
+                    unsigned int    const cols,
                     unsigned short  const bpp, 
-                    colorhash_table const cht) {
+                    colorhash_table const cht,
+                    unsigned int *  const nBytesP) {
 /*----------------------------------------------------------------------------
-  Return the number of bytes written, or -1 on error.
+   Write a row to the raster in paletted format.
+
+   Return the number of bytes written as *nBytesP.
 -----------------------------------------------------------------------------*/
-    BITSTREAM    b;
-    int retval;
+    BITSTREAM b;
     
     b = pm_bitinit(fp, "w");
     if (b == NULL)
-        retval = -1;
+        pm_error("Failed to initialize output file for output");
     else {
+        int rc;
         unsigned int nbyte;
-        unsigned int x;
-        bool         error;
+        unsigned int col;
         
         nbyte = 0;      /* initial value */
-        error = FALSE;  /* initial value */
         
-        for (x = 0; x < cx && !error; ++x) {
+        for (col = 0; col < cols; ++col) {
+            unsigned int colorIndex;
             int rc;
-            rc = pm_bitwrite(b, bpp, ppm_lookupcolor(cht, &row[x]));
+
+            lookupColor(cht, row[col], &colorIndex);
+
+            rc = pm_bitwrite(b, bpp, colorIndex);
             if (rc == -1)
-                error = TRUE;
+                pm_error("Failed in writing a pixel "
+                         "to the raster in the output file");
             else
                 nbyte += rc;
         }
-        if (error)
-            retval = -1;
-        else {
-            int rc;
 
-            rc = pm_bitfini(b);
-            if (rc == -1)
-                retval = -1;
-            else {
-                nbyte += rc;
+        rc = pm_bitfini(b);
+
+        nbyte += rc;
                 
-                /* Make sure we write a multiple of 4 bytes.  */
-                while (nbyte % 4 != 0) {
-                    PutByte(fp, 0);
-                    ++nbyte;
-                }
-                retval = nbyte;
-            }
+        /* Make sure we write a multiple of 4 bytes.  */
+        while (nbyte % 4 != 0) {
+            PutByte(fp, 0);
+            ++nbyte;
         }
+        *nBytesP = nbyte;
     }
-    return retval;
 }
 
 
 
-static int
-BMPwriterow_truecolor(FILE *        const fp, 
-                      const pixel * const row, 
-                      unsigned long const cols,
-                      pixval        const maxval) {
+static void
+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 
   'row', which is 'cols' columns long.
 
-  Return the number of bytes written.
 
-  On error, issue error message and exit program.
+  Return the number of bytes written as *nBytesP.
 -----------------------------------------------------------------------------*/
     /* This works only for 24 bits per pixel.  To implement this for the
        general case (which is only hypothetical -- this program doesn't
@@ -380,7 +414,7 @@ BMPwriterow_truecolor(FILE *        const fp,
     int col;  
         
     nbyte = 0;  /* initial value */
-    for (col = 0; col < cols; col++) {
+    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);
@@ -393,18 +427,18 @@ BMPwriterow_truecolor(FILE *        const fp,
      */
     while (nbyte % 4) {
         PutByte(fp, 0);
-        nbyte++;
+        ++nbyte;
     }
     
-    return nbyte;
+    *nBytesP = nbyte;
 }
 
 
 
 static int
 BMPwritebits(FILE *          const fp, 
-             unsigned long   const cx, 
-             unsigned long   const cy, 
+             unsigned long   const cols, 
+             unsigned long   const rows,
              enum colortype  const colortype,
              unsigned short  const cBitCount, 
              const pixel **  const pixels, 
@@ -413,29 +447,29 @@ BMPwritebits(FILE *          const fp,
 /*----------------------------------------------------------------------------
   Return the number of bytes written, or -1 on error.
 -----------------------------------------------------------------------------*/
-    int  nbyte;
-    long y;
+    unsigned int nbyte;
+    int row;
 
     if (cBitCount > 24)
-        pm_error("cannot handle cBitCount: %d", cBitCount);
+        pm_error("cannot handle cBitCount: %hu", cBitCount);
 
     nbyte = 0;  /* initial value */
 
     /* The picture is stored bottom line first, top line last */
 
-    for (y = cy - 1; y >= 0; --y) {
-        int rc;
+    for (row = rows - 1; row >= 0; --row) {
+        unsigned int nBytesThisRow;
+
         if (colortype == PALETTE)
-            rc = BMPwriterow_palette(fp, pixels[y], cx, 
-                                     cBitCount, cht);
+            bmpWriteRow_palette(fp, pixels[row], cols, 
+                                cBitCount, cht, &nBytesThisRow);
         else 
-            rc = BMPwriterow_truecolor(fp, pixels[y], cx, maxval);
+            bmpWriteRow_truecolor(fp, pixels[row], cols, maxval,
+                                  &nBytesThisRow);
 
-        if (rc == -1)
-            pm_error("couldn't write row %ld", y);
-        if (rc % 4 != 0)
-            pm_error("row had bad number of bytes: %d", rc);
-        nbyte += rc;
+        if (nBytesThisRow % 4 != 0)
+            pm_error("row had bad number of bytes: %u", nBytesThisRow);
+        nbyte += nBytesThisRow;
     }
 
     return nbyte;
@@ -444,10 +478,10 @@ BMPwritebits(FILE *          const fp,
 
 
 static void
-BMPEncode(FILE *           const ifP, 
+bmpEncode(FILE *           const ifP, 
           int              const class, 
           enum colortype   const colortype,
-          int              const bpp,
+          unsigned int     const bpp,
           int              const x, 
           int              const y, 
           const pixel **   const pixels, 
@@ -459,25 +493,25 @@ BMPEncode(FILE *           const ifP,
     unsigned long nbyte;
 
     if (colortype == PALETTE)
-        pm_message("Writing %d bits per pixel with a color palette", bpp);
+        pm_message("Writing %u bits per pixel with a color palette", bpp);
     else
-        pm_message("Writing %d bits per pixel truecolor (no palette)", bpp);
+        pm_message("Writing %u bits per pixel truecolor (no palette)", bpp);
 
     nbyte = 0;  /* initial value */
     nbyte += BMPwritefileheader(ifP, class, bpp, x, y);
     nbyte += BMPwriteinfoheader(ifP, class, bpp, x, y);
     if (colortype == PALETTE)
-        nbyte += BMPwritecolormap(ifP, class, bpp, colorMapP);
+        nbyte += BMPwriteColormap(ifP, class, bpp, colorMapP);
 
     if (nbyte != (BMPlenfileheader(class)
                   + BMPleninfoheader(class)
                   + BMPlencolormap(class, bpp, 0)))
-        pm_error(er_internal, "BMPEncode 1");
+        pm_error(er_internal, "BmpEncode 1");
 
     nbyte += BMPwritebits(ifP, x, y, colortype, bpp, pixels, maxval,
                           colorMapP->cht);
     if (nbyte != BMPlenfile(class, bpp, -1, x, y))
-        pm_error(er_internal, "BMPEncode 2");
+        pm_error(er_internal, "BmpEncode 2");
 }
 
 
@@ -487,18 +521,18 @@ makeBilevelColorMap(colorMap * const colorMapP) {
 
     colorMapP->count  = 2;
     colorMapP->cht    = NULL;
-    colorMapP->red[0] = 0;
-    colorMapP->grn[0] = 0;
-    colorMapP->blu[0] = 0;
-    colorMapP->red[1] = 255;
-    colorMapP->grn[1] = 255;
-    colorMapP->blu[1] = 255;
+    colorMapP->bmpMap[0].red = 0;
+    colorMapP->bmpMap[0].grn = 0;
+    colorMapP->bmpMap[0].blu = 0;
+    colorMapP->bmpMap[1].red = 255;
+    colorMapP->bmpMap[1].grn = 255;
+    colorMapP->bmpMap[1].blu = 255;
 }
 
 
 
 static void
-BMPEncodePBM(FILE *           const ifP, 
+bmpEncodePbm(FILE *           const ifP, 
              int              const class, 
              int              const cols, 
              int              const rows, 
@@ -508,7 +542,7 @@ BMPEncodePBM(FILE *           const ifP,
 -----------------------------------------------------------------------------*/
     /* Note:
        Only PBM input uses this routine.  Color images represented by 1 bpp via
-       color palette use the general BMPEncode().
+       color palette use the general bmpEncode().
     */
     unsigned int const adjustedCols = (cols + 31) / 32 * 32;
     unsigned int const packedBytes  = adjustedCols / 8;
@@ -526,12 +560,12 @@ BMPEncodePBM(FILE *           const ifP,
 
     makeBilevelColorMap(&bilevelColorMap);
 
-    nbyte += BMPwritecolormap(ifP, class, 1, &bilevelColorMap);
+    nbyte += BMPwriteColormap(ifP, class, 1, &bilevelColorMap);
 
     if (nbyte != (BMPlenfileheader(class)
                   + BMPleninfoheader(class)
                   + BMPlencolormap(class, 1, 0)))
-        pm_error(er_internal, "BMPEncodePBM 1");
+        pm_error(er_internal, "bmpEncodePBM 1");
    
     for (row = 0; row < rows; ++row){
         size_t bytesWritten;
@@ -548,23 +582,129 @@ BMPEncodePBM(FILE *           const ifP,
     }
 
     if (nbyte != BMPlenfile(class, 1, -1, cols, rows))
-        pm_error(er_internal, "BMPEncodePBM 2");
+        pm_error(er_internal, "bmpEncodePbm 2");
 }
 
 
 
 static void
-analyze_colors(const pixel **    const pixels, 
-               int               const cols, 
-               int               const rows, 
-               pixval            const maxval, 
-               int *             const minimum_bpp_p,
-               colorMap *        const colorMapP) {
+makeHashFromBmpMap(const struct rgb * const bmpMap,
+                   unsigned int       const nColors,
+                   colorhash_table *  const chtP) {
+
+    colorhist_vector chv;
+    unsigned int i;
+
+    MALLOCARRAY_NOFAIL(chv, nColors);
+
+    for (i = 0; i < nColors; ++i) {
+        const struct rgb * const mapEntryP = &bmpMap[i];
+
+        PPM_ASSIGN(chv[i].color,
+                   mapEntryP->red, mapEntryP->grn, mapEntryP->blu);
+    }
+
+    *chtP = ppm_colorhisttocolorhash(chv, nColors);
+
+    ppm_freecolorhist(chv);
+}
+
+
+
+static unsigned int
+minBmpBitsForColorCount(unsigned int const colorCount) {
+
+    unsigned int const minbits = pm_maxvaltobits(colorCount - 1);
+
+    /* Only 1, 4, 8, and 24 are defined in the BMP spec we
+       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).  
+    */
+    if (minbits == 1)
+        return 1;
+    else if (minbits <= 4)
+        return 4;
+    else if (minbits <= 8)
+        return 8;
+    else
+        return 24;
+}
+
+
+
+static void
+getMapFile(const char *   const mapFileName,
+           unsigned int * const minimumBppP,
+           colorMap *     const colorMapP) {
+/*----------------------------------------------------------------------------
+   Get the color map (palette) for the BMP from file 'mapFileName'.
+
+   Return the color map as *colormapP.
+
+   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;
+    pixel ** pixels;
+    unsigned int row;
+    unsigned int count;
+
+    mapFileP = pm_openr(mapFileName);
+
+    pixels = ppm_readppm(mapFileP, &cols, &rows, &maxval);
+
+    if (cols * rows > MAXCOLORS)
+        pm_error("The colormap file you gave (-mapfile) has too "
+                 "many entries for a BMP.  A BMP can have at most "
+                 "%u colors; the file has %u pixels, each of which "
+                 "represents an entry in the color map.",
+                 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++];
+
+            assert(count <= ARRAY_SIZE(colorMapP->bmpMap));
+
+            mapEntryP->red = PPM_GETR(color) * 255 / maxval;
+            mapEntryP->grn = PPM_GETG(color) * 255 / maxval;
+            mapEntryP->blu = PPM_GETB(color) * 255 / maxval;
+        }
+    }
+    ppm_freearray(pixels, rows);
+
+    colorMapP->count = count;
+
+    makeHashFromBmpMap(colorMapP->bmpMap, colorMapP->count, &colorMapP->cht);
+
+    *minimumBppP = minBmpBitsForColorCount(count);
+
+    pm_close(mapFileP);
+}
+
+
+
+static void
+analyzeColors(const pixel **    const pixels, 
+              int               const cols, 
+              int               const rows, 
+              pixval            const maxval, 
+              unsigned int *    const minimumBppP,
+              colorMap *        const colorMapP) {
 /*----------------------------------------------------------------------------
   Look at the colors in the image 'pixels' and compute values to use in
   representing those colors in a BMP image.  
 
-  First of all, count the distinct colors.  Return as *minimum_bpp_p
+  First of all, count the distinct colors.  Return as *minimumBppP
   the minimum number of bits per pixel it will take to represent all
   the colors in BMP format.
 
@@ -589,37 +729,23 @@ analyze_colors(const pixel **    const pixels,
     colorMapP->count = colorCount;
     if (chv == NULL) {
         pm_message("More than %u colors found", MAXCOLORS);
-        *minimum_bpp_p = 24;
+        *minimumBppP = 24;
         colorMapP->cht = NULL;
     } else {
-        unsigned int const minbits = pm_maxvaltobits(colorMapP->count - 1);
-
         unsigned int i;
 
         pm_message("%u colors found", colorMapP->count);
 
-        /* Only 1, 4, 8, and 24 are defined in the BMP spec we
-           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).  
-        */
-        if (minbits == 1)
-            *minimum_bpp_p = 1;
-        else if (minbits <= 4)
-            *minimum_bpp_p = 4;
-        else if (minbits <= 8)
-            *minimum_bpp_p = 8;
-        else
-            *minimum_bpp_p = 24;
+        *minimumBppP = minBmpBitsForColorCount(colorMapP->count);
 
         /*
          * Now scale the maxval to 255 as required by BMP format.
          */
         for (i = 0; i < colorMapP->count; ++i) {
-            colorMapP->red[i] = (pixval) PPM_GETR(chv[i].color) * 255 / maxval;
-            colorMapP->grn[i] = (pixval) PPM_GETG(chv[i].color) * 255 / maxval;
-            colorMapP->blu[i] = (pixval) PPM_GETB(chv[i].color) * 255 / maxval;
+            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. */
@@ -631,38 +757,52 @@ analyze_colors(const pixel **    const pixels,
 
 
 static void
-choose_colortype_bpp(struct cmdline_info const cmdline,
-                     unsigned int        const colors, 
-                     unsigned int        const minimum_bpp,
-                     enum colortype *    const colortype_p, 
-                     unsigned int *      const bits_per_pixel_p) {
+chooseColortypeBpp(bool             const userRequestsBpp,
+                   unsigned int     const requestedBpp,
+                   unsigned int     const minimumBpp,
+                   enum colortype * const colortypeP, 
+                   unsigned int *   const bitsPerPixelP) {
+/*----------------------------------------------------------------------------
+   Determine whether the BMP raster should contain RGB values or palette
+   indices and how many bits is should have for each pixel.
 
-    if (!cmdline.bppSpec) {
+   'userRequestsBpp' says the user has requested a particular number of
+   bits per pixel.  'requestedBpp' is his request, and we assume it's a
+   valid value for a BMP.
+
+   'colors' is how many colors are in the image.
+
+   'minimumBpp' is the minimum number of bits it takes to represent all
+   the colors in the image.  We assume it is valid for a BMP.
+
+   We return our choices as *colortypeP and *bitsPerPixelP.
+-----------------------------------------------------------------------------*/
+    if (!userRequestsBpp) {
         /* User has no preference as to bits per pixel.  Choose the
            smallest number possible for this image.
         */
-        *bits_per_pixel_p = minimum_bpp;
+        *bitsPerPixelP = minimumBpp;
     } else {
-        if (cmdline.bpp < minimum_bpp)
+        if (requestedBpp < minimumBpp)
             pm_error("There are too many colors in the image to "
-                     "represent in the\n"
-                     "number of bits per pixel you requested: %d.\n"
-                     "You may use Ppmquant to reduce the number of "
+                     "represent in the "
+                     "number of bits per pixel you requested: %d.  "
+                     "You may use Pnmquant to reduce the number of "
                      "colors in the image.",
-                     cmdline.bpp);
+                     requestedBpp);
         else
-            *bits_per_pixel_p = cmdline.bpp;
+            *bitsPerPixelP = requestedBpp;
     }
 
-    assert(*bits_per_pixel_p == 1 || 
-           *bits_per_pixel_p == 4 || 
-           *bits_per_pixel_p == 8 || 
-           *bits_per_pixel_p == 24);
+    assert(*bitsPerPixelP == 1 || 
+           *bitsPerPixelP == 4 || 
+           *bitsPerPixelP == 8 || 
+           *bitsPerPixelP == 24);
 
-    if (*bits_per_pixel_p > 8) 
-        *colortype_p = TRUECOLOR;
+    if (*bitsPerPixelP > 8) 
+        *colortypeP = TRUECOLOR;
     else {
-        *colortype_p = PALETTE;
+        *colortypeP = PALETTE;
     }
 }
 
@@ -676,12 +816,12 @@ doPbm(FILE *       const ifP,
       int          const class,
       FILE *       const ofP) {
     
-    /*  In the PBM case the raster is read directly from the input by 
-        pbm_readpbmrow_packed.  The raster format is almost identical,
-        except that BMP specifies rows to be zero-filled to 32 bit borders 
-        and that in BMP the bottom row comes first in order.
+    /* 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
+        32 bit borders and that in BMP the bottom row comes first in
+        order.
     */
-
     int const CHARBITS = (sizeof(unsigned char)*8); 
     int const colChars = pbm_packed_bytes(cols);
     int const adjustedCols = (cols+31) /32 * 32;
@@ -709,7 +849,7 @@ doPbm(FILE *       const ifP,
                 thisRow[i] = ~thisRow[i]; /* flip all pixels */
         }
         /* This may seem unnecessary, because the color palette 
-           (RGB[] in BMPEncodePBM) can be inverted for the same effect.
+           (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.
         */
@@ -721,25 +861,28 @@ doPbm(FILE *       const ifP,
         }
     }
 
-    BMPEncodePBM(ofP, class, cols, rows, bitrow);
+    bmpEncodePbm(ofP, class, cols, rows, bitrow);
 }            
 
 
 
 static void
-doPgmPpm(FILE * const ifP,
+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) {
 
-    /* PGM and PPM.  The input image is read into a PPM array, scanned
-       for color analysis and converted to a BMP raster.
-       Logic works for PBM.
+    /* 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
+       works for PBM.
     */
-    int minimumBpp;
+    unsigned int minimumBpp;
     unsigned int bitsPerPixel;
     enum colortype colortype;
     unsigned int row;
@@ -752,13 +895,16 @@ doPgmPpm(FILE * const ifP,
     for (row = 0; row < rows; ++row)
         ppm_readppmrow(ifP, pixels[row], cols, maxval, ppmFormat);
     
-    analyze_colors((const pixel**)pixels, cols, rows, maxval, 
-                   &minimumBpp, &colorMap);
+    if (mapFileName)
+        getMapFile(mapFileName, &minimumBpp, &colorMap);
+    else
+        analyzeColors((const pixel**)pixels, cols, rows, maxval, 
+                      &minimumBpp, &colorMap);
     
-    choose_colortype_bpp(cmdline, colorMap.count, minimumBpp, &colortype, 
-                         &bitsPerPixel);
+    chooseColortypeBpp(userRequestsBpp, requestedBpp, minimumBpp,
+                       &colortype, &bitsPerPixel);
     
-    BMPEncode(stdout, class, colortype, bitsPerPixel,
+    bmpEncode(ofP, class, colortype, bitsPerPixel,
               cols, rows, (const pixel**)pixels, maxval, &colorMap);
     
     freeColorMap(&colorMap);
@@ -767,17 +913,19 @@ doPgmPpm(FILE * const ifP,
 
 
 int
-main(int argc, char **argv) {
+main(int           argc,
+     const char ** argv) {
 
+    struct cmdlineInfo cmdline;
     FILE * ifP;
     int rows;
     int cols;
     pixval maxval;
     int ppmFormat;
 
-    ppm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
-    parse_command_line(argc, argv, &cmdline);
+    parseCommandLine(argc, argv, &cmdline);
 
     ifP = pm_openr(cmdline.input_filename);
     
@@ -786,7 +934,9 @@ main(int argc, char **argv) {
     if (PPM_FORMAT_TYPE(ppmFormat) == PBM_TYPE)
         doPbm(ifP, cols, rows, ppmFormat, cmdline.class, stdout);
     else
-        doPgmPpm(ifP, cols, rows, maxval, ppmFormat, cmdline.class, stdout);
+        doPgmPpm(ifP, cols, rows, maxval, ppmFormat,
+                 cmdline.class, cmdline.bppSpec, cmdline.bpp, cmdline.mapfile,
+                 stdout);
 
     pm_close(ifP);
     pm_close(stdout);
diff --git a/converter/ppm/ppmtogif.c b/converter/ppm/ppmtogif.c
index 21ac1989..93feaa95 100644
--- a/converter/ppm/ppmtogif.c
+++ b/converter/ppm/ppmtogif.c
@@ -1,101 +1,63 @@
-/* ppmtogif.c - read a portable pixmap and produce a GIF file
-**
-** Based on GIFENCOD by David Rowley <mgardi@watdscu.waterloo.edu>.A
-** Lempel-Zim compression based on "compress".
-**
-** Modified by Marcel Wijkstra <wijkstra@fwi.uva.nl>
-**
-** The non-LZW GIF generation stuff was adapted from the Independent
-** JPEG Group's djpeg on 2001.09.29.  The uncompressed output subroutines
-** are derived directly from the corresponding subroutines in djpeg's
-** wrgif.c source file.  Its copyright notice say:
-
- * Copyright (C) 1991-1997, Thomas G. Lane.
- * This file is part of the Independent JPEG Group's software.
- * For conditions of distribution and use, see the accompanying README file.
- *
-   The reference README file is README.JPEG in the Netpbm package.
-**
-** 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.
-*/
+/* This is a backward compatibility interface to Pamtogif.
+
+   Pamtogif replaced Ppmtogif in Netpbm 10.37 (December 2006).
+
+   The only significant ways Pamtogif are not backward compatible with
+   old Ppmtogif are:
 
-/* TODO: merge the LZW and uncompressed subroutines.  They are separate
-   only because they had two different lineages and the code is too
-   complicated for me quickly to rewrite it.
+     - Pamtogif does not have a -alpha option.
+
+     - Pamtogif requires a user-specififed map file (-mapfile) to
+       match the input in depth.
 */
+#define _BSD_SOURCE   /* Make sure strdup() is in string.h */
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
+
 #include <assert.h>
 #include <string.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <unistd.h>
 
+#include "pm_c_util.h"
 #include "mallocvar.h"
 #include "shhopt.h"
-#include "ppm.h"
+#include "nstring.h"
+#include "pam.h"
 
-#define MAXCMAPSIZE 256
 
-static unsigned int const gifMaxval = 255;
 
-static bool verbose;
-/*
- * a code_int must be able to hold 2**BITS values of type int, and also -1
- */
-typedef int code_int;
+static const char *
+dirname(const char * const fileName) {
 
-typedef long int          count_int;
+    char * buffer;
+    char * slashPos;
 
+    buffer = strdup(fileName);
+
+    slashPos = strchr(buffer, '/');
+
+    if (slashPos)
+        *slashPos = '\0';
+
+    return buffer;
+}
 
-struct cmap {
-    /* This is the information for the GIF colormap (aka palette). */
 
-    int red[MAXCMAPSIZE], green[MAXCMAPSIZE], blue[MAXCMAPSIZE];
-        /* These arrays arrays map a color index, as is found in
-           the raster part of the GIF, to an intensity value for the indicated
-           RGB component.
-        */
-    int perm[MAXCMAPSIZE], permi[MAXCMAPSIZE];
-        /* perm[i] is the position in the sorted colormap of the color which
-           is at position i in the unsorted colormap.  permi[] is the inverse
-           function of perm[].
-        */
-    int cmapsize;
-        /* Number of entries in the GIF colormap.  I.e. number of colors
-           in the image, plus possibly one fake transparency color.
-        */
-    int transparent;
-        /* color index number in GIF palette of the color that is to be
-           transparent.  -1 if no color is transparent.
-        */
-    colorhash_table cht;
-        /* A hash table that relates a PPM pixel value to to a pre-sort
-           GIF colormap index.
-        */
-    pixval maxval;
-        /* The maxval for the colors in 'cht'. */
-};
 
 struct cmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    const char *input_filespec;  /* Filespec of input file */
-    const char *alpha_filespec;  /* Filespec of alpha file; NULL if none */
-    const char *alphacolor;      /* -alphacolor option value or default */
-    unsigned int interlace; /* -interlace option value */
-    unsigned int sort;     /* -sort option value */
+    const char *inputFileName;  /* Name of input file */
+    const char *alpha_filespec; /* Filespec of alpha file; NULL if none */
+    const char *alphacolor;     /* -alphacolor option value or default */
+    unsigned int interlace;     /* -interlace option value */
+    unsigned int sort;          /* -sort option value */
     const char *mapfile;        /* -mapfile option value.  NULL if none. */
     const char *transparent;    /* -transparent option value.  NULL if none. */
     const char *comment;        /* -comment option value; NULL if none */
-    unsigned int nolzw;    /* -nolzw option */
+    unsigned int nolzw;         /* -nolzw option */
     unsigned int verbose;
 };
 
@@ -122,8 +84,8 @@ handleLatex2htmlHack(void) {
   -Bryan 2001.11.14
 -----------------------------------------------------------------------------*/
      pm_error("latex2html, you should just try the -interlace and "
-             "-transparent options to see if they work instead of "
-             "expecting a 'usage' message from -h");
+              "-transparent options to see if they work instead of "
+              "expecting a 'usage' message from -h");
 }
 
 
@@ -191,12 +153,12 @@ parseCommandLine(int argc, char ** argv,
         handleLatex2htmlHack();
 
     if (argc-1 == 0) 
-        cmdlineP->input_filespec = "-";
+        cmdlineP->inputFileName = "-";
     else if (argc-1 != 1)
         pm_error("Program takes zero or one argument (filename).  You "
                  "specified %d", argc-1);
     else
-        cmdlineP->input_filespec = argv[1];
+        cmdlineP->inputFileName = argv[1];
 
     if (cmdlineP->alpha_filespec && cmdlineP->transparent)
         pm_error("You cannot specify both -alpha and -transparent.");
@@ -204,1362 +166,257 @@ parseCommandLine(int argc, char ** argv,
 
 
 
-/*
- * Write out a word to the GIF file
- */
 static void
-Putword(int const w, FILE * const fp) {
-
-    fputc( w & 0xff, fp );
-    fputc( (w / 256) & 0xff, fp );
-}
-
-
-static int
-closestcolor(pixel         const color,
-             pixval        const maxval,
-             struct cmap * const cmapP) {
+openPnmremapStream(const char * const inputFileName,
+                   const char * const mapFileName,
+                   bool         const verbose,
+                   FILE **      const pnmremapPipeP) {
 /*----------------------------------------------------------------------------
-   Return the pre-sort colormap index of the color in the colormap *cmapP
-   that is closest to the color 'color', whose maxval is 'maxval'.
-
-   Also add 'color' to the colormap hash, with the colormap index we
-   are returning.  Caller must ensure that the color is not already in
-   there.
+   Create a process to run the image in file inputFileName[] through
+   Pnmremap, remapping it to the colors in mapFileName[].  Have it
+   write its output to a pipe and return as *pnmremapPipeP the other
+   end of that pipe.
 -----------------------------------------------------------------------------*/
-    unsigned int i;
-    unsigned int imin, dmin;
-
-    pixval const r = PPM_GETR(color) * gifMaxval / maxval;
-    pixval const g = PPM_GETG(color) * gifMaxval / maxval;
-    pixval const b = PPM_GETB(color) * gifMaxval / maxval;
-
-    dmin = SQR(255) * 3;
-    imin = 0;
-    for (i=0;i < cmapP->cmapsize; i++) {
-        int const d = SQR(r-cmapP->red[i]) + 
-            SQR(g-cmapP->green[i]) + 
-            SQR(b-cmapP->blue[i]);
-        if (d < dmin) {
-            dmin = d;
-            imin = i; 
-        } 
-    }
-    ppm_addtocolorhash(cmapP->cht, &color, cmapP->permi[imin]);
-
-    return cmapP->permi[imin];
-}
-
+    FILE * pnmremapPipe;
+    const char * pnmremapCommand;
 
+    assert(inputFileName != NULL);
+    assert(mapFileName != NULL);
 
-enum pass {MULT8PLUS0, MULT8PLUS4, MULT4PLUS2, MULT2PLUS1};
-
-
-struct pixelCursor {
-    unsigned int width;
-        /* Width of the image, in columns */
-    unsigned int height;
-        /* Height of the image, in rows */
-    bool interlace;
-        /* We're accessing the image in interlace fashion */
-    unsigned int nPixelsLeft;
-        /* Number of pixels left to be read */
-    unsigned int curCol;
-        /* Location of pointed-to pixel, column */
-    unsigned int curRow;
-        /* Location of pointed-to pixel, row */
-    enum pass pass;
-        /* The interlace pass.  Undefined if !interlace */
-};
+    asprintfN(&pnmremapCommand, "pnmremap -mapfile='%s' %s",
+              mapFileName, inputFileName);
 
+    if (verbose)
+        pm_message("Preprocessing Pamtogif input with shell command '%s'",
+                   pnmremapCommand);
 
+    pnmremapPipe = popen(pnmremapCommand, "r");
 
-static struct pixelCursor pixelCursor;
-    /* Current location in the input pixels.  */
-
+    if (pnmremapPipe == NULL)
+        pm_error("Shell command '%s', via popen(), to prepare the input "
+                 "for Pamtogif, failed.", pnmremapCommand);
+    else
+        *pnmremapPipeP = pnmremapPipe;
 
-static void
-initPixelCursor(unsigned int const width,
-                unsigned int const height,
-                bool         const interlace) {
-
-    pixelCursor.width       = width;
-    pixelCursor.height      = height;
-    pixelCursor.interlace   = interlace;
-    pixelCursor.pass        = MULT8PLUS0;
-    pixelCursor.curCol      = 0;
-    pixelCursor.curRow      = 0;
-    pixelCursor.nPixelsLeft = width * height;
+    strfree(pnmremapCommand);
 }
 
 
 
-static void
-getPixel(pixel **           const pixels,
-         pixval             const inputMaxval,
-         gray **            const alpha,
-         gray               const alphaThreshold, 
-         struct cmap *      const cmapP,
-         struct pixelCursor const pixelCursor,
-         int *              const retvalP) {
-/*----------------------------------------------------------------------------
-   Return as *retvalP the colormap index of the pixel at location
-   pointed to by 'pixelCursor' in the PPM raster 'pixels', using
-   colormap *cmapP.
------------------------------------------------------------------------------*/
-    unsigned int const x = pixelCursor.curCol;
-    unsigned int const y = pixelCursor.curRow;
+static const char *
+pamtogifCommand(const char *       const arg0,
+                struct cmdlineInfo const cmdline) {
 
-    int colorindex;
+    const char * const pamtogifName = "pamtogif";
 
-    if (alpha && alpha[y][x] < alphaThreshold)
-        colorindex = cmapP->transparent;
-    else {
-        int presortColorindex;
+    const char * retval;
 
-        presortColorindex = ppm_lookupcolor(cmapP->cht, &pixels[y][x]);
-        if (presortColorindex == -1)
-            presortColorindex = 
-                closestcolor(pixels[y][x], inputMaxval, cmapP);
-        colorindex = cmapP->perm[presortColorindex];
-    }
-    *retvalP = colorindex;
-}
+    const char * commandVerb;
+    const char * transparentOpt;
+    const char * commentOpt;
 
+    if (strchr(arg0, '/')) {
+        const char * const arg0DirName = dirname(arg0);
+        const char * progName;
 
+        struct stat statbuf;
 
-static void
-bumpRowInterlace(struct pixelCursor * const pixelCursorP) {
-/*----------------------------------------------------------------------------
-   Move *pixelCursorP to the next row in the interlace pattern.
------------------------------------------------------------------------------*/
-    /* There are 4 passes:
-       MULT8PLUS0: Rows 8, 16, 24, 32, etc.
-       MULT8PLUS4: Rows 4, 12, 20, 28, etc.
-       MULT4PLUS2: Rows 2, 6, 10, 14, etc.
-       MULT2PLUS1: Rows 1, 3, 5, 7, 9, etc.
-    */
-    
-    switch (pixelCursorP->pass) {
-    case MULT8PLUS0:
-        pixelCursorP->curRow += 8;
-        break;
-    case MULT8PLUS4:
-        pixelCursorP->curRow += 8;
-        break;
-    case MULT4PLUS2:
-        pixelCursorP->curRow += 4;
-        break;
-    case MULT2PLUS1:
-        pixelCursorP->curRow += 2;
-        break;
-    }
-    /* Set the proper pass for the next read.  Note that if there are
-       more than 4 rows, the sequence of passes is sequential, but
-       when there are fewer than 4, we may skip e.g. from MULT8PLUS0
-       to MULT4PLUS2.
-    */
-    while (pixelCursorP->curRow >= pixelCursorP->height) {
-        switch (pixelCursorP->pass) {
-        case MULT8PLUS0:
-            pixelCursorP->pass = MULT8PLUS4;
-            pixelCursorP->curRow = 4;
-            break;
-        case MULT8PLUS4:
-            pixelCursorP->pass = MULT4PLUS2;
-            pixelCursorP->curRow = 2;
-            break;
-        case MULT4PLUS2:
-            pixelCursorP->pass = MULT2PLUS1;
-            pixelCursorP->curRow = 1;
-            break;
-        case MULT2PLUS1:
-            /* We've read the entire image; pass and current row are
-               now undefined.
-            */
-            pixelCursorP->curRow = 0;
-            break;
-        }
-    }
-}
+        asprintfN(&progName, "%s/%s", arg0DirName, pamtogifName);
 
-
-
-static void
-bumpPixel(struct pixelCursor * const pixelCursorP) {
-/*----------------------------------------------------------------------------
-   Bump *pixelCursorP to point to the next pixel to go into the GIF
-
-   Must not call when there are no pixels left.
------------------------------------------------------------------------------*/
-    assert(pixelCursorP->nPixelsLeft > 0);
-
-    /* Move one column to the right */
-    ++pixelCursorP->curCol;
-    
-    if (pixelCursorP->curCol >= pixelCursorP->width) {
-        /* That pushed us past the end of a row. */
-        /* Reset to the left edge ... */
-        pixelCursorP->curCol = 0;
-        
-        /* ... of the next row */
-        if (!pixelCursorP->interlace)
-            /* Go to the following row */
-            ++pixelCursorP->curRow;
+        if (stat(progName, &statbuf) == 0)
+            commandVerb = progName;
         else
-            bumpRowInterlace(pixelCursorP);
-    }
-    --pixelCursorP->nPixelsLeft;
-}
-
-
-
-static int
-gifNextPixel(pixel **      const pixels,
-             pixval        const inputMaxval,
-             gray **       const alpha,
-             gray          const alphaThreshold, 
-             struct cmap * const cmapP) {
-/*----------------------------------------------------------------------------
-   Return the pre-sort color index (index into the unsorted GIF color map)
-   of the next pixel to be processed from the input image.
-
-   'alpha_threshold' is the gray level such that a pixel in the alpha
-   map whose value is less that that represents a transparent pixel
-   in the output.
------------------------------------------------------------------------------*/
-    int retval;
-
-    if (pixelCursor.nPixelsLeft == 0 )
-        retval = EOF;
-    else {
-        getPixel(pixels, inputMaxval, alpha, alphaThreshold, cmapP, 
-                 pixelCursor, &retval);
-
-        bumpPixel(&pixelCursor);
-    }
-    return retval;
-}
-
-
-
-static void
-write_transparent_color_index_extension(FILE *fp, const int Transparent) {
-/*----------------------------------------------------------------------------
-   Write out extension for transparent color index.
------------------------------------------------------------------------------*/
-
-    fputc( '!', fp );
-    fputc( 0xf9, fp );
-    fputc( 4, fp );
-    fputc( 1, fp );
-    fputc( 0, fp );
-    fputc( 0, fp );
-    fputc( Transparent, fp );
-    fputc( 0, fp );
-}
-
-
-
-static void
-write_comment_extension(FILE *fp, const char comment[]) {
-/*----------------------------------------------------------------------------
-   Write out extension for a comment
------------------------------------------------------------------------------*/
-    char *segment;
-    
-    fputc('!', fp);   /* Identifies an extension */
-    fputc(0xfe, fp);  /* Identifies a comment */
-
-    /* Write it out in segments no longer than 255 characters */
-    for (segment = (char *) comment; 
-         segment < comment+strlen(comment); 
-         segment += 255) {
+            commandVerb = strdup(pamtogifName);
 
-        const int length_this_segment = MIN(255, strlen(segment));
+        strfree(arg0DirName);
+    } else
+        commandVerb = strdup(pamtogifName);
 
-        fputc(length_this_segment, fp);
-
-        fwrite(segment, 1, length_this_segment, fp);
-    }
-
-    fputc(0, fp);   /* No more comment blocks in this extension */
-}
-
-
-
-/***************************************************************************
- *
- *  GIFCOMPR.C       - GIF Image compression routines
- *
- *  Lempel-Ziv compression based on 'compress'.  GIF modifications by
- *  David Rowley (mgardi@watdcsu.waterloo.edu)
- *
- ***************************************************************************/
-
-/*
- * General DEFINEs
- */
-
-#define BITS    12
-
-#define HSIZE  5003            /* 80% occupancy */
-
-#ifdef NO_UCHAR
- typedef char   char_type;
-#else /*NO_UCHAR*/
- typedef        unsigned char   char_type;
-#endif /*NO_UCHAR*/
-
-/*
- *
- * GIF Image compression - modified 'compress'
- *
- * Based on: compress.c - File compression ala IEEE Computer, June 1984.
- *
- * By Authors:  Spencer W. Thomas       (decvax!harpo!utah-cs!utah-gr!thomas)
- *              Jim McKie               (decvax!mcvax!jim)
- *              Steve Davies            (decvax!vax135!petsd!peora!srd)
- *              Ken Turkowski           (decvax!decwrl!turtlevax!ken)
- *              James A. Woods          (decvax!ihnp4!ames!jaw)
- *              Joe Orost               (decvax!vax135!petsd!joe)
- *
- */
-#include <ctype.h>
-
-#define ARGVAL() (*++(*argv) || (--argc && *++argv))
-
-static code_int const maxmaxcode = (code_int)1 << BITS;
-    /* should NEVER generate this code */
-#define MAXCODE(n_bits)        (((code_int) 1 << (n_bits)) - 1)
-
-static long htab [HSIZE];
-static unsigned short codetab [HSIZE];
-#define HashTabOf(i)       htab[i]
-#define CodeTabOf(i)    codetab[i]
-
-/*
- * To save much memory, we overlay the table used by compress() with those
- * used by decompress().  The tab_prefix table is the same size and type
- * as the codetab.  The tab_suffix table needs 2**BITS characters.  We
- * get this from the beginning of htab.  The output stack uses the rest
- * of htab, and contains characters.  There is plenty of room for any
- * possible stack (stack used to be 8000 characters).
- */
-
-#define tab_prefixof(i) CodeTabOf(i)
-#define tab_suffixof(i)        ((char_type*)(htab))[i]
-#define de_stack               ((char_type*)&tab_suffixof((code_int)1<<BITS))
-
-static code_int free_ent = 0;                  /* first unused entry */
-
-/*
- * block compression parameters -- after all codes are used up,
- * and compression rate changes, start over.
- */
-static int clear_flg = 0;
-
-static int offset;
-static long int in_count = 1;            /* length of input */
-static long int out_count = 0;           /* # of codes output (for debugging) */
-
-/*
- * compress stdin to stdout
- *
- * Algorithm:  use open addressing double hashing (no chaining) on the
- * prefix code / next character combination.  We do a variant of Knuth's
- * algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
- * secondary probe.  Here, the modular division first probe is gives way
- * to a faster exclusive-or manipulation.  Also do block compression with
- * an adaptive reset, whereby the code table is cleared when the compression
- * ratio decreases, but after the table fills.  The variable-length output
- * codes are re-sized at this point, and a special CLEAR code is generated
- * for the decompressor.  Late addition:  construct the table according to
- * file size for noticeable speed improvement on small files.  Please direct
- * questions about this implementation to ames!jaw.
- */
-
-static int ClearCode;
-static int EOFCode;
-
-/***************************************************************************
-*                          BYTE OUTPUTTER                 
-***************************************************************************/
-
-typedef struct {
-    FILE * fileP;  /* The file to which to output */
-    unsigned int count;
-        /* Number of bytes so far in the current data block */
-    unsigned char buffer[256];
-        /* The current data block, under construction */
-} byteBuffer;
-
-
-
-static byteBuffer *
-byteBuffer_create(FILE * const fileP) {
-
-    byteBuffer * byteBufferP;
-
-    MALLOCVAR_NOFAIL(byteBufferP);
-
-    byteBufferP->fileP = fileP;
-    byteBufferP->count = 0;
-
-    return byteBufferP;
-}
-
-
-
-static void
-byteBuffer_destroy(byteBuffer * const byteBufferP) {
-
-    free(byteBufferP);
-}
-
-
-
-static void
-byteBuffer_flush(byteBuffer * const byteBufferP) {
-/*----------------------------------------------------------------------------
-   Write the current data block to the output file, then reset the current 
-   data block to empty.
------------------------------------------------------------------------------*/
-    if (byteBufferP->count > 0 ) {
-        if (verbose)
-            pm_message("Writing %u byte block", byteBufferP->count);
-        fputc(byteBufferP->count, byteBufferP->fileP);
-        fwrite(byteBufferP->buffer, 1, byteBufferP->count, byteBufferP->fileP);
-        byteBufferP->count = 0;
-    }
-}
-
-
-
-static void
-byteBuffer_flushFile(byteBuffer * const byteBufferP) {
-    
-    fflush(byteBufferP->fileP);
-    
-    if (ferror(byteBufferP->fileP))
-        pm_error("error writing output file");
-}
-
-
-
-static void
-byteBuffer_out(byteBuffer *  const byteBufferP,
-               unsigned char const c) {
-/*----------------------------------------------------------------------------
-  Add a byte to the end of the current data block, and if it is now 254
-  characters, flush the data block to the output file.
------------------------------------------------------------------------------*/
-    byteBufferP->buffer[byteBufferP->count++] = c;
-    if (byteBufferP->count >= 254)
-        byteBuffer_flush(byteBufferP);
-}
-
-
-
-struct gif_dest {
-    /* This structure controls output of uncompressed GIF raster */
-
-    byteBuffer * byteBufferP;  /* Where the full bytes go */
-
-    /* State for packing variable-width codes into a bitstream */
-    int n_bits;         /* current number of bits/code */
-    int maxcode;        /* maximum code, given n_bits */
-    int cur_accum;      /* holds bits not yet output */
-    int cur_bits;       /* # of bits in cur_accum */
-
-    /* State for GIF code assignment */
-    int ClearCode;      /* clear code (doesn't change) */
-    int EOFCode;        /* EOF code (ditto) */
-    int code_counter;   /* counts output symbols */
-};
-
-
-
-static unsigned long const masks[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F,
-                                       0x001F, 0x003F, 0x007F, 0x00FF,
-                                       0x01FF, 0x03FF, 0x07FF, 0x0FFF,
-                                       0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF };
-
-typedef struct {
-    byteBuffer * byteBufferP;
-    unsigned int initBits;
-    unsigned int n_bits;                        /* number of bits/code */
-    code_int maxcode;                  /* maximum code, given n_bits */
-    unsigned long curAccum;
-    int curBits;
-} codeBuffer;
-
-
-
-static codeBuffer *
-codeBuffer_create(FILE *       const ofP,
-                  unsigned int const initBits) {
-
-    codeBuffer * codeBufferP;
-
-    MALLOCVAR_NOFAIL(codeBufferP);
-
-    codeBufferP->initBits    = initBits;
-    codeBufferP->n_bits      = codeBufferP->initBits;
-    codeBufferP->maxcode     = MAXCODE(codeBufferP->n_bits);
-    codeBufferP->byteBufferP = byteBuffer_create(ofP);
-    codeBufferP->curAccum    = 0;
-    codeBufferP->curBits     = 0;
-
-    return codeBufferP;
-}
-
-
-
-static void
-codeBuffer_destroy(codeBuffer * const codeBufferP) {
-
-    byteBuffer_destroy(codeBufferP->byteBufferP);
-
-    free(codeBufferP);
-}
-
-
-
-static void
-codeBuffer_output(codeBuffer * const codeBufferP,
-                  code_int     const code) {
-/*----------------------------------------------------------------------------
-   Output one GIF code to the file, through the code buffer.
-
-   The code is represented as n_bits bits in the file -- the lower
-   n_bits bits of 'code'.
-
-   If the code is EOF, flush the code buffer to the file.
-
-   In some cases, change n_bits and recalculate maxcode to go with it.
------------------------------------------------------------------------------*/
-    /*
-      Algorithm:
-      Maintain a BITS character long buffer (so that 8 codes will
-      fit in it exactly).  Use the VAX insv instruction to insert each
-      code in turn.  When the buffer fills up empty it and start over.
-    */
-    
-    codeBufferP->curAccum &= masks[codeBufferP->curBits];
-
-    if (codeBufferP->curBits > 0)
-        codeBufferP->curAccum |= ((long)code << codeBufferP->curBits);
+    if (cmdline.transparent)
+        asprintfN(&transparentOpt, "-transparent=%s", cmdline.transparent);
     else
-        codeBufferP->curAccum = code;
-
-    codeBufferP->curBits += codeBufferP->n_bits;
+        transparentOpt = strdup("");
 
-    while (codeBufferP->curBits >= 8) {
-        byteBuffer_out(codeBufferP->byteBufferP,
-                       codeBufferP->curAccum & 0xff);
-        codeBufferP->curAccum >>= 8;
-        codeBufferP->curBits -= 8;
-    }
-
-    if (clear_flg) {
-        codeBufferP->n_bits = codeBufferP->initBits;
-        codeBufferP->maxcode = MAXCODE(codeBufferP->n_bits);
-        clear_flg = 0;
-    } else if (free_ent > codeBufferP->maxcode) {
-        /* The next entry is going to be too big for the code size, so
-           increase it, if possible.
-        */
-        ++codeBufferP->n_bits;
-        if (codeBufferP->n_bits == BITS)
-            codeBufferP->maxcode = maxmaxcode;
-        else
-            codeBufferP->maxcode = MAXCODE(codeBufferP->n_bits);
-    }
+    if (cmdline.comment)
+        asprintfN(&commentOpt, "-comment=%s", cmdline.comment);
+    else
+        commentOpt = strdup("");
+
+    asprintfN(&retval, "%s - -alphacolor=%s %s %s %s %s %s %s",
+              commandVerb,
+              cmdline.alphacolor,
+              cmdline.interlace ? "-interlace" : "",
+              cmdline.sort ? "-sort" : "",
+              transparentOpt,
+              commentOpt,
+              cmdline.nolzw ? "-nolzw" : "",
+              cmdline.verbose ? "-verbose" : "");
     
-    if (code == EOFCode) {
-        /* We're at EOF.  Output the possible partial byte in the buffer */
-        if (codeBufferP->curBits > 0) {
-            byteBuffer_out(codeBufferP->byteBufferP,
-                           codeBufferP->curAccum & 0xff);
-            codeBufferP->curBits = 0;
-        }
-        byteBuffer_flush(codeBufferP->byteBufferP);
-        
-        byteBuffer_flushFile(codeBufferP->byteBufferP);
-    }
-}
-
+    strfree(transparentOpt);
+    strfree(commentOpt);
 
-
-static void
-cl_hash(long const hsize) {
-    /* reset code table */
-
-    long const m1 = -1;
-
-    long * htab_p;
-    long i;
-
-    htab_p = htab + hsize;  /* initial value */
-
-    i = hsize - 16;
-    do {                            /* might use Sys V memset(3) here */
-        *(htab_p-16) = m1;
-        *(htab_p-15) = m1;
-        *(htab_p-14) = m1;
-        *(htab_p-13) = m1;
-        *(htab_p-12) = m1;
-        *(htab_p-11) = m1;
-        *(htab_p-10) = m1;
-        *(htab_p-9) = m1;
-        *(htab_p-8) = m1;
-        *(htab_p-7) = m1;
-        *(htab_p-6) = m1;
-        *(htab_p-5) = m1;
-        *(htab_p-4) = m1;
-        *(htab_p-3) = m1;
-        *(htab_p-2) = m1;
-        *(htab_p-1) = m1;
-        htab_p -= 16;
-    } while ((i -= 16) >= 0);
-
-    for (i += 16; i > 0; --i)
-        *--htab_p = m1;
+    return retval;
 }
 
 
 
 static void
-cl_block(codeBuffer * const codeBufferP) {
-/*----------------------------------------------------------------------------
-  Clear out the hash table
------------------------------------------------------------------------------*/
-    cl_hash(HSIZE);
-    free_ent = ClearCode + 2;
-    clear_flg = 1;
+feedPamtogifNoAlpha(struct pam * const inPamP,
+                    FILE *       const pipeToPamtogif) {
     
-    codeBuffer_output(codeBufferP, (code_int)ClearCode);
-}
+    unsigned int row;
+    struct pam outPam;
+    tuple * tuplerow;
 
+    tuplerow = pnm_allocpamrow(inPamP);
 
-
-static void
-write_raster_LZW(pixel **      const pixels,
-                 pixval        const input_maxval,
-                 gray **       const alpha,
-                 gray          const alpha_maxval, 
-                 struct cmap * const cmapP, 
-                 int           const initBits,
-                 FILE *        const ofP) {
-/*----------------------------------------------------------------------------
-   Write the raster to file 'ofP'.
-
-   The raster to write is 'pixels', which has maxval 'input_maxval',
-   modified by alpha mask 'alpha', which has maxval 'alpha_maxval'.
-
-   Use the colormap 'cmapP' to generate the raster ('pixels' is 
-   composed of RGB samples; the GIF raster is colormap indices).
-
-   Write the raster using LZW compression.
------------------------------------------------------------------------------*/
-    gray const alpha_threshold = (alpha_maxval + 1) / 2;
-        /* gray levels below this in the alpha mask indicate transparent
-           pixels in the output image.
-        */
-    code_int ent;
-    code_int disp;
-    int hshift;
-    bool eof;
-    codeBuffer * codeBufferP;
-    
-    codeBufferP = codeBuffer_create(ofP, initBits);
+    outPam = *inPamP;
+    outPam.file = pipeToPamtogif;
     
-    /*
-     * Set up the necessary values
-     */
-    offset = 0;
-    out_count = 0;
-    clear_flg = 0;
-    in_count = 1;
-
-    ClearCode = (1 << (initBits - 1));
-    EOFCode = ClearCode + 1;
-    free_ent = ClearCode + 2;
-
-    ent = gifNextPixel(pixels, input_maxval, alpha, alpha_threshold, cmapP);
-
-    {
-        long fcode;
-        hshift = 0;
-        for (fcode = HSIZE; fcode < 65536L; fcode *= 2L)
-            ++hshift;
-        hshift = 8 - hshift;                /* set hash code range bound */
-    }
-    cl_hash(HSIZE);            /* clear hash table */
-
-    codeBuffer_output(codeBufferP, (code_int)ClearCode);
-
-    eof = FALSE;
-    while (!eof) {
-        int gifpixel;
-            /* The value for the pixel in the GIF image.  I.e. the colormap
-               index.  Or -1 to indicate "no more pixels."
-            */
-        gifpixel = gifNextPixel(pixels, 
-                                input_maxval, alpha, alpha_threshold, cmapP);
-        if (gifpixel == EOF) eof = TRUE;
-        if (!eof) {
-            long const fcode = (long) (((long) gifpixel << BITS) + ent);
-            code_int i;
-                /* xor hashing */
-
-            ++in_count;
-
-            i = (((code_int)gifpixel << hshift) ^ ent);    
-
-            if (HashTabOf (i) == fcode) {
-                ent = CodeTabOf (i);
-                continue;
-            } else if ((long)HashTabOf(i) < 0)      /* empty slot */
-                goto nomatch;
-            disp = HSIZE - i;        /* secondary hash (after G. Knott) */
-            if (i == 0)
-                disp = 1;
-        probe:
-            if ((i -= disp) < 0)
-                i += HSIZE;
-
-            if (HashTabOf(i) == fcode) {
-                ent = CodeTabOf(i);
-                continue;
-            }
-            if ((long)HashTabOf(i) > 0)
-                goto probe;
-        nomatch:
-            codeBuffer_output(codeBufferP, (code_int)ent);
-            ++out_count;
-            ent = gifpixel;
-            if (free_ent < maxmaxcode) {
-                CodeTabOf(i) = free_ent++; /* code -> hashtable */
-                HashTabOf(i) = fcode;
-            } else
-                cl_block(codeBufferP);
-        }
-    }
-    /* Put out the final code. */
-    codeBuffer_output(codeBufferP, (code_int)ent);
-    ++out_count;
-    codeBuffer_output(codeBufferP, (code_int) EOFCode);
-
-    codeBuffer_destroy(codeBufferP);
-}
-
+    pnm_writepaminit(&outPam);
 
+    for (row = 0; row < inPamP->height; ++row) {
+        pnm_readpamrow(inPamP, tuplerow);
 
-/* Routine to convert variable-width codes into a byte stream */
-
-static void
-outputUncompressed(struct gif_dest * const dinfoP,
-                   int               const code) {
-
-    /* Emit a code of n_bits bits */
-    /* Uses cur_accum and cur_bits to reblock into 8-bit bytes */
-    dinfoP->cur_accum |= ((int) code) << dinfoP->cur_bits;
-    dinfoP->cur_bits += dinfoP->n_bits;
-
-    while (dinfoP->cur_bits >= 8) {
-        byteBuffer_out(dinfoP->byteBufferP, dinfoP->cur_accum & 0xFF);
-        dinfoP->cur_accum >>= 8;
-        dinfoP->cur_bits -= 8;
-    }
-}
-
-
-static void
-writeRasterUncompressedInit(FILE *            const ofP,
-                            struct gif_dest * const dinfoP, 
-                            int               const i_bits) {
-/*----------------------------------------------------------------------------
-   Initialize pseudo-compressor
------------------------------------------------------------------------------*/
-
-    /* init all the state variables */
-    dinfoP->n_bits = i_bits;
-    dinfoP->maxcode = MAXCODE(dinfoP->n_bits);
-    dinfoP->ClearCode = (1 << (i_bits - 1));
-    dinfoP->EOFCode = dinfoP->ClearCode + 1;
-    dinfoP->code_counter = dinfoP->ClearCode + 2;
-    /* init output buffering vars */
-    dinfoP->byteBufferP = byteBuffer_create(ofP);
-    dinfoP->cur_accum = 0;
-    dinfoP->cur_bits = 0;
-    /* GIF specifies an initial Clear code */
-    outputUncompressed(dinfoP, dinfoP->ClearCode);
-}
-
-
-
-static void
-writeRasterUncompressedPixel(struct gif_dest * const dinfoP, 
-                             unsigned int      const colormapIndex) {
-/*----------------------------------------------------------------------------
-   "Compress" one pixel value and output it as a symbol.
-
-   'colormapIndex' must be less than dinfoP->n_bits wide.
------------------------------------------------------------------------------*/
-    assert(colormapIndex >> dinfoP->n_bits == 0);
-
-    outputUncompressed(dinfoP, colormapIndex);
-    /* Issue Clear codes often enough to keep the reader from ratcheting up
-     * its symbol size.
-     */
-    if (dinfoP->code_counter < dinfoP->maxcode) {
-        ++dinfoP->code_counter;
-    } else {
-        outputUncompressed(dinfoP, dinfoP->ClearCode);
-        dinfoP->code_counter = dinfoP->ClearCode + 2;	/* reset the counter */
+        pnm_writepamrow(&outPam, tuplerow);
     }
+    pnm_freepamrow(tuplerow);
 }
 
 
 
 static void
-writeRasterUncompressedTerm(struct gif_dest * const dinfoP) {
-
-    outputUncompressed(dinfoP, dinfoP->EOFCode);
-
-    if (dinfoP->cur_bits > 0)
-        byteBuffer_out(dinfoP->byteBufferP, dinfoP->cur_accum & 0xFF);
-
-    byteBuffer_flush(dinfoP->byteBufferP);
-
-    byteBuffer_destroy(dinfoP->byteBufferP);
-}
-
-
-
-static void
-writeRasterUncompressed(FILE *         const ofP, 
-                        pixel **       const pixels,
-                        pixval         const inputMaxval,
-                        gray **        const alpha,
-                        gray           const alphaMaxval, 
-                        struct cmap *  const cmapP, 
-                        int            const initBits) {
-/*----------------------------------------------------------------------------
-   Write the raster to file 'ofP'.
-   
-   Same as write_raster_LZW(), except written out one code per
-   pixel (plus some clear codes), so no compression.  And no use
-   of the LZW patent.
------------------------------------------------------------------------------*/
-    gray const alphaThreshold = (alphaMaxval + 1) / 2;
-        /* gray levels below this in the alpha mask indicate transparent
-           pixels in the output image.
-        */
-    bool eof;
-
-    struct gif_dest gifDest;
-
-    writeRasterUncompressedInit(ofP, &gifDest, initBits);
-
-    eof = FALSE;
-    while (!eof) {
-        int gifpixel;
-            /* The value for the pixel in the GIF image.  I.e. the colormap
-               index.  Or -1 to indicate "no more pixels."
-            */
-        gifpixel = gifNextPixel(pixels, 
-                                inputMaxval, alpha, alphaThreshold, cmapP);
-        if (gifpixel == EOF)
-            eof = TRUE;
-        else
-            writeRasterUncompressedPixel(&gifDest, gifpixel);
-    }
-    writeRasterUncompressedTerm(&gifDest);
-}
-
-
-
-/******************************************************************************
- *
- * GIF Specific routines
- *
- *****************************************************************************/
-
-static void
-writeGifHeader(FILE * const fp,
-               int const Width, int const Height, 
-               int const GInterlace, int const Background, 
-               int const BitsPerPixel, struct cmap * const cmapP,
-               const char comment[]) {
-
-    int B;
-    int const Resolution = BitsPerPixel;
-    int const ColorMapSize = 1 << BitsPerPixel;
-
-    /* Write the Magic header */
-    if (cmapP->transparent != -1 || comment)
-        fwrite("GIF89a", 1, 6, fp);
-    else
-        fwrite("GIF87a", 1, 6, fp);
-
-    /* Write out the screen width and height */
-    Putword( Width, fp );
-    Putword( Height, fp );
-
-    /* Indicate that there is a global color map */
-    B = 0x80;       /* Yes, there is a color map */
-
-    /* OR in the resolution */
-    B |= (Resolution - 1) << 4;
-
-    /* OR in the Bits per Pixel */
-    B |= (BitsPerPixel - 1);
-
-    /* Write it out */
-    fputc( B, fp );
-
-    /* Write out the Background color */
-    fputc( Background, fp );
-
-    /* Byte of 0's (future expansion) */
-    fputc( 0, fp );
-
-    {
-        /* Write out the Global Color Map */
-        /* Note that the Global Color Map is always a power of two colors
-           in size, but *cmapP could be smaller than that.  So we pad with
-           black.
-        */
-        int i;
-        for ( i=0; i < ColorMapSize; ++i ) {
-            if ( i < cmapP->cmapsize ) {
-                fputc( cmapP->red[i], fp );
-                fputc( cmapP->green[i], fp );
-                fputc( cmapP->blue[i], fp );
-            } else {
-                fputc( 0, fp );
-                fputc( 0, fp );
-                fputc( 0, fp );
-            }
+copyRasterWithAlpha(struct pam * const inPamP,
+                    struct pam * const alphaPamP,
+                    struct pam * const outPamP,
+                    unsigned int const alphaPlane) {
+
+    tuple * tuplerow;
+    tuple * alpharow;
+    unsigned int row;
+
+    inPamP->allocation_depth = outPamP->depth;
+
+    tuplerow = pnm_allocpamrow(inPamP);
+    alpharow = pnm_allocpamrow(alphaPamP);
+
+    for (row = 0; row < inPamP->height; ++row) {
+        unsigned int col;
+            
+        pnm_readpamrow(inPamP, tuplerow);
+        pnm_readpamrow(alphaPamP, alpharow);
+
+        for (col = 0; col < inPamP->width; ++col) {
+            tuplerow[col][alphaPlane] = pnm_scalesample(alpharow[col][0],
+                                                        alphaPamP->maxval,
+                                                        inPamP->maxval);
         }
+        pnm_writepamrow(outPamP, tuplerow);
     }
-        
-    if ( cmapP->transparent >= 0 ) 
-        write_transparent_color_index_extension(fp, cmapP->transparent);
-
-    if ( comment )
-        write_comment_extension(fp, comment);
-}
-
-
-
-static void
-writeImageHeader(FILE *       const ofP,
-                 unsigned int const leftOffset,
-                 unsigned int const topOffset,
-                 unsigned int const gWidth,
-                 unsigned int const gHeight,
-                 unsigned int const gInterlace,
-                 unsigned int const initCodeSize) {
-
-    Putword(leftOffset, ofP);
-    Putword(topOffset,  ofP);
-    Putword(gWidth,     ofP);
-    Putword(gHeight,    ofP);
-
-    /* Write out whether or not the image is interlaced */
-    if (gInterlace)
-        fputc(0x40, ofP);
-    else
-        fputc(0x00, ofP);
-
-    /* Write out the initial code size */
-    fputc(initCodeSize, ofP);
-}
-
-
-
-static void
-gifEncode(FILE *        const ofP, 
-          pixel **      const pixels,
-          pixval        const inputMaxval,
-          int           const gWidth,
-          int           const gHeight, 
-          gray **       const alpha,
-          gray          const alphaMaxval,
-          int           const gInterlace,
-          int           const background, 
-          int           const bitsPerPixel,
-          struct cmap * const cmapP,
-          char          const comment[],
-          bool          const nolzw) {
-
-    unsigned int const leftOffset = 0;
-    unsigned int const topOffset  = 0;
-
-    unsigned int const initCodeSize = bitsPerPixel <= 1 ? 2 : bitsPerPixel;
-        /* The initial code size */
-
-    if (gWidth > 65535)
-        pm_error("Image width %u too large for GIF format.  (Max 65535)",
-                 gWidth);  
-    
-    if (gHeight > 65535)
-        pm_error("Image height %u too large for GIF format.  (Max 65535)",
-                 gHeight);  
-
-    writeGifHeader(ofP, gWidth, gHeight, gInterlace, background,
-                   bitsPerPixel, cmapP, comment);
-
-    /* Write an Image separator */
-    fputc(',', ofP);
-
-    writeImageHeader(ofP, leftOffset, topOffset, gWidth, gHeight, gInterlace,
-                     initCodeSize);
-
-    initPixelCursor(gWidth, gHeight, gInterlace);
-
-    /* Write the actual raster */
-    if (nolzw)
-        writeRasterUncompressed(ofP, pixels, 
-                                inputMaxval, alpha, alphaMaxval, cmapP, 
-                                initCodeSize + 1);
-    else
-        write_raster_LZW(pixels, 
-                         inputMaxval, alpha, alphaMaxval, cmapP, 
-                         initCodeSize + 1, ofP);
-
-    /* Write out a zero length data block (to end the series) */
-    fputc(0, ofP);
-
-    /* Write the GIF file terminator */
-    fputc(';', ofP);
-}
-
-
-
-static int
-compute_transparent(const char colorarg[], 
-                    struct cmap * const cmapP) {
-/*----------------------------------------------------------------------------
-   Figure out the color index (index into the colormap) of the color
-   that is to be transparent in the GIF.
-
-   colorarg[] is the string that specifies the color the user wants to
-   be transparent (e.g. "red", "#fefefe").  Its maxval is the maxval
-   of the colormap.  'cmap' is the full colormap except that its
-   'transparent' component isn't valid.
-
-   colorarg[] is a standard Netpbm color specification, except that
-   may have a "=" prefix, which means it specifies a particular exact
-   color, as opposed to without the "=", which means "the color that
-   is closest to this and actually in the image."
-
-   Return -1 if colorarg[] specifies an exact color and that color is not
-   in the image.  Also issue an informational message.
------------------------------------------------------------------------------*/
-    int retval;
-
-    const char *colorspec;
-    bool exact;
-    int presort_colorindex;
-    pixel transcolor;
-
-    if (colorarg[0] == '=') {
-        colorspec = &colorarg[1];
-        exact = TRUE;
-    } else {
-        colorspec = colorarg;
-        exact = FALSE;
-    }
-        
-    transcolor = ppm_parsecolor((char*)colorspec, cmapP->maxval);
-    presort_colorindex = ppm_lookupcolor(cmapP->cht, &transcolor);
-    
-    if (presort_colorindex != -1)
-        retval = cmapP->perm[presort_colorindex];
-    else if (!exact)
-        retval = cmapP->perm[closestcolor(transcolor, cmapP->maxval, cmapP)];
-    else {
-        retval = -1;
-        pm_message(
-            "Warning: specified transparent color does not occur in image.");
-    }
-    return retval;
+    pnm_freepamrow(alpharow);
+    pnm_freepamrow(tuplerow);
 }
 
 
 
 static void
-sort_colormap(int const sort, struct cmap * const cmapP) {
-/*----------------------------------------------------------------------------
-   Sort (in place) the colormap *cmapP.
-
-   Create the perm[] and permi[] mappings for the colormap.
-
-   'sort' is logical:  true means to sort the colormap by red intensity,
-   then by green intensity, then by blue intensity.  False means a null
-   sort -- leave it in the same order in which we found it.
------------------------------------------------------------------------------*/
-    int * const Red = cmapP->red;
-    int * const Blue = cmapP->blue;
-    int * const Green = cmapP->green;
-    int * const perm = cmapP->perm;
-    int * const permi = cmapP->permi;
-    unsigned int const cmapsize = cmapP->cmapsize;
-    
-    int i;
-
-    for (i=0; i < cmapsize; i++)
-        permi[i] = i;
-
-    if (sort) {
-        pm_message("sorting colormap");
-        for (i=0; i < cmapsize; i++) {
-            int j;
-            for (j=i+1; j < cmapsize; j++)
-                if (((Red[i]*MAXCMAPSIZE)+Green[i])*MAXCMAPSIZE+Blue[i] >
-                    ((Red[j]*MAXCMAPSIZE)+Green[j])*MAXCMAPSIZE+Blue[j]) {
-                    int tmp;
-                    
-                    tmp=permi[i]; permi[i]=permi[j]; permi[j]=tmp;
-                    tmp=Red[i]; Red[i]=Red[j]; Red[j]=tmp;
-                    tmp=Green[i]; Green[i]=Green[j]; Green[j]=tmp;
-                    tmp=Blue[i]; Blue[i]=Blue[j]; Blue[j]=tmp; } }
+feedPamtogifWithAlpha(struct pam * const inPamP,
+                      struct pam * const alphaPamP,
+                      FILE *       const pipeToPamtogif) {
+
+    unsigned int alphaPlane;
+    struct pam outPam;
+
+    if (inPamP->width != alphaPamP->width ||
+        inPamP->height != alphaPamP->height)
+        pm_error("-alpha image dimensions (%u w x %u h) do not match "
+                 "the input image dimensions (%u x %u)",
+                 alphaPamP->width, alphaPamP->height,
+                 inPamP->width, inPamP->height);
+
+    outPam = *inPamP;
+    outPam.file        = pipeToPamtogif;
+    outPam.format      = PAM_FORMAT;
+    outPam.plainformat = 0;
+
+    if (inPamP->depth == 1) {
+        alphaPlane = 1;
+        strcpy(outPam.tuple_type, "GRAYSCALE_ALPHA");
+    } else if (inPamP->depth == 3) {
+        alphaPlane = 3;
+        strcpy(outPam.tuple_type, "RGB_ALPHA");
     }
+    outPam.depth = alphaPlane + 1;
 
-    for (i=0; i < cmapsize; i++)
-        perm[permi[i]] = i;
-}
+    pnm_writepaminit(&outPam);
 
-
-
-static void
-normalize_to_255(colorhist_vector const chv, struct cmap * const cmapP) {
-/*----------------------------------------------------------------------------
-   With a PPM color histogram vector 'chv' as input, produce a colormap
-   of integers 0-255 as output in *cmapP.
------------------------------------------------------------------------------*/
-    int i;
-    pixval const maxval = cmapP->maxval;
-
-    if ( maxval != 255 )
-        pm_message(
-            "maxval is not 255 - automatically rescaling colors" );
-
-    for ( i = 0; i < cmapP->cmapsize; ++i ) {
-        if ( maxval == 255 ) {
-            cmapP->red[i] = (int) PPM_GETR( chv[i].color );
-            cmapP->green[i] = (int) PPM_GETG( chv[i].color );
-            cmapP->blue[i] = (int) PPM_GETB( chv[i].color );
-        } else {
-            cmapP->red[i] = (int) PPM_GETR( chv[i].color ) * 255 / maxval;
-            cmapP->green[i] = (int) PPM_GETG( chv[i].color ) * 255 / maxval;
-            cmapP->blue[i] = (int) PPM_GETB( chv[i].color ) * 255 / maxval;
-        }
-    }
-}
-
-
-
-static void add_to_colormap(struct cmap * const cmapP, 
-                            const char *  const colorspec, 
-                            int *         const new_indexP) {
-/*----------------------------------------------------------------------------
-  Add a new entry to the colormap.  Make the color that specified by
-  'colorspec', and return the index of the new entry as *new_indexP.
-
-  'colorspec' is a color specification given by the user, e.g.
-  "red" or "rgb:ff/03.0d".  The maxval for this color specification is
-  that for the colormap *cmapP.
------------------------------------------------------------------------------*/
-    pixel const transcolor = ppm_parsecolor((char*)colorspec, cmapP->maxval);
-    
-    *new_indexP = cmapP->cmapsize++; 
-
-    cmapP->red[*new_indexP] = PPM_GETR(transcolor);
-    cmapP->green[*new_indexP] = PPM_GETG(transcolor); 
-    cmapP->blue[*new_indexP] = PPM_GETB(transcolor); 
+    copyRasterWithAlpha(inPamP, alphaPamP, &outPam, alphaPlane);
 }
 
 
 
 static void
-colormap_from_file(const char filespec[], unsigned int const maxcolors,
-                   colorhist_vector * const chvP, pixval * const maxvalP,
-                   int * const colorsP) {
-/*----------------------------------------------------------------------------
-   Read a colormap from the PPM file filespec[].  Return the color histogram
-   vector (which is practically a colormap) of the input image as *cvhP
-   and the maxval for that histogram as *maxvalP.
------------------------------------------------------------------------------*/
-    FILE *mapfile;
-    int cols, rows;
-    pixel ** colormap_ppm;
-
-    mapfile = pm_openr(filespec);
-    colormap_ppm = ppm_readppm(mapfile, &cols, &rows, maxvalP);
-    pm_close(mapfile);
+feedPamtogif(struct pam * const inPamP,
+             const char * const alphaFilespec,
+             FILE *       const pipeToPamtogif) {
     
-    /* Figure out the colormap from the <mapfile>. */
-    pm_message("computing other colormap...");
-    *chvP = 
-        ppm_computecolorhist(colormap_ppm, cols, rows, maxcolors, colorsP);
-    
-    ppm_freearray(colormap_ppm, rows); 
-}
-
-
-
-static void
-get_alpha(const char * const alpha_filespec, int const cols, int const rows,
-          gray *** const alphaP, gray * const maxvalP) {
-
-    if (alpha_filespec) {
-        int alpha_cols, alpha_rows;
-        *alphaP = pgm_readpgm(pm_openr(alpha_filespec),
-                              &alpha_cols, &alpha_rows, maxvalP);
-        if (alpha_cols != cols || alpha_rows != rows)
-            pm_error("alpha mask is not the same dimensions as the "
-                     "input file (alpha is %dW x %dH; image is %dW x %dH)",
-                     alpha_cols, alpha_rows, cols, rows);
-    } else 
-        *alphaP = NULL;
-}
-
-
-
-static void
-compute_ppm_colormap(pixel ** const pixels, int const cols, int const rows,
-                     int const input_maxval, bool const have_alpha, 
-                     const char * const mapfile, colorhist_vector * const chvP,
-                     colorhash_table * const chtP,
-                     pixval * const colormap_maxvalP, 
-                     int * const colorsP) {
-/*----------------------------------------------------------------------------
-   Compute a colormap, PPM style, for the image 'pixels', which is
-   'cols' by 'rows' with maxval 'input_maxval'.  If 'mapfile' is
-   non-null, Use the colors in that (PPM) file for the color map
-   instead of the colors in 'pixels'.
-
-   Return the colormap as *chvP and *chtP.  Return the maxval for that
-   colormap as *colormap_maxvalP.
-
-   While we're at it, count the colors and validate that there aren't
-   too many.  Return the count as *colorsP.  In determining if there are
-   too many, allow one slot for a fake transparency color if 'have_alpha'
-   is true.  If there are too many, issue an error message and abort the
-   program.
------------------------------------------------------------------------------*/
-    unsigned int maxcolors;
-        /* The most colors we can tolerate in the image.  If we have
-           our own made-up entry in the colormap for transparency, it
-           isn't included in this count.
-        */
-
-    if (have_alpha)
-        maxcolors = MAXCMAPSIZE - 1;
-    else
-        maxcolors = MAXCMAPSIZE;
-
-    if (mapfile) {
-        /* Read the colormap from a separate colormap file. */
-        colormap_from_file(mapfile, maxcolors, chvP, colormap_maxvalP, 
-                           colorsP);
-    } else {
-        /* Figure out the color map from the input file */
-        pm_message("computing colormap...");
-        *chvP = ppm_computecolorhist(pixels, cols, rows, maxcolors, colorsP); 
-        *colormap_maxvalP = input_maxval;
-    }
-
-    if (*chvP == NULL)
-        pm_error("too many colors - try doing a 'pnmquant %d'", maxcolors);
-    pm_message("%d colors found", *colorsP);
-
-    /* And make a hash table for fast lookup. */
-    *chtP = ppm_colorhisttocolorhash(*chvP, *colorsP);
+    if (alphaFilespec) {
+        FILE * afP;
+        struct pam alphaPam;
+        afP = pm_openr(alphaFilespec);
+        pnm_readpaminit(afP, &alphaPam, PAM_STRUCT_SIZE(tuple_type));
+        feedPamtogifWithAlpha(inPamP, &alphaPam, pipeToPamtogif);
+        pm_close(afP);
+    } else
+        feedPamtogifNoAlpha(inPamP, pipeToPamtogif);
 }
 
 
 
 int
-main(int argc, char *argv[]) {
+main(int    argc,
+     char * argv[]) {
+
     struct cmdlineInfo cmdline;
     FILE * ifP;
-    int rows, cols;
-    int BitsPerPixel;
-    pixel ** pixels;   /* The input image, in PPM format */
-    pixval input_maxval;  /* Maxval for 'pixels' */
-    gray ** alpha;     /* The supplied alpha mask; NULL if none */
-    gray alpha_maxval; /* Maxval for 'alpha' */
-
-    struct cmap cmap;
-        /* The colormap, with all its accessories */
-    colorhist_vector chv;
-    int fake_transparent;
-        /* colormap index of the fake transparency color we're using to
-           implement the alpha mask.  Undefined if we're not doing an alpha
-           mask.
-        */
+    struct pam inPam;
+    const char * command;
+    FILE * pipeToPamtogif;
+    int rc;
 
-    ppm_init( &argc, argv );
+    pnm_init(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
-    verbose = cmdline.verbose;
-
-    ifP = pm_openr(cmdline.input_filespec);
-
-    pixels = ppm_readppm(ifP, &cols, &rows, &input_maxval);
-
-    pm_close(ifP);
+    if (cmdline.mapfile)
+        openPnmremapStream(cmdline.inputFileName, cmdline.mapfile,
+                           cmdline.verbose, &ifP);
+    else
+        ifP = pm_openr(cmdline.inputFileName);
+        
+    command = pamtogifCommand(argv[0], cmdline);
 
-    get_alpha(cmdline.alpha_filespec, cols, rows, &alpha, &alpha_maxval);
+    if (cmdline.verbose)
+        pm_message("Executing shell command '%s'", command);
+    
+    pipeToPamtogif = popen(command, "w");
 
-    compute_ppm_colormap(pixels, cols, rows, input_maxval, (alpha != NULL), 
-                         cmdline.mapfile, 
-                         &chv, &cmap.cht, &cmap.maxval, &cmap.cmapsize);
+    if (pipeToPamtogif == NULL)
+        pm_error("Shell command '%s', via popen(), failed.", command);
 
-    /* Now turn the ppm colormap into the appropriate GIF colormap. */
+    pnm_readpaminit(ifP, &inPam, PAM_STRUCT_SIZE(allocation_depth));
 
-    normalize_to_255(chv, &cmap);
+    feedPamtogif(&inPam, cmdline.alpha_filespec, pipeToPamtogif);
 
-    ppm_freecolorhist(chv);
+    rc = pclose(pipeToPamtogif);
 
-    if (alpha) {
-        /* Add a fake entry to the end of the colormap for transparency.  
-           Make its color black. 
-        */
-        add_to_colormap(&cmap, cmdline.alphacolor, &fake_transparent);
-    }
-    sort_colormap(cmdline.sort, &cmap);
-
-    BitsPerPixel = pm_maxvaltobits(cmap.cmapsize-1);
-
-    if (alpha) {
-        cmap.transparent = cmap.perm[fake_transparent];
-    } else {
-        if (cmdline.transparent)
-            cmap.transparent = 
-                compute_transparent(cmdline.transparent, &cmap);
-        else 
-            cmap.transparent = -1;
-    }
-    /* All set, let's do it. */
-    gifEncode(stdout, pixels, input_maxval, cols, rows, 
-              alpha, alpha_maxval, 
-              cmdline.interlace, 0, BitsPerPixel, &cmap, cmdline.comment,
-              cmdline.nolzw);
+    if (rc != 0)
+        pm_error("Pamtogif process failed.  pclose() failed.");
 
-    ppm_freearray(pixels, rows);
-    if (alpha)
-        pgm_freearray(alpha, rows);
+    strfree(command);
 
-    fclose(stdout);
+    pm_close(ifP);
+    pm_close(stdout);
 
     return 0;
 }
diff --git a/converter/ppm/ppmtoilbm.c b/converter/ppm/ppmtoilbm.c
index fcc6053f..d296f498 100644
--- a/converter/ppm/ppmtoilbm.c
+++ b/converter/ppm/ppmtoilbm.c
@@ -439,14 +439,14 @@ compute_ham_cmap(cols, rows, maxval, maxcolors, colorsP, hbits)
                     tmp = hmap[i].b - b; dist += tmp * tmp;
 
                     if( dist <= maxdist ) {
-                        int sum = hmap[i].count + hmap[col].count;
-
-                        hmap[i].r = (hmap[i].r * hmap[i].count + 
-                                     r * hmap[col].count + sum/2)/sum;
-                        hmap[i].g = (hmap[i].g * hmap[i].count + 
-                                     g * hmap[col].count + sum/2)/sum;
-                        hmap[i].b = (hmap[i].b * hmap[i].count + 
-                                     b * hmap[col].count + sum/2)/sum;
+                        unsigned int sum = hmap[i].count + hmap[col].count;
+
+                        hmap[i].r = ROUNDDIV(hmap[i].r * hmap[i].count + 
+                                             r * hmap[col].count, sum);
+                        hmap[i].g = ROUNDDIV(hmap[i].g * hmap[i].count + 
+                                             g * hmap[col].count, sum);
+                        hmap[i].b = ROUNDDIV(hmap[i].b * hmap[i].count + 
+                                             b * hmap[col].count, sum);
                         hmap[i].count = sum;
 
                         hmap[col] = hmap[i];    /* temp store */
@@ -641,20 +641,9 @@ ppm_to_ham(fp, cols, rows, maxval, colormap, colors, cmapmaxval, hamplanes)
 
 
 static long
-#ifdef __STDC__
 do_ham_body(FILE *ifP, FILE *ofp, int cols, int rows,
             pixval maxval, pixval hammaxval, int nPlanes,
             pixel *colormap, int colors)
-#else
-do_ham_body(ifP, ofp, cols, rows, maxval, hammaxval, nPlanes, colormap, colors)
-    FILE *ifP, *ofp;
-    int cols, rows;
-    pixval maxval;      /* maxval of image color components */
-    pixval hammaxval;   /* maxval of HAM color changes */
-    int nPlanes;
-    pixel *colormap;
-    int colors;
-#endif
 {
     register int col, row, i;
     rawtype *raw_rowbuf;
@@ -882,16 +871,8 @@ ppm_to_deep(fp, cols, rows, maxval, bitspercolor)
 
 
 static long
-#if __STDC__
 do_deep_body(FILE *ifP, FILE *ofp, int cols, int rows, pixval maxval, 
              int bitspercolor)
-#else
-do_deep_body(ifP, ofp, cols, rows, maxval, bitspercolor)
-    FILE *ifP, *ofp;
-    int cols, rows;
-    pixval maxval;
-    int bitspercolor;
-#endif
 {
     register int row, col;
     pixel *pP;
@@ -1020,16 +1001,8 @@ ppm_to_dcol(fp, cols, rows, maxval, dcol)
 
 
 static long
-#if __STDC__
 do_dcol_body(FILE *ifP, FILE *ofp, int cols, int rows, pixval maxval, 
              DirectColor *dcol)
-#else
-do_dcol_body(ifP, ofp, cols, rows, maxval, dcol)
-    FILE *ifP, *ofp;
-    int cols, rows;
-    pixval maxval;
-    DirectColor *dcol;
-#endif
 {
     register int row, col;
     pixel *pP;
@@ -1167,18 +1140,8 @@ ppm_to_std(fp, cols, rows, maxval, colormap, colors, cmapmaxval,
 
 
 static long
-#if __STDC__
 do_std_body(FILE *ifP, FILE *ofp, int cols, int rows, pixval maxval,
             pixel *colormap, int colors, int nPlanes)
-#else
-do_std_body(ifP, ofp, cols, rows, maxval, colormap, colors, nPlanes)
-    FILE *ifP, *ofp;
-    int cols, rows;
-    pixval maxval;
-    pixel *colormap;
-    int colors;
-    int nPlanes;
-#endif
 {
     register int row, col, i;
     pixel *pP;
@@ -1789,12 +1752,7 @@ runbyte1(size)
 /************ other utility functions ************/
 
 static void
-#if __STDC__
 put_big_short(short s)
-#else
-put_big_short(s)
-    short s;
-#endif
 {
     if ( pm_writebigshort( stdout, s ) == -1 )
         pm_error( "write error" );
@@ -1824,12 +1782,12 @@ static int *
 make_val_table(oldmaxval, newmaxval)
     int oldmaxval, newmaxval;
 {
-    int i;
-    int *table;
+    unsigned int i;
+    int * table;
 
     MALLOCARRAY_NOFAIL(table, oldmaxval + 1);
-    for(i = 0; i <= oldmaxval; i++ )
-        table[i] = (i * newmaxval + oldmaxval/2) / oldmaxval;
+    for (i = 0; i <= oldmaxval; ++i)
+        table[i] = ROUNDDIV(i * newmaxval, oldmaxval);
 
     return table;
 }
diff --git a/converter/ppm/ppmtoleaf.c b/converter/ppm/ppmtoleaf.c
index fa5fdaf5..dcff0985 100644
--- a/converter/ppm/ppmtoleaf.c
+++ b/converter/ppm/ppmtoleaf.c
@@ -1,4 +1,4 @@
-/* ppmtoleaf.c - read a portable pixmap and produce a ileaf img file
+/* ppmtoleaf.c - read a PPM and produce a ileaf img file
  *
  * Copyright (C) 1994 by Bill O'Donnell.
  *
@@ -15,57 +15,60 @@
  */
 
 #include <stdio.h>
+
 #include "ppm.h"
 
 #define MAXCOLORS 256
 
-pixel **pixels;
+pixel ** pixels;
 colorhash_table cht;
 
-int Red[MAXCOLORS], Green[MAXCOLORS], Blue[MAXCOLORS];
+static int Red[MAXCOLORS], Green[MAXCOLORS], Blue[MAXCOLORS];
 
 
 
 static int
-colorstobpp( colors )
-int colors;
-{
+colorstobpp(unsigned int const colors) {
+
     int bpp;
     
-    if ( colors <= 2 )
-	bpp = 1;
-    else if ( colors <= 256 )
-	bpp = 8;
+    if (colors <= 2)
+        bpp = 1;
+    else if (colors <= 256)
+        bpp = 8;
     else
-	bpp = 24;
+        bpp = 24;
+
     return bpp;
 }
 
 
 
 static int
-GetPixel( x, y )
-int x, y;
-{
+GetPixel(int const x,
+         int const y) {
+
     int color;
     
-    color = ppm_lookupcolor( cht, &pixels[y][x] );
+    color = ppm_lookupcolor(cht, &pixels[y][x]);
+    
     return color;
 }
 
 
 
-/* OK, this routine is not wicked efficient, but it is simple to follow
-   and it works. */
+
 static void
-leaf_writeimg(width, height, depth, ncolors, maxval)
-int width;
-int height;
-int depth;
-int ncolors;
-{
-    int i,row,col;
-    
+leaf_writeimg(unsigned int const width,
+              unsigned int const height,
+              unsigned int const depth,
+              unsigned int const ncolors,
+              pixval       const maxval) {
+
+    /* OK, this routine is not wicked efficient, but it is simple to follow
+       and it works.
+    */
+
     /* NOTE: byte order in ileaf img file fmt is big-endian, always! */
     
     /* magic */
@@ -115,136 +118,142 @@ int ncolors;
     
     /* format, mono/gray = 0x20000000, RGB=0x29000000 */
     if (depth == 1)
-	fputc(0x20, stdout);
+        fputc(0x20, stdout);
     else
-	fputc(0x29, stdout);
+        fputc(0x29, stdout);
+
     fputc(0x00, stdout);
     fputc(0x00, stdout);
     fputc(0x00, stdout);
     
     /* colormap size */
-    if (depth == 8)
-    {
-	fputc((unsigned char)((ncolors >> 8) & 0x00ff), stdout);
-	fputc((unsigned char)(ncolors  & 0x00ff), stdout);
-	for (i=0; i<256; i++)
-	    fputc((unsigned char) Red[i]*255/maxval, stdout);
-	for (i=0; i<256; i++)
-	    fputc((unsigned char) Green[i]*255/maxval, stdout);
-	for (i=0; i<256; i++)
-	    fputc((unsigned char) Blue[i]*255/maxval, stdout);
-	
-	for (row=0; row<height; row++) 
-	{
-	    for (col=0; col<width; col++) 
-		fputc(GetPixel(col, row), stdout);
-	    if (width % 2)
-		fputc(0x00, stdout); /* pad to 2-bytes */
-	}
+    if (depth == 8) {
+        unsigned int i;
+        unsigned int row;
+
+        fputc((unsigned char)((ncolors >> 8) & 0x00ff), stdout);
+        fputc((unsigned char)(ncolors  & 0x00ff), stdout);
+        for (i = 0; i < 256; ++i)
+            fputc((unsigned char) Red[i]*255/maxval, stdout);
+        for (i=0; i < 256; ++i)
+            fputc((unsigned char) Green[i]*255/maxval, stdout);
+        for (i = 0; i < 256; ++i)
+            fputc((unsigned char) Blue[i]*255/maxval, stdout);
+    
+        for (row=0; row < height; ++row) {
+            unsigned int col;
+            for (col = 0; col < width; ++col) 
+                fputc(GetPixel(col, row), stdout);
+            if ((width % 2) != 0)
+                fputc(0x00, stdout); /* pad to 2-bytes */
+        }
     } else if (depth == 1) {
-	/* mono image */
-	/* no colormap */
-	fputc(0x00, stdout);
-	fputc(0x00, stdout);
-
-	for (row=0; row<height; row++) 
-	{
-	    unsigned char bits = 0;
-	    for (col=0; col<width; col++) {
-		if (GetPixel(col,row))
-		    bits |= (unsigned char) (0x0080 >> (col % 8));
-		if (((col + 1) % 8) == 0)  {
-		    fputc(bits, stdout);
-		    bits = 0;
-		}
-	    }
-	    if ((width % 8) != 0)
-		fputc(bits, stdout);
-	    if ((width % 16) && (width % 16) <= 8)
-		fputc(0x00, stdout);  /* 16 bit pad */
-	}
+        /* mono image */
+        /* no colormap */
+
+        unsigned int row;
+
+        fputc(0x00, stdout);
+        fputc(0x00, stdout);
+
+        for (row = 0; row < height; ++row) {
+            unsigned char bits;
+            unsigned int col;
+            bits = 0;
+            for (col = 0; col < width; ++col) {
+                if (GetPixel(col,row))
+                    bits |= (unsigned char) (0x0080 >> (col % 8));
+                if (((col + 1) % 8) == 0)  {
+                    fputc(bits, stdout);
+                    bits = 0;
+                }
+            }
+            if ((width % 8) != 0)
+                fputc(bits, stdout);
+            if ((width % 16) && (width % 16) <= 8)
+                fputc(0x00, stdout);  /* 16 bit pad */
+        }
     } else {
-	/* no colormap, direct or true color (24 bit) image */
-	fputc(0x00, stdout);
-	fputc(0x00, stdout);
-	
-	for (row=0; row<height; row++) 
-	{
-	    for (col=0; col<width; col++) 
-		fputc(pixels[row][col].r * 255 / maxval, stdout);
-	    if (width % 2)
-		fputc(0x00, stdout); /* pad to 2-bytes */
-	    for (col=0; col<width; col++) 
-		fputc(pixels[row][col].g * 255 / maxval, stdout);
-	    if (width % 2)
-		fputc(0x00, stdout); /* pad to 2-bytes */
-	    for (col=0; col<width; col++) 
-		fputc(pixels[row][col].b * 255 / maxval, stdout);
-	    if (width % 2)
-		fputc(0x00, stdout); /* pad to 2-bytes */
-	}
+        /* no colormap, direct or true color (24 bit) image */
+
+        unsigned int row;
+
+        fputc(0x00, stdout);
+        fputc(0x00, stdout);
+        
+        for (row = 0; row < height; ++row) {
+            unsigned int col;
+            for (col = 0; col < width; ++col) 
+                fputc(pixels[row][col].r * 255 / maxval, stdout);
+            if (width % 2 != 0)
+                fputc(0x00, stdout); /* pad to 2-bytes */
+            for (col = 0; col < width; ++col) 
+                fputc(pixels[row][col].g * 255 / maxval, stdout);
+            if (width % 2 != 0)
+                fputc(0x00, stdout); /* pad to 2-bytes */
+            for (col = 0; col < width; ++col) 
+                fputc(pixels[row][col].b * 255 / maxval, stdout);
+            if (width % 2 != 0)
+                fputc(0x00, stdout); /* pad to 2-bytes */
+        }
     }
 }
 
 
 
 int
-main( argc, argv )
-int argc;
-char *argv[];
-{
-    FILE *ifd;
-    int argn, rows, cols, colors, i, BitsPerPixel;
+main(int argc, const char * argv[]) {
+
+    FILE * ifP;
+    int argn;
+    int rows, cols;
+    unsigned int BitsPerPixel;
+    int colors;
     pixval maxval;
     colorhist_vector chv;
     const char * const usage = "[ppmfile]";
     
-    ppm_init(&argc, argv);
+    pm_proginit(&argc, argv);
     
     argn = 1;
     
-    if ( argn < argc )
-    {
-	ifd = pm_openr( argv[argn] );
-	argn++;
+    if (argn < argc) {
+        ifP = pm_openr(argv[argn]);
+        argn++;
     } else
-	ifd = stdin;
+        ifP = stdin;
     
-    if ( argn != argc )
-	pm_usage( usage );
+    if (argn != argc)
+        pm_usage(usage);
     
-    pixels = ppm_readppm( ifd, &cols, &rows, &maxval );
+    pixels = ppm_readppm(ifP, &cols, &rows, &maxval);
     
-    pm_close( ifd );
+    pm_close(ifP);
     
     /* Figure out the colormap. */
-    fprintf( stderr, "(Computing colormap..." );
-    fflush( stderr );
-    chv = ppm_computecolorhist( pixels, cols, rows, MAXCOLORS, &colors );
-    if ( chv != (colorhist_vector) 0 )
-    {
-	fprintf( stderr, "  Done.  %d colors found.)\n", colors );
-	
-	for ( i = 0; i < colors; i++ )
-	{
-	    Red[i] = (int) PPM_GETR( chv[i].color );
-	    Green[i] = (int) PPM_GETG( chv[i].color );
-	    Blue[i] = (int) PPM_GETB( chv[i].color );
-	}
-	BitsPerPixel = colorstobpp( colors );
-	
-	/* And make a hash table for fast lookup. */
-	cht = ppm_colorhisttocolorhash( chv, colors );
-	ppm_freecolorhist( chv );
+    pm_message("Computing colormap...");
+    chv = ppm_computecolorhist(pixels, cols, rows, MAXCOLORS, &colors);
+    if (chv) {
+        unsigned int i;
+
+        pm_message("... Done.  %u colors found.", colors);
+    
+        for (i = 0; i < colors; ++i) {
+            Red[i]   = (int) PPM_GETR( chv[i].color);
+            Green[i] = (int) PPM_GETG( chv[i].color);
+            Blue[i]  = (int) PPM_GETB( chv[i].color);
+        }
+        BitsPerPixel = colorstobpp(colors);
+    
+        /* And make a hash table for fast lookup. */
+        cht = ppm_colorhisttocolorhash(chv, colors);
+        ppm_freecolorhist(chv);
     } else {
-	BitsPerPixel = 24;
-	fprintf( stderr, "  Done.  24-bit true color %d color image.)\n", colors );
+        BitsPerPixel = 24;
+        pm_message("  ... Done.  24-bit true color %u color image.", colors);
     }
     
     leaf_writeimg(cols, rows, BitsPerPixel, colors, maxval);
     
-    return( 0 );
+    return 0;
 }
-
-
-
diff --git a/converter/ppm/ppmtomitsu.c b/converter/ppm/ppmtomitsu.c
index 3934ae45..e59f09b3 100644
--- a/converter/ppm/ppmtomitsu.c
+++ b/converter/ppm/ppmtomitsu.c
@@ -1,10 +1,10 @@
-/* ppmtomitsu.c - read a portable pixmap and produce output for the
+/* ppmtomitsu.c - read a PPM image and produce output for the
 **                Mitsubishi S340-10 Thermo-Sublimation Printer
 **                (or the S3410-30 parallel interface)
 **
 ** Copyright (C) 1992,93 by S.Petra Zeidler
 ** Minor modifications by Ingo Wilken:
-x**  - mymalloc() and check_and_rotate() functions for often used
+**  - mymalloc() and check_and_rotate() functions for often used
 **    code fragments.  Reduces code size by a few KB.
 **  - use pm_error() instead of fprintf(stderr)
 **  - localized allocation of colorhastable
@@ -20,362 +20,70 @@ x**  - mymalloc() and check_and_rotate() functions for often used
 ** implied warranty.
 */
 
+#include <assert.h>
 #include <string.h>
+#include <stdio.h>
 
 #include "pm_c_util.h"
-#include "ppm.h"
 #include "nstring.h"
 #include "mallocvar.h"
+#include "ppm.h"
 
 #include "mitsu.h"
 
 
-#include <stdio.h>
-
 #define HASHSIZE 2048
 #define myhash(x) ((PPM_GETR(x)*3 + PPM_GETG(x)*5 + PPM_GETB(x)*7) % HASHSIZE)
 
 typedef struct hashinfo {
         pixel     color;
         long      flag;
-        struct hashinfo *next;
+        struct hashinfo * next;
 } hashinfo;
 
-#ifdef __STDC__
-static void lineputinit(int cols, int rows, int sharpness, int enlarge, int
-                        copy, struct mediasize medias);
-static void frametransferinit(int cols, int rows, int sharpness, int enlarge,
-                              int copy, struct mediasize medias);
-static void lookuptableinit(int sharpness, int enlarge, int copy,
-                            struct mediasize medias);
-static void lookuptabledata(int cols, int rows, int enlarge,
-                            struct mediasize medias);
-static void check_and_rotate(int cols, int rows, int enlarge,
-                             struct mediasize medias);
-#define CONST const
-#else /*__STDC__*/
-static int lineputinit();
-static int lookuptableinit();
-static int lookuptabledata();
-static int frametransferinit();
-static int check_and_rotate();
-#define CONST
-#endif
-
 #define cmd(arg)           fputc((arg), stdout)
 #define datum(arg)         fputc((char)(arg), stdout)
 #define data(arg,num)      fwrite((arg), sizeof(char), (num), stdout)
 
 
-#ifdef __STDC__
-int main(int argc, char *argv[] )
-#else
-int main( argc, argv )
-    int argc;
-    char* argv[];
-#endif
-    {
-    FILE             *ifp;
-    /*hashinfo         colorhashtable[HASHSIZE];*/
-    struct hashinfo  *hashrun;
-    pixel            *xP;
-    int              argn;
-    bool             dpi300;
-    int              cols, rows, format, col, row;
-    int              sharpness, enlarge, copy, tiny;
-    pixval           maxval;
-    struct mediasize medias;
-    char             media[16];
-    const char * const usage = "[-sharpness <1-4>] [-enlarge <1-3>] [-media <a,a4,as,a4s>] [-copy <1-9>] [-tiny] [-dpi300] [ppmfile]";
-
-    ppm_init(&argc, argv);
-
-    dpi300 = FALSE;
-    argn = 1;
-    sharpness = 32;
-    enlarge   = 1;
-    copy      = 1;
-    memset(media, '\0', 16);
-    tiny      = FALSE;
-
-    /* check for flags */
-    while (argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0') {
-    if (pm_keymatch(argv[argn], "-sharpness", 2)) {
-        ++argn;
-        if (argn == argc || sscanf(argv[argn], "%d", &sharpness) != 1)
-            pm_usage(usage);
-        else if (sharpness < 1 || sharpness > 4)
-            pm_usage(usage);
-        }
-    else if (pm_keymatch(argv[argn], "-enlarge", 2)) {
-        ++argn;
-        if (argn == argc || sscanf(argv[argn], "%d", &enlarge) != 1)
-            pm_usage(usage);
-        else if (enlarge < 1 || enlarge > 3)
-            pm_usage(usage);
-        }
-    else if (pm_keymatch(argv[argn], "-media", 2)) {
-        ++argn;
-        if (argn == argc || sscanf(argv[argn], "%15s", media) < 1)
-            pm_usage(usage);
-        else if (TOUPPER(media[0]) != 'A')
-            pm_usage(usage);
-    }
-    else if (pm_keymatch(argv[argn], "-copy", 2)) {
-        ++argn;
-        if (argn == argc || sscanf(argv[argn], "%d", &copy) != 1)
-            pm_usage(usage);
-        else if (copy < 1 || copy > 9)
-            pm_usage(usage);
-        }
-    else if (pm_keymatch(argv[argn], "-dpi300", 2))
-        dpi300 = TRUE;
-    else if (pm_keymatch(argv[argn], "-tiny", 2))
-        tiny = TRUE;
-    else
-        pm_usage(usage);
-        ++argn;
-    }
-
-    if (argn < argc) {
-        ifp = pm_openr(argv[argn]);
-        ++argn;
-    }
-    else
-        ifp = stdin;
-
-    if (argn != argc)
-        pm_usage(usage);
 
-    if (TOUPPER(media[0]) == 'A')
-        switch (TOUPPER(media[1])) {
-        case 'S':
-            medias = MSize_AS;
-            break;
-        case '4':
-            if(TOUPPER(media[2]) == 'S')
-                medias = MSize_A4S;
-            else {
-                medias = MSize_A4;
-            }
-            break;
-        default:
-            medias = MSize_A;
-        }
-    else
-        medias = MSize_User;
-
-        if (dpi300) {
-                medias.maxcols *= 2;
-                medias.maxrows *= 2;
-        }
+static void
+check_and_rotate(int              const cols,
+                 int              const rows,
+                 int              const enlarge,
+                 struct mediasize const medias) {
 
-    if (tiny) {
-        pixel            *pixelrow;
-        char             *redrow, *greenrow, *bluerow;
-
-        ppm_readppminit(ifp, &cols, &rows, &maxval, &format);
-        pixelrow = (pixel *) ppm_allocrow(cols);
-        MALLOCARRAY_NOFAIL(redrow, cols);
-        MALLOCARRAY_NOFAIL(greenrow, cols);
-        MALLOCARRAY_NOFAIL(bluerow, cols);
-        lineputinit(cols, rows, sharpness, enlarge, copy, medias);
-
-        for ( row = 0; row < rows; ++row ) {
-            ppm_readppmrow(ifp, pixelrow, cols, maxval, format);
-            switch(PPM_FORMAT_TYPE(format)) {
-            /* color */
-            case PPM_TYPE:
-                for (col = 0, xP = pixelrow; col < cols; col++, xP++) {
-                    /* First red. */
-                    redrow[col] = PPM_GETR(*xP);
-                    /* Then green. */
-                    greenrow[col] = PPM_GETG(*xP);
-                    /* And blue. */
-                    bluerow[col] = PPM_GETB(*xP);
-                }
-                data(redrow,   cols);
-                data(greenrow, cols);
-                data(bluerow,  cols);
-                break;
-            /* grayscale */
-            default:
-                for (col = 0, xP = pixelrow; col < cols; col++, xP++)
-                    bluerow[col] = PPM_GETB(*xP);
-                data(bluerow, cols);
-                data(bluerow, cols);
-                data(bluerow, cols);
-                break;
-            }
-        }
-        pm_close(ifp);
+    if (cols > rows) {
+        ROTATEIMG(DOROTATE);                        /* rotate image */
+        if (enlarge * rows > medias.maxcols || enlarge * cols > medias.maxrows)
+            pm_error("Image too large, MaxPixels = %u x %u",
+                     medias.maxrows, medias.maxcols);
+        HPIXELS(cols);
+        VPIXELS(rows);
+        HPIXELSOFF((medias.maxcols/enlarge - rows)/2);
+        VPIXELSOFF((medias.maxrows/enlarge - cols)/2);
+        pm_message("rotating image for output");
+    } else {
+        ROTATEIMG(DONTROTATE);
+        if (enlarge * rows > medias.maxrows || enlarge * cols > medias.maxcols)
+            pm_error("Image too large, MaxPixels = %u x %u",
+                     medias.maxrows, medias.maxcols);
+        HPIXELS(cols);
+        VPIXELS(rows);
+        HPIXELSOFF((medias.maxcols/enlarge - cols)/2);
+        VPIXELSOFF((medias.maxrows/enlarge - rows)/2);
     }
-    else {
-        pixel            **pixelpic;
-        int              colanz, colval;
-        int                 i;
-        colorhist_vector table;
-
-        ppm_readppminit( ifp, &cols, &rows, &maxval, &format );
-        pixelpic = ppm_allocarray( cols, rows );
-        for (row = 0; row < rows; row++)
-            ppm_readppmrow( ifp, pixelpic[row], cols, maxval, format );
-        pm_close(ifp);
-
-        /* first check wether we can use the lut transfer */
+}
 
-        table = ppm_computecolorhist(pixelpic, cols, rows, MAXLUTCOL+1, 
-                                     &colanz);
-        if (table != NULL) {
-            hashinfo *colorhashtable;
 
-            MALLOCARRAY_NOFAIL(colorhashtable, HASHSIZE);
-            for (i=0; i<HASHSIZE; i++) {
-                colorhashtable[i].flag = -1;
-                colorhashtable[i].next = NULL;
-            }
 
-            /* we can use the lookuptable */
-            pm_message("found %d colors - using the lookuptable-method",
-                       colanz);
-            lookuptableinit(sharpness, enlarge, copy, medias);
-            switch(PPM_FORMAT_TYPE(format)) {
-            /* color */
-            case PPM_TYPE:
-                for (colval=0; colval<colanz; colval++) {
-                    cmd('$');
-                    datum(colval);
-                    datum(PPM_GETR((table[colval]).color));
-                    datum(PPM_GETG((table[colval]).color));
-                    datum(PPM_GETB((table[colval]).color));
-
-                    hashrun = &colorhashtable[myhash((table[colval]).color)];
-                    if (hashrun->flag == -1) {
-                        hashrun->color = (table[colval]).color;
-                        hashrun->flag  = colval;
-                    }
-                    else {
-                        while (hashrun->next != NULL)
-                            hashrun = hashrun->next;
-                        MALLOCVAR_NOFAIL(hashrun->next);
-                        hashrun = hashrun->next;
-                        hashrun->color = (table[colval]).color;
-                        hashrun->flag  = colval;
-                        hashrun->next  = NULL;
-                    }
-                }
-                break;
-            /* other */
-            default:
-                for (colval=0; colval<colanz; colval++) {
-                    cmd('$');
-                    datum(colval);
-                    datum(PPM_GETB((table[colval]).color));
-                    datum(PPM_GETB((table[colval]).color));
-                    datum(PPM_GETB((table[colval]).color));
-
-                    hashrun = &colorhashtable[myhash((table[colval]).color)];
-                    if (hashrun->flag == -1) {
-                        hashrun->color = (table[colval]).color;
-                        hashrun->flag  = colval;
-                    }
-                    else {
-                        while (hashrun->next != NULL)
-                            hashrun = hashrun->next;
-                        MALLOCVAR_NOFAIL(hashrun->next);
-                        hashrun = hashrun->next;
-                        hashrun->color = (table[colval]).color;
-                        hashrun->flag  = colval;
-                        hashrun->next  = NULL;
-                    }
-                }
-            }
-            lookuptabledata(cols, rows, enlarge, medias);
-            for (row=0; row<rows; row++) {
-                xP = pixelpic[row];
-                for (col=0; col<cols; col++, xP++) {
-                    hashrun = &colorhashtable[myhash(*xP)];
-                    while (!PPM_EQUAL((hashrun->color), *xP))
-                        if (hashrun->next != NULL)
-                            hashrun = hashrun->next;
-                        else {
-                            pm_error("you just found a lethal bug.");
-                        }
-                    datum(hashrun->flag);
-                }
-            }
-            free(colorhashtable);
-        }
-        else {
-        /* $#%@^!& no lut possible, so send the pic as 24bit */
-            pm_message("found too many colors for fast lookuptable mode");
-            frametransferinit(cols, rows, sharpness, enlarge, copy, medias);
-            switch(PPM_FORMAT_TYPE(format)) {
-            /* color */
-            case PPM_TYPE:
-                COLORDES(RED);
-                DATASTART;                    /* red coming */
-                for (row=0; row<rows; row++) {
-                    xP = pixelpic[row];
-                    for (col=0; col<cols; col++, xP++)
-                        datum(PPM_GETR(*xP));
-                }
-                COLORDES(GREEN);
-                DATASTART;                    /* green coming */
-                for (row=0; row<rows; row++) {
-                    xP = pixelpic[row];
-                    for (col=0; col<cols; col++, xP++)
-                        datum(PPM_GETG(*xP));
-                }
-                COLORDES(BLUE);
-                DATASTART;                    /* blue coming */
-                for (row=0; row<rows; row++) {
-                    xP = pixelpic[row];
-                    for (col=0; col<cols; col++, xP++)
-                        datum(PPM_GETB(*xP));
-                }
-                break;
-            /* grayscale */
-            default:
-                COLORDES(RED);
-                DATASTART;                    /* red coming */
-                for (row=0; row<rows; row++) {
-                    xP = pixelpic[row];
-                    for (col=0; col<cols; col++, xP++)
-                        datum(PPM_GETB(*xP));
-                }
-                COLORDES(GREEN);
-                DATASTART;                    /* green coming */
-                for (row=0; row<rows; row++) {
-                    xP = pixelpic[row];
-                    for (col=0; col<cols; col++, xP++)
-                        datum(PPM_GETB(*xP));
-                }
-                COLORDES(BLUE);
-                DATASTART;                    /* blue coming */
-                for (row=0; row<rows; row++) {
-                    xP = pixelpic[row];
-                    for (col=0; col<cols; col++, xP++)
-                        datum(PPM_GETB(*xP));
-                }
-            }
-        }
-    }
-    PRINTIT;
-    exit(0);
-}
-
-#ifdef __STDC__
-static void lineputinit(int cols, int rows,
-                        int sharpness, int enlarge, int copy,
-                        struct mediasize medias)
-#else /*__STDC__*/
-static int lineputinit(cols, rows, sharpness, enlarge, copy, medias)
-    int cols, rows;
-    int sharpness, enlarge, copy;
-    struct mediasize medias;
-#endif /*__STDC__*/
-{
+static void
+lineputinit(int              const cols,
+            int              const rows,
+            int              const sharpness,
+            int              const enlarge,
+            int              const copy,
+            struct mediasize const medias) {
     ONLINE;
     CLRMEM;
     MEDIASIZE(medias);
@@ -426,18 +134,16 @@ static int lineputinit(cols, rows, sharpness, enlarge, copy, medias)
     }
     check_and_rotate(cols, rows, enlarge, medias);
     DATASTART;
-    return;
 }
 
-#ifdef __STDC__
-static void lookuptableinit(int sharpness, int enlarge, int copy,
-                            struct mediasize medias)
-#else /*__STDC__*/
-static int lookuptableinit(sharpness, enlarge, copy, medias)
-    int sharpness, enlarge, copy;
-    struct mediasize medias;
-#endif /*__STDC__*/
-{
+
+
+static void
+lookuptableinit(int              const sharpness,
+                int              const enlarge,
+                int              const copy,
+                struct mediasize const medias) {
+
     ONLINE;
     CLRMEM;
     MEDIASIZE(medias);
@@ -486,36 +192,31 @@ static int lookuptableinit(sharpness, enlarge, copy, medias)
     }
 
     LOADLOOKUPTABLE;
-    return;
 }
 
-#ifdef __STDC__
-static void lookuptabledata(int cols, int rows, int enlarge,
-                                                        struct mediasize medias)
-#else /*__STDC__*/
-static int lookuptabledata(cols, rows, enlarge, medias)
-    int   rows, cols;
-    int   enlarge;
-    struct mediasize medias;
-#endif /*__STDC__*/
-{
+
+
+static void
+lookuptabledata(int              const cols,
+                int              const rows,
+                int              const enlarge,
+                struct mediasize const medias) {
+
     DONELOOKUPTABLE;
     check_and_rotate(cols, rows, enlarge, medias);
     DATASTART;
-    return;
 }
 
-#ifdef __STDC__
-static void frametransferinit(int cols, int rows, int sharpness,
-                              int enlarge, int copy, struct mediasize medias)
-#else
-static int frametransferinit(cols, rows, sharpness, enlarge, copy, medias)
 
-    int     rows, cols;
-    int     sharpness, enlarge, copy;
-    struct mediasize medias;
-#endif
-{
+
+static void
+frametransferinit(int              const cols,
+                  int              const rows,
+                  int              const sharpness,
+                  int              const enlarge,
+                  int              const copy,
+                  struct mediasize const medias) {
+
     ONLINE;
     CLRMEM;
     MEDIASIZE(medias);
@@ -565,40 +266,458 @@ static int frametransferinit(cols, rows, sharpness, enlarge, copy, medias)
         SHARPNESS(SP_USER);
     }
     check_and_rotate(cols, rows, enlarge, medias);
-    return;
 }
 
 
-#ifdef __STDC__
+
 static void
-check_and_rotate(int cols, int rows, int enlarge, struct mediasize medias)
-#else
-static int
-check_and_rotate(cols, rows, enlarge, medias)
-    int cols, rows, enlarge;
-    struct mediasize medias;
-#endif
-{
-    if (cols > rows) {
-        ROTATEIMG(DOROTATE);                        /* rotate image */
-        if (enlarge*rows > medias.maxcols || enlarge*cols > medias.maxrows) {
-            pm_error("Image too large, MaxPixels = %d x %d", medias.maxrows, medias.maxcols);
+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 =
+            &colorhashtable[myhash((table[colval]).color)];
+
+        struct hashinfo * hashrun;
+
+        cmd('$');
+        datum(colval);
+        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) {
+            hashrun->color = (table[colval]).color;
+            hashrun->flag  = colval;
+        } else {
+            while (hashrun->next != NULL)
+                hashrun = hashrun->next;
+            MALLOCVAR_NOFAIL(hashrun->next);
+            hashrun = hashrun->next;
+            hashrun->color = (table[colval]).color;
+            hashrun->flag  = colval;
+            hashrun->next  = NULL;
         }
-        HPIXELS(cols);
-        VPIXELS(rows);
-        HPIXELSOFF((medias.maxcols/enlarge - rows)/2);
-        VPIXELSOFF((medias.maxrows/enlarge - cols)/2);
-        pm_message("rotating image for output");
     }
-    else {
-        ROTATEIMG(DONTROTATE);
-        if (enlarge*rows > medias.maxrows || enlarge*cols > medias.maxcols) {
-            pm_error("Image too large, MaxPixels = %d x %d", medias.maxrows, medias.maxcols);
+}
+
+
+
+static void
+doLookupTableGrays(colorhist_vector const table,
+                   unsigned int     const nColor,
+                   hashinfo *       const colorhashtable) {
+
+    unsigned int colval;
+    for (colval = 0; colval < nColor; ++colval) {
+        struct hashinfo * const hashchain =
+            &colorhashtable[myhash((table[colval]).color)];
+        struct hashinfo * hashrun;
+
+        cmd('$');
+        datum(colval);
+        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) {
+            hashrun->color = (table[colval]).color;
+            hashrun->flag  = colval;
+        } else {
+            while (hashrun->next != NULL)
+                hashrun = hashrun->next;
+            MALLOCVAR_NOFAIL(hashrun->next);
+            hashrun = hashrun->next;
+            hashrun->color = (table[colval]).color;
+            hashrun->flag  = colval;
+            hashrun->next  = NULL;
         }
-        HPIXELS(cols);
-        VPIXELS(rows);
-        HPIXELSOFF((medias.maxcols/enlarge - cols)/2);
-        VPIXELSOFF((medias.maxrows/enlarge - rows)/2);
     }
 }
 
+
+
+static void
+generateLookupTable(colorhist_vector const table,
+                    unsigned int     const nColor,
+                    unsigned int     const cols,
+                    unsigned int     const rows,
+                    int              const format,
+                    int              const sharpness,
+                    int              const enlarge,
+                    int              const copy,
+                    struct mediasize const medias,
+                    hashinfo **      const colorhashtableP) {
+/*----------------------------------------------------------------------------
+   Write to the output file the palette (color lookup table) indicated by
+   'table' and generate a hash table to use with it: *colorhashtableP.
+
+   Also write the various properties 'sharpness', 'enlarge', 'copy', and
+   'medias' to the output file.
+-----------------------------------------------------------------------------*/
+    hashinfo * colorhashtable;
+
+    lookuptableinit(sharpness, enlarge, copy, medias);
+
+    /* Initialize the hash table to empty */
+
+    MALLOCARRAY_NOFAIL(colorhashtable, HASHSIZE);
+    {
+        unsigned int i;
+        for (i = 0; i < HASHSIZE; ++i) {
+            colorhashtable[i].flag = -1;
+                    colorhashtable[i].next = NULL;
+        }
+    }
+
+    switch(PPM_FORMAT_TYPE(format)) {
+    case PPM_TYPE:
+        doLookupTableColors(table, nColor, colorhashtable);
+        break;
+    default:
+        doLookupTableGrays(table, nColor, colorhashtable);
+    }
+    lookuptabledata(cols, rows, enlarge, medias);
+
+    *colorhashtableP = colorhashtable;
+}
+
+
+
+static void
+writeColormapRaster(pixel **         const pixels,
+                    unsigned int     const cols,
+                    unsigned int     const rows,
+                    hashinfo *       const colorhashtable) {
+/*----------------------------------------------------------------------------
+   Write a colormapped raster: write the pixels pixels[][] (dimensions cols x
+   rows) as indices into the colormap (palette; lookup table) indicated by
+   'colorhashtable'.
+-----------------------------------------------------------------------------*/
+    unsigned int row;
+
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+
+        for (col = 0; col < cols; ++col) {
+            pixel * const pixrow = pixels[row];
+            struct hashinfo * const hashchain =
+                &colorhashtable[myhash(pixrow[col])];
+            struct hashinfo * p;
+                
+            p = hashchain;
+            while (!PPM_EQUAL((p->color), pixrow[col])) {
+                assert(p->next);
+                p = p->next;
+            }
+            datum(p->flag);
+        }
+    }
+}
+
+
+
+static void
+useLookupTable(pixel **         const pixels,
+               colorhist_vector const table,
+               int              const sharpness,
+               int              const enlarge,
+               int              const copy,
+               struct mediasize const medias,
+               unsigned int     const cols,
+               unsigned int     const rows,
+               int              const format,
+               unsigned int     const nColor) {
+
+    hashinfo * colorhashtable;
+
+    pm_message("found %u colors - using the lookuptable-method", nColor);
+
+    generateLookupTable(table, nColor, cols, rows, format,
+                        sharpness, enlarge, copy, medias,
+                        &colorhashtable);
+
+    writeColormapRaster(pixels, cols, rows, colorhashtable);
+
+    free(colorhashtable);
+}
+
+
+
+static void
+noLookupColor(pixel **     const pixels,
+              unsigned int const cols,
+              unsigned int const rows) {
+
+    unsigned int row;
+    COLORDES(RED);
+    DATASTART;                    /* red coming */
+    for (row = 0; row < rows; ++row) {
+        pixel * const pixrow = pixels[row];
+        unsigned int col;
+        for (col = 0; col < cols; ++col)
+            datum(PPM_GETR(pixrow[col]));
+    }
+    COLORDES(GREEN);
+    DATASTART;                    /* green coming */
+    for (row = 0; row < rows; ++row) {
+        pixel * const pixrow = pixels[row];
+        unsigned int col;
+        for (col = 0; col < cols; ++col)
+            datum(PPM_GETG(pixrow[col]));
+    }
+    COLORDES(BLUE);
+    DATASTART;                    /* blue coming */
+    for (row = 0; row < rows; ++row) {
+        pixel * const pixrow = pixels[row];
+        unsigned int col;
+        for (col = 0; col < cols; ++col)
+            datum(PPM_GETB(pixrow[col]));
+    }
+}
+
+
+
+static void
+noLookupGray(pixel **     const pixels,
+             unsigned int const cols,
+             unsigned int const rows) {
+
+    unsigned int row;
+    COLORDES(RED);
+    DATASTART;                    /* red coming */
+    for (row = 0; row < rows; ++row) {
+        pixel * const pixrow = pixels[row];
+        unsigned int col;
+        for (col = 0; col < cols; ++col)
+            datum(PPM_GETB(pixrow[col]));
+    }
+    COLORDES(GREEN);
+    DATASTART;                    /* green coming */
+    for (row = 0; row < rows; ++row) {
+        pixel * const pixrow = pixels[row];
+        unsigned int col;
+        for (col = 0; col < cols; ++col)
+            datum(PPM_GETB(pixrow[col]));
+    }
+    COLORDES(BLUE);
+    DATASTART;                    /* blue coming */
+    for (row = 0; row < rows; ++row) {
+        pixel * const pixrow = pixels[row];
+        unsigned int col;
+        for (col = 0; col < cols; ++col)
+            datum(PPM_GETB(pixrow[col]));
+    }
+}
+
+
+
+static void
+useNoLookupTable(pixel **         const pixels,
+                 int              const sharpness,
+                 int              const enlarge,
+                 int              const copy,
+                 struct mediasize const medias,
+                 unsigned int     const cols,
+                 unsigned int     const rows,
+                 int              const format) {
+
+    /* $#%@^!& no lut possible, so send the pic as 24bit */
+
+    pm_message("found too many colors for fast lookuptable mode");
+
+    frametransferinit(cols, rows, sharpness, enlarge, copy, medias);
+    switch(PPM_FORMAT_TYPE(format)) {
+    case PPM_TYPE:
+        noLookupColor(pixels, cols, rows);
+        break;
+    default:
+        noLookupGray(pixels, cols, rows);
+    }
+}
+
+
+
+static void
+doTiny(FILE *           const ifP,
+       unsigned int     const cols,
+       unsigned int     const rows,
+       pixval           const maxval,
+       int              const format,
+       int              const sharpness,
+       int              const enlarge,
+       int              const copy,
+       struct mediasize const medias) {
+       
+    pixel * pixelrow;
+    unsigned char * redrow;
+    unsigned char * grnrow;
+    unsigned char * blurow;
+    unsigned int row;
+
+    pixelrow = ppm_allocrow(cols);
+    MALLOCARRAY_NOFAIL(redrow, cols);
+    MALLOCARRAY_NOFAIL(grnrow, cols);
+    MALLOCARRAY_NOFAIL(blurow, cols);
+    lineputinit(cols, rows, sharpness, enlarge, copy, medias);
+
+    for (row = 0; row < rows; ++row) {
+        ppm_readppmrow(ifP, pixelrow, cols, maxval, format);
+        switch(PPM_FORMAT_TYPE(format)) {
+        case PPM_TYPE: {            /* color */
+            unsigned int col;
+            for (col = 0; col < cols; ++col) {
+                redrow[col] = PPM_GETR(pixelrow[col]);
+                grnrow[col] = PPM_GETG(pixelrow[col]);
+                blurow[col] = PPM_GETB(pixelrow[col]);
+            }
+            data(redrow, cols);
+            data(grnrow, cols);
+            data(blurow, cols);
+        } break;
+        default: {           /* grayscale */
+            unsigned int col;
+            for (col = 0; col < cols; ++col)
+                blurow[col] = PPM_GETB(pixelrow[col]);
+            data(blurow, cols);
+            data(blurow, cols);
+            data(blurow, cols);
+        }
+        }
+    }
+}
+
+
+
+int
+main(int argc, char * argv[]) {
+    FILE * ifP;
+    int              argn;
+    bool             dpi300;
+    int              cols, rows, format;
+    pixval           maxval;
+    int              sharpness, enlarge, copy, tiny;
+    struct mediasize medias;
+    char             media[16];
+    const char * const usage = "[-sharpness <1-4>] [-enlarge <1-3>] [-media <a,a4,as,a4s>] [-copy <1-9>] [-tiny] [-dpi300] [ppmfile]";
+
+    ppm_init(&argc, argv);
+
+    dpi300 = FALSE;
+    argn = 1;
+    sharpness = 32;
+    enlarge   = 1;
+    copy      = 1;
+    memset(media, '\0', 16);
+    tiny      = FALSE;
+
+    /* check for flags */
+    while (argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0') {
+    if (pm_keymatch(argv[argn], "-sharpness", 2)) {
+        ++argn;
+        if (argn == argc || sscanf(argv[argn], "%d", &sharpness) != 1)
+            pm_usage(usage);
+        else if (sharpness < 1 || sharpness > 4)
+            pm_usage(usage);
+        }
+    else if (pm_keymatch(argv[argn], "-enlarge", 2)) {
+        ++argn;
+        if (argn == argc || sscanf(argv[argn], "%d", &enlarge) != 1)
+            pm_usage(usage);
+        else if (enlarge < 1 || enlarge > 3)
+            pm_usage(usage);
+        }
+    else if (pm_keymatch(argv[argn], "-media", 2)) {
+        ++argn;
+        if (argn == argc || sscanf(argv[argn], "%15s", media) < 1)
+            pm_usage(usage);
+        else if (TOUPPER(media[0]) != 'A')
+            pm_usage(usage);
+    }
+    else if (pm_keymatch(argv[argn], "-copy", 2)) {
+        ++argn;
+        if (argn == argc || sscanf(argv[argn], "%d", &copy) != 1)
+            pm_usage(usage);
+        else if (copy < 1 || copy > 9)
+            pm_usage(usage);
+        }
+    else if (pm_keymatch(argv[argn], "-dpi300", 2))
+        dpi300 = TRUE;
+    else if (pm_keymatch(argv[argn], "-tiny", 2))
+        tiny = TRUE;
+    else
+        pm_usage(usage);
+        ++argn;
+    }
+
+    if (argn < argc) {
+        ifP = pm_openr(argv[argn]);
+        ++argn;
+    }
+    else
+        ifP = stdin;
+
+    if (argn != argc)
+        pm_usage(usage);
+
+    if (TOUPPER(media[0]) == 'A')
+        switch (TOUPPER(media[1])) {
+        case 'S':
+            medias = MSize_AS;
+            break;
+        case '4':
+            if(TOUPPER(media[2]) == 'S')
+                medias = MSize_A4S;
+            else {
+                medias = MSize_A4;
+            }
+            break;
+        default:
+            medias = MSize_A;
+        }
+    else
+        medias = MSize_User;
+
+    if (dpi300) {
+        medias.maxcols *= 2;
+        medias.maxrows *= 2;
+    }
+
+    ppm_readppminit(ifP, &cols, &rows, &maxval, &format);
+    
+    if (tiny) {
+        doTiny(ifP, cols, rows, maxval, format,
+               sharpness, enlarge, copy, medias);
+
+    } else {
+        pixel ** pixels;
+        int nColor;
+        colorhist_vector table;
+        unsigned int row;
+
+        pixels = ppm_allocarray(cols, rows);
+        for (row = 0; row < rows; ++row)
+            ppm_readppmrow(ifP, pixels[row], cols, maxval, format);
+
+        /* first check wether we can use the lut transfer */
+
+        table = ppm_computecolorhist(pixels, cols, rows, MAXLUTCOL+1, 
+                                     &nColor);
+        if (table)
+            useLookupTable(pixels, table, sharpness, enlarge, copy, medias,
+                           cols, rows, format, nColor);
+        else
+            useNoLookupTable(pixels, sharpness, enlarge, copy, medias,
+                             cols, rows, format);
+        ppm_freearray(pixels, rows);
+    }
+    PRINTIT;
+    pm_close(ifP);
+    return 0;
+}
diff --git a/converter/ppm/ppmtomitsu.test b/converter/ppm/ppmtomitsu.test
new file mode 100644
index 00000000..f574d927
--- /dev/null
+++ b/converter/ppm/ppmtomitsu.test
@@ -0,0 +1,12 @@
+echo Test 1.  Should print 3110813682 101562
+./ppmtomitsu ../../testimg.ppm | cksum 
+echo Test 2.  Should print 239186803 34399
+pnmquant 100 ../../testimg.ppm | ./ppmtomitsu | cksum 
+echo Test 3.  Should print 3201293405 310
+./ppmtomitsu ../../testgrid.pbm | cksum 
+echo Test 4.  Should print 3354679572 752
+./ppmtomitsu -tiny ../../testgrid.pbm | cksum 
+echo Test 5.  Should print 3999654426 101549
+./ppmtomitsu -tiny ../../testimg.ppm | cksum 
+echo Test 2.  Should print 3078685526 101549
+pnmquant 100 ../../testimg.ppm | ./ppmtomitsu -tiny | cksum 
diff --git a/converter/ppm/ppmtompeg/Makefile b/converter/ppm/ppmtompeg/Makefile
index 4e0ad8d6..a1004fdd 100644
--- a/converter/ppm/ppmtompeg/Makefile
+++ b/converter/ppm/ppmtompeg/Makefile
@@ -5,7 +5,7 @@ endif
 SUBDIR = converter/ppm/ppmtompeg
 VPATH=.:$(SRCDIR)/$(SUBDIR)
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
 ifeq ($(JPEGLIB),NONE)
   # 'nojpeg' is a module that implements all the jpeg access routines as
@@ -18,11 +18,12 @@ else
   JPEGLIBX = $(JPEGLIB)
 endif
 
-INCLUDES = -I$(SRCDIR)/$(SUBDIR)/headers 
+COMP_INCLUDES = -I$(SRCDIR)/$(SUBDIR)/headers 
 
+EXTERN_INCLUDES =
 ifneq ($(JPEGHDR_DIR),NONE)
   ifneq ($(JPEGHDR_DIR)x,x)
-    INCLUDES += -I$(JPEGHDR_DIR)
+    EXTERN_INCLUDES += -I$(JPEGHDR_DIR)
   endif
 endif
 
@@ -38,17 +39,31 @@ endif
 #
 
 MP_BASE_OBJS = mfwddct.o postdct.o huff.o bitio.o mheaders.o
-MP_ENCODE_OBJS = iframe.o pframe.o bframe.o psearch.o bsearch.o block.o 
+MP_ENCODE_OBJS = \
+  frames.o \
+  iframe.o \
+  pframe.o \
+  bframe.o \
+  psearch.o \
+  bsearch.o \
+  block.o 
+
 MP_OTHER_OBJS = mpeg.o subsample.o param.o rgbtoycc.o \
 	readframe.o combine.o jrevdct.o frame.o fsize.o frametype.o \
-	specifics.o rate.o opts.o input.o gethostname.o
+	specifics.o rate.o opts.o input.o
 ifeq ($(OMIT_NETWORK),y)
-  MP_PARALLEL_OBJS = noparallel.o
+  MP_OTHER_OBJS += noparallel.o
+else
+  MP_OTHER_OBJS += parallel.o psocket.o
+endif
+ifeq ($(MSVCRT),Y)
+  MP_OTHER_OBJS += gethostname_win32.o
 else
-  MP_PARALLEL_OBJS = parallel.o psocket.o
+  MP_OTHER_OBJS += gethostname.o
 endif
+
 NONMAIN_OBJS = $(MP_BASE_OBJS) $(MP_OTHER_OBJS) $(MP_ENCODE_OBJS) \
-	      $(MP_PARALLEL_OBJS) $(JPEG_MODULE).o
+	      	$(JPEG_MODULE).o
 OBJECTS = ppmtompeg.o $(NONMAIN_OBJS)
 MERGE_OBJECTS = ppmtompeg.o2 $(NONMAIN_OBJS)
 MP_INCLUDE = mproto.h mtypes.h huff.h bitio.h
@@ -61,7 +76,7 @@ SCRIPTS =
 .PHONY: all
 all: ppmtompeg
 
-include $(SRCDIR)/Makefile.common
+include $(SRCDIR)/common.mk
 
 ifeq ($(NEED_RUNTIME_PATH),Y)
   LIBOPTR = -runtime
diff --git a/converter/ppm/ppmtompeg/bframe.c b/converter/ppm/ppmtompeg/bframe.c
index 5dfb76d3..1dbc1846 100644
--- a/converter/ppm/ppmtompeg/bframe.c
+++ b/converter/ppm/ppmtompeg/bframe.c
@@ -84,7 +84,6 @@ static struct bframeStats {
  *====================*/
 
 extern Block **dct, **dctr, **dctb;
-extern dct_data_type **dct_data;
 #define NO_MOTION 0
 #define MOTION 1
 #define SKIP 2  /* used in useMotion in dct_data */
diff --git a/converter/ppm/ppmtompeg/bsearch.c b/converter/ppm/ppmtompeg/bsearch.c
index 142987f5..70edfef6 100644
--- a/converter/ppm/ppmtompeg/bsearch.c
+++ b/converter/ppm/ppmtompeg/bsearch.c
@@ -77,6 +77,7 @@
 #include "frames.h"
 #include "motion_search.h"
 #include "fsize.h"
+#include "block.h"
 
 
 /*==================*
diff --git a/converter/ppm/ppmtompeg/combine.c b/converter/ppm/ppmtompeg/combine.c
index 52cc646d..8e0d3281 100644
--- a/converter/ppm/ppmtompeg/combine.c
+++ b/converter/ppm/ppmtompeg/combine.c
@@ -35,8 +35,8 @@
 #include <errno.h>
 #include <unistd.h>
 
-#include "ppm.h"
 #include "nstring.h"
+#include "nsleep.h"
 
 #include "mtypes.h"
 #include "frames.h"
@@ -111,11 +111,9 @@ appendSpecifiedGopFiles(struct inputSource * const inputSourceP,
              ++nAttempts) {
 
             ifP = fopen(fileName, "rb");
-            if (ifP == NULL) {
+            if (ifP == NULL)
                 pm_message("ERROR:  Couldn't read file '%s'.  retry %u", 
                            fileName, nAttempts);
-                sleep(1);
-            }
         }
         if (ifP) {
             if (!realQuiet)
diff --git a/converter/ppm/ppmtompeg/file.c b/converter/ppm/ppmtompeg/file.c
index ae741962..223170a4 100644
--- a/converter/ppm/ppmtompeg/file.c
+++ b/converter/ppm/ppmtompeg/file.c
@@ -19,17 +19,6 @@
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  */
 
-/*  
- *  $Header: /n/picasso/project/mm/mpeg/mpeg_dist/mpeg_encode/RCS/file.c,v 1.2 1993/06/30 20:06:09 keving Exp $
- *  $Log: file.c,v $
- * Revision 1.2  1993/06/30  20:06:09  keving
- * nothing
- *
- * Revision 1.1  1993/06/03  21:08:08  keving
- * nothing
- *
- */
-
 #include "tk.h"
 
 #include "all.h"
@@ -40,11 +29,10 @@
 #include <time.h>
 #include <string.h>
 #include <dirent.h>
-#include <strings.h>
 
 #define MAX_FILES   1000
-#define MAX_NAME_LEN	256
-#define MAX_STRING_LEN	MAX_NAME_LEN
+#define MAX_NAME_LEN    256
+#define MAX_STRING_LEN  MAX_NAME_LEN
 
 typedef int boolean;
 #define TRUE 1
@@ -52,77 +40,76 @@ typedef int boolean;
 
 extern char currentPath[MAXPATHLEN];
 
-char	globString[1024];
+char globString[1024];
 
-static DIR *dfd;
+static DIR * dfd;
 
-void	ResetPath(void);
+void ResetPath(void);
 int ListDirectory(ClientData nulldata, Tcl_Interp *interp, int argc,
-		  char **argv);
+                  char **argv);
 int ChangeDirectory(ClientData nulldata, Tcl_Interp *interp, int argc,
-		  char **argv);
-void	SortFiles(int numStrings, char strings[MAX_FILES][MAX_NAME_LEN],
-		  boolean *dirList, int permute[]);
+                    char **argv);
+void SortFiles(int numStrings, char strings[MAX_FILES][MAX_NAME_LEN],
+               boolean *dirList, int permute[]);
+
+static void UpdatePath(Tcl_Interp *interp, char *directory);
+static boolean MatchesGlob(char *string, char *glob);
 
-static void	UpdatePath(Tcl_Interp *interp, char *directory);
-static boolean	MatchesGlob(char *string, char *glob);
 
 
+void
+ResetPath() {
 
-void	ResetPath()
-{
-    if ( getwd(currentPath) == 0 )
-    {
-	fprintf(stderr, "Error getting pathname!!!\n");
-	exit(1);
+    if (getwd(currentPath) == 0) {
+        fprintf(stderr, "Error getting pathname!!!\n");
+        exit(1);
     }
 
     strcpy(&currentPath[strlen(currentPath)], "/");
 
     dfd = opendir(currentPath);
-    if ( dfd == NULL )
-    {
-	fprintf(stderr, "can't open '%s'\n", currentPath);
-	exit(1);
+    if (dfd == NULL) {
+        fprintf(stderr, "can't open '%s'\n", currentPath);
+        exit(1);
     }
 }
 
 
-static void	UpdatePath(Tcl_Interp *interp, char *directory)
-{
+
+static void
+UpdatePath(Tcl_Interp *interp, char *directory) {
+
     int length;
     char *charPtr;
 
     length = strlen(currentPath);
 
-    if ( strcmp(directory, "./") == 0 )
-	return /* nothing */ ;
-    else if ( strcmp(directory, "../") == 0 )
-    {
-	/* delete backwards up to '/' */
-
-	if ( length < 2 )
-	{
-	    fprintf(stderr, "Error:  backing up from root directory!!!\n");
-	    exit(1);
-	}
-
-	charPtr = &currentPath[length-2];
-	while ( (charPtr != currentPath) && (*charPtr != '/') )
-	    charPtr--;
-	charPtr++;	/* leave the '/' */
-	*charPtr = '\0';
-    }
-    else
-    {
-	strcpy(&currentPath[length], directory);
+    if (streq(directory, "./"))
+        return /* nothing */ ;
+    else if (streq(directory, "../")) {
+        /* delete backwards up to '/' */
+
+        if (length < 2) {
+            fprintf(stderr, "Error:  backing up from root directory!!!\n");
+            exit(1);
+        }
+
+        charPtr = &currentPath[length-2];
+        while ((charPtr != currentPath) && (*charPtr != '/'))
+            --charPtr;
+        ++charPtr;  /* leave the '/' */
+        *charPtr = '\0';
+    } else {
+        strcpy(&currentPath[length], directory);
     }
 }
 
 
-int ChangeDirectory(ClientData nulldata, Tcl_Interp *interp, int argc,
-		  char **argv)
-{
+
+int
+ChangeDirectory(ClientData nulldata, Tcl_Interp *interp, int argc,
+                char **argv) {
+
     char *directory = argv[1];
 
     UpdatePath(interp, directory);
@@ -130,82 +117,74 @@ int ChangeDirectory(ClientData nulldata, Tcl_Interp *interp, int argc,
     fprintf(stdout, "Opening directory: '%s'\n", currentPath);
 
     dfd = opendir(currentPath);
-    if ( dfd == NULL )
-    {
-	fprintf(stderr, "can't open '%s'\n", currentPath);
-	return TCL_OK;	/* shouldn't, really */
+    if (dfd == NULL) {
+        fprintf(stderr, "can't open '%s'\n", currentPath);
+        return TCL_OK;  /* shouldn't, really */
     }
-
+    
     return TCL_OK;
 }
 
 
-int ListDirectory(ClientData nulldata, Tcl_Interp *interp, int argc,
-		  char **argv)
-{
-    struct dirent *dp;
+
+int
+ListDirectory(ClientData nulldata, Tcl_Interp *interp, int argc,
+              char **argv) {
+
+    struct dirent * dp;
     struct stat stbuf;
     char command[256];
     char fileName[MAX_FILES][MAX_NAME_LEN];
     boolean dirList[MAX_FILES];
     int permute[MAX_FILES];
-    int	fileCount = 0;
-    register int index;
+    int fileCount = 0;
+    int index;
     char fullName[MAXPATHLEN];
-    char    *restPtr;
+    char * restPtr;
 
     sprintf(command, "ShowCurrentDirectory %s", currentPath);
     Tcl_Eval(interp, command, 0, (char **) NULL);
 
-    if ( dfd == NULL )
-    {
-	fprintf(stderr, "TRIED TO LIST NULL DIRECTORY\n");
+    if (dfd == NULL) {
+        fprintf(stderr, "TRIED TO LIST NULL DIRECTORY\n");
 
-	return TCL_OK;
+        return TCL_OK;
     }
 
-/* check if root directory */
-    if ( strlen(currentPath) != 1 )
-    {
-	sprintf(fileName[fileCount], "../");
-	dirList[fileCount] = TRUE;
-	fileCount++;
+    /* check if root directory */
+    if (strlen(currentPath) != 1) {
+        sprintf(fileName[fileCount], "../");
+        dirList[fileCount] = TRUE;
+        ++fileCount;
     }
 
     strcpy(fullName, currentPath);
     restPtr = &fullName[strlen(fullName)];
 
-    while ( (dp = readdir(dfd)) != NULL )
-    {
-	strcpy(restPtr, dp->d_name);
-	stat(fullName, &stbuf);
-
-	if ( dp->d_name[0] != '.' )
-	{
-	    if ( S_ISDIR(stbuf.st_mode) )
-	    {
-		sprintf(fileName[fileCount], "%s/", dp->d_name);
-		dirList[fileCount] = TRUE;
-		fileCount++;
-	    }
-	    else
-	    {
-		if ( MatchesGlob(dp->d_name, globString) )
-		{
-		    strcpy(fileName[fileCount], dp->d_name);
-		    dirList[fileCount] = FALSE;
-		    fileCount++;
-		}
-	    }
-	}
+    while ((dp = readdir(dfd)) != NULL) {
+        strcpy(restPtr, dp->d_name);
+        stat(fullName, &stbuf);
+        
+        if (dp->d_name[0] != '.') {
+            if (S_ISDIR(stbuf.st_mode)) {
+                sprintf(fileName[fileCount], "%s/", dp->d_name);
+                dirList[fileCount] = TRUE;
+                ++fileCount;
+            } else {
+                if (MatchesGlob(dp->d_name, globString)) {
+                    strcpy(fileName[fileCount], dp->d_name);
+                    dirList[fileCount] = FALSE;
+                    ++fileCount;
+                }
+            }
+        }
     }
 
     SortFiles(fileCount, fileName, dirList, permute);
 
-    for ( index = 0; index < fileCount; index++ )
-    {
-	sprintf(command, "AddBrowseFile %s", fileName[permute[index]]);
-	Tcl_Eval(interp, command, 0, (char **) NULL);
+    for (index = 0; index < fileCount; ++index) {
+        sprintf(command, "AddBrowseFile %s", fileName[permute[index]]);
+        Tcl_Eval(interp, command, 0, (char **) NULL);
     }
 
     closedir(dfd);
@@ -214,108 +193,110 @@ int ListDirectory(ClientData nulldata, Tcl_Interp *interp, int argc,
 }
 
 
-void	SortFiles(int numStrings, char strings[MAX_FILES][MAX_NAME_LEN],
-		  boolean *dirList, int permute[])
-{
-    register int i, j;
-    int temp;
-    int	numDirs;
-    int	ptr;
 
-    for ( i = 0; i < numStrings; i++ )
-	permute[i] = i;
+void
+SortFiles(int numStrings, char strings[MAX_FILES][MAX_NAME_LEN],
+          boolean *dirList, int permute[]) {
+
+    int i;
+    int numDirs;
+    int ptr;
+
+    for (i = 0; i < numStrings; ++i)
+        permute[i] = i;
 
     /* put all directories at front */
     numDirs = 0;
     ptr = numStrings-1;
-    while ( numDirs != ptr )
-    {
-	/* go past dirs */
-	while ( (numDirs < ptr) && (dirList[permute[numDirs]]) )
-	    numDirs++;
-
-	/* go past non-dirs */
-	while ( (numDirs < ptr) && (! dirList[permute[ptr]]) )
-	    ptr--;
-
-	if ( numDirs != ptr )
-	{
-	    temp = permute[numDirs];
-	    permute[numDirs] = ptr;
-	    permute[ptr] = temp;
-	}
+    while (numDirs != ptr) {
+        /* go past dirs */
+        while ((numDirs < ptr) && (dirList[permute[numDirs]]))
+            ++numDirs;
+
+        /* go past non-dirs */
+        while ((numDirs < ptr) && (! dirList[permute[ptr]]))
+            --ptr;
+
+        if (numDirs != ptr) {
+            int const temp = permute[numDirs];
+            permute[numDirs] = ptr;
+            permute[ptr] = temp;
+        }
     }
 
-    if ( dirList[permute[numDirs]] )
-	numDirs++;
-
-    for ( i = 0; i < numDirs; i++ )
-	for ( j = i+1; j < numDirs; j++ )
-	{
-	    if ( strcmp(&strings[permute[j]][0], &strings[permute[i]][0]) < 0 )
-	    {
-		temp = permute[j];
-		permute[j] = permute[i];
-		permute[i] = temp;
-	    }
-	}
-
-    for ( i = numDirs; i < numStrings; i++ )
-	for ( j = i+1; j < numStrings; j++ )
-	{
-	    if ( strcmp(&strings[permute[j]][0], &strings[permute[i]][0]) < 0 )
-	    {
-		temp = permute[j];
-		permute[j] = permute[i];
-		permute[i] = temp;
-	    }
-	}
+    if (dirList[permute[numDirs]])
+        ++numDirs;
+
+    for (i = 0; i < numDirs; ++i) {
+        int j;
+        for (j = i + 1; j < numDirs; ++j) {
+            if (strcmp(&strings[permute[j]][0], &strings[permute[i]][0]) < 0) {
+                int const temp = permute[j];
+                permute[j] = permute[i];
+                permute[i] = temp;
+            }
+        }
+    }
+    for (i = numDirs; i < numStrings; ++i) {
+        int j;
+        for (j = i + 1; j < numStrings; ++j) {
+            if (strcmp(&strings[permute[j]][0], &strings[permute[i]][0]) < 0) {
+                int const temp = permute[j];
+                permute[j] = permute[i];
+                permute[i] = temp;
+            }
+        }
+    }
 }
 
 
-int SetBrowseGlob (ClientData nulldata, Tcl_Interp *interp,
-		   int argc, char **argv)
-{
-    if (argc == 2 )
-    {
-	strcpy(globString, argv[1]);
 
-	fprintf(stdout, "GLOB:  %s\n", globString);
+int
+SetBrowseGlob(ClientData nulldata, Tcl_Interp *interp,
+              int argc, char **argv) {
+
+    if (argc == 2) {
+        strcpy(globString, argv[1]);
+
+        fprintf(stdout, "GLOB:  %s\n", globString);
 
-	return TCL_OK;
+        return TCL_OK;
     }
 
-	Tcl_AppendResult (interp, 
-            "wrong args: should be \"", argv[0]," string\"", (char *) NULL);
-	return TCL_ERROR;
+    Tcl_AppendResult(interp, 
+                     "wrong args: should be \"", argv[0]," string\"",
+                     NULL);
+    return TCL_ERROR;
 }
 
 
-static boolean	MatchesGlob(char *string, char *glob)
-{
-    char    *stringRight, *globRight;
 
-    while ( (*glob != '\0') && (*glob != '*') )	    /* match left side */
-    {
-	if ( (*string == '\0') || (*string != *glob) )
-	    return FALSE;
-	string++;
-	glob++;
+static boolean
+MatchesGlob(char *string, char *glob) {
+
+    char * stringRight;
+    char * globRight;
+
+    while ((*glob != '\0') && (*glob != '*')) {
+        /* match left side */
+        if ((*string == '\0') || (*string != *glob))
+            return FALSE;
+        ++string;
+        ++glob;
     }
 
-    if ( *glob == '\0' )	/* no star */
-	return TRUE;
+    if (*glob == '\0')    /* no star */
+        return TRUE;
 
     /* now match right side */
     stringRight = &string[strlen(string)-1];
     globRight = &glob[strlen(glob)-1];
 
-    while ( *globRight != '*' )
-    {
-	if ( (stringRight < string) || (*stringRight != *globRight) )
-	    return FALSE;
-	globRight--;
-	stringRight--;
+    while (*globRight != '*') {
+        if ((stringRight < string) || (*stringRight != *globRight))
+            return FALSE;
+        --globRight;
+        --stringRight;
     }
 
     return TRUE;
diff --git a/converter/ppm/ppmtompeg/frames.c b/converter/ppm/ppmtompeg/frames.c
new file mode 100644
index 00000000..a0764890
--- /dev/null
+++ b/converter/ppm/ppmtompeg/frames.c
@@ -0,0 +1,58 @@
+#include "mallocvar.h"
+#include "fsize.h"
+
+#include "frames.h"
+
+
+Block **dct=NULL, **dctr=NULL, **dctb=NULL;
+dct_data_type   **dct_data; /* used in p/bframe.c */
+
+
+/*===========================================================================*
+ *
+ * AllocDctBlocks
+ *
+ *  allocate memory for dct blocks
+ *
+ * RETURNS: nothing
+ *
+ * SIDE EFFECTS:    creates dct, dctr, dctb
+ *
+ *===========================================================================*/
+void
+AllocDctBlocks(void) {
+
+    int dctx, dcty;
+    int i;
+
+    dctx = Fsize_x / DCTSIZE;
+    dcty = Fsize_y / DCTSIZE;
+
+    MALLOCARRAY(dct, dcty);
+    ERRCHK(dct, "malloc");
+    for (i = 0; i < dcty; ++i) {
+        dct[i] = (Block *) malloc(sizeof(Block) * dctx);
+        ERRCHK(dct[i], "malloc");
+    }
+
+    MALLOCARRAY(dct_data, dcty);
+    ERRCHK(dct_data, "malloc");
+    for (i = 0; i < dcty; ++i) {
+        MALLOCARRAY(dct_data[i], dctx);
+        ERRCHK(dct[i], "malloc");
+    }
+
+    MALLOCARRAY(dctr, dcty/2);
+    ERRCHK(dctr, "malloc");
+    MALLOCARRAY(dctb, dcty/2);
+    ERRCHK(dctb, "malloc");
+    for (i = 0; i < dcty/2; ++i) {
+        MALLOCARRAY(dctr[i], dctx/2);
+        ERRCHK(dctr[i], "malloc");
+        MALLOCARRAY(dctb[i], dctx/2);
+        ERRCHK(dctb[i], "malloc");
+    }
+}
+
+
+
diff --git a/converter/ppm/ppmtompeg/gethostname_win32.c b/converter/ppm/ppmtompeg/gethostname_win32.c
new file mode 100644
index 00000000..383f4e55
--- /dev/null
+++ b/converter/ppm/ppmtompeg/gethostname_win32.c
@@ -0,0 +1,404 @@
+/* define this macro for activating debugging version */
+/* #define GETHOSTNAME_LOCAL_DEBUG     1*/
+
+#include <windows.h>
+#include <tchar.h>
+#include <stdarg.h>
+
+#ifndef GETHOSTNAME_LOCAL_DEBUG
+#include "pm.h"
+#include "gethostname.h"
+#endif
+
+#define BUFSIZE 80
+
+typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO);
+typedef BOOL (WINAPI *PGPI)(DWORD, DWORD, DWORD, DWORD, PDWORD);
+
+typedef struct {
+    char  str[256];
+    int   level;
+} push_string_t;
+
+static void
+pushString(push_string_t *p, const char *fmt, ...)
+{
+    va_list args;
+
+    va_start(args, fmt);
+    p->level += _vsnprintf(p->str + p->level, sizeof(p->str)-p->level, fmt, args);
+    va_end(args);
+}
+
+#if _WIN32_WINNT < 0x0600
+/*
+ * Reference available here:
+ *
+ * GetProductInfo() Function
+ * http://msdn2.microsoft.com/en-us/library/ms724358.aspx
+ */
+#define PRODUCT_BUSINESS                        0x00000006  /* Business Edition */
+#define PRODUCT_BUSINESS_N                      0x00000010  /* Business Edition */
+#define PRODUCT_CLUSTER_SERVER                  0x00000012  /* Cluster Server Edition */
+#define PRODUCT_DATACENTER_SERVER               0x00000008  /* Server Datacenter Edition (full installation) */
+#define PRODUCT_DATACENTER_SERVER_CORE          0x0000000C  /* Server Datacenter Edition (core installation) */
+#define PRODUCT_ENTERPRISE                      0x00000004  /* Enterprise Edition */
+#define PRODUCT_ENTERPRISE_N                    0x0000001B  /* Enterprise Edition */
+#define PRODUCT_ENTERPRISE_SERVER               0x0000000A  /* Server Enterprise Edition (full installation) */
+#define PRODUCT_ENTERPRISE_SERVER_CORE          0x0000000E  /* Server Enterprise Edition (core installation) */
+#define PRODUCT_ENTERPRISE_SERVER_IA64          0x0000000F  /* Server Enterprise Edition for Itanium-based Systems */
+#define PRODUCT_HOME_BASIC                      0x00000002  /* Home Basic Edition */
+#define PRODUCT_HOME_BASIC_N                    0x00000005  /* Home Basic Edition */
+#define PRODUCT_HOME_PREMIUM                    0x00000003  /* Home Premium Edition */
+#define PRODUCT_HOME_PREMIUM_N                  0x0000001A  /* Home Premium Edition */
+#define PRODUCT_HOME_SERVER                     0x00000013  /* Home Server Edition */
+#define PRODUCT_SERVER_FOR_SMALLBUSINESS        0x00000018  /* Server for Small Business Edition */
+#define PRODUCT_SMALLBUSINESS_SERVER            0x00000009  /* Small Business Server */
+#define PRODUCT_SMALLBUSINESS_SERVER_PREMIUM    0x00000019  /* Small Business Server Premium Edition */
+#define PRODUCT_STANDARD_SERVER                 0x00000007  /* Server Standard Edition (full installation) */
+#define PRODUCT_STANDARD_SERVER_CORE            0x0000000D  /* Server Standard Edition (core installation) */
+#define PRODUCT_STARTER                         0x0000000B  /* Starter Edition */
+#define PRODUCT_STORAGE_ENTERPRISE_SERVER       0x00000017  /* Storage Server Enterprise Edition */
+#define PRODUCT_STORAGE_EXPRESS_SERVER          0x00000014  /* Storage Server Express Edition */
+#define PRODUCT_STORAGE_STANDARD_SERVER         0x00000015  /* Storage Server Standard Edition */
+#define PRODUCT_STORAGE_WORKGROUP_SERVER        0x00000016  /* Storage Server Workgroup Edition */
+#define PRODUCT_UNDEFINED                       0x00000000  /* An unknown product */
+#define PRODUCT_ULTIMATE                        0x00000001  /* Ultimate Edition */
+#define PRODUCT_ULTIMATE_N                      0x0000001C  /* Ultimate Edition */
+#define PRODUCT_WEB_SERVER                      0x00000011  /* Web Server Edition (full installation) */
+#define PRODUCT_WEB_SERVER_CORE                 0x0000001D  /* Web Server Edition (core installation) */
+#endif
+
+static BOOL
+get_string_version(push_string_t *str)
+{
+    OSVERSIONINFOEX osvi;
+    SYSTEM_INFO si;
+    PGPI pGPI;
+    PGNSI pGNSI;
+    BOOL bOsVersionInfoEx;
+    DWORD dwType;
+
+    ZeroMemory(&si, sizeof(SYSTEM_INFO));
+    ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
+
+    /*
+     * Try calling GetVersionEx using the OSVERSIONINFOEX structure.
+     * If that fails, try using the OSVERSIONINFO structure.
+     */
+    osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+    if( !(bOsVersionInfoEx = GetVersionEx ((OSVERSIONINFO *) &osvi)) )
+    {
+        osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
+        if (! GetVersionEx ( (OSVERSIONINFO *) &osvi) ) 
+            return FALSE;
+    }
+
+    /* Call GetNativeSystemInfo if supported or GetSystemInfo otherwise. */
+    pGNSI = (PGNSI)
+            GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), 
+            "GetNativeSystemInfo");
+    if (NULL != pGNSI)
+        pGNSI(&si);
+    else
+        GetSystemInfo(&si);
+
+    switch (osvi.dwPlatformId)
+    {
+    /* Test for the Windows NT product family. */
+    case VER_PLATFORM_WIN32_NT:
+        /* Test for the specific product. */
+        if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 0)
+        {
+            if (osvi.wProductType == VER_NT_WORKSTATION)
+                pushString(str, "Windows Vista ");
+            else
+                pushString(str, "Windows Server 2008 ");
+
+            pGPI = (PGPI) GetProcAddress(
+                GetModuleHandle(TEXT("kernel32.dll")),
+                "GetProductInfo");
+
+            pGPI( 6, 0, 0, 0, &dwType);
+            switch (dwType)
+            {
+            case PRODUCT_ULTIMATE:
+                pushString(str, "Ultimate Edition");
+                break;
+            case PRODUCT_HOME_PREMIUM:
+                pushString(str, "Home Premium Edition");
+                break;
+            case PRODUCT_HOME_BASIC:
+                pushString(str, "Home Basic Edition");
+                break;
+            case PRODUCT_ENTERPRISE:
+                pushString(str, "Enterprise Edition");
+                break;
+            case PRODUCT_BUSINESS:
+                pushString(str, "Business Edition");
+                break;
+            case PRODUCT_STARTER:
+                pushString(str, "Starter Edition");
+                break;
+            case PRODUCT_CLUSTER_SERVER:
+                pushString(str, "Cluster Server Edition");
+                break;
+            case PRODUCT_DATACENTER_SERVER:
+                pushString(str, "Datacenter Edition");
+                break;
+            case PRODUCT_DATACENTER_SERVER_CORE:
+                pushString(str, "Datacenter Edition (core installation)");
+                break;
+            case PRODUCT_ENTERPRISE_SERVER:
+                pushString(str, "Enterprise Edition");
+                break;
+            case PRODUCT_ENTERPRISE_SERVER_CORE:
+                pushString(str, "Enterprise Edition (core installation)");
+                break;
+            case PRODUCT_ENTERPRISE_SERVER_IA64:
+                pushString(str, "Enterprise Edition for Itanium-based Systems");
+                break;
+            case PRODUCT_SMALLBUSINESS_SERVER:
+                pushString(str, "Small Business Server");
+                break;
+            case PRODUCT_SMALLBUSINESS_SERVER_PREMIUM:
+                pushString(str, "Small Business Server Premium Edition");
+                break;
+            case PRODUCT_STANDARD_SERVER:
+                pushString(str, "Standard Edition");
+                break;
+            case PRODUCT_STANDARD_SERVER_CORE:
+                pushString(str, "Standard Edition (core installation)");
+                break;
+            case PRODUCT_WEB_SERVER:
+                pushString(str, "Web Server Edition");
+                break;
+            }
+            if (si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64)
+                pushString(str,  ", 64-bit");
+            else
+            if (si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_INTEL)
+                pushString(str, ", 32-bit");
+            else
+                /* space for optional build number */
+                pushString(str, " ");
+        }
+        else
+        if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2 )
+        {
+            if( GetSystemMetrics(SM_SERVERR2) )
+                pushString(str, "Microsoft Windows Server 2003 \"R2\" ");
+            else
+            if ( osvi.wSuiteMask==VER_SUITE_STORAGE_SERVER )
+                pushString(str, "Windows Storage Server 2003 ");
+            else
+            if( osvi.wProductType == VER_NT_WORKSTATION &&
+                si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64)
+                pushString(str, "Microsoft Windows XP Professional x64 Edition ");
+            else
+                pushString(str, "Microsoft Windows Server 2003, ");
+
+            /* Test for the server type. */
+            if ( osvi.wProductType != VER_NT_WORKSTATION )
+            {
+                switch (si.wProcessorArchitecture)
+                {
+                case PROCESSOR_ARCHITECTURE_IA64:
+                    if (osvi.wSuiteMask & VER_SUITE_DATACENTER)
+                       pushString(str, "Datacenter Edition for Itanium-based Systems ");
+                    else
+                    if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE)
+                       pushString(str, "Enterprise Edition for Itanium-based Systems ");
+                    break;
+
+                case PROCESSOR_ARCHITECTURE_AMD64:
+                    if (osvi.wSuiteMask & VER_SUITE_DATACENTER)
+                        pushString(str, "Datacenter x64 Edition ");
+                    else
+                    if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE)
+                        pushString(str, "Enterprise x64 Edition ");
+                    else
+                        pushString(str, "Standard x64 Edition ");
+                    break;
+
+                default:
+                    if ( osvi.wSuiteMask & VER_SUITE_COMPUTE_SERVER)
+                        pushString(str, "Compute Cluster Edition ");
+                    else
+                    if ( osvi.wSuiteMask & VER_SUITE_DATACENTER)
+                        pushString(str, "Datacenter Edition ");
+                    else
+                    if ( osvi.wSuiteMask & VER_SUITE_ENTERPRISE)
+                        pushString(str, "Enterprise Edition ");
+                    else
+                    if ( osvi.wSuiteMask & VER_SUITE_BLADE)
+                        pushString(str, "Web Edition ");
+                    else
+                        pushString(str, "Standard Edition ");
+                    break;
+                }
+            }
+        }
+        else
+        if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1 )
+        {
+            pushString(str, "Microsoft Windows XP ");
+            if( osvi.wSuiteMask & VER_SUITE_PERSONAL )
+                pushString(str, "Home Edition ");
+            else
+                pushString(str, "Professional ");
+        }
+        else
+        if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0 )
+        {
+            pushString(str, "Microsoft Windows 2000 ");
+            if (osvi.wProductType == VER_NT_WORKSTATION)
+                pushString(str, "Professional ");
+            else 
+            if (osvi.wSuiteMask & VER_SUITE_DATACENTER)
+                pushString(str, "Datacenter Server ");
+            else
+            if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE)
+                pushString(str, "Advanced Server ");
+            else
+                pushString(str, "Server ");
+        } else
+        if ( osvi.dwMajorVersion <= 4 )
+            pushString(str, "Microsoft Windows NT ");
+
+        /* Test for specific product on Windows NT 4.0 SP6 and later. */
+        if (bOsVersionInfoEx)
+        {
+            /* Test for the workstation type. */
+            switch (osvi.wProductType)
+            {
+            case VER_NT_WORKSTATION:
+                if (si.wProcessorArchitecture!=PROCESSOR_ARCHITECTURE_AMD64 &&
+                    osvi.dwMajorVersion == 4)
+                    pushString(str, "Workstation 4.0 ");
+                break;
+
+            case VER_NT_SERVER:
+            case VER_NT_DOMAIN_CONTROLLER:
+                if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE)
+                    pushString(str, "Server 4.0, Enterprise Edition ");
+                else
+                    pushString(str, "Server 4.0 ");
+                break;
+            }
+        }
+        /* Test for specific product on Windows NT 4.0 SP5 and earlier */
+        else  
+        {
+            HKEY hKey;
+            TCHAR szProductType[BUFSIZE];
+            DWORD dwBufLen=BUFSIZE*sizeof(TCHAR);
+            LONG lRet;
+
+            lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+                TEXT("SYSTEM\\CurrentControlSet\\Control\\ProductOptions"),
+                0, KEY_QUERY_VALUE, &hKey);
+            if (lRet != ERROR_SUCCESS)
+                return FALSE;
+
+            lRet = RegQueryValueEx(hKey, TEXT("ProductType"), NULL, NULL,
+                (LPBYTE) szProductType, &dwBufLen);
+            RegCloseKey( hKey );
+
+            if ((lRet != ERROR_SUCCESS) || (dwBufLen > BUFSIZE*sizeof(TCHAR)))
+                return FALSE;
+
+            if (lstrcmpi(TEXT("WINNT"), szProductType) == 0)
+                pushString(str, "Workstation ");
+            else
+            if (lstrcmpi(TEXT("LANMANNT"), szProductType) == 0)
+                pushString(str, "Server ");
+            else
+            if (lstrcmpi( TEXT("SERVERNT"), szProductType) == 0)
+                pushString(str, "Advanced Server ");
+            else
+                pushString(str, "%d.%d ", osvi.dwMajorVersion, osvi.dwMinorVersion);
+        }
+
+        /* Display service pack (if any) and build number. */
+        if (osvi.dwMajorVersion == 4 && 
+            lstrcmpi(osvi.szCSDVersion, TEXT("Service Pack 6")) == 0)
+        { 
+            HKEY hKey;
+            LONG lRet;
+
+            /* Test for SP6 versus SP6a. */
+            lRet = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
+                TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Hotfix\\Q246009"),
+                0, KEY_QUERY_VALUE, &hKey );
+            if( lRet == ERROR_SUCCESS )
+                pushString(str, "Service Pack 6a (Build %d)\n", osvi.dwBuildNumber & 0xFFFF );         
+            else
+                /* Windows NT 4.0 prior to SP6a */
+                pushString(str, "%s (Build %d)\n", osvi.szCSDVersion, osvi.dwBuildNumber & 0xFFFF);
+
+             RegCloseKey( hKey );
+        }
+        else /* not Windows NT 4.0 */
+            pushString(str, "%s (Build %d)\n", osvi.szCSDVersion, osvi.dwBuildNumber & 0xFFFF);
+
+        break;
+
+    /* Test for the Windows Me/98/95. */
+    case VER_PLATFORM_WIN32_WINDOWS:
+        if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 0)
+        {
+            pushString(str, "Microsoft Windows 95");
+            if (osvi.szCSDVersion[1]=='C' || osvi.szCSDVersion[1]=='B')
+                pushString(str, " OSR2");
+        }
+        else
+        if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 10)
+        {
+            pushString(str, "Microsoft Windows 98");
+            if (osvi.szCSDVersion[1]=='A' || osvi.szCSDVersion[1]=='B')
+                pushString(str, " SE");
+        } 
+        else
+        if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 90)
+            pushString(str, "Microsoft Windows Millennium Edition\n");
+        break;
+
+    case VER_PLATFORM_WIN32s:
+        pushString(str, "Microsoft Win32s\n");
+        break;
+    }
+    return TRUE; 
+}
+
+const char *
+GetHostName(void)
+{
+/*----------------------------------------------------------------------------
+   Return the host name of this system.
+-----------------------------------------------------------------------------*/
+    push_string_t str;
+
+    ZeroMemory(&str, sizeof(str));
+    if (!get_string_version(&str)) {
+#ifndef GETHOSTNAME_LOCAL_DEBUG
+        pm_error("Unable to find out host name.");
+#endif
+        pushString(&str, "unknown");
+    }
+    return (const char *)_strdup(str.str);
+}
+
+#ifdef GETHOSTNAME_LOCAL_DEBUG
+int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst,
+                   LPSTR lpsCmdLine, int nCmdShow)
+{
+    char *hostName = (char *)GetHostName();
+
+    /* compile as ascii only (not UNICODE) */
+    MessageBox(NULL, hostName, TEXT("GetHostName"), MB_OK);
+    free(hostName);
+
+    return 0;
+}
+#endif
diff --git a/converter/ppm/ppmtompeg/headers/bitio.h b/converter/ppm/ppmtompeg/headers/bitio.h
index 89e61fbb..a24c21cd 100644
--- a/converter/ppm/ppmtompeg/headers/bitio.h
+++ b/converter/ppm/ppmtompeg/headers/bitio.h
@@ -60,9 +60,7 @@
 #define BIT_IO_INCLUDED
 
 
-/*==============*
- * HEADER FILES *
- *==============*/
+#include <stdio.h>
 
 #include "general.h"
 #include "ansi.h"
diff --git a/converter/ppm/ppmtompeg/headers/block.h b/converter/ppm/ppmtompeg/headers/block.h
index 46050492..22d306a1 100644
--- a/converter/ppm/ppmtompeg/headers/block.h
+++ b/converter/ppm/ppmtompeg/headers/block.h
@@ -1,3 +1,46 @@
+#ifndef BLOCK_H_INCLUDED
+
+#include "frame.h"
+#include "mtypes.h"
+
+/* DIFFERENCE FUNCTIONS */
+
+int32
+LumBlockMAD(const LumBlock * const currentBlockP,
+            const LumBlock * const motionBlockP,
+            int32            const bestSoFar);
+
+int32
+LumBlockMSE(const LumBlock * const currentBlockP,
+            const LumBlock * const motionBlockP,
+            int32            const bestSoFar);
+
+int32
+LumMotionError(const LumBlock * const currentBlockP,
+               MpegFrame *      const prev,
+               int              const by,
+               int              const bx,
+               vector           const m,
+               int32            const bestSoFar);
+
+int32
+LumAddMotionError(const LumBlock * const currentBlockP,
+                  const LumBlock * const blockSoFarP,
+                  MpegFrame *      const prev,
+                  int              const by,
+                  int              const bx,
+                  vector           const m,
+                  int32            const bestSoFar);
+
+int32
+LumMotionErrorSubSampled(const LumBlock * const currentBlockP,
+                         MpegFrame *      const prevFrame,
+                         int              const by,
+                         int              const bx,
+                         vector           const m,
+                         int              const startY,
+                         int              const startX);
+
 void
 ComputeDiffDCTs(MpegFrame * const current,
                 MpegFrame * const prev,
@@ -51,3 +94,4 @@ AddBMotionBlock(Block          block,
 void
 BlockifyFrame(MpegFrame * const frameP);
 
+#endif
diff --git a/converter/ppm/ppmtompeg/headers/byteorder.h b/converter/ppm/ppmtompeg/headers/byteorder.h
index 0070252a..e2d8030c 100644
--- a/converter/ppm/ppmtompeg/headers/byteorder.h
+++ b/converter/ppm/ppmtompeg/headers/byteorder.h
@@ -72,6 +72,8 @@
 #else
     /* let in.h handle it, if possible */		   
 #include <sys/types.h>
+#if !defined(WIN32) || defined(__CYGWIN__)
 #include <netinet/in.h>
+#endif
 #endif /* FORCE_LITTLE_ENDIAN */
 #endif /* FORCE_BIG_ENDIAN */
diff --git a/converter/ppm/ppmtompeg/headers/frame.h b/converter/ppm/ppmtompeg/headers/frame.h
index 6df3d19f..acd74419 100644
--- a/converter/ppm/ppmtompeg/headers/frame.h
+++ b/converter/ppm/ppmtompeg/headers/frame.h
@@ -130,18 +130,4 @@ Frame_Resize(MpegFrame * const omf,
              int         const outsize_x,
              int         const outsize_y);
 
-
-extern void	  Frame_Free _ANSI_ARGS_((MpegFrame * const frame));
-extern void	  Frame_Exit _ANSI_ARGS_((void));
-extern void	  Frame_AllocPPM _ANSI_ARGS_((MpegFrame * frame));
-extern void	  Frame_AllocYCC _ANSI_ARGS_((MpegFrame * const mf));
-extern void	  Frame_AllocDecoded _ANSI_ARGS_((MpegFrame * const frame,
-						  boolean const makeReference));
-extern void	  Frame_AllocHalf _ANSI_ARGS_((MpegFrame * const frame));
-extern void	  Frame_AllocBlocks _ANSI_ARGS_((MpegFrame * const mf));
-extern void	  Frame_Resize _ANSI_ARGS_((MpegFrame * const omf, MpegFrame * const mf,
-					    int const insize_x, int const insize_y,
-					    int const outsize_x, int const outsize_y));
-
-
-#endif /* FRAME_INCLUDED */
+#endif
diff --git a/converter/ppm/ppmtompeg/headers/frames.h b/converter/ppm/ppmtompeg/headers/frames.h
index 14304c48..f2bcf6ae 100644
--- a/converter/ppm/ppmtompeg/headers/frames.h
+++ b/converter/ppm/ppmtompeg/headers/frames.h
@@ -16,6 +16,7 @@
 #include "ansi.h"
 #include "mtypes.h"
 #include "mheaders.h"
+#include "iframe.h"
 #include "frame.h"
 
 
@@ -86,11 +87,13 @@ typedef struct dct_data_tye_struct {
   int fmotionX, fmotionY, bmotionX, bmotionY;
 } dct_data_type;
 
-void    EncodeYDC _ANSI_ARGS_((int32 const dc_term, int32 * const pred_term, BitBucket * const bb));
-void
-EncodeCDC(int32       const dc_term,
-          int32     * const pred_term,
-          BitBucket * const bb);
+
+/*==================*
+ * GLOBAL VARIABLES *
+ *==================*/
+
+extern Block    **dct, **dctr, **dctb;
+extern dct_data_type **dct_data;
 
 
 /*========*
@@ -101,63 +104,6 @@ EncodeCDC(int32       const dc_term,
 #define int_ceil_div(a,b,c)     ((b*(c = a/b) < a) ? (c+1) : c)
 #define int_floor_div(a,b,c)    ((b*(c = a/b) > a) ? (c-1) : c)
 
-/* assumes many things:
- * block indices are (y,x)
- * variables y_dc_pred, cr_dc_pred, and cb_dc_pred
- * flat block fb exists
- */
-#define GEN_I_BLOCK(frameType, frame, bb, mbAI, qscale) {                   \
-    boolean overflow, overflowChange=FALSE;                             \
-        int overflowValue = 0;                                              \
-        do {                                                                \
-      overflow =  Mpost_QuantZigBlock(dct[y][x], fb[0],                 \
-             qscale, TRUE)==MPOST_OVERFLOW;                     \
-          overflow |= Mpost_QuantZigBlock(dct[y][x+1], fb[1],               \
-                 qscale, TRUE)==MPOST_OVERFLOW;                     \
-      overflow |= Mpost_QuantZigBlock(dct[y+1][x], fb[2],               \
-                         qscale, TRUE)==MPOST_OVERFLOW;                     \
-      overflow |= Mpost_QuantZigBlock(dct[y+1][x+1], fb[3],             \
-                         qscale, TRUE)==MPOST_OVERFLOW;                     \
-      overflow |= Mpost_QuantZigBlock(dctb[y >> 1][x >> 1],             \
-                         fb[4], qscale, TRUE)==MPOST_OVERFLOW;              \
-      overflow |= Mpost_QuantZigBlock(dctr[y >> 1][x >> 1],             \
-             fb[5], qscale, TRUE)==MPOST_OVERFLOW;              \
-          if ((overflow) && (qscale!=31)) {                                 \
-           overflowChange = TRUE; overflowValue++;                          \
-       qscale++;                                                        \
-       } else overflow = FALSE;                                         \
-    } while (overflow);                                                 \
-        Mhead_GenMBHeader(bb,                           \
-            frameType /* pict_code_type */, mbAI /* addr_incr */,   \
-            qscale /* q_scale */,                               \
-            0 /* forw_f_code */, 0 /* back_f_code */,           \
-            0 /* horiz_forw_r */, 0 /* vert_forw_r */,          \
-            0 /* horiz_back_r */, 0 /* vert_back_r */,          \
-            0 /* motion_forw */, 0 /* m_horiz_forw */,          \
-            0 /* m_vert_forw */, 0 /* motion_back */,           \
-            0 /* m_horiz_back */, 0 /* m_vert_back */,          \
-            0 /* mb_pattern */, TRUE /* mb_intra */);           \
-                                        \
-    /* Y blocks */                              \
-        EncodeYDC(fb[0][0], &y_dc_pred, bb);                            \
-    Mpost_RLEHuffIBlock(fb[0], bb);                         \
-    EncodeYDC(fb[1][0], &y_dc_pred, bb);                        \
-        Mpost_RLEHuffIBlock(fb[1], bb);                             \
-    EncodeYDC(fb[2][0], &y_dc_pred, bb);                        \
-    Mpost_RLEHuffIBlock(fb[2], bb);                         \
-    EncodeYDC(fb[3][0], &y_dc_pred, bb);                        \
-    Mpost_RLEHuffIBlock(fb[3], bb);                         \
-                                        \
-    /* CB block */                              \
-    EncodeCDC(fb[4][0], &cb_dc_pred, bb);                   \
-    Mpost_RLEHuffIBlock(fb[4], bb);                     \
-                                        \
-    /* CR block */                              \
-    EncodeCDC(fb[5][0], &cr_dc_pred, bb);                   \
-    Mpost_RLEHuffIBlock(fb[5], bb);                     \
-    if (overflowChange) qscale -= overflowValue;                        \
-    }
-
 #define BLOCK_TO_FRAME_COORD(bx1, bx2, x1, x2) {    \
     x1 = (bx1)*DCTSIZE;             \
     x2 = (bx2)*DCTSIZE;             \
@@ -254,6 +200,9 @@ encodeMotionVector(int      const x,
  *===============================*/
 
 void
+AllocDctBlocks(void);
+
+void
 ComputeBMotionLumBlock(MpegFrame * const prev,
                        MpegFrame * const next,
                        int         const by,
@@ -271,8 +220,6 @@ BMotionSearch(const LumBlock * const currentBlockP,
               motion *         const motionP,
               int              const oldMode);
 
-void GenIFrame (BitBucket * const bb,
-                MpegFrame * const mf);
 void GenPFrame (BitBucket * const bb,
                 MpegFrame * const current,
                 MpegFrame * const prev);
@@ -280,11 +227,6 @@ void GenBFrame (BitBucket * const bb,
                 MpegFrame * const curr,
                 MpegFrame * const prev,
                 MpegFrame * const next);
-void    AllocDctBlocks _ANSI_ARGS_((void ));
-
-
-float
-IFrameTotalTime(void);
 
 float
 PFrameTotalTime(void);
@@ -293,11 +235,6 @@ float
 BFrameTotalTime(void);
 
 void
-ShowIFrameSummary(unsigned int const inputFrameBits, 
-                  unsigned int const totalBits, 
-                  FILE *       const fpointer);
-
-void
 ShowPFrameSummary(unsigned int const inputFrameBits, 
                   unsigned int const totalBits, 
                   FILE *       const fpointer);
@@ -307,87 +244,6 @@ ShowBFrameSummary(unsigned int const inputFrameBits,
                   unsigned int const totalBits, 
                   FILE *       const fpointer);
 
-/* DIFFERENCE FUNCTIONS */
-
-int32
-LumBlockMAD(const LumBlock * const currentBlockP,
-            const LumBlock * const motionBlockP,
-            int32            const bestSoFar);
-
-int32
-LumBlockMSE(const LumBlock * const currentBlockP,
-            const LumBlock * const motionBlockP,
-            int32            const bestSoFar);
-
-int32
-LumMotionError(const LumBlock * const currentBlockP,
-               MpegFrame *      const prev,
-               int              const by,
-               int              const bx,
-               vector           const m,
-               int32            const bestSoFar);
-
-int32
-LumAddMotionError(const LumBlock * const currentBlockP,
-                  const LumBlock * const blockSoFarP,
-                  MpegFrame *      const prev,
-                  int              const by,
-                  int              const bx,
-                  vector           const m,
-                  int32            const bestSoFar);
-
-int32
-LumMotionErrorA(const LumBlock * const currentBlockP,
-                MpegFrame *      const prevFrame,
-                int              const by,
-                int              const bx,
-                vector           const m,
-                int32            const bestSoFar);
-
-int32
-LumMotionErrorB(const LumBlock * const currentP,
-                MpegFrame *      const prevFrame,
-                int              const by,
-                int              const bx,
-                vector           const m,
-                int32            const bestSoFar);
-
-int32
-LumMotionErrorC(const LumBlock * const currentP,
-                MpegFrame *      const prevFrame,
-                int              const by,
-                int              const bx,
-                vector           const m,
-                int32            const bestSoFar);
-
-int32
-LumMotionErrorD(const LumBlock * const currentP,
-                MpegFrame *      const prevFrame,
-                int              const by,
-                int              const bx,
-                vector           const m,
-                int32            const bestSoFar);
-
-int32
-LumMotionErrorSubSampled(const LumBlock * const currentBlockP,
-                         MpegFrame *      const prevFrame,
-                         int              const by,
-                         int              const bx,
-                         vector           const m,
-                         int              const startY,
-                         int              const startX);
-
-void
-BlockComputeSNR(MpegFrame * const current,
-                float *     const snr,
-                float *     const psnr);
-
-int32
-time_elapsed(void);
-
-void
-AllocDctBlocks(void);
-
 /*==================*
  * GLOBAL VARIABLES *
  *==================*/
@@ -399,7 +255,7 @@ extern int gopSize;
 extern int slicesPerFrame;
 extern int blocksPerSlice;
 extern int referenceFrame;
-extern int specificsOn;
+extern boolean specificsOn;
 extern int quietTime;       /* shut up for at least quietTime seconds;
                  * negative means shut up forever
                  */
@@ -413,7 +269,7 @@ extern int  fCodeI,fCodeP,fCodeB;
 extern boolean    forceEncodeLast;
 extern int TIME_RATE;
 
-#endif /* FRAMES_INCLUDED */
+#endif
 
 
 /*
diff --git a/converter/ppm/ppmtompeg/headers/iframe.h b/converter/ppm/ppmtompeg/headers/iframe.h
new file mode 100644
index 00000000..c4f77c74
--- /dev/null
+++ b/converter/ppm/ppmtompeg/headers/iframe.h
@@ -0,0 +1,118 @@
+#ifndef IFRAME_H_INCLUDED
+#define IFRAME_H_INCLUDED
+
+#include "frame.h"
+
+void
+SetFCode(void);
+
+void
+SetSlicesPerFrame(int const number);
+
+void
+SetBlocksPerSlice(void);
+
+void
+SetIQScale(int const qI);
+
+int
+GetIQScale(void);
+
+void
+GenIFrame(BitBucket * const bb, 
+          MpegFrame * const current);
+
+void
+ResetIFrameStats(void);
+
+float
+IFrameTotalTime(void);
+
+
+void
+ShowIFrameSummary(unsigned int const inputFrameBits, 
+                  unsigned int const totalBits, 
+                  FILE *       const fpointer);
+
+void
+EncodeYDC(int32       const dc_term,
+          int32 *     const pred_term,
+          BitBucket * const bb);
+
+void
+EncodeCDC(int32       const dc_term,
+          int32     * const pred_term,
+          BitBucket * const bb);
+
+void
+BlockComputeSNR(MpegFrame * const current,
+                float *     const snr,
+                float *     const psnr);
+
+void
+WriteDecodedFrame(MpegFrame * const frame);
+
+void
+PrintItoIBitRate(int const numBits,
+                 int const frameNum);
+
+int32 time_elapsed(void);
+
+/* assumes many things:
+ * block indices are (y,x)
+ * variables y_dc_pred, cr_dc_pred, and cb_dc_pred
+ * flat block fb exists
+ */
+#define GEN_I_BLOCK(frameType, frame, bb, mbAI, qscale) {                   \
+    boolean overflow, overflowChange=FALSE;                             \
+        int overflowValue = 0;                                              \
+        do {                                                                \
+      overflow =  Mpost_QuantZigBlock(dct[y][x], fb[0],                 \
+             qscale, TRUE)==MPOST_OVERFLOW;                     \
+          overflow |= Mpost_QuantZigBlock(dct[y][x+1], fb[1],               \
+                 qscale, TRUE)==MPOST_OVERFLOW;                     \
+      overflow |= Mpost_QuantZigBlock(dct[y+1][x], fb[2],               \
+                         qscale, TRUE)==MPOST_OVERFLOW;                     \
+      overflow |= Mpost_QuantZigBlock(dct[y+1][x+1], fb[3],             \
+                         qscale, TRUE)==MPOST_OVERFLOW;                     \
+      overflow |= Mpost_QuantZigBlock(dctb[y >> 1][x >> 1],             \
+                         fb[4], qscale, TRUE)==MPOST_OVERFLOW;              \
+      overflow |= Mpost_QuantZigBlock(dctr[y >> 1][x >> 1],             \
+             fb[5], qscale, TRUE)==MPOST_OVERFLOW;              \
+          if ((overflow) && (qscale!=31)) {                                 \
+           overflowChange = TRUE; overflowValue++;                          \
+       qscale++;                                                        \
+       } else overflow = FALSE;                                         \
+    } while (overflow);                                                 \
+        Mhead_GenMBHeader(bb,                           \
+            frameType /* pict_code_type */, mbAI /* addr_incr */,   \
+            qscale /* q_scale */,                               \
+            0 /* forw_f_code */, 0 /* back_f_code */,           \
+            0 /* horiz_forw_r */, 0 /* vert_forw_r */,          \
+            0 /* horiz_back_r */, 0 /* vert_back_r */,          \
+            0 /* motion_forw */, 0 /* m_horiz_forw */,          \
+            0 /* m_vert_forw */, 0 /* motion_back */,           \
+            0 /* m_horiz_back */, 0 /* m_vert_back */,          \
+            0 /* mb_pattern */, TRUE /* mb_intra */);           \
+                                        \
+    /* Y blocks */                              \
+        EncodeYDC(fb[0][0], &y_dc_pred, bb);                            \
+    Mpost_RLEHuffIBlock(fb[0], bb);                         \
+    EncodeYDC(fb[1][0], &y_dc_pred, bb);                        \
+        Mpost_RLEHuffIBlock(fb[1], bb);                             \
+    EncodeYDC(fb[2][0], &y_dc_pred, bb);                        \
+    Mpost_RLEHuffIBlock(fb[2], bb);                         \
+    EncodeYDC(fb[3][0], &y_dc_pred, bb);                        \
+    Mpost_RLEHuffIBlock(fb[3], bb);                         \
+                                        \
+    /* CB block */                              \
+    EncodeCDC(fb[4][0], &cb_dc_pred, bb);                   \
+    Mpost_RLEHuffIBlock(fb[4], bb);                     \
+                                        \
+    /* CR block */                              \
+    EncodeCDC(fb[5][0], &cr_dc_pred, bb);                   \
+    Mpost_RLEHuffIBlock(fb[5], bb);                     \
+    if (overflowChange) qscale -= overflowValue;                        \
+    }
+
+#endif
diff --git a/converter/ppm/ppmtompeg/headers/motion_search.h b/converter/ppm/ppmtompeg/headers/motion_search.h
index ab83cbca..62f3abab 100644
--- a/converter/ppm/ppmtompeg/headers/motion_search.h
+++ b/converter/ppm/ppmtompeg/headers/motion_search.h
@@ -100,6 +100,9 @@ PMotionSearch(const LumBlock * const currentBlockP,
               int              const bx, 
               vector *         const motionP);
 
+void
+MotionSearchPreComputation(MpegFrame * const frameP);
+
 /*==================*
  * GLOBAL VARIABLES *
  *==================*/
diff --git a/converter/ppm/ppmtompeg/headers/mpeg.h b/converter/ppm/ppmtompeg/headers/mpeg.h
index 23875c2c..56862c42 100644
--- a/converter/ppm/ppmtompeg/headers/mpeg.h
+++ b/converter/ppm/ppmtompeg/headers/mpeg.h
@@ -90,8 +90,9 @@ void
 ReadDecodedRefFrame(MpegFrame *  const frameP, 
                     unsigned int const frameNumber);
 
-extern void	WriteDecodedFrame _ANSI_ARGS_((MpegFrame * const frame));
-extern void	SetBitRateFileName _ANSI_ARGS_((char *fileName));
+void
+SetBitRateFileName(const char * const fileName);
+
 extern void	SetFrameRate _ANSI_ARGS_((void));
 
 
diff --git a/converter/ppm/ppmtompeg/headers/mproto.h b/converter/ppm/ppmtompeg/headers/mproto.h
index c3b0f4b3..d8fefd84 100644
--- a/converter/ppm/ppmtompeg/headers/mproto.h
+++ b/converter/ppm/ppmtompeg/headers/mproto.h
@@ -74,7 +74,6 @@
 #include "bitio.h"
 
 
-#define DCTSIZE2    DCTSIZE*DCTSIZE
 typedef short DCTELEM;
 typedef DCTELEM DCTBLOCK[DCTSIZE2];
 
@@ -111,9 +110,6 @@ void	ReadEYUV _ANSI_ARGS_((MpegFrame * mf, FILE *fpointer, int width,
 boolean	ReadPPM _ANSI_ARGS_((MpegFrame *mf, FILE *fpointer));
 void PPMtoYCC _ANSI_ARGS_((MpegFrame * mf));
 
-void	MotionSearchPreComputation _ANSI_ARGS_((MpegFrame *frame));
-boolean	PMotionSearch _ANSI_ARGS_((LumBlock currentBlock, MpegFrame *prev,
-				   int by, int bx, int *motionY, int *motionX));
 void	ComputeHalfPixelData _ANSI_ARGS_((MpegFrame *frame));
 void mp_validate_size _ANSI_ARGS_((int *x, int *y));
 void AllocYCC _ANSI_ARGS_((MpegFrame * mf));
@@ -126,7 +122,3 @@ void j_rev_dct _ANSI_ARGS_((DCTBLOCK data ));
 void j_rev_dct_sparse _ANSI_ARGS_((DCTBLOCK data , int pos ));
 void j_rev_dct _ANSI_ARGS_((DCTBLOCK data ));
 
-/* block.c */
-void	BlockToData _ANSI_ARGS_((uint8 **data, Block block, int by, int bx));
-void	AddMotionBlock _ANSI_ARGS_((Block block, uint8 **prev, int by, int bx,
-		       int my, int mx));
diff --git a/converter/ppm/ppmtompeg/headers/param.h b/converter/ppm/ppmtompeg/headers/param.h
index 31be61ee..c7f57b44 100644
--- a/converter/ppm/ppmtompeg/headers/param.h
+++ b/converter/ppm/ppmtompeg/headers/param.h
@@ -1,30 +1,4 @@
-/*===========================================================================*
- * param.h								     *
- *									     *
- *	reading the parameter file					     *
- *									     *
- *===========================================================================*/
-
-/*
- * Copyright (c) 1995 The Regents of the University of California.
- * All rights reserved.
- *
- * Permission to use, copy, modify, and distribute this software and its
- * documentation for any purpose, without fee, and without written agreement is
- * hereby granted, provided that the above copyright notice and the following
- * two paragraphs appear in all copies of this software.
- *
- * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
- * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
- * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- */
+/* COPYRIGHT information is at end of file */
 
 #include "pm_c_util.h"
 #include "ansi.h"
@@ -35,14 +9,21 @@
  * CONSTANTS *
  *===========*/
 
-#define MAX_MACHINES	    256
+#define MAX_MACHINES        256
 #ifndef MAXPATHLEN
 #define MAXPATHLEN  1024
 #endif
 
-#define	ENCODE_FRAMES	0
-#define COMBINE_GOPS	1
-#define COMBINE_FRAMES	2
+typedef enum {
+    ENCODE_FRAMES,
+        /* The regular, default function: Input is individual single unencoded
+           frames.
+        */
+    COMBINE_GOPS,
+        /* Input is pre-encoded GOPs */
+    COMBINE_FRAMES
+        /* Input is pre-encoded individual frames */
+} majorProgramFunction;
 
 
 struct params {
@@ -53,9 +34,9 @@ struct params {
 
 
 void
-ReadParamFile(const char *    const fileName, 
-              int             const function,
-              struct params * const paramP);
+ReadParamFile(const char *         const fileName, 
+              majorProgramFunction const function,
+              struct params *      const paramP);
 
 /*==================*
  * GLOBAL VARIABLES *
@@ -63,25 +44,47 @@ ReadParamFile(const char *    const fileName,
 
 /* All this stuff ought to be in a struct param instead */
 
-extern char	outputFileName[256];
-extern int	whichGOP;
+extern char outputFileName[256];
+extern int whichGOP;
 extern int numMachines;
-extern char	machineName[MAX_MACHINES][256];
-extern char	userName[MAX_MACHINES][256];
-extern char	executable[MAX_MACHINES][1024];
-extern char	remoteParamFile[MAX_MACHINES][1024];
-extern boolean	remote[MAX_MACHINES];
-extern char	currentPath[MAXPATHLEN];
-extern char	currentFramePath[MAXPATHLEN];
-extern char	currentGOPPath[MAXPATHLEN];
+extern char machineName[MAX_MACHINES][256];
+extern char userName[MAX_MACHINES][256];
+extern char executable[MAX_MACHINES][1024];
+extern char remoteParamFile[MAX_MACHINES][1024];
+extern boolean remote[MAX_MACHINES];
+extern char currentPath[MAXPATHLEN];
+extern char currentFramePath[MAXPATHLEN];
+extern char currentGOPPath[MAXPATHLEN];
 extern char inputConversion[1024];
 extern char yuvConversion[256];
-extern int  yuvWidth, yuvHeight;
-extern int  realWidth, realHeight;
+extern int yuvWidth, yuvHeight;
+extern int realWidth, realHeight;
 extern char ioConversion[1024];
 extern char slaveConversion[1024];
-extern FILE *bitRateFile;
+extern FILE * bitRateFile;
 extern boolean showBitRatePerFrame;
 extern boolean computeMVHist;
 extern const double VidRateNum[9];
 extern boolean keepTempFiles;
+
+
+/*
+ * Copyright (c) 1995 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice and the following
+ * two paragraphs appear in all copies of this software.
+ *
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ */
diff --git a/converter/ppm/ppmtompeg/headers/prototypes.h b/converter/ppm/ppmtompeg/headers/prototypes.h
index a284888f..b421af35 100644
--- a/converter/ppm/ppmtompeg/headers/prototypes.h
+++ b/converter/ppm/ppmtompeg/headers/prototypes.h
@@ -44,33 +44,26 @@ void    ResetBFrameStats _ANSI_ARGS_((void));
 void    ResetPFrameStats _ANSI_ARGS_((void));
 void SetSearchRange (int const pixelsP,
                      int const pixelsB);
-void    ResetIFrameStats _ANSI_ARGS_((void));
 void
 SetPixelSearch(const char * const searchType);
-void    SetIQScale _ANSI_ARGS_((int const qI));
 void    SetPQScale _ANSI_ARGS_((int qP));
 void    SetBQScale _ANSI_ARGS_((int qB));
-float   EstimateSecondsPerIFrame _ANSI_ARGS_((void));
 float   EstimateSecondsPerPFrame _ANSI_ARGS_((void));
 float   EstimateSecondsPerBFrame _ANSI_ARGS_((void));
 void    SetGOPSize _ANSI_ARGS_((int size));
 void
 SetStatFileName(const char * const fileName);
-void    SetSlicesPerFrame _ANSI_ARGS_((int const number));
-void    SetBlocksPerSlice _ANSI_ARGS_((void));
 
 
 void DCTFrame _ANSI_ARGS_((MpegFrame * mf));
 
 void PPMtoYCC _ANSI_ARGS_((MpegFrame * mf));
 
-void    MotionSearchPreComputation _ANSI_ARGS_((MpegFrame * const frame));
+void    MotionSearchPreComputation _ANSI_ARGS_((MpegFrame *frame));
 
 void    ComputeHalfPixelData _ANSI_ARGS_((MpegFrame *frame));
 void mp_validate_size _ANSI_ARGS_((int *x, int *y));
 
-extern void SetFCode _ANSI_ARGS_((void));
-
 
 /* psearch.c */
 void    ShowPMVHistogram _ANSI_ARGS_((FILE *fpointer));
diff --git a/converter/ppm/ppmtompeg/headers/subsample.h b/converter/ppm/ppmtompeg/headers/subsample.h
new file mode 100644
index 00000000..deedf251
--- /dev/null
+++ b/converter/ppm/ppmtompeg/headers/subsample.h
@@ -0,0 +1,39 @@
+#ifndef SUBSAMPLE_H_INCLUDED
+#define SUBSAMPLE_H_INCLUDED
+
+#include "frame.h"
+#include "mtypes.h"
+
+int32
+LumMotionErrorA(const LumBlock * const currentBlockP,
+                MpegFrame *      const prevFrame,
+                int              const by,
+                int              const bx,
+                vector           const m,
+                int32            const bestSoFar);
+
+int32
+LumMotionErrorB(const LumBlock * const currentP,
+                MpegFrame *      const prevFrame,
+                int              const by,
+                int              const bx,
+                vector           const m,
+                int32            const bestSoFar);
+
+int32
+LumMotionErrorC(const LumBlock * const currentP,
+                MpegFrame *      const prevFrame,
+                int              const by,
+                int              const bx,
+                vector           const m,
+                int32            const bestSoFar);
+
+int32
+LumMotionErrorD(const LumBlock * const currentP,
+                MpegFrame *      const prevFrame,
+                int              const by,
+                int              const bx,
+                vector           const m,
+                int32            const bestSoFar);
+
+#endif
diff --git a/converter/ppm/ppmtompeg/huff.h b/converter/ppm/ppmtompeg/huff.h
deleted file mode 100644
index 4d0b8840..00000000
--- a/converter/ppm/ppmtompeg/huff.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (c) 1995 The Regents of the University of California.
- * All rights reserved.
- *
- * Permission to use, copy, modify, and distribute this software and its
- * documentation for any purpose, without fee, and without written agreement is
- * hereby granted, provided that the above copyright notice and the following
- * two paragraphs appear in all copies of this software.
- *
- * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
- * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
- * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- */
-
-/*  
- *  $Header: /n/charlie-brown/project/mm/mpeg/mpeg_dist/mpeg_encode/RCS/huff.h,v 1.2 1995/01/19 23:08:28 eyhung Exp $
- */
-
-/*  
- *  THIS FILE IS MACHINE GENERATED!  DO NOT EDIT!
- */
-#define HUFF_MAXRUN	32
-#define HUFF_MAXLEVEL	41
-
-extern int huff_maxlevel[];
-extern uint32 *huff_table[];
-extern int *huff_bits[];
diff --git a/converter/ppm/ppmtompeg/iframe.c b/converter/ppm/ppmtompeg/iframe.c
index f4d7665a..7552f413 100644
--- a/converter/ppm/ppmtompeg/iframe.c
+++ b/converter/ppm/ppmtompeg/iframe.c
@@ -11,10 +11,9 @@
  *  GetIQScale                               *
  *  ResetIFrameStats                             *
  *  ShowIFrameSummary                            *
- *  EstimateSecondsPerIFrame                         *
  *  EncodeYDC                                *
  *  EncodeCDC                                *
- *      time_elapsed                                                         *
+ *  time_elapsed                                                         *
  *                                       *
  *===========================================================================*/
 
@@ -43,11 +42,11 @@
  * HEADER FILES *
  *==============*/
 
+#include <time.h>  /* Defines CLOCKS_PER_SEC, if this system has clock() */
 
-#ifdef CLOCKS_PER_SEC
-#include <times.h>
-#else
-#include <sys/times.h>
+#ifndef CLOCKS_PER_SEC
+  /* System doesn't have clock(); we assume it has times() instead */
+  #include <sys/times.h>
 #endif
 
 #include <sys/types.h>
@@ -67,12 +66,14 @@
 #include "specifics.h"
 #include "opts.h"
 
+#include "iframe.h"
+
 /*==================*
  * STATIC VARIABLES *
  *==================*/
 
-static  int lastNumBits = 0;
-static  int lastIFrame = 0;
+static int lastNumBits = 0;
+static int lastIFrame = 0;
 static int numBlocks = 0;
 static int numBits;
 static int numFrames = 0;
@@ -128,24 +129,11 @@ int fCodeI, fCodeP, fCodeB;
 boolean printSNR = FALSE;
 boolean printMSE = FALSE;
 boolean decodeRefFrames = FALSE;
-Block **dct=NULL, **dctr=NULL, **dctb=NULL;
-dct_data_type   **dct_data; /* used in p/bframe.c */
 int  TIME_RATE;
 
 
-/*=====================*
- * EXPORTED PROCEDURES *
- *=====================*/
-extern void PrintItoIBitRate _ANSI_ARGS_((int const numBits, int const frameNum));
 
-/*===============================*
- * INTERNAL PROCEDURE prototypes *
- *===============================*/
-void AllocDctBlocks(void );
-int SetFCodeHelper (int const sr);
-void CalcDistortion (MpegFrame * const current, int const y, int const x);
-
-int
+static int
 SetFCodeHelper(int const SR) {
 
     int     range,fCode;
@@ -296,6 +284,68 @@ GetIQScale(void) {
 
 
 
+static void
+CalcDistortion(MpegFrame * const current,
+               int         const y,
+               int         const x) {
+
+    int qscale, distort=0;
+    Block decblk;
+    FlatBlock fblk;
+    int datarate = 0;
+  
+    for (qscale = 1; qscale < 32; qscale ++) {
+        distort = 0;
+        datarate = 0;
+        Mpost_QuantZigBlock(dct[y][x], fblk, qscale, TRUE);
+        Mpost_UnQuantZigBlock(fblk, decblk, qscale, TRUE);
+        if (collect_distortion_detailed) datarate += CalcRLEHuffLength(fblk);
+        mpeg_jrevdct((int16 *)decblk);
+        distort += mse(current->y_blocks[y][x], decblk);
+
+        Mpost_QuantZigBlock(dct[y][x+1], fblk, qscale, TRUE);
+        Mpost_UnQuantZigBlock(fblk, decblk, qscale, TRUE);
+        if (collect_distortion_detailed) datarate += CalcRLEHuffLength(fblk);
+        mpeg_jrevdct((int16 *)decblk);
+        distort += mse(current->y_blocks[y][x+1], decblk);
+
+        Mpost_QuantZigBlock(dct[y+1][x], fblk, qscale, TRUE);
+        Mpost_UnQuantZigBlock(fblk, decblk, qscale, TRUE);
+        if (collect_distortion_detailed) datarate += CalcRLEHuffLength(fblk);
+        mpeg_jrevdct((int16 *)decblk);
+        distort += mse(current->y_blocks[y+1][x], decblk);
+
+        Mpost_QuantZigBlock(dct[y+1][x+1], fblk, qscale, TRUE);
+        Mpost_UnQuantZigBlock(fblk, decblk, qscale, TRUE);
+        if (collect_distortion_detailed) datarate += CalcRLEHuffLength(fblk);
+        mpeg_jrevdct((int16 *)decblk);
+        distort += mse(current->y_blocks[y+1][x+1], decblk);
+
+        Mpost_QuantZigBlock(dctb[y >> 1][x >> 1], fblk, qscale, TRUE);
+        Mpost_UnQuantZigBlock(fblk, decblk, qscale, TRUE);
+        if (collect_distortion_detailed) datarate += CalcRLEHuffLength(fblk);
+        mpeg_jrevdct((int16 *)decblk);
+        distort += mse(current->cb_blocks[y>>1][x>>1], decblk);
+
+        Mpost_QuantZigBlock(dctr[y >> 1][x >> 1], fblk, qscale, TRUE);
+        Mpost_UnQuantZigBlock(fblk, decblk, qscale, TRUE);
+        if (collect_distortion_detailed) datarate += CalcRLEHuffLength(fblk);
+        mpeg_jrevdct((int16 *)decblk);
+        distort += mse(current->cr_blocks[y >> 1][x >> 1], decblk);
+
+        if (!collect_distortion_detailed) {
+            fprintf(distortion_fp, "\t%d\n", distort);
+        } else if (collect_distortion_detailed == 1) {
+            fprintf(distortion_fp, "\t%d\t%d\n", distort, datarate);
+        } else {
+            fprintf(fp_table_rate[qscale-1], "%d\n", datarate);
+            fprintf(fp_table_dist[qscale-1], "%d\n", distort);
+        }
+    }
+}
+
+
+
 /*===========================================================================*
  *
  * GenIFrame
@@ -618,24 +668,6 @@ ShowIFrameSummary(unsigned int const inputFrameBits,
 
 /*===========================================================================*
  *
- * EstimateSecondsPerIFrame
- *
- *  estimates the number of seconds required per I-frame
- *
- * RETURNS: seconds (floating point value)
- *
- * SIDE EFFECTS:    none
- *
- *===========================================================================*/
-float
-EstimateSecondsPerIFrame()
-{
-    return (float)totalTime/((float)TIME_RATE*(float)numFrames);
-}
-
-
-/*===========================================================================*
- *
  * EncodeYDC
  *
  *  Encode the DC portion of a DCT of a luminance block
@@ -926,53 +958,6 @@ PrintItoIBitRate(int const numBits,
 
 
 
-/*===========================================================================*
- *
- * AllocDctBlocks
- *
- *  allocate memory for dct blocks
- *
- * RETURNS: nothing
- *
- * SIDE EFFECTS:    creates dct, dctr, dctb
- *
- *===========================================================================*/
-void
-AllocDctBlocks(void) {
-    int dctx, dcty;
-    int i;
-
-    dctx = Fsize_x / DCTSIZE;
-    dcty = Fsize_y / DCTSIZE;
-
-    dct = (Block **) malloc(sizeof(Block *) * dcty);
-    ERRCHK(dct, "malloc");
-    for (i = 0; i < dcty; i++) {
-        dct[i] = (Block *) malloc(sizeof(Block) * dctx);
-        ERRCHK(dct[i], "malloc");
-    }
-
-    dct_data = (dct_data_type **) malloc(sizeof(dct_data_type *) * dcty);
-    ERRCHK(dct_data, "malloc");
-    for (i = 0; i < dcty; i++) {
-        dct_data[i] = (dct_data_type *) malloc(sizeof(dct_data_type) * dctx);
-        ERRCHK(dct[i], "malloc");
-    }
-
-    dctr = (Block **) malloc(sizeof(Block *) * (dcty >> 1));
-    dctb = (Block **) malloc(sizeof(Block *) * (dcty >> 1));
-    ERRCHK(dctr, "malloc");
-    ERRCHK(dctb, "malloc");
-    for (i = 0; i < (dcty >> 1); i++) {
-        dctr[i] = (Block *) malloc(sizeof(Block) * (dctx >> 1));
-        dctb[i] = (Block *) malloc(sizeof(Block) * (dctx >> 1));
-        ERRCHK(dctr[i], "malloc");
-        ERRCHK(dctb[i], "malloc");
-    }
-}
-
-
-
 /*======================================================================*
  *
  * time_elapsed
@@ -994,69 +979,3 @@ int32 time_elapsed(void) {
     return timeBuffer.tms_utime + timeBuffer.tms_stime;
 #endif
 }
-
-
-
-void
-CalcDistortion(MpegFrame * const current,
-               int         const y,
-               int         const x) {
-
-    int qscale, distort=0;
-    Block decblk;
-    FlatBlock fblk;
-    int datarate = 0;
-  
-    for (qscale = 1; qscale < 32; qscale ++) {
-        distort = 0;
-        datarate = 0;
-        Mpost_QuantZigBlock(dct[y][x], fblk, qscale, TRUE);
-        Mpost_UnQuantZigBlock(fblk, decblk, qscale, TRUE);
-        if (collect_distortion_detailed) datarate += CalcRLEHuffLength(fblk);
-        mpeg_jrevdct((int16 *)decblk);
-        distort += mse(current->y_blocks[y][x], decblk);
-
-        Mpost_QuantZigBlock(dct[y][x+1], fblk, qscale, TRUE);
-        Mpost_UnQuantZigBlock(fblk, decblk, qscale, TRUE);
-        if (collect_distortion_detailed) datarate += CalcRLEHuffLength(fblk);
-        mpeg_jrevdct((int16 *)decblk);
-        distort += mse(current->y_blocks[y][x+1], decblk);
-
-        Mpost_QuantZigBlock(dct[y+1][x], fblk, qscale, TRUE);
-        Mpost_UnQuantZigBlock(fblk, decblk, qscale, TRUE);
-        if (collect_distortion_detailed) datarate += CalcRLEHuffLength(fblk);
-        mpeg_jrevdct((int16 *)decblk);
-        distort += mse(current->y_blocks[y+1][x], decblk);
-
-        Mpost_QuantZigBlock(dct[y+1][x+1], fblk, qscale, TRUE);
-        Mpost_UnQuantZigBlock(fblk, decblk, qscale, TRUE);
-        if (collect_distortion_detailed) datarate += CalcRLEHuffLength(fblk);
-        mpeg_jrevdct((int16 *)decblk);
-        distort += mse(current->y_blocks[y+1][x+1], decblk);
-
-        Mpost_QuantZigBlock(dctb[y >> 1][x >> 1], fblk, qscale, TRUE);
-        Mpost_UnQuantZigBlock(fblk, decblk, qscale, TRUE);
-        if (collect_distortion_detailed) datarate += CalcRLEHuffLength(fblk);
-        mpeg_jrevdct((int16 *)decblk);
-        distort += mse(current->cb_blocks[y>>1][x>>1], decblk);
-
-        Mpost_QuantZigBlock(dctr[y >> 1][x >> 1], fblk, qscale, TRUE);
-        Mpost_UnQuantZigBlock(fblk, decblk, qscale, TRUE);
-        if (collect_distortion_detailed) datarate += CalcRLEHuffLength(fblk);
-        mpeg_jrevdct((int16 *)decblk);
-        distort += mse(current->cr_blocks[y >> 1][x >> 1], decblk);
-
-        if (!collect_distortion_detailed) {
-            fprintf(distortion_fp, "\t%d\n", distort);
-        } else if (collect_distortion_detailed == 1) {
-            fprintf(distortion_fp, "\t%d\t%d\n", distort, datarate);
-        } else {
-            fprintf(fp_table_rate[qscale-1], "%d\n", datarate);
-            fprintf(fp_table_dist[qscale-1], "%d\n", distort);
-        }
-    }
-}
-
-
-
-
diff --git a/converter/ppm/ppmtompeg/jpeg.c b/converter/ppm/ppmtompeg/jpeg.c
index b51cf083..a703cf39 100644
--- a/converter/ppm/ppmtompeg/jpeg.c
+++ b/converter/ppm/ppmtompeg/jpeg.c
@@ -15,6 +15,7 @@
  * HEADER FILES *
  *==============*/
 #define _XOPEN_SOURCE    /* Make sure stdio.h contains fileno() */
+#include <unistd.h>
 #include <stdio.h>
 #include "all.h"
 /* With the lossless jpeg patch applied to the Jpeg library
@@ -52,6 +53,17 @@ extern void jcopy_sample_rows JPP((JSAMPARRAY input_array, int source_row,
 #define HEADER_SIZE 607   /*JFIF header size used on output images*/
 
 
+static int
+minDctVScaledSize(struct jpeg_decompress_struct const cinfo) {
+
+#if JPEG_LIB_VERSION >= 70
+    return cinfo.min_DCT_v_scaled_size;
+#else
+    return cinfo.min_DCT_scaled_size;
+#endif
+}
+
+
 
 /*=======================================================================*
  *                                                                       *
@@ -416,11 +428,7 @@ ReadJPEG(MpegFrame * const mf,
      */
   
     /* set parameters for decompression */
-#ifdef JPEG4
-    cinfo.want_raw_output = TRUE;
-#else
     cinfo.raw_data_out = TRUE;
-#endif
     cinfo.out_color_space = JCS_YCbCr;
   
     /* calculate image output dimensions */
@@ -466,13 +474,7 @@ ReadJPEG(MpegFrame * const mf,
     /* Make an 8-row-high sample array that will go away when done
        with image
     */
-#ifdef JPEG4
-    buffer_height = 8;  /* could be 2, 4,8 rows high */
-#elif JPEG_LIB_VERSION >= 70
-    buffer_height = cinfo.max_v_samp_factor * cinfo.min_DCT_v_scaled_size;
-#else
-    buffer_height = cinfo.max_v_samp_factor * cinfo.min_DCT_scaled_size;
-#endif
+    buffer_height = cinfo.max_v_samp_factor * minDctVScaledSize(cinfo);
   
     for(cp=0,compptr = cinfo.comp_info;cp<cinfo.num_components;
         cp++,compptr++) {
@@ -496,11 +498,7 @@ ReadJPEG(MpegFrame * const mf,
 
     while (cinfo.output_scanline < cinfo.output_height) {
 
-#ifdef JPEG4
-        (void) jpeg_read_raw_scanlines(&cinfo, scanarray, buffer_height);
-#else
         (void) jpeg_read_raw_data(&cinfo, scanarray, buffer_height);
-#endif
 
         /* alter subsample ratio's if neccessary */
         if ((h_samp[0]==2) && (h_samp[1]==1) && (h_samp[2]==1) &&
diff --git a/converter/ppm/ppmtompeg/mheaders.c b/converter/ppm/ppmtompeg/mheaders.c
index 8a51c089..4cfe49ac 100644
--- a/converter/ppm/ppmtompeg/mheaders.c
+++ b/converter/ppm/ppmtompeg/mheaders.c
@@ -278,24 +278,350 @@ const double VidRateNum[9]={1.0, 23.976, 24.0, 25.0, 29.97, 30.0,
                              50.0 ,59.94, 60.0};
 
 
-/*===============================*
- * INTERNAL PROCEDURE prototypes *
- *===============================*/
-
-static void	GenMBAddrIncr _ANSI_ARGS_((BitBucket *bb, uint32 addr_incr));
-static void	GenPictHead _ANSI_ARGS_((BitBucket *bb, uint32 temp_ref,
-		    uint32 code_type, uint32 vbv_delay,
-		    int32 full_pel_forw_flag, uint32 forw_f_code,
-		    int32 full_pel_back_flag, uint32 back_f_code,
-		    uint8 *extra_info, uint32 extra_info_size,
-		    uint8 *ext_data, uint32 ext_data_size,
-		    uint8 *user_data, uint32 user_data_size));
-static void	GenMBType _ANSI_ARGS_((BitBucket *bb, uint32 pict_code_type,
-		  uint32 mb_quant, uint32 motion_forw, uint32 motion_back,
-		  uint32 mb_pattern, uint32 mb_intra));
-static void	GenMotionCode _ANSI_ARGS_((BitBucket * const bb, int32 const vector));
-static void	GenBlockPattern _ANSI_ARGS_((BitBucket *bb,
-					     uint32 mb_pattern));
+/*=====================*
+ * INTERNAL PROCEDURES *
+ *=====================*/
+
+/*===========================================================================*
+ *
+ * GenMBType
+ *
+ *	generate macroblock type with given attributes
+ *	append result to the specified bitstream
+ *
+ * RETURNS:	nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+static void
+GenMBType(bbPtr, pict_code_type, mb_quant, motion_forw, motion_back,
+          mb_pattern, mb_intra)
+    BitBucket *bbPtr;
+    uint32 pict_code_type;
+    uint32 mb_quant;
+    uint32 motion_forw;
+    uint32 motion_back;
+    uint32 mb_pattern;
+    uint32 mb_intra;
+{
+    int code;
+
+    switch (pict_code_type) {
+    case 1:
+        if ((motion_forw != 0) || (motion_back != 0) || (mb_pattern != 0) || (mb_intra != 1)) {
+            perror("Illegal parameters for macroblock type.");
+            exit(-1);
+        }
+        if (mb_quant) {
+            Bitio_Write(bbPtr, 0x1, 2);
+        } else {
+            Bitio_Write(bbPtr, 0x1, 1);
+        }
+        break;
+
+    case 2:
+        code = 0;
+        if (mb_quant) {
+            code += 16;
+        }
+        if (motion_forw) {
+            code += 8;
+        }
+        if (motion_back) {
+            code += 4;
+        }
+        if (mb_pattern) {
+            code += 2;
+        }
+        if (mb_intra) {
+            code += 1;
+        }
+
+        switch (code) {
+        case 1:
+            Bitio_Write(bbPtr, 0x3, 5);
+            break;
+        case 2:
+            Bitio_Write(bbPtr, 0x1, 2);
+            break;
+        case 8:
+            Bitio_Write(bbPtr, 0x1, 3);
+            break;
+        case 10:
+            Bitio_Write(bbPtr, 0x1, 1);
+            break;
+        case 17:
+            Bitio_Write(bbPtr, 0x1, 6);
+            break;
+        case 18:
+            Bitio_Write(bbPtr, 0x1, 5);
+            break;
+        case 26:
+            Bitio_Write(bbPtr, 0x2, 5);
+            break;
+        default:
+            perror("Illegal parameters for macroblock type.");
+            exit(-1);
+            break;
+        }
+        break;
+
+    case 3:
+        code = 0;
+        if (mb_quant) {
+            code += 16;
+        }
+        if (motion_forw) {
+            code += 8;
+        }
+        if (motion_back) {
+            code += 4;
+        }
+        if (mb_pattern) {
+            code += 2;
+        }
+        if (mb_intra) {
+            code += 1;
+        }
+
+        switch (code) {
+        case 12:
+            Bitio_Write(bbPtr, 0x2, 2);
+            break;
+        case 14:
+            Bitio_Write(bbPtr, 0x3, 2);
+            break;
+        case 4:
+            Bitio_Write(bbPtr, 0x2, 3);
+            break;
+        case 6:
+            Bitio_Write(bbPtr, 0x3, 3);
+            break;
+        case 8:
+            Bitio_Write(bbPtr, 0x2, 4);
+            break;
+        case 10:
+            Bitio_Write(bbPtr, 0x3, 4);
+            break;
+        case 1:
+            Bitio_Write(bbPtr, 0x3, 5);
+            break;
+        case 30:
+            Bitio_Write(bbPtr, 0x2, 5);
+            break;
+        case 26:
+            Bitio_Write(bbPtr, 0x3, 6);
+            break;
+        case 22:
+            Bitio_Write(bbPtr, 0x2, 6);
+            break;
+        case 17:
+            Bitio_Write(bbPtr, 0x1, 6);
+            break;
+        default:
+            perror("Illegal parameters for macroblock type.");
+            exit(-1);
+            break;
+        }
+        break;
+    }
+}
+
+
+/*===========================================================================*
+ *
+ * GenMotionCode
+ *
+ *	generate motion vector output with given value
+ *	append result to the specified bitstream
+ *
+ * RETURNS:	nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+static void
+GenMotionCode(BitBucket * const bbPtr,
+              int32       const vector) {
+
+    uint32 code, num;
+
+    if ((vector < -16) || (vector > 16)) {
+        perror("Motion vector out of range.");
+        fprintf(stderr, "Motion vector out of range:  vector = %d\n", vector);
+        exit(-1);
+    }
+    code = mbMotionVectorTable[vector + 16][0];
+    num = mbMotionVectorTable[vector + 16][1];
+
+    Bitio_Write(bbPtr, code, num);
+}
+
+
+/*===========================================================================*
+ *
+ * GenBlockPattern
+ *
+ *	generate macroblock pattern output
+ *	append result to the specified bitstream
+ *
+ * RETURNS:	nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+static void
+GenBlockPattern(bbPtr, mb_pattern)
+    BitBucket *bbPtr;
+    uint32 mb_pattern;
+{
+    uint32 code, num;
+
+    code = mbPatTable[mb_pattern][0];
+    num = mbPatTable[mb_pattern][1];
+
+    Bitio_Write(bbPtr, code, num);
+}
+
+
+/*===========================================================================*
+ *
+ * GenMBAddrIncr
+ *
+ *	generate macroblock address increment output
+ *	append result to the specified bitstream
+ *
+ * RETURNS:	nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+static void
+GenMBAddrIncr(bbPtr, addr_incr)
+    BitBucket *bbPtr;
+    uint32 addr_incr;
+{
+    uint32 code;
+    uint32 num;
+
+    code = mbAddrIncrTable[addr_incr][0];
+    num = mbAddrIncrTable[addr_incr][1];
+
+    Bitio_Write(bbPtr, code, num);
+}
+
+
+/*===========================================================================*
+ *
+ * GenPictHead
+ *
+ *	generate picture header with given attributes
+ *	append result to the specified bitstream
+ *
+ * RETURNS:	nothing
+ *
+ * SIDE EFFECTS:    none
+ *
+ *===========================================================================*/
+static void
+GenPictHead(bbPtr, temp_ref, code_type, vbv_delay, full_pel_forw_flag,
+            forw_f_code, full_pel_back_flag, back_f_code, extra_info,
+            extra_info_size, ext_data, ext_data_size, user_data,
+            user_data_size)
+    BitBucket *bbPtr;
+    uint32 temp_ref;
+    uint32 code_type;
+    uint32 vbv_delay;
+    int32 full_pel_forw_flag;
+    uint32 forw_f_code;
+    int32 full_pel_back_flag;
+    uint32 back_f_code;
+    uint8 *extra_info;
+    uint32 extra_info_size;
+    uint8 *ext_data;
+    uint32 ext_data_size;
+    uint8 *user_data;
+    uint32 user_data_size;
+{
+    /* Write picture start code. */
+    Bitio_Write(bbPtr, PICT_START_CODE, 32);
+
+    /* Temp reference. */
+    Bitio_Write(bbPtr, temp_ref, 10);
+
+    /* Code_type. */
+    if (code_type == 0)
+        code_type = 1;
+
+    Bitio_Write(bbPtr, code_type, 3);
+
+    /* vbv_delay. */
+    vbv_delay = 0xffff;		    /* see page 36 (section 2.4.3.4) */
+    Bitio_Write(bbPtr, vbv_delay, 16);
+
+    if ((code_type == 2) || (code_type == 3)) {
+
+        /* Full pel forw flag. */
+
+        if (full_pel_forw_flag)
+            Bitio_Write(bbPtr, 0x01, 1);
+        else
+            Bitio_Write(bbPtr, 0x00, 1);
+
+        /* Forw f code. */
+
+        Bitio_Write(bbPtr, forw_f_code, 3);
+    }
+    if (code_type == 3) {
+
+        /* Full pel back flag. */
+
+        if (full_pel_back_flag)
+            Bitio_Write(bbPtr, 0x01, 1);
+        else
+            Bitio_Write(bbPtr, 0x00, 1);
+
+        /* Back f code. */
+
+        Bitio_Write(bbPtr, back_f_code, 3);
+    }
+    /* Extra bit picture info. */
+
+    if (extra_info != NULL) {
+        unsigned int i;
+        for (i = 0; i < extra_info_size; ++i) {
+            Bitio_Write(bbPtr, 0x01, 1);
+            Bitio_Write(bbPtr, extra_info[i], 8);
+        }
+    }
+    Bitio_Write(bbPtr, 0x00, 1);
+
+    /* next start code */
+    Bitio_BytePad(bbPtr);
+
+    /* Write ext data if present. */
+
+    if (ext_data != NULL) {
+        unsigned int i;
+
+        Bitio_Write(bbPtr, EXT_START_CODE, 32);
+
+        for (i = 0; i < ext_data_size; ++i)
+            Bitio_Write(bbPtr, ext_data[i], 8);
+        Bitio_BytePad(bbPtr);
+    }
+    /* Write user data if present. */
+    if (user_data != NULL) {
+        unsigned int i;
+        Bitio_Write(bbPtr, USER_START_CODE, 32);
+
+        for (i = 0; i < user_data_size; ++i)
+            Bitio_Write(bbPtr, user_data[i], 8);
+
+        Bitio_BytePad(bbPtr);
+    }
+}
 
 
 /*=====================*
@@ -767,7 +1093,8 @@ if ( addr_incr != 1 )
     }
 
     /* Generate mb type code. */
-    GenMBType(bbPtr, pict_code_type, mb_quant, motion_forw, motion_back, mb_pattern, mb_intra);
+    GenMBType(bbPtr, pict_code_type, mb_quant,
+              motion_forw, motion_back, mb_pattern, mb_intra);
 
     /* MB quant. */
     if (mb_quant) {
@@ -831,353 +1158,6 @@ if ( addr_incr != 1 )
 }
 
 
-/*=====================*
- * INTERNAL PROCEDURES *
- *=====================*/
-
-/*===========================================================================*
- *
- * GenMBType
- *
- *	generate macroblock type with given attributes
- *	append result to the specified bitstream
- *
- * RETURNS:	nothing
- *
- * SIDE EFFECTS:    none
- *
- *===========================================================================*/
-static void
-GenMBType(bbPtr, pict_code_type, mb_quant, motion_forw, motion_back,
-	  mb_pattern, mb_intra)
-    BitBucket *bbPtr;
-    uint32 pict_code_type;
-    uint32 mb_quant;
-    uint32 motion_forw;
-    uint32 motion_back;
-    uint32 mb_pattern;
-    uint32 mb_intra;
-{
-    int code;
-
-    switch (pict_code_type) {
-    case 1:
-	if ((motion_forw != 0) || (motion_back != 0) || (mb_pattern != 0) || (mb_intra != 1)) {
-	    perror("Illegal parameters for macroblock type.");
-	    exit(-1);
-	}
-	if (mb_quant) {
-	    Bitio_Write(bbPtr, 0x1, 2);
-	} else {
-	    Bitio_Write(bbPtr, 0x1, 1);
-	}
-	break;
-
-    case 2:
-	code = 0;
-	if (mb_quant) {
-	    code += 16;
-	}
-	if (motion_forw) {
-	    code += 8;
-	}
-	if (motion_back) {
-	    code += 4;
-	}
-	if (mb_pattern) {
-	    code += 2;
-	}
-	if (mb_intra) {
-	    code += 1;
-	}
-
-	switch (code) {
-	case 1:
-	    Bitio_Write(bbPtr, 0x3, 5);
-	    break;
-	case 2:
-	    Bitio_Write(bbPtr, 0x1, 2);
-	    break;
-	case 8:
-	    Bitio_Write(bbPtr, 0x1, 3);
-	    break;
-	case 10:
-	    Bitio_Write(bbPtr, 0x1, 1);
-	    break;
-	case 17:
-	    Bitio_Write(bbPtr, 0x1, 6);
-	    break;
-	case 18:
-	    Bitio_Write(bbPtr, 0x1, 5);
-	    break;
-	case 26:
-	    Bitio_Write(bbPtr, 0x2, 5);
-	    break;
-	default:
-	    perror("Illegal parameters for macroblock type.");
-	    exit(-1);
-	    break;
-	}
-	break;
-
-    case 3:
-	code = 0;
-	if (mb_quant) {
-	    code += 16;
-	}
-	if (motion_forw) {
-	    code += 8;
-	}
-	if (motion_back) {
-	    code += 4;
-	}
-	if (mb_pattern) {
-	    code += 2;
-	}
-	if (mb_intra) {
-	    code += 1;
-	}
-
-	switch (code) {
-	case 12:
-	    Bitio_Write(bbPtr, 0x2, 2);
-	    break;
-	case 14:
-	    Bitio_Write(bbPtr, 0x3, 2);
-	    break;
-	case 4:
-	    Bitio_Write(bbPtr, 0x2, 3);
-	    break;
-	case 6:
-	    Bitio_Write(bbPtr, 0x3, 3);
-	    break;
-	case 8:
-	    Bitio_Write(bbPtr, 0x2, 4);
-	    break;
-	case 10:
-	    Bitio_Write(bbPtr, 0x3, 4);
-	    break;
-	case 1:
-	    Bitio_Write(bbPtr, 0x3, 5);
-	    break;
-	case 30:
-	    Bitio_Write(bbPtr, 0x2, 5);
-	    break;
-	case 26:
-	    Bitio_Write(bbPtr, 0x3, 6);
-	    break;
-	case 22:
-	    Bitio_Write(bbPtr, 0x2, 6);
-	    break;
-	case 17:
-	    Bitio_Write(bbPtr, 0x1, 6);
-	    break;
-	default:
-	    perror("Illegal parameters for macroblock type.");
-	    exit(-1);
-	    break;
-	}
-	break;
-    }
-}
-
-
-/*===========================================================================*
- *
- * GenMotionCode
- *
- *	generate motion vector output with given value
- *	append result to the specified bitstream
- *
- * RETURNS:	nothing
- *
- * SIDE EFFECTS:    none
- *
- *===========================================================================*/
-static void
-GenMotionCode(BitBucket * const bbPtr,
-              int32       const vector) {
-
-    uint32 code, num;
-
-    if ((vector < -16) || (vector > 16)) {
-	perror("Motion vector out of range.");
-	fprintf(stderr, "Motion vector out of range:  vector = %d\n", vector);
-	exit(-1);
-    }
-    code = mbMotionVectorTable[vector + 16][0];
-    num = mbMotionVectorTable[vector + 16][1];
-
-    Bitio_Write(bbPtr, code, num);
-}
-
-
-/*===========================================================================*
- *
- * GenBlockPattern
- *
- *	generate macroblock pattern output
- *	append result to the specified bitstream
- *
- * RETURNS:	nothing
- *
- * SIDE EFFECTS:    none
- *
- *===========================================================================*/
-static void
-GenBlockPattern(bbPtr, mb_pattern)
-    BitBucket *bbPtr;
-    uint32 mb_pattern;
-{
-    uint32 code, num;
-
-    code = mbPatTable[mb_pattern][0];
-    num = mbPatTable[mb_pattern][1];
-
-    Bitio_Write(bbPtr, code, num);
-}
-
-
-/*===========================================================================*
- *
- * GenMBAddrIncr
- *
- *	generate macroblock address increment output
- *	append result to the specified bitstream
- *
- * RETURNS:	nothing
- *
- * SIDE EFFECTS:    none
- *
- *===========================================================================*/
-static void
-GenMBAddrIncr(bbPtr, addr_incr)
-    BitBucket *bbPtr;
-    uint32 addr_incr;
-{
-    uint32 code;
-    uint32 num;
-
-    code = mbAddrIncrTable[addr_incr][0];
-    num = mbAddrIncrTable[addr_incr][1];
-
-    Bitio_Write(bbPtr, code, num);
-}
-
-
-/*===========================================================================*
- *
- * GenPictHead
- *
- *	generate picture header with given attributes
- *	append result to the specified bitstream
- *
- * RETURNS:	nothing
- *
- * SIDE EFFECTS:    none
- *
- *===========================================================================*/
-static void
-GenPictHead(bbPtr, temp_ref, code_type, vbv_delay, full_pel_forw_flag,
-	    forw_f_code, full_pel_back_flag, back_f_code, extra_info,
-	    extra_info_size, ext_data, ext_data_size, user_data,
-	    user_data_size)
-    BitBucket *bbPtr;
-    uint32 temp_ref;
-    uint32 code_type;
-    uint32 vbv_delay;
-    int32 full_pel_forw_flag;
-    uint32 forw_f_code;
-    int32 full_pel_back_flag;
-    uint32 back_f_code;
-    uint8 *extra_info;
-    uint32 extra_info_size;
-    uint8 *ext_data;
-    uint32 ext_data_size;
-    uint8 *user_data;
-    uint32 user_data_size;
-{
-    int i;
-
-    /* Write picture start code. */
-    Bitio_Write(bbPtr, PICT_START_CODE, 32);
-
-    /* Temp reference. */
-    Bitio_Write(bbPtr, temp_ref, 10);
-
-    /* Code_type. */
-    if (code_type == 0) {
-	code_type = 1;
-    }
-    Bitio_Write(bbPtr, code_type, 3);
-
-    /* vbv_delay. */
-    vbv_delay = 0xffff;		    /* see page 36 (section 2.4.3.4) */
-    Bitio_Write(bbPtr, vbv_delay, 16);
-
-    if ((code_type == 2) || (code_type == 3)) {
-
-	/* Full pel forw flag. */
-
-	if (full_pel_forw_flag) {
-	    Bitio_Write(bbPtr, 0x01, 1);
-	} else {
-	    Bitio_Write(bbPtr, 0x00, 1);
-	}
-
-	/* Forw f code. */
-
-	Bitio_Write(bbPtr, forw_f_code, 3);
-    }
-    if (code_type == 3) {
-
-	/* Full pel back flag. */
-
-	if (full_pel_back_flag) {
-	    Bitio_Write(bbPtr, 0x01, 1);
-	} else {
-	    Bitio_Write(bbPtr, 0x00, 1);
-	}
-
-	/* Back f code. */
-
-	Bitio_Write(bbPtr, back_f_code, 3);
-    }
-    /* Extra bit picture info. */
-
-    if (extra_info != NULL) {
-	for (i = 0; i < extra_info_size; i++) {
-	    Bitio_Write(bbPtr, 0x01, 1);
-	    Bitio_Write(bbPtr, extra_info[i], 8);
-	}
-    }
-    Bitio_Write(bbPtr, 0x00, 1);
-
-    /* next start code */
-    Bitio_BytePad(bbPtr);
-
-    /* Write ext data if present. */
-
-    if (ext_data != NULL) {
-	Bitio_Write(bbPtr, EXT_START_CODE, 32);
-
-	for (i = 0; i < ext_data_size; i++) {
-	    Bitio_Write(bbPtr, ext_data[i], 8);
-	}
-	Bitio_BytePad(bbPtr);
-    }
-    /* Write user data if present. */
-    if (user_data != NULL) {
-	Bitio_Write(bbPtr, USER_START_CODE, 32);
-
-	for (i = 0; i < user_data_size; i++) {
-	    Bitio_Write(bbPtr, user_data[i], 8);
-	}
-	Bitio_BytePad(bbPtr);
-    }
-}
-
-
 #ifdef UNUSED_PROCEDURES
 
 /* GenMBEnd only used for `D` pictures. Shouldn't really ever be called. */
diff --git a/converter/ppm/ppmtompeg/mpeg.c b/converter/ppm/ppmtompeg/mpeg.c
index fafbb97a..6557f377 100644
--- a/converter/ppm/ppmtompeg/mpeg.c
+++ b/converter/ppm/ppmtompeg/mpeg.c
@@ -43,8 +43,8 @@
 #endif
 #include <sys/stat.h>
 
-#include "ppm.h"
 #include "nstring.h"
+#include "nsleep.h"
 
 #include "mtypes.h"
 #include "frames.h"
@@ -276,9 +276,8 @@ SetReferenceFrameType(const char * const type) {
 
 
 void
-SetBitRateFileName(fileName)
-    char *fileName;
-{
+SetBitRateFileName(const char * const fileName) {
+
     strcpy(bitRateFileName, fileName);
 }
 
@@ -318,7 +317,7 @@ finishFrameOutput(MpegFrame * const frameP,
 
 static void
 outputIFrame(MpegFrame * const frameP,
-             BitBucket * const bb,
+             BitBucket * const bbP,
              int         const realStart,
              int         const realEnd,
              MpegFrame * const pastRefFrameP,
@@ -326,7 +325,7 @@ outputIFrame(MpegFrame * const frameP,
       
     /* only start a new GOP with I */
     /* don't start GOP if only doing frames */
-    if ((!separateFiles) && (currentGOP >= gopSize)) {
+    if (!separateFiles && currentGOP >= gopSize) {
         boolean const closed = 
             (totalFramesSent == frameP->id || pastRefFrameP == NULL);
 
@@ -344,7 +343,7 @@ outputIFrame(MpegFrame * const frameP,
             }
       
             Mhead_GenSequenceHeader(
-                bb, Fsize_x, Fsize_y,
+                bbP, Fsize_x, Fsize_y,
                 /* pratio */    aspectRatio,
                 /* pict_rate */ frameRate, /* bit_rate */ bit_rate,
                 /* buf_size */  buf_size,  /* c_param_flag */ 1,
@@ -359,7 +358,7 @@ outputIFrame(MpegFrame * const frameP,
                        closed ? "YES" : "NO", frameP->id);
     
         ++num_gop;
-        Mhead_GenGOPHeader(bb,  /* drop_frame_flag */ 0,
+        Mhead_GenGOPHeader(bbP,  /* drop_frame_flag */ 0,
                            tc_hrs, tc_min, tc_sec, tc_pict,
                            closed, /* broken_link */ 0,
                            /* ext_data */ NULL, /* ext_data_size */ 0,
@@ -368,16 +367,16 @@ outputIFrame(MpegFrame * const frameP,
         if (pastRefFrameP == NULL)
             SetGOPStartTime(0);
         else
-            SetGOPStartTime(pastRefFrameP->id+1);
+            SetGOPStartTime(pastRefFrameP->id + 1);
     }
       
-    if ((frameP->id >= realStart) && (frameP->id <= realEnd))
-        GenIFrame(bb, frameP);
+    if (frameP->id >= realStart && frameP->id <= realEnd)
+        GenIFrame(bbP, frameP);
       
-    numI--;
+    --numI;
     timeMask &= 0x6;
       
-    currentGOP++;
+    ++currentGOP;
     IncrementTCTime();
 }
 
@@ -393,10 +392,10 @@ outputPFrame(MpegFrame * const frameP,
     if ((frameP->id >= realStart) && (frameP->id <= realEnd))
         GenPFrame(bbP, frameP, pastRefFrameP);
 
-    numP--;
+    --numP;
     timeMask &= 0x5;
     
-    currentGOP++;
+    ++currentGOP;
     IncrementTCTime();
 }
 
@@ -498,6 +497,9 @@ processBFrames(MpegFrame *          const pastRefFrameP,
 
    But do only those B frames whose frame numbers are within the range
    'realStart' through 'realEnd'.
+
+   Output the frames to the output stream 'wholeStreamBbP'.  If NULL,
+   output each frame to its own individual file instead.
 -----------------------------------------------------------------------------*/
     boolean const separateFiles = (wholeStreamBbP == NULL);
     unsigned int const firstBFrameNum = pastRefFrameP->id + 1;
@@ -551,7 +553,7 @@ processBFrames(MpegFrame *          const pastRefFrameP,
 
 static void
 processRefFrame(MpegFrame *    const frameP, 
-                BitBucket *    const bb_arg,
+                BitBucket *    const wholeStreamBbP,
                 int            const realStart,
                 int            const realEnd,
                 MpegFrame *    const pastRefFrameP,
@@ -564,26 +566,28 @@ processRefFrame(MpegFrame *    const frameP,
 
    But only if its frame number is within the range 'realStart'
    through 'realEnd'.
+
+   Output the frame to the output stream 'wholeStreamBbP'.  If NULL,
+   output it to its own individual file instead.
 -----------------------------------------------------------------------------*/
     if (frameP->id >= realStart && frameP->id <= realEnd) {
-        boolean separateFiles;
-        BitBucket * bb;
+        bool const separateFiles = (wholeStreamBbP == NULL);
   
-        separateFiles = (bb_arg == NULL);
+        BitBucket * bbP;
   
-        if ( separateFiles )
-            bb = bitioNew(outputFileName, frameP->id, remoteIO);
+        if (separateFiles)
+            bbP = bitioNew(outputFileName, frameP->id, remoteIO);
         else
-            bb = bb_arg;
+            bbP = wholeStreamBbP;
   
         /* first, output this reference frame */
         switch (frameP->type) {
         case TYPE_IFRAME:
-            outputIFrame(frameP, bb, realStart, realEnd, pastRefFrameP, 
+            outputIFrame(frameP, bbP, realStart, realEnd, pastRefFrameP, 
                          separateFiles);
             break;
         case TYPE_PFRAME:
-            outputPFrame(frameP, bb, realStart, realEnd, pastRefFrameP);
+            outputPFrame(frameP, bbP, realStart, realEnd, pastRefFrameP);
             ShowRemainingTime(childProcess);
             break;
         default:
@@ -593,7 +597,7 @@ processRefFrame(MpegFrame *    const frameP,
         
         ++(*framesOutputP);
         
-        finishFrameOutput(frameP, bb, separateFiles, referenceFrame,
+        finishFrameOutput(frameP, bbP, separateFiles, referenceFrame,
                           childProcess, remoteIO);
     }
 }
@@ -702,7 +706,7 @@ readAndSaveFrame(struct inputSource * const inputSourceP,
 static void
 doFirstFrameStuff(enum frameContext const context,
                   const char *      const userDataFileName,
-                  BitBucket *       const bb,
+                  BitBucket *       const bbP,
                   int               const fsize_x,
                   int               const fsize_y,
                   int               const aspectRatio,
@@ -713,8 +717,12 @@ doFirstFrameStuff(enum frameContext const context,
 /*----------------------------------------------------------------------------
    Do stuff we have to do after reading the first frame in a sequence
    of frames requested of GenMPEGStream().
+
+   *bbP is the output stream to which to write any header stuff we have
+   to write.  If 'context' is such that there is no header stuff to write,
+   then 'bbP' is irrelevant.
 -----------------------------------------------------------------------------*/
-    *inputFrameBitsP = 24*Fsize_x*Fsize_y;
+    *inputFrameBitsP = 24 * Fsize_x * Fsize_y;
     SetBlocksPerSlice();
           
     if (context == CONTEXT_WHOLESTREAM) {
@@ -722,7 +730,7 @@ doFirstFrameStuff(enum frameContext const context,
         char * userData;
         unsigned int userDataSize;
 
-        assert(bb != NULL);
+        assert(bbP != NULL);
 
         DBG_PRINT(("Generating sequence header\n"));
         if (bitstreamMode == FIXED_RATE) {
@@ -770,7 +778,7 @@ doFirstFrameStuff(enum frameContext const context,
             userDataSize = strlen(userData);
             strfree(userDataString);
         }
-        Mhead_GenSequenceHeader(bb, Fsize_x, Fsize_y,
+        Mhead_GenSequenceHeader(bbP, Fsize_x, Fsize_y,
                                 /* pratio */ aspectRatio,
                                 /* pict_rate */ frameRate, 
                                 /* bit_rate */ bit_rate,
@@ -990,13 +998,21 @@ doAFrame(unsigned int         const frameNumber,
          unsigned int *       const framesReadP,
          unsigned int *       const framesOutputP,
          bool *               const firstFrameDoneP,
-         BitBucket *          const bbP,
+         BitBucket *          const wholeStreamBbP,
          unsigned int *       const inputFrameBitsP,
          bool *               const endOfStreamP) {
 /*----------------------------------------------------------------------------
    *endOfStreamP returned means we were unable to do a frame because
    the input stream has ended.  In that case, none of the other outputs
    are valid.
+
+   Process an input frame.  This can involve writing its description
+   to the output stream, saving it for later use, and/or writing
+   descriptions of previously saved frames to the output stream
+   because we now have enough information to do so.
+
+   Output the frames to the output stream 'wholeStreamBbP'.  If NULL,
+   output each frame to its own individual file instead.
 -----------------------------------------------------------------------------*/
     char const frameType = FType_Type(frameNumber);
     
@@ -1026,21 +1042,21 @@ doAFrame(unsigned int         const frameNumber,
             *endOfStreamP = FALSE;
 
             if (!*firstFrameDoneP) {
-                doFirstFrameStuff(context, userDataFileName,
-                                  bbP, Fsize_x, Fsize_y, aspectRatio,
+                doFirstFrameStuff(context, userDataFileName, wholeStreamBbP,
+                                  Fsize_x, Fsize_y, aspectRatio,
                                   frameRate, qtable, niqtable, 
                                   inputFrameBitsP);
             
                 *firstFrameDoneP = TRUE;
             }
-            processRefFrame(frameP, bbP, frameStart, frameEnd,
+            processRefFrame(frameP, wholeStreamBbP, frameStart, frameEnd,
                             pastRefFrameP, childProcess, outputFileName, 
                             framesReadP, framesOutputP);
                 
             if (pastRefFrameP) {
                 processBFrames(pastRefFrameP, frameP, realStart, realEnd,
                                inputSourceP, remoteIO, childProcess, 
-                               &IOtime, bbP, outputFileName,
+                               &IOtime, wholeStreamBbP, outputFileName,
                                framesReadP, framesOutputP, &currentGOP);
             }
             if (pastRefFrameP != NULL)
@@ -1086,17 +1102,20 @@ GenMPEGStream(struct inputSource * const inputSourceP,
    we stop where the stream ends if that is before 'frameEnd'.
 
 -----------------------------------------------------------------------------*/
-    BitBucket * bbP;
+    BitBucket * streamBbP;
+        /* The output stream to which we write all the frames.  NULL
+           means the frames are going to individual frame files.
+        */
     unsigned int frameNumber;
     bool endOfStream;
     bool firstFrameDone;
     int numBits;
     unsigned int firstFrame, lastFrame;
-    /* Frame numbers of the first and last frames we look at.  This
-       could be more than the the frames we actually encode because
-       we may need context (i.e. to encode a B frame, we need the subsequent
-       I or P frame).
-    */
+        /* Frame numbers of the first and last frames we look at.
+           This could be more than the the frames we actually encode
+           because we may need context (i.e. to encode a B frame, we
+           need the subsequent I or P frame).
+        */
     unsigned int framesRead;
         /* Number of frames we have read; for statistical purposes */
     MpegFrame * pastRefFrameP;
@@ -1150,10 +1169,10 @@ GenMPEGStream(struct inputSource * const inputSourceP,
     if (showBitRatePerFrame)
         OpenBitRateFile();  /* May modify showBitRatePerFrame */
 
-    if (context == CONTEXT_WHOLESTREAM || context == CONTEXT_GOP)
-        bbP = Bitio_New(ofP);
+    if (context == CONTEXT_JUSTFRAMES)
+        streamBbP = NULL;
     else
-        bbP = NULL;
+        streamBbP = Bitio_New(ofP);
 
     initTCTime(firstFrame);
 
@@ -1171,7 +1190,7 @@ GenMPEGStream(struct inputSource * const inputSourceP,
                  frameStart, frameEnd, realStart, realEnd,
                  childProcess, outputFileName,
                  pastRefFrameP, &pastRefFrameP,
-                 &framesRead, &framesOutput, &firstFrameDone, bbP,
+                 &framesRead, &framesOutput, &firstFrameDone, streamBbP,
                  inputFrameBitsP, &endOfStream);
     }
     
@@ -1180,10 +1199,10 @@ GenMPEGStream(struct inputSource * const inputSourceP,
     
     /* SEQUENCE END CODE */
     if (context == CONTEXT_WHOLESTREAM)
-        Mhead_GenSequenceEnder(bbP);
+        Mhead_GenSequenceEnder(streamBbP);
     
-    if (context == CONTEXT_WHOLESTREAM)
-        numBits = bbP->cumulativeBits;
+    if (streamBbP)
+        numBits = streamBbP->cumulativeBits;
     else {
         /* What should the correct value be?  Most likely 1.  "numBits" is
            used below, so we need to make sure it's properly initialized 
@@ -1192,9 +1211,8 @@ GenMPEGStream(struct inputSource * const inputSourceP,
         numBits = 1;
     }
 
-    if (context != CONTEXT_JUSTFRAMES) {
-        Bitio_Flush(bbP);
-        bbP = NULL;
+    if (streamBbP) {
+        Bitio_Flush(streamBbP);
         fclose(ofP);
     }
     handleBitRate(realEnd, numBits, childProcess, showBitRatePerFrame);
@@ -1662,7 +1680,7 @@ ReadDecodedRefFrame(MpegFrame *  const frameP,
     }
 
     if ((fpointer = fopen(fileName, "rb")) == NULL) {
-        sleep(1);
+        sleepN(1000);
         if ((fpointer = fopen(fileName, "rb")) == NULL) {
             fprintf(stderr, "Cannot open %s\n", fileName);
             exit(1);
diff --git a/converter/ppm/ppmtompeg/noparallel.c b/converter/ppm/ppmtompeg/noparallel.c
index b27934fb..6e6449a4 100644
--- a/converter/ppm/ppmtompeg/noparallel.c
+++ b/converter/ppm/ppmtompeg/noparallel.c
@@ -164,7 +164,7 @@ void
 CombineServer(int          const numFrames, 
               const char * const masterHostName, 
               int          const masterPortNum,
-              const char * const outputFileName) {
+              const char*  const outputFileName) {
 
     pm_error("This version of Ppmtompeg cannot run combine server because "
              "it does not have socket capability.");
diff --git a/converter/ppm/ppmtompeg/parallel.c b/converter/ppm/ppmtompeg/parallel.c
index 021e6d2b..e13bf221 100644
--- a/converter/ppm/ppmtompeg/parallel.c
+++ b/converter/ppm/ppmtompeg/parallel.c
@@ -41,6 +41,7 @@
 
 #include "mallocvar.h"
 #include "nstring.h"
+#include "nsleep.h"
 
 #include "pm.h"
 
@@ -128,7 +129,7 @@ boolean niceProcesses = FALSE;
 boolean forceIalign = FALSE;
 int     machineNumber = -1;
 boolean remoteIO = FALSE;
-bool separateConversion;
+boolean separateConversion;
     /* The I/O server will convert from the input format to the base format,
        and the slave will convert from the base format to the YUV internal
        format.  If false, the I/O server assumes the input format is the
@@ -630,7 +631,7 @@ static int safe_fork(command)       /* fork child process and remember its PID *
  *
  *===========================================================================*/
 void
-SetIOConvert(bool const separate) {
+SetIOConvert(boolean const separate) {
     separateConversion = separate;
 }
 
@@ -648,8 +649,7 @@ SetIOConvert(bool const separate) {
  *
  *===========================================================================*/
 void
-SetParallelPerfect(val)
-boolean val;
+SetParallelPerfect(boolean val)
 {
     parallelPerfect = val;
 }
@@ -1209,7 +1209,7 @@ openInputFile(const char * const fileName,
             pm_message("ERROR  Couldn't read frame file '%s' errno = %d (%s)"
                        "attempt %d", 
                        fileName, errno, strerror(errno), attempts);
-            sleep(1);
+            sleepN(1000);
         }
         ++attempts;
     }
diff --git a/converter/ppm/ppmtompeg/param.c b/converter/ppm/ppmtompeg/param.c
index 69e38a6c..a145d33c 100644
--- a/converter/ppm/ppmtompeg/param.c
+++ b/converter/ppm/ppmtompeg/param.c
@@ -665,66 +665,66 @@ processParamLine(char const input[],
 
     switch(input[0]) {
     case 'A':
-        if (STRNEQ(input, "ASPECT_RATIO", 12)) {
+        if (strneq(input, "ASPECT_RATIO", 12)) {
             aspectRatio = GetAspectRatio(SkipSpacesTabs(&input[12]));
             optionSeen[OPTION_ASPECT_RATIO] = TRUE;
         }
         break;
         
     case 'B':
-        if (STRNEQ(input, "BQSCALE", 7)) {
+        if (strneq(input, "BQSCALE", 7)) {
             SetBQScale(atoi(SkipSpacesTabs(&input[7])));
             optionSeen[OPTION_BQSCALE] = TRUE;
-        } else if (STRNEQ(input, "BASE_FILE_FORMAT", 16)) {
+        } else if (strneq(input, "BASE_FILE_FORMAT", 16)) {
             const char * arg = SkipSpacesTabs(&input[16]);
             SetFileFormat(arg);
-            if (STRNEQ(arg, "YUV", 3) || STREQ(arg, "Y"))
+            if (strneq(arg, "YUV", 3) || streq(arg, "Y"))
                 *yuvUsedP = TRUE;
             optionSeen[OPTION_BASE_FORMAT] = TRUE;
-        } else if (STRNEQ(input, "BSEARCH_ALG", 11)) {
+        } else if (strneq(input, "BSEARCH_ALG", 11)) {
             SetBSearchAlg(SkipSpacesTabs(&input[11]));
             optionSeen[OPTION_BSEARCH_ALG] = TRUE;
-        } else if (STRNEQ(input, "BIT_RATE", 8)) {
+        } else if (strneq(input, "BIT_RATE", 8)) {
             setBitRate(SkipSpacesTabs(&input[8]));
             optionSeen[OPTION_BIT_RATE] = TRUE;
-        } else if (STRNEQ(input, "BUFFER_SIZE", 11)) {
+        } else if (strneq(input, "BUFFER_SIZE", 11)) {
             setBufferSize(SkipSpacesTabs(&input[11]));
             optionSeen[OPTION_BUFFER_SIZE] = TRUE;                  
         }
         break;
 
     case 'C':
-        if (STRNEQ(input, "CDL_FILE", 8)) {
+        if (strneq(input, "CDL_FILE", 8)) {
             strcpy(specificsFile, SkipSpacesTabs(&input[8]));
             specificsOn = TRUE;
             optionSeen[OPTION_SPECIFICS] = TRUE;
-        } else if (STRNEQ(input, "CDL_DEFINES", 11)) {
+        } else if (strneq(input, "CDL_DEFINES", 11)) {
             strcpy(specificsDefines, SkipSpacesTabs(&input[11]));
             optionSeen[OPTION_DEFS_SPECIFICS] = TRUE;
         }
         break;
 
     case 'F':
-        if (STRNEQ(input, "FRAME_INPUT_DIR", 15)) {
+        if (strneq(input, "FRAME_INPUT_DIR", 15)) {
             const char * const arg = SkipSpacesTabs(&input[15]);
-            if (STRNCASEEQ(arg, "stdin", 5))
+            if (strncaseeq(arg, "stdin", 5))
                 SetStdinInput(frameInputSourceP);
 
             strcpy(currentFramePath, arg);
-        } else if (STRNEQ(input, "FRAME_INPUT", 11)) {
+        } else if (strneq(input, "FRAME_INPUT", 11)) {
             ReadInputFileNames(fpointer, "FRAME_END_INPUT", 
                                frameInputSourceP->stdinUsed ? 
                                NULL : frameInputSourceP);
             optionSeen[OPTION_FRAME_INPUT] = TRUE;
-        } else if (STRNEQ(input, "FORCE_I_ALIGN", 13)) {
+        } else if (strneq(input, "FORCE_I_ALIGN", 13)) {
             forceIalign = TRUE;
-        } else if (STRNEQ(input, "FORCE_ENCODE_LAST_FRAME", 23)) {
+        } else if (strneq(input, "FORCE_ENCODE_LAST_FRAME", 23)) {
             /* NO-OP.  We used to drop trailing B frames by default and you
                needed this option to change the last frame to I so you could
                encode all the frames.  Now we just do that all the time.  
                Why wouldn't we?
             */
-        } else if (STRNEQ(input, "FRAME_RATE", 10)) {
+        } else if (strneq(input, "FRAME_RATE", 10)) {
             frameRate = GetFrameRate(SkipSpacesTabs(&input[10]));
             frameRateRounded = (int) VidRateNum[frameRate];
             if ((frameRate % 3) == 1)
@@ -734,21 +734,21 @@ processParamLine(char const input[],
         break;
         
     case 'G':
-        if (STRNEQ(input, "GOP_SIZE", 8)) {
+        if (strneq(input, "GOP_SIZE", 8)) {
             SetGOPSize(atoi(SkipSpacesTabs(&input[8])));
             optionSeen[OPTION_GOP] = TRUE;
-        } else if (STRNEQ(input, "GOP_INPUT_DIR", 13)) {
+        } else if (strneq(input, "GOP_INPUT_DIR", 13)) {
             const char * const arg = SkipSpacesTabs(&input[13]);
-            if (STRNCASEEQ(arg, "stdin", 5))
+            if (strncaseeq(arg, "stdin", 5))
                 SetStdinInput(gopInputSourceP);
 
             strcpy(currentGOPPath, arg);
-        } else if (STRNEQ(input, "GOP_INPUT", 9)) {
+        } else if (strneq(input, "GOP_INPUT", 9)) {
             ReadInputFileNames(fpointer, "GOP_END_INPUT", 
                                gopInputSourceP->stdinUsed ? 
                                NULL : gopInputSourceP);
             optionSeen[OPTION_GOP_INPUT] = TRUE;
-        } else if (STRNEQ(input, "GAMMA", 5)) {
+        } else if (strneq(input, "GAMMA", 5)) {
             GammaCorrection = TRUE;
             sscanf(SkipSpacesTabs(&input[5]), "%f", &GammaValue);
             optionSeen[OPTION_GAMMA] = TRUE;
@@ -756,27 +756,27 @@ processParamLine(char const input[],
         break;
         
     case 'I':
-        if (STRNEQ(input, "IQSCALE", 7)) {
+        if (strneq(input, "IQSCALE", 7)) {
             SetIQScale(atoi(SkipSpacesTabs(&input[7])));
             optionSeen[OPTION_IQSCALE] = TRUE;
-        } else if (STRNEQ(input, "INPUT_DIR", 9)) {
+        } else if (strneq(input, "INPUT_DIR", 9)) {
             const char * const arg = SkipSpacesTabs(&input[9]);
-            if (STRNCASEEQ(arg, "stdin", 5))
+            if (strncaseeq(arg, "stdin", 5))
                 SetStdinInput(inputSourceP);
             strcpy(currentPath, arg);
             optionSeen[OPTION_INPUT_DIR] = TRUE;
-        } else if (STRNEQ(input, "INPUT_CONVERT", 13)) {
+        } else if (strneq(input, "INPUT_CONVERT", 13)) {
             strcpy(inputConversion, SkipSpacesTabs(&input[13]));
             optionSeen[OPTION_INPUT_CONVERT] = TRUE;
-        } else if (STREQ(input, "INPUT")) {
+        } else if (streq(input, "INPUT")) {
             ReadInputFileNames(fpointer, "END_INPUT", 
                                inputSourceP->stdinUsed ?
                                NULL : inputSourceP);
             optionSeen[OPTION_INPUT] = TRUE;
-        } else if (STRNEQ(input, "IO_SERVER_CONVERT", 17)) {
+        } else if (strneq(input, "IO_SERVER_CONVERT", 17)) {
             strcpy(ioConversion, SkipSpacesTabs(&input[17]));
             optionSeen[OPTION_IO_CONVERT] = TRUE;
-        } else if (STRNEQ(input, "IQTABLE", 7)) {
+        } else if (strneq(input, "IQTABLE", 7)) {
             processIqtable(fpointer);
 
             optionSeen[OPTION_IQTABLE] = TRUE;
@@ -784,12 +784,12 @@ processParamLine(char const input[],
         break;
 
     case 'K':
-        if (STRNEQ(input, "KEEP_TEMP_FILES", 15))
+        if (strneq(input, "KEEP_TEMP_FILES", 15))
             keepTempFiles = TRUE;
         break;
         
     case 'N':
-      if (STRNEQ(input, "NIQTABLE", 8)) {
+      if (strneq(input, "NIQTABLE", 8)) {
           readNiqTable(fpointer);
 
           optionSeen[OPTION_NIQTABLE] = TRUE;
@@ -797,7 +797,7 @@ processParamLine(char const input[],
       break;
 
     case 'O':
-        if (STRNEQ(input, "OUTPUT", 6)) {
+        if (strneq(input, "OUTPUT", 6)) {
             const char * const arg = SkipSpacesTabs(&input[6]);
             if ( whichGOP == -1 )
                 strcpy(outputFileName, arg);
@@ -809,45 +809,45 @@ processParamLine(char const input[],
         break;
         
     case 'P':
-        if (STRNEQ(input, "PATTERN", 7)) {
+        if (strneq(input, "PATTERN", 7)) {
             SetFramePattern(SkipSpacesTabs(&input[7]));
             optionSeen[OPTION_PATTERN] = TRUE;
-        } else if (STRNEQ(input, "PIXEL", 5)) {
+        } else if (strneq(input, "PIXEL", 5)) {
             SetPixelSearch(SkipSpacesTabs(&input[5]));
             optionSeen[OPTION_PIXEL] = TRUE;
-        } else if (STRNEQ(input, "PQSCALE", 7)) {
+        } else if (strneq(input, "PQSCALE", 7)) {
             SetPQScale(atoi(SkipSpacesTabs(&input[7])));
             optionSeen[OPTION_PQSCALE] = TRUE;
-        } else if (STRNEQ(input, "PSEARCH_ALG", 11)) {
+        } else if (strneq(input, "PSEARCH_ALG", 11)) {
             SetPSearchAlg(SkipSpacesTabs(&input[11]));
             optionSeen[OPTION_PSEARCH_ALG] = TRUE;
-        } else if (STRNEQ(input, "PARALLEL_TEST_FRAMES", 20)) {
+        } else if (strneq(input, "PARALLEL_TEST_FRAMES", 20)) {
             SetParallelPerfect(FALSE);
             parallelTestFrames = atoi(SkipSpacesTabs(&input[20]));
-        } else if (STRNEQ(input, "PARALLEL_TIME_CHUNKS", 20)) {
+        } else if (strneq(input, "PARALLEL_TIME_CHUNKS", 20)) {
             SetParallelPerfect(FALSE);
             parallelTimeChunks = atoi(SkipSpacesTabs(&input[20]));
-        } else if (STRNEQ(input, "PARALLEL_CHUNK_TAPER", 20)) {
+        } else if (strneq(input, "PARALLEL_CHUNK_TAPER", 20)) {
             SetParallelPerfect(FALSE);
             parallelTimeChunks = -1;
-        } else if (STRNEQ(input, "PARALLEL_PERFECT", 16)) {
+        } else if (strneq(input, "PARALLEL_PERFECT", 16)) {
             SetParallelPerfect(TRUE);
-        } else if (STRNEQ(input, "PARALLEL", 8)) {
+        } else if (strneq(input, "PARALLEL", 8)) {
             ReadMachineNames(fpointer);
             optionSeen[OPTION_PARALLEL] = TRUE;
         }
         break;
         
     case 'R':
-        if (STRNEQ(input, "RANGE", 5)) {
+        if (strneq(input, "RANGE", 5)) {
             processRanges(SkipSpacesTabs(&input[5]));
             optionSeen[OPTION_RANGE] = TRUE;
-        } else if (STRNEQ(input, "REFERENCE_FRAME", 15)) {
+        } else if (strneq(input, "REFERENCE_FRAME", 15)) {
             SetReferenceFrameType(SkipSpacesTabs(&input[15]));
             optionSeen[OPTION_REF_FRAME] = TRUE;
-        } else if (STRNEQ(input, "RSH", 3)) {
+        } else if (strneq(input, "RSH", 3)) {
             SetRemoteShell(SkipSpacesTabs(&input[3]));
-        } else if (STRNEQ(input, "RESIZE", 6)) {
+        } else if (strneq(input, "RESIZE", 6)) {
             const char * const arg = SkipSpacesTabs(&input[6]);
             sscanf(arg, "%dx%d", &outputWidth, &outputHeight);
             outputWidth &= ~(DCTSIZE * 2 - 1);
@@ -857,22 +857,22 @@ processParamLine(char const input[],
         break;
 
     case 'S':
-        if (STRNEQ(input, "SLICES_PER_FRAME", 16)) {
+        if (strneq(input, "SLICES_PER_FRAME", 16)) {
             SetSlicesPerFrame(atoi(SkipSpacesTabs(&input[16])));
             optionSeen[OPTION_SPF] = TRUE;
-        } else if (STRNEQ(input, "SLAVE_CONVERT", 13)) {
+        } else if (strneq(input, "SLAVE_CONVERT", 13)) {
             strcpy(slaveConversion, SkipSpacesTabs(&input[13]));
             optionSeen[OPTION_SLAVE_CONVERT] = TRUE;
-        } else if (STRNEQ(input, "SPECIFICS_FILE", 14)) {
+        } else if (strneq(input, "SPECIFICS_FILE", 14)) {
             strcpy(specificsFile, SkipSpacesTabs(&input[14]));
             specificsOn = TRUE;
             optionSeen[OPTION_SPECIFICS] = TRUE;
-        } else if (STRNEQ(input, "SPECIFICS_DEFINES", 16)) {
+        } else if (strneq(input, "SPECIFICS_DEFINES", 16)) {
             strcpy(specificsDefines, SkipSpacesTabs(&input[17]));
             optionSeen[OPTION_DEFS_SPECIFICS] = TRUE;
-        } else if (STRNEQ(input, "SEQUENCE_SIZE", 13)) {
+        } else if (strneq(input, "SEQUENCE_SIZE", 13)) {
             mult_seq_headers = atoi(SkipSpacesTabs(&input[13]));
-        } else if (STRNEQ(input, "SIZE", 4)) {
+        } else if (strneq(input, "SIZE", 4)) {
             const char * const arg = SkipSpacesTabs(&input[4]);
             sscanf(arg, "%dx%d", &yuvWidth, &yuvHeight);
             realWidth = yuvWidth;
@@ -883,42 +883,42 @@ processParamLine(char const input[],
         break;
 
     case 'T':
-        if (STRNEQ(input, "TUNE", 4)) {
+        if (strneq(input, "TUNE", 4)) {
             tuneingOn = TRUE;
             ParseTuneParam(SkipSpacesTabs(&input[4]));
         }
         break;
 
     case 'U':
-        if (STRNEQ(input, "USER_DATA", 9)) {
+        if (strneq(input, "USER_DATA", 9)) {
             strcpy(userDataFileName, SkipSpacesTabs(&input[9]));
             optionSeen[OPTION_USER_DATA] = TRUE;
         }
         break;
         
     case 'W':
-        if (STRNEQ(input, "WARN_UNDERFLOW", 14))
+        if (strneq(input, "WARN_UNDERFLOW", 14))
             paramP->warnUnderflow = TRUE;
-        if (STRNEQ(input, "WARN_OVERFLOW", 13))
+        if (strneq(input, "WARN_OVERFLOW", 13))
             paramP->warnOverflow = TRUE;
         break;
         
     case 'Y':
-        if (STRNEQ(input, "YUV_SIZE", 8)) {
+        if (strneq(input, "YUV_SIZE", 8)) {
             const char * const arg = SkipSpacesTabs(&input[8]);
             sscanf(arg, "%dx%d", &yuvWidth, &yuvHeight);
             realWidth = yuvWidth;
             realHeight = yuvHeight;
             Fsize_Validate(&yuvWidth, &yuvHeight);
             optionSeen[OPTION_YUV_SIZE] = TRUE;
-        } else if (STRNEQ(input, "Y_SIZE", 6)) {
+        } else if (strneq(input, "Y_SIZE", 6)) {
             const char * const arg = SkipSpacesTabs(&input[6]);
             sscanf(arg, "%dx%d", &yuvWidth, &yuvHeight);
             realWidth = yuvWidth;
             realHeight = yuvHeight;
             Fsize_Validate(&yuvWidth, &yuvHeight);
             optionSeen[OPTION_YUV_SIZE] = TRUE;
-        } else if (STRNEQ(input, "YUV_FORMAT", 10)) {
+        } else if (strneq(input, "YUV_FORMAT", 10)) {
             strcpy(yuvConversion,  SkipSpacesTabs(&input[10]));
             optionSeen[OPTION_YUV_FORMAT] = TRUE;
         }
@@ -928,26 +928,21 @@ processParamLine(char const input[],
 
 
 
-/*===========================================================================*
- *
- * ReadParamFile
- *
- *	read the parameter file
- *	function is ENCODE_FRAMES, COMBINE_GOPS, or COMBINE_FRAMES, and
- *	    will slightly modify the procedure's behavior as to what it
- *	    is looking for in the parameter file
- *
- * SIDE EFFECTS:    sets parameters accordingly, as well as machine info for
- *		    parallel execution and input file names
- *
- *===========================================================================*/
 void
-ReadParamFile(const char *    const fileName, 
-              int             const function,
-              struct params * const paramP) {
+ReadParamFile(const char *         const fileName, 
+              majorProgramFunction const function,
+              struct params *      const paramP) {
+/*----------------------------------------------------------------------------
+   Read the parameter file 'fileName' as *paramP.
 
-  FILE *fpointer;
-  char    buffer[256];
+   'function' slightly modifies our behavior as to what it is looking for
+   in the parameter file.
+
+   As a side effect, set machine info for parallel execution and input
+   file names
+-----------------------------------------------------------------------------*/
+  FILE * fpointer;
+  char buffer[256];
   bool yuvUsed;
   struct inputSource * inputSourceP;
       /* Contents of INPUT section */
diff --git a/converter/ppm/ppmtompeg/pframe.c b/converter/ppm/ppmtompeg/pframe.c
index e72fe5d6..de91e32c 100644
--- a/converter/ppm/ppmtompeg/pframe.c
+++ b/converter/ppm/ppmtompeg/pframe.c
@@ -60,8 +60,6 @@ static int32    totalTime = 0;
 static int      qscaleP;
 static float    totalSNR = 0.0;
 static float    totalPSNR = 0.0;
-extern Block    **dct, **dctr, **dctb;
-extern dct_data_type   **dct_data;
 
 /*=====================*
  * INTERNAL PROCEDURES *
diff --git a/converter/ppm/ppmtompeg/ppmtompeg.c b/converter/ppm/ppmtompeg/ppmtompeg.c
index 2296e2cb..f53ffea9 100644
--- a/converter/ppm/ppmtompeg/ppmtompeg.c
+++ b/converter/ppm/ppmtompeg/ppmtompeg.c
@@ -33,7 +33,6 @@
 #define _BSD_SOURCE   /* Make sure strdup() is in string.h */
 
 #include <assert.h>
-#include <sys/utsname.h>
 
 #include "all.h"
 #include "mtypes.h"
@@ -98,7 +97,7 @@ void init_fdct _ANSI_ARGS_((void));
 
 struct cmdlineInfo {
     bool         childProcess;
-    int          function;
+    majorProgramFunction function;
     const char * masterHostname;
     int          masterPortNumber;
     unsigned int outputFrames;
@@ -130,36 +129,33 @@ parseArgs(int     const argc,
     
     /* parse the arguments */
     idx = 1;
-    while ( idx < argc-1 ) {
-        if ( argv[idx][0] != '-' )
+    while (idx < argc-1) {
+        if (argv[idx][0] != '-')
             pm_error("argument '%s', which must be an option because "
                      "it is not the last argument, "
                      "does not start with '-'", argv[idx]);
 
-        if ( strcmp(argv[idx], "-stat") == 0 ) {
-            if ( idx+1 < argc-1 ) {
+        if (streq(argv[idx], "-stat")) {
+            if (idx+1 < argc-1) {
                 SetStatFileName(argv[idx+1]);
                 idx += 2;
-            } else {
+            } else
                 pm_error("Invalid -stat option");
-            }
-        } else if ( strcmp(argv[idx], "-gop") == 0 ) {
-            if ((cmdlineP->function != ENCODE_FRAMES) || 
-                (cmdlineP->specificFrames))
+        } else if (streq(argv[idx], "-gop")) {
+            if (cmdlineP->function != ENCODE_FRAMES || 
+                cmdlineP->specificFrames)
                 pm_error("Invalid -gop option");
             
-            if ( idx+1 < argc-1 ) {
+            if (idx+1 < argc-1) {
                 whichGOP = atoi(argv[idx+1]);
                 idx += 2;
-            } else {
+            } else
                 pm_error("Invalid -gop option");
-            }
-        } else if ( strcmp(argv[idx], "-frames") == 0 ) {
-            if ( (cmdlineP->function != ENCODE_FRAMES) || (whichGOP != -1) ) {
+        } else if (streq(argv[idx], "-frames")) {
+            if (cmdlineP->function != ENCODE_FRAMES || whichGOP != -1)
                 pm_error("invalid -frames option");
-            }
 
-            if ( idx+2 < argc-1 ) {
+            if (idx+2 < argc-1) {
                 int const frameStart = atoi(argv[idx+1]);
                 int const frameEnd = atoi(argv[idx+2]);
 
@@ -177,23 +173,23 @@ parseArgs(int     const argc,
                 idx += 3;
             } else
                 pm_error("-frames needs to be followed by two values");
-        } else if (strcmp(argv[idx], "-combine_gops") == 0) {
-            if ((cmdlineP->function != ENCODE_FRAMES) || (whichGOP != -1) || 
-                (cmdlineP->specificFrames)) {
+        } else if (streq(argv[idx], "-combine_gops")) {
+            if (cmdlineP->function != ENCODE_FRAMES || whichGOP != -1 || 
+                cmdlineP->specificFrames) {
                 pm_error("Invalid -combine_gops option");
             }
 
             cmdlineP->function = COMBINE_GOPS;
-            idx++;
-        } else if (strcmp(argv[idx], "-combine_frames") == 0) {
-            if ((cmdlineP->function != ENCODE_FRAMES) || (whichGOP != -1) ||
-                (cmdlineP->specificFrames))
+            ++idx;
+        } else if (streq(argv[idx], "-combine_frames")) {
+            if (cmdlineP->function != ENCODE_FRAMES || whichGOP != -1 ||
+                cmdlineP->specificFrames)
                 pm_error("Invalid -combine_frames option");
 
             cmdlineP->function = COMBINE_FRAMES;
-            idx++;
-        } else if ( strcmp(argv[idx], "-child") == 0 ) {
-            if ( idx+7 < argc-1 ) {
+            ++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
@@ -215,104 +211,94 @@ parseArgs(int     const argc,
 
             cmdlineP->childProcess = TRUE;
             idx += 8;
-        } else if ( strcmp(argv[idx], "-io_server") == 0 ) {
-            if ( idx+2 < argc-1 ) {
-                cmdlineP->masterHostname = argv[idx+1];
+        } else if (streq(argv[idx], "-io_server")) {
+            if (idx+2 < argc-1) {
+                cmdlineP->masterHostname   = argv[idx+1];
                 cmdlineP->masterPortNumber = atoi(argv[idx+2]);
-            } else {
+            } else
                 pm_error("Invalid -io_server option");
-            }
 
             ioServer = TRUE;
             idx += 3;
-        } else if ( strcmp(argv[idx], "-output_server") == 0 ) {
-            if ( idx+3 < argc-1 ) {
-                cmdlineP->masterHostname = argv[idx+1];
+        } else if (streq(argv[idx], "-output_server")) {
+            if (idx+3 < argc-1) {
+                cmdlineP->masterHostname   = argv[idx+1];
                 cmdlineP->masterPortNumber = atoi(argv[idx+2]);
-                cmdlineP->outputFrames = atoi(argv[idx+3]);
-            } else {
+                cmdlineP->outputFrames     = atoi(argv[idx+3]);
+            } else
                 pm_error("-output_server option requires 3 option values.  "
-                         "You specified %d", argc-1 - idx);
-            }
+                         "You specified %u", argc-1 - idx);
 
             outputServer = TRUE;
             idx += 4;
-        } else if ( strcmp(argv[idx], "-decode_server") == 0 ) {
-            if ( idx+3 < argc-1 ) {
-                cmdlineP->masterHostname = argv[idx+1];
+        } else if (streq(argv[idx], "-decode_server")) {
+            if (idx+3 < argc-1) {
+                cmdlineP->masterHostname   = argv[idx+1];
                 cmdlineP->masterPortNumber = atoi(argv[idx+2]);
-                cmdlineP->outputFrames = atoi(argv[idx+3]);
-            } else {
+                cmdlineP->outputFrames     = atoi(argv[idx+3]);
+            } else
                 pm_error("Invalid -decode_server option");
-            }
 
             cmdlineP->function = COMBINE_FRAMES;
             decodeServer = TRUE;
             idx += 4;
-        } else if ( strcmp(argv[idx], "-nice") == 0 ) {
+        } else if (streq(argv[idx], "-nice")) {
             niceProcesses = TRUE;
             idx++;
-        } else if ( strcmp(argv[idx], "-max_machines") == 0 ) {
-            if ( idx+1 < argc-1 ) {
+        } else if (streq(argv[idx], "-max_machines")) {
+            if (idx+1 < argc-1) {
                 cmdlineP->maxMachines = atoi(argv[idx+1]);
-            } else {
+            } else
                 pm_error("Invalid -max_machines option");
-            }
 
             idx += 2;
-        } else if ( strcmp(argv[idx], "-quiet") == 0 ) {
-            if ( idx+1 < argc-1 ) {
+        } else if (streq(argv[idx], "-quiet")) {
+            if (idx+1 < argc-1)
                 quietTime = atoi(argv[idx+1]);
-            } else {
+            else
                 pm_error("Invalid -quiet option");
-            }
 
             idx += 2;
-        } else if ( strcmp(argv[idx], "-realquiet") == 0 ) {
+        } else if (streq(argv[idx], "-realquiet")) {
             realQuiet = TRUE;
-            idx++;
-        } else if (( strcmp(argv[idx], "-float_dct") == 0 ) ||
-                   ( strcmp(argv[idx], "-float-dct") == 0 )) {
+            ++idx;
+        } else if (streq(argv[idx], "-float_dct") ||
+                   streq(argv[idx], "-float-dct")) {
             pureDCT = TRUE;
             init_idctref();
             init_fdct();
-            idx++;
-        } else if ( strcmp(argv[idx], "-no_frame_summary") == 0 ) {
-            if ( idx < argc-1 ) {
+            ++idx;
+        } else if (streq(argv[idx], "-no_frame_summary")) {
+            if (idx < argc-1)
                 noFrameSummaryOption = TRUE;
-            } else {
+            else
                 pm_error("Invalid -no_frame_summary option");
-            }
-
-            idx++;
-        } else if ( strcmp(argv[idx], "-snr") == 0 ) {
+            ++idx;
+        } else if (streq(argv[idx], "-snr")) {
             printSNR = TRUE;
-            idx++;
-        } else if ( strcmp(argv[idx], "-mse") == 0 ) {
-            printSNR =  printMSE = TRUE;
-            idx++;
-        } else if ( strcmp(argv[idx], "-debug_sockets") == 0 ) {
+            ++idx;
+        } else if (streq(argv[idx], "-mse")) {
+            printSNR = printMSE = TRUE;
+            ++idx;
+        } else if (streq(argv[idx], "-debug_sockets")) {
             debugSockets = TRUE;
-            idx++;
-        } else if ( strcmp(argv[idx], "-debug_machines") == 0 ) {
+            ++idx;
+        } else if (streq(argv[idx], "-debug_machines")) {
             debugMachines = TRUE;
-            idx++;
-        } else if ( strcmp(argv[idx], "-bit_rate_info") == 0 ) {
-            if ( idx+1 < argc-1 ) {
+            ++idx;
+        } else if (streq(argv[idx], "-bit_rate_info")) {
+            if (idx+1 < argc-1) {
                 bitRateInfoOption = TRUE;
                 SetBitRateFileName(argv[idx+1]);
                 idx += 2;
-            } else {
+            } else
                 pm_error("Invalid -bit_rate_info option");
-            }
-        } else if ( strcmp(argv[idx], "-mv_histogram") == 0 ) {
+        } else if (streq(argv[idx], "-mv_histogram")) {
             computeMVHist = TRUE;
-            idx++;
-        } else {
+            ++idx;
+        } else
             pm_error("Unrecognized option: '%s'", argv[idx]);
-        }
     }
-
     cmdlineP->paramFileName = argv[argc-1];
 }
 
@@ -686,8 +672,8 @@ main(int argc, char **argv) {
         DecodeServer(cmdline.outputFrames, outputFileName, 
                      cmdline.masterHostname, cmdline.masterPortNumber);
     } else {
-        if ((!cmdline.specificFrames) &&
-            ((numMachines == 0) || (cmdline.function != ENCODE_FRAMES)) ) {
+        if (!cmdline.specificFrames &&
+            (numMachines == 0 || cmdline.function != ENCODE_FRAMES) ) {
             ofP = fopen(outputFileName, "wb");
             if (ofP == NULL)
                 pm_error("Could not open output file!");
@@ -695,7 +681,7 @@ main(int argc, char **argv) {
             ofP = NULL;
         
         if (cmdline.function == ENCODE_FRAMES) {
-            if ((numMachines == 0) || (cmdline.specificFrames)) {
+            if (numMachines == 0 || cmdline.specificFrames) {
                 encodeFrames(params.inputSourceP,
                              cmdline.childProcess, 
                              cmdline.masterHostname, cmdline.masterPortNumber,
diff --git a/converter/ppm/ppmtompeg/psearch.c b/converter/ppm/ppmtompeg/psearch.c
index aea1a29b..83c62d04 100644
--- a/converter/ppm/ppmtompeg/psearch.c
+++ b/converter/ppm/ppmtompeg/psearch.c
@@ -16,6 +16,8 @@
 #include "prototypes.h"
 #include "fsize.h"
 #include "param.h"
+#include "subsample.h"
+#include "block.h"
 
 
 /*==================*
diff --git a/converter/ppm/ppmtompeg/rate.c b/converter/ppm/ppmtompeg/rate.c
index 3940956c..1a44cb95 100644
--- a/converter/ppm/ppmtompeg/rate.c
+++ b/converter/ppm/ppmtompeg/rate.c
@@ -50,7 +50,6 @@
 
 #include <assert.h>
 #include <sys/types.h>
-#include <sys/times.h>
 
 #include "ppm.h"
 #include "nstring.h"
diff --git a/converter/ppm/ppmtompeg/specifics.c b/converter/ppm/ppmtompeg/specifics.c
index c7bbfb5b..38e8fc43 100644
--- a/converter/ppm/ppmtompeg/specifics.c
+++ b/converter/ppm/ppmtompeg/specifics.c
@@ -47,12 +47,6 @@
 #include <string.h>
 #include "prototypes.h"
 
-/*====================*
- * System Information *
- *====================*/
-
-#define CPP_LOC "/lib/cpp"
-
 /*==================*
  * GLOBAL VARIABLES *
  *==================*/
@@ -153,26 +147,27 @@ static char version = -1;
  *
  *================================================================
  */
-void Specifics_Init()
-{
-  char command[1100];
-  FILE *specificsFP;
-  
-  sprintf(command, "/bin/rm -f %s.cpp", specificsFile);
-  system(command);
-  sprintf(command, "%s -P %s %s %s.cpp",
-	  CPP_LOC, specificsDefines, specificsFile, specificsFile);
-  system(command);
-  strcat(specificsFile, ".cpp");
-  if ((specificsFP = fopen(specificsFile, "r")) == NULL) {
-    fprintf(stderr, "Error with specifics file, cannot open %s\n", specificsFile);
-    exit(1);
-  }
-  printf("Specifics file: %s\n", specificsFile);
-  Parse_Specifics_File(specificsFP);
-  sprintf(command, "/bin/rm -f %s.cpp", specificsFile);
-  system(command);
+void
+Specifics_Init() {
 
+    char command[1100];
+    FILE *specificsFP;
+  
+    sprintf(command, "rm -f %s.cpp", specificsFile);
+    system(command);
+    sprintf(command, "cpp -P %s %s %s.cpp",
+            specificsDefines, specificsFile, specificsFile);
+    system(command);
+    strcat(specificsFile, ".cpp");
+    if ((specificsFP = fopen(specificsFile, "r")) == NULL) {
+        fprintf(stderr, "Error with specifics file, cannot open %s\n",
+                specificsFile);
+        exit(1);
+    }
+    printf("Specifics file: %s\n", specificsFile);
+    Parse_Specifics_File(specificsFP);
+    sprintf(command, "rm -f %s.cpp", specificsFile);
+    system(command);
 }
 
 
diff --git a/converter/ppm/ppmtompeg/subsample.c b/converter/ppm/ppmtompeg/subsample.c
index 5ec71814..69401a1d 100644
--- a/converter/ppm/ppmtompeg/subsample.c
+++ b/converter/ppm/ppmtompeg/subsample.c
@@ -43,6 +43,7 @@
 #include "bitio.h"
 #include "prototypes.h"
 
+#include "subsample.h"
 
 
 static void
diff --git a/converter/ppm/ppmtopcx.c b/converter/ppm/ppmtopcx.c
index bdcfc5c7..edc44149 100644
--- a/converter/ppm/ppmtopcx.c
+++ b/converter/ppm/ppmtopcx.c
@@ -15,6 +15,7 @@
 */
 #include <assert.h>
 
+#include "pm_c_util.h"
 #include "ppm.h"
 #include "shhopt.h"
 #include "mallocvar.h"
diff --git a/converter/ppm/ppmtopict.c b/converter/ppm/ppmtopict.c
index e2428fb6..1b9f2d5c 100644
--- a/converter/ppm/ppmtopict.c
+++ b/converter/ppm/ppmtopict.c
@@ -282,15 +282,8 @@ int i;
 	(void) putc(i & 0xff, fd);
 }
 
-#if __STDC__
 static void
 putLong( FILE *fd, long i )
-#else /*__STDC__*/
-static void
-putLong(fd, i)
-FILE *fd;
-long i;
-#endif /*__STDC__*/
 {
 	(void) putc((int)((i >> 24) & 0xff), fd);
 	(void) putc(((int)(i >> 16) & 0xff), fd);
@@ -405,7 +398,7 @@ char *packed;
 		*p++ = counttochar(count);
 
 	packcols = p - packed;		/* how many did we write? */
-	if (cols > 250)
+	if (cols > 200)
 	{
 		putShort(fd, packcols);
 		oc = packcols + 2;
@@ -443,7 +436,7 @@ char *packed;
 	bzero(aux, cols); /* aux?? */
 #endif /*notdef*/
 	bc = cols + (cols + MAX_COUNT - 1) / MAX_COUNT;
-	if (bc > 250)
+	if (bc > 200)
 	{
 		putShort(fd, bc);
 		oc = bc + 2;
diff --git a/converter/ppm/ppmtopj.c b/converter/ppm/ppmtopj.c
index 5d449f7a..d116773f 100644
--- a/converter/ppm/ppmtopj.c
+++ b/converter/ppm/ppmtopj.c
@@ -121,7 +121,7 @@ char *argv[];
 		{
 		++argn;
 		for (r = 0; rmode[r] != NULL; r++)
-		     if (STREQ(rmode[r], argv[argn]))
+		     if (streq(rmode[r], argv[argn]))
 			 break;
 		if (rmode[r] != NULL)
 		    render_mode = r;
@@ -131,9 +131,9 @@ char *argv[];
 	    else if ( pm_keymatch(argv[argn],"-back",2) && argn + 1 < argc )
 		{
 		++argn;
-		if (STREQ(argv[argn], "dark"))
+		if (streq(argv[argn], "dark"))
 		    back_scale = C_BACK_SCALE_DARK;
-		else if (STREQ(argv[argn], "lite"))
+		else if (streq(argv[argn], "lite"))
 		    back_scale = C_BACK_SCALE_LIGHT;
 		else
 		    pm_usage(usage);
diff --git a/converter/ppm/ppmtopjxl.c b/converter/ppm/ppmtopjxl.c
index 72d299fd..91cd1a45 100644
--- a/converter/ppm/ppmtopjxl.c
+++ b/converter/ppm/ppmtopjxl.c
@@ -1,4 +1,4 @@
-/* ppmtopcl.c - convert portable pixmap into PCL language for HP PaintJet and
+/* ppmtopcl.c - convert PPM into PCL language for HP PaintJet and
  *              PaintJet XL color printers
  * AJCD 12/3/91
  * 
@@ -17,8 +17,8 @@
 #include <string.h>
 
 #include "pm_c_util.h"
-#include "ppm.h"
 #include "nstring.h"
+#include "ppm.h"
 
 #define MAXCOLORS 1024
 
@@ -106,318 +106,306 @@ putbits(b, n) {
       int xo = 0;
       int xc = 0;
       if (cnt+n > 8) {  /* overflowing current byte? */
-	 xc = cnt + n - 8;
-	 xo = (b & ~(-1 << xc)) << (8-xc);
-	 n -= xc;
-	 b >>= xc;
+     xc = cnt + n - 8;
+     xo = (b & ~(-1 << xc)) << (8-xc);
+     n -= xc;
+     b >>= xc;
       }
       cnt += n;
       out |= (b & ~(-1 << n)) << (8-cnt);
       if (cnt >= 8) {
-	 inrow[num++] = out;
-	 out = xo;
-	 cnt = xc;
+     inrow[num++] = out;
+     out = xo;
+     cnt = xc;
       }
    } else { /* flush row */
       int i;
       if (cnt) {
-	 inrow[num++] = out;
-	 out = cnt = 0;
+     inrow[num++] = out;
+     out = cnt = 0;
       }
       for (; num > 0 && inrow[num-1] == 0; num--); /* remove trailing zeros */
       printf("\033*b"); 
       if (num && !nopack) {            /* TIFF 4.0 packbits encoding */
-	 int start = 0;
-	 int next;
-	 runcnt[start] = 0;
-	 for (i = 1; i < num; i++) {
-	    if (inrow[i] == inrow[i-1]) {
-	       if (runcnt[start] <= 0 && runcnt[start] > -127)
-		  runcnt[start]--;
-	       else
-		  runcnt[start = i] = 0;
-	    } else {
-	       if (runcnt[start] >= 0 && runcnt[start] < 127)
-		  runcnt[start]++;
-	       else
-		  runcnt[start = i] = 0;
-	    }
-	 }
-	 start = 0;
-	 for (i = 0; i < num; i = next) {
-	    int count = runcnt[i];
-	    int from = i;
-	    if (count >= 0) { /* merge two-byte runs */
-	       for (;;) {
-		  next = i+1+runcnt[i];
-		  if(next >= num || runcnt[next] < 0 ||
-		     count+runcnt[next]+1 > 127)
-		     break;
-		  count += runcnt[next]+1;
-		  i = next;
-	       }
-	    }
-	    next =  i + 1 + ((runcnt[i] < 0) ? -runcnt[i] : runcnt[i]);
-	    if (next < num && count > 0 &&
-		runcnt[next] < 0 && runcnt[next] > -127) {
-	       count--;
-	       next--;
-	       runcnt[next] = runcnt[next+1]-1;
-	    }
-	    outrow[start++] = count;
-	    if (count >= 0) {
-	       while (count-- >= 0)
-		  outrow[start++] = inrow[from++];
-	    } else
-	       outrow[start++] = inrow[from];
-	 }
-	 if (start < num) {
-	    num = start;
-	    if (!pack) {
-	       printf("2m");
-	       pack = 1;
-	    }
-	 } else {
-	    if (pack) {
-	       printf("0m");
-	       pack = 0;
-	    }
-	 }
+     int start = 0;
+     int next;
+     runcnt[start] = 0;
+     for (i = 1; i < num; i++) {
+        if (inrow[i] == inrow[i-1]) {
+           if (runcnt[start] <= 0 && runcnt[start] > -127)
+          runcnt[start]--;
+           else
+          runcnt[start = i] = 0;
+        } else {
+           if (runcnt[start] >= 0 && runcnt[start] < 127)
+          runcnt[start]++;
+           else
+          runcnt[start = i] = 0;
+        }
+     }
+     start = 0;
+     for (i = 0; i < num; i = next) {
+        int count = runcnt[i];
+        int from = i;
+        if (count >= 0) { /* merge two-byte runs */
+           for (;;) {
+          next = i+1+runcnt[i];
+          if(next >= num || runcnt[next] < 0 ||
+             count+runcnt[next]+1 > 127)
+             break;
+          count += runcnt[next]+1;
+          i = next;
+           }
+        }
+        next =  i + 1 + ((runcnt[i] < 0) ? -runcnt[i] : runcnt[i]);
+        if (next < num && count > 0 &&
+        runcnt[next] < 0 && runcnt[next] > -127) {
+           count--;
+           next--;
+           runcnt[next] = runcnt[next+1]-1;
+        }
+        outrow[start++] = count;
+        if (count >= 0) {
+           while (count-- >= 0)
+          outrow[start++] = inrow[from++];
+        } else
+           outrow[start++] = inrow[from];
+     }
+     if (start < num) {
+        num = start;
+        if (!pack) {
+           printf("2m");
+           pack = 1;
+        }
+     } else {
+        if (pack) {
+           printf("0m");
+           pack = 0;
+        }
+     }
       }
       printf("%dW", num);
       for (i = 0; i < num; i++)
-	 putchar(pack ? outrow[i] : inrow[i]);
+     putchar(pack ? outrow[i] : inrow[i]);
       num = 0; /* new row */
    }
 }
 
+
+
 int
-main(argc, argv)
-     int argc;
-     char *argv[];
-{
-   FILE *ifd;
-   register pixel **pixels, *pixrow;
-   register int  row, col, bpp, i;
-   int rows, cols;
-   pixval maxval;
-   int bpr, bpg, bpb;
-   int render;
-   int colors, pclindex;
-   colorhist_vector chv;
-   colorhash_table cht;
+main(int argc, const char * argv[]) {
+
+    FILE * ifP;
+    pixel ** pixels;
+    unsigned int row;
+    unsigned int bpp;
+    int rows, cols;
+    pixval maxval;
+    int bpr, bpg, bpb;
+    int render;
+    int colors, pclindex;
+    colorhist_vector chv;
+    colorhash_table cht;
    
-   ppm_init( &argc, argv );
-
-   while (argc > 1 && argv[1][0] == '-') {
-      char *c;
-      for (i = 0; i < sizeof(options)/sizeof(struct options); i++) {
-	 if (pm_keymatch(argv[1], options[i].name,
-			 MIN(strlen(argv[1]), strlen(options[i].name)))) {
-	    switch (options[i].type) {
-	    case DIM:
-	       if (++argv, --argc == 1)
-		  pm_usage(usage);
-	       for (c = argv[1]; ISDIGIT(*c); c++);
-	       if (c[0] == 'p' && c[1] == 't') /* points */
-		  *(int *)(options[i].value) = atoi(argv[1])*10;
-	       else if (c[0] == 'd' && c[1] == 'p') /* decipoints */
-		  *(int *)(options[i].value) = atoi(argv[1]);
-	       else if (c[0] == 'i' && c[1] == 'n') /* inches */
-		  *(int *)(options[i].value) = atoi(argv[1])*720;
-	       else if (c[0] == 'c' && c[1] == 'm') /* centimetres */
-		  *(int *)(options[i].value) = atoi(argv[1])*283.46457;
-	       else if (!c[0]) /* dots */
-		  *(int *)(options[i].value) = atoi(argv[1])*4;
-	       else
-		  pm_error("illegal unit of measure %s", c);
-	       break;
-	    case REAL:
-	       if (++argv, --argc == 1)
-		  pm_usage(usage);
-	       *(double *)(options[i].value) = atof(argv[1]);
-	       break;
-	    case BOOL:
-	       *(int *)(options[i].value) = 1;
-	       break;
-	    }
-	    break;
-	 }
-      }
-      if (i >= sizeof(options)/sizeof(struct options))
-	 pm_usage(usage);
-      argv++; argc--;
-   }
-   if (argc > 2)
-      pm_usage(usage);
-   else if (argc == 2)
-      ifd = pm_openr(argv[1]);
-   else
-      ifd = stdin ;
-
-   /* validate arguments */
-   if (diffuse+cluster+dither > 1)
-      pm_error("only one of -diffuse, -dither and -cluster may be used");
-   render = diffuse ? 4 : dither ? 3 : cluster ? 7 : 0;
-
-   if (xsize != 0.0 && xscale != 0.0)
-      pm_error("only one of -xsize and -xscale may be used");
-
-   if (ysize != 0.0 && yscale != 0.0)
-      pm_error("only one of -ysize and -yscale may be used");
-
-   pixels = ppm_readppm( ifd, &cols, &rows, &maxval );
-   pm_close( ifd );
-
-   /* limit checks */
-   if (cols > PCL_MAXWIDTH || rows > PCL_MAXHEIGHT)
-      pm_error("image too large; reduce with ppmscale");
-   if (maxval > PCL_MAXVAL)
-      pm_error("color range too large; reduce with ppmcscale");
-
-   /* Figure out the colormap. */
-   fprintf( stderr, "(Computing colormap..." ); fflush( stderr );
-   chv = ppm_computecolorhist( pixels, cols, rows, MAXCOLORS, &colors );
-   if ( chv == (colorhist_vector) 0 )
-      pm_error("too many colors; reduce with pnmquant");
-   fprintf( stderr, "  Done.  %d colors found.)\n", colors );
-
-   /* And make a hash table for fast lookup. */
-   cht = ppm_colorhisttocolorhash( chv, colors );
-
-   /* work out color downloading mode */
-   pclindex = bitsperpixel(colors);
-   if (pclindex > 8) /* can't use indexed mode */
-      pclindex = 0;
-   else
-      switch (pclindex) { /* round up to 1,2,4,8 */
-      case 0: /* direct mode (no palette) */
-	 bpp = bitsperpixel(maxval); /* bits per pixel */
-	 bpg = bpp; bpb = bpp;
-	 bpp = (bpp*3+7)>>3;     /* bytes per pixel now */
-	 bpr = (bpp<<3)-bpg-bpb; 
-	 bpp *= cols;            /* bytes per row now */
-	 break;
-      case 5:         pclindex++;
-      case 6:         pclindex++;
-      case 3: case 7: pclindex++;
-      default:
-	 bpp = 8/pclindex;
-	 bpp = (cols+bpp-1)/bpp;      /* bytes per row */
-      }
+    pm_proginit(&argc, argv);
 
-   if ((inrow = (char *)malloc((unsigned)bpp)) == NULL ||
-       (outrow = (char *)malloc((unsigned)bpp*2)) == NULL ||
-       (runcnt = (signed char *)malloc((unsigned)bpp)) == NULL)
-      pm_error("can't allocate space for row");
-
-   /* set up image details */
-   if (xscale != 0.0)
-      xsize = cols * xscale * 4;
-   if (yscale != 0.0)
-      ysize = rows * yscale * 4;
-
-#ifdef DEBUG
-   fprintf(stderr, "dark    =%d\n", dark);
-   fprintf(stderr, "diffuse =%d\n", diffuse);
-   fprintf(stderr, "dither  =%d\n", dither);
-   fprintf(stderr, "cluster =%d\n", cluster);
-   fprintf(stderr, "quality =%d\n", quality);
-   fprintf(stderr, "xsize   =%d\n", xsize);
-   fprintf(stderr, "ysize   =%d\n", ysize);
-   fprintf(stderr, "xshift  =%d\n", xshift);
-   fprintf(stderr, "yshift  =%d\n", yshift);
-   fprintf(stderr, "xscale  =%lf\n", xscale);
-   fprintf(stderr, "yscale  =%lf\n", yscale);
-   fprintf(stderr, "gamma   =%lf\n", gamma_val);
-   fprintf(stderr, "pclindex   =%d\n", pclindex);
-   fprintf(stderr, "nopack  =%d\n", nopack);
-#endif
+    while (argc > 1 && argv[1][0] == '-') {
+        unsigned int i;
+        for (i = 0; i < sizeof(options)/sizeof(struct options); i++) {
+            if (pm_keymatch(argv[1], options[i].name,
+                            MIN(strlen(argv[1]), strlen(options[i].name)))) {
+                const char * c;
+                switch (options[i].type) {
+                case DIM:
+                    if (++argv, --argc == 1)
+                        pm_usage(usage);
+                    for (c = argv[1]; ISDIGIT(*c); c++);
+                    if (c[0] == 'p' && c[1] == 't') /* points */
+                        *(int *)(options[i].value) = atoi(argv[1])*10;
+                    else if (c[0] == 'd' && c[1] == 'p') /* decipoints */
+                        *(int *)(options[i].value) = atoi(argv[1]);
+                    else if (c[0] == 'i' && c[1] == 'n') /* inches */
+                        *(int *)(options[i].value) = atoi(argv[1])*720;
+                    else if (c[0] == 'c' && c[1] == 'm') /* centimetres */
+                        *(int *)(options[i].value) = atoi(argv[1])*283.46457;
+                    else if (!c[0]) /* dots */
+                        *(int *)(options[i].value) = atoi(argv[1])*4;
+                    else
+                        pm_error("illegal unit of measure %s", c);
+                    break;
+                case REAL:
+                    if (++argv, --argc == 1)
+                        pm_usage(usage);
+                    *(double *)(options[i].value) = atof(argv[1]);
+                    break;
+                case BOOL:
+                    *(int *)(options[i].value) = 1;
+                    break;
+                }
+                break;
+            }
+        }
+        if (i >= sizeof(options)/sizeof(struct options))
+            pm_usage(usage);
+        argv++; argc--;
+    }
+    if (argc > 2)
+        pm_usage(usage);
+    else if (argc == 2)
+        ifP = pm_openr(argv[1]);
+    else
+        ifP = stdin ;
+
+    /* validate arguments */
+    if (diffuse+cluster+dither > 1)
+        pm_error("only one of -diffuse, -dither and -cluster may be used");
+    render = diffuse ? 4 : dither ? 3 : cluster ? 7 : 0;
+
+    if (xsize != 0.0 && xscale != 0.0)
+        pm_error("only one of -xsize and -xscale may be used");
+
+    if (ysize != 0.0 && yscale != 0.0)
+        pm_error("only one of -ysize and -yscale may be used");
+
+    pixels = ppm_readppm(ifP, &cols, &rows, &maxval);
+    pm_close(ifP);
+
+    /* limit checks */
+    if (cols > PCL_MAXWIDTH || rows > PCL_MAXHEIGHT)
+        pm_error("image too large; reduce with ppmscale");
+    if (maxval > PCL_MAXVAL)
+        pm_error("color range too large; reduce with ppmcscale");
 
-   /* write PCL header */
+    /* Figure out the colormap. */
+    pm_message("Computing colormap...");
+    chv = ppm_computecolorhist(pixels, cols, rows, MAXCOLORS, &colors);
+    if (!chv)
+        pm_error("too many colors; reduce with pnmquant");
+    pm_message("... Done.  %u colors found.", colors);
+
+    /* And make a hash table for fast lookup. */
+    cht = ppm_colorhisttocolorhash(chv, colors);
+
+    /* work out color downloading mode */
+    pclindex = bitsperpixel(colors);
+    if (pclindex > 8) /* can't use indexed mode */
+        pclindex = 0;
+    else {
+        switch (pclindex) { /* round up to 1,2,4,8 */
+        case 0: /* direct mode (no palette) */
+            bpp = bitsperpixel(maxval); /* bits per pixel */
+            bpg = bpp; bpb = bpp;
+            bpp = (bpp*3+7)>>3;     /* bytes per pixel now */
+            bpr = (bpp<<3)-bpg-bpb; 
+            bpp *= cols;            /* bytes per row now */
+            break;
+        case 5:         pclindex++;
+        case 6:         pclindex++;
+        case 3: case 7: pclindex++;
+        default:
+            bpp = 8/pclindex;
+            bpp = (cols+bpp-1)/bpp;      /* bytes per row */
+        }
+    }
+    inrow = (char *)malloc((unsigned)bpp);
+    outrow = (char *)malloc((unsigned)bpp*2);
+    runcnt = (signed char *)malloc((unsigned)bpp);
+    if (inrow == NULL || outrow == NULL || runcnt == NULL)
+        pm_error("can't allocate space for row");
+
+    /* set up image details */
+    if (xscale != 0.0)
+        xsize = cols * xscale * 4;
+    if (yscale != 0.0)
+        ysize = rows * yscale * 4;
+
+    /* write PCL header */
 #if 0
-   printf("\033&l26A");                         /* paper size */
+    printf("\033&l26A");                         /* paper size */
 #endif
-   printf("\033*r%ds%dT", cols, rows);          /* source width, height */
-   if (xshift != 0 || yshift != 0)
-      printf("\033&a%+dh%+dV", xshift, yshift); /* xshift, yshift */
-   if (quality)
-      printf("\033*o%dQ", quality);             /* print quality */
-   printf("\033*t");
-   if (xsize == 0 && ysize == 0)
-      printf("180r");                   /* resolution */
-   else {                               /* destination width, height */
-      if (xsize != 0)
-	 printf("%dh", xsize);
-      if (ysize != 0)
-	 printf("%dv", ysize);
-   }
-   if (gamma_val != 0)
-      printf("%.3fi", gamma_val);                    /* gamma correction */
-   if (dark)
-      printf("%dk", dark);              /* scaling algorithms */
-   printf("%dJ", render);               /* rendering algorithm */
-   printf("\033*v18W");                           /* configure image data */
-      putchar(0); /* relative colors */
-      putchar(pclindex ? 1 : 3); /* index/direct pixel mode */
-      putchar(pclindex); /* ignored in direct pixel mode */
-      if (pclindex) {
-	 putchar(0);
-	 putchar(0);
-	 putchar(0);
-      } else {
-	 putchar(bpr); /* bits per red */
-	 putchar(bpg); /* bits per green */
-	 putchar(bpb); /* bits per blue */
-      }
-      putword(maxval); /* max red reference */
-      putword(maxval); /* max green reference */
-      putword(maxval); /* max blue reference */
-      putword(0); /* min red reference */
-      putword(0); /* min green reference */
-      putword(0); /* min blue reference */
-   if (pclindex) {                        /* set palette */
-      for (i = 0; i < colors; i++) {
-	 int r, g, b;
-	 r = PPM_GETR( chv[i].color );
-	 g = PPM_GETG( chv[i].color );
-	 b = PPM_GETB( chv[i].color );
-	 if (i == 0)
-	    printf("\033*v");
-	 if (r)
-	    printf("%da", r);
-	 if (g)
-	    printf("%db", g);
-	 if (b)
-	    printf("%dc", b);
-	 if (i == colors-1)
-	    printf("%dI", i);    /* assign color index */
-	 else
-	    printf("%di", i);    /* assign color index */
-      }
-   }
-   ppm_freecolorhist( chv );
-
-   /* start raster graphics at CAP */
-   printf("\033*r%dA", (xsize != 0 || ysize != 0) ? 3 : 1);
-
-   for (row = 0; row < rows; row++) {
-      if (pclindex) { /* indexed color mode */
-	 int out, cnt;
-	 out = cnt = 0;
-	 for (col = 0, pixrow=pixels[row]; col < cols; col++, pixrow++) {
-	    putbits(ppm_lookupcolor( cht, pixrow ), pclindex);
-	 }
-	 putbits(0, 0); /* flush row */
-      } else { /* direct color mode */
-	 for (col = 0, pixrow=pixels[row]; col < cols; col++, pixrow++) {
-	    putbits(PPM_GETR( *pixrow ), bpr);
-	    putbits(PPM_GETG( *pixrow ), bpg);
-	    putbits(PPM_GETB( *pixrow ), bpb); /* don't need to flush */
-	 }
-	 putbits(0, 0); /* flush row */
-      }
-   }
-   printf("\033*rC"); /* end raster graphics */
-   exit(0);
+    printf("\033*r%ds%dT", cols, rows);          /* source width, height */
+    if (xshift != 0 || yshift != 0)
+        printf("\033&a%+dh%+dV", xshift, yshift); /* xshift, yshift */
+    if (quality)
+        printf("\033*o%dQ", quality);             /* print quality */
+    printf("\033*t");
+    if (xsize == 0 && ysize == 0)
+        printf("180r");                   /* resolution */
+    else {                               /* destination width, height */
+        if (xsize != 0)
+            printf("%dh", xsize);
+        if (ysize != 0)
+            printf("%dv", ysize);
+    }
+    if (gamma_val != 0)
+        printf("%.3fi", gamma_val);                    /* gamma correction */
+    if (dark)
+        printf("%dk", dark);              /* scaling algorithms */
+    printf("%dJ", render);               /* rendering algorithm */
+    printf("\033*v18W");                           /* configure image data */
+    putchar(0); /* relative colors */
+    putchar(pclindex ? 1 : 3); /* index/direct pixel mode */
+    putchar(pclindex); /* ignored in direct pixel mode */
+    if (pclindex) {
+        putchar(0);
+        putchar(0);
+        putchar(0);
+    } else {
+        putchar(bpr); /* bits per red */
+        putchar(bpg); /* bits per green */
+        putchar(bpb); /* bits per blue */
+    }
+    putword(maxval); /* max red reference */
+    putword(maxval); /* max green reference */
+    putword(maxval); /* max blue reference */
+    putword(0); /* min red reference */
+    putword(0); /* min green reference */
+    putword(0); /* min blue reference */
+    if (pclindex) {                        /* set palette */
+        unsigned int i;
+        for (i = 0; i < colors; ++i) {
+            int const r = PPM_GETR( chv[i].color);
+            int const g = PPM_GETG( chv[i].color);
+            int const b = PPM_GETB( chv[i].color);
+            if (i == 0)
+                printf("\033*v");
+            if (r)
+                printf("%da", r);
+            if (g)
+                printf("%db", g);
+            if (b)
+                printf("%dc", b);
+            if (i == colors-1)
+                printf("%dI", i);    /* assign color index */
+            else
+                printf("%di", i);    /* assign color index */
+        }
+    }
+    ppm_freecolorhist(chv);
+
+    /* start raster graphics at CAP */
+    printf("\033*r%dA", (xsize != 0 || ysize != 0) ? 3 : 1);
+
+    for (row = 0; row < rows; row++) {
+        pixel * const pixrow = pixels[row];
+        if (pclindex) { /* indexed color mode */
+            unsigned int col;
+            for (col = 0; col < cols; ++col)
+                putbits(ppm_lookupcolor(cht, &pixrow[col]), pclindex);
+            putbits(0, 0); /* flush row */
+        } else { /* direct color mode */
+            unsigned int col;
+            for (col = 0; col < cols; ++col) {
+                putbits(PPM_GETR(pixrow[col]), bpr);
+                putbits(PPM_GETG(pixrow[col]), bpg);
+                putbits(PPM_GETB(pixrow[col]), bpb);
+                /* don't need to flush */
+            }
+            putbits(0, 0); /* flush row */
+        }
+    }
+    printf("\033*rC"); /* end raster graphics */
+
+    return 0;
 }
diff --git a/converter/ppm/ppmtoterm.c b/converter/ppm/ppmtoterm.c
index 72105705..81df614e 100644
--- a/converter/ppm/ppmtoterm.c
+++ b/converter/ppm/ppmtoterm.c
@@ -21,6 +21,7 @@
 
 #include <string.h>
 
+#include "pm_c_util.h"
 #include "ppm.h"
 #include "shhopt.h"
 
diff --git a/converter/ppm/ppmtowinicon.c b/converter/ppm/ppmtowinicon.c
index d7f12dd0..a96840a1 100644
--- a/converter/ppm/ppmtowinicon.c
+++ b/converter/ppm/ppmtowinicon.c
@@ -13,6 +13,7 @@
 #include <math.h>
 #include <string.h>
 
+#include "pm_c_util.h"
 #include "winico.h"
 #include "ppm.h"
 #include "mallocvar.h"
@@ -203,8 +204,6 @@ createAndBitmap (gray ** const ba, int const cols, int const rows,
     * How wide should the u1 string for each row be?
     * each byte is 8 pixels, but must be a multiple of 4 bytes.
     */
-
-#define ROUNDUP(X,M) (((X)+(M)-1)/(M)*(M))
    unsigned int const xBytes = ROUNDUP(cols, 32)/8;
    ICON_bmp icBitmap;
    int y,x;
diff --git a/converter/ppm/ppmtoxpm.c b/converter/ppm/ppmtoxpm.c
index 630653df..fc8ba9d7 100644
--- a/converter/ppm/ppmtoxpm.c
+++ b/converter/ppm/ppmtoxpm.c
@@ -39,6 +39,8 @@
 #include <stdio.h>
 #include <ctype.h>
 #include <string.h>
+
+#include "pm_c_util.h"
 #include "ppm.h"
 #include "shhopt.h"
 #include "nstring.h"
@@ -138,7 +140,7 @@ parseCommandLine(int argc, char ** argv,
     /* If output filename not specified, use input filename as default. */
     if (nameSpec)
         cmdlineP->name = nameOpt;
-    else if (STREQ(cmdlineP->inputFilename, "-"))
+    else if (streq(cmdlineP->inputFilename, "-"))
         cmdlineP->name = "noname";
     else {
         static char name[80+1];
diff --git a/converter/ppm/ppmtoyuvsplit.c b/converter/ppm/ppmtoyuvsplit.c
index 133161c1..b5809d0b 100644
--- a/converter/ppm/ppmtoyuvsplit.c
+++ b/converter/ppm/ppmtoyuvsplit.c
@@ -4,7 +4,7 @@
 ** - basename.V : The Chrominance chunk V at 1/4
 ** The subsampled U and V values are made by arithmetic mean.
 **
-** If CCIR601 is defined, the produced YUV triples are scaled again
+** The produced YUV triples are scaled again
 ** to fit into the smaller range of values for this standard.
 **
 ** by A.Beck
@@ -20,12 +20,6 @@
 ** implied warranty.
 */
 
-/* Wether to create YUV in JFIF(JPEG) or CCIR.601(MPEG) scale */
-#define CCIR601
-
-/* Wether to use pm_close() or fake it -- don't ask me why */
-/* #define ORIGINAL */
-
 /* ALPHA Kludge by Franky */
 
 #ifdef __alpha
@@ -70,156 +64,148 @@ termFileNameSet(struct FileNameSet const fname) {
 
 
 int
-main(argc, argv)
-char **argv;
-{
-        FILE *ifp,*vf,*uf,*yf;
-        pixel          *pixelrow1,*pixelrow2;
-        register pixel *pP1,*pP2;
-        int             rows, cols, format, row;
-        register int    col;
-        pixval          maxval;
-        myLONG u,v,y0,y1,y2,y3,u0,u1,u2,u3,v0,v1,v2,v3;
-        unsigned char  *y1buf,*y2buf,*ubuf,*vbuf;
-        struct FileNameSet fname;
-            /* Output file names - .U, .V, .Y */
-
-        ppm_init(&argc, argv);
-
-        if ((argc>3)||(argc<2)) pm_usage("basename [ppmfile]");
-
-        if (argc == 3) ifp = pm_openr(argv[2]);
-        else ifp = stdin;
-
-        makeOutputFileName(argv[1], &fname);
-
-        uf = pm_openw(fname.u);
-        vf = pm_openw(fname.v);
-        yf = pm_openw(fname.y);
-
-        termFileNameSet(fname);
-
-        if(!(uf && vf && yf)) {
-         perror("error opening output files");
-         exit(0);
-        }
-        ppm_readppminit(ifp, &cols, &rows, &maxval, &format);
-
-        if(cols & 1) fprintf(stderr,
-                             "%s: Warning: odd columns count, exceed ignored\n",
-                             argv[0]);
-        if(rows & 1) fprintf(stderr,
-                             "%s: Warning: odd rows count, exceed ignored\n",
-                             argv[0]);
-
-        pixelrow1 = ((pixel*) pm_allocrow( cols, sizeof(pixel) ));
-        pixelrow2 = ((pixel*) pm_allocrow( cols, sizeof(pixel) ));
-
-        y1buf = (unsigned char *) pm_allocrow( cols, 1 );
-        y2buf = (unsigned char *) pm_allocrow( cols, 1 );
-        ubuf = (unsigned char *) pm_allocrow( cols, 1 );
-        vbuf = (unsigned char *) pm_allocrow( cols, 1 );
-
-        for (row = 0; row < (rows & ~1); row += 2) {
-                unsigned char *y1ptr,*y2ptr,*uptr,*vptr;
-
-                ppm_readppmrow(ifp, pixelrow1, cols, maxval, format);
-                ppm_readppmrow(ifp, pixelrow2, cols, maxval, format);
-
-                pP1 = pixelrow1; pP2 = pixelrow2;
-                y1ptr = y1buf; y2ptr = y2buf; vptr = vbuf; uptr = ubuf;
-
-                for (col = 0 ; col < (cols & ~1); col += 2) {
-                        pixval r0,g0,b0,r1,g1,b1,r2,g2,b2,r3,g3,b3;
-
-                        /* first pixel */
-                        r0 = PPM_GETR(*pP1);
-                        g0 = PPM_GETG(*pP1);
-                        b0 = PPM_GETB(*pP1);
-                        pP1++;
-                        /* 2nd pixel */
-                        r1 = PPM_GETR(*pP1);
-                        g1 = PPM_GETG(*pP1);
-                        b1 = PPM_GETB(*pP1);
-                        pP1++;
-                        /* 3rd pixel */
-                        r2 = PPM_GETR(*pP2);
-                        g2 = PPM_GETG(*pP2);
-                        b2 = PPM_GETB(*pP2);
-                        pP2++;
-                        /* 4th pixel */
-                        r3 = PPM_GETR(*pP2);
-                        g3 = PPM_GETG(*pP2);
-                        b3 = PPM_GETB(*pP2);
-                        pP2++;
-
-
-/* The JFIF RGB to YUV Matrix for $00010000 = 1.0
-
-[Y]   [19595   38469    7471][R]
-[U] = [-11056  -21712  32768][G]
-[V]   [32768   -27440  -5328][B]
+main(int argc, const char ** argv) {
+
+    FILE * ifP;
+    FILE *vf, *uf, *yf;
+    pixel *pixelrow1, *pixelrow2;
+    int rows, cols;
+    int format;
+    unsigned int row;
+    pixval maxval;
+    unsigned char *y1buf, *y2buf, *ubuf, *vbuf;
+    struct FileNameSet fname;
+        /* Output file names - .U, .V, .Y */
+
+    pm_proginit(&argc, argv);
 
-*/
+    if ((argc-1 > 2) || (argc-1 < 1))
+        pm_error("Wrong number of arguments: %u.  "
+                 "Arguments are basename for output files "
+                 "and optional input file name", argc-1);
+
+    if (argc-1 == 2)
+        ifP = pm_openr(argv[2]);
+    else
+        ifP = stdin;
+
+    makeOutputFileName(argv[1], &fname);
+
+    uf = pm_openw(fname.u);
+    vf = pm_openw(fname.v);
+    yf = pm_openw(fname.y);
 
-                        y0 =  19595 * r0 + 38469 * g0 +  7471 * b0;
-                        u0 = -11056 * r0 - 21712 * g0 + 32768 * b0;
-                        v0 =  32768 * r0 - 27440 * g0 -  5328 * b0;
+    termFileNameSet(fname);
 
-                        y1 =  19595 * r1 + 38469 * g1 +  7471 * b1;
-                        u1 = -11056 * r1 - 21712 * g1 + 32768 * b1;
-                        v1 =  32768 * r1 - 27440 * g1 -  5328 * b1;
+    ppm_readppminit(ifP, &cols, &rows, &maxval, &format);
 
-                        y2 =  19595 * r2 + 38469 * g2 +  7471 * b2;
-                        u2 = -11056 * r2 - 21712 * g2 + 32768 * b2;
-                        v2 =  32768 * r2 - 27440 * g2 -  5328 * b2;
+    if (cols % 2 == 1)
+        pm_message("Warning: odd columns count %u, excess ignored", cols);
 
-                        y3 =  19595 * r3 + 38469 * g3 +  7471 * b3;
-                        u3 = -11056 * r3 - 21712 * g3 + 32768 * b3;
-                        v3 =  32768 * r3 - 27440 * g3 -  5328 * b3;
+    if (rows % 2 == 1)
+        pm_message("Warning: odd rows count %u, excess ignored", rows);
 
-                        /* mean the chroma for subsampling */
+    pixelrow1 = ((pixel*) pm_allocrow(cols, sizeof(pixel)));
+    pixelrow2 = ((pixel*) pm_allocrow(cols, sizeof(pixel)));
 
-                        u  = (u0+u1+u2+u3)>>2;
-                        v  = (v0+v1+v2+v3)>>2;
+    y1buf = (unsigned char *) pm_allocrow(cols, 1);
+    y2buf = (unsigned char *) pm_allocrow(cols, 1);
+    ubuf = (unsigned char *) pm_allocrow(cols, 1);
+    vbuf = (unsigned char *) pm_allocrow(cols, 1);
 
-#ifdef CCIR601
+    for (row = 0; row < (rows & ~1); row += 2) {
+        unsigned char *y1ptr, *y2ptr, *uptr, *vptr;
+        pixel *pP1, *pP2;
+        unsigned int col;
 
-                        y0 = (y0 * 219)/255 + 1048576;
-                        y1 = (y1 * 219)/255 + 1048576;
-                        y2 = (y2 * 219)/255 + 1048576;
-                        y3 = (y3 * 219)/255 + 1048576;
+        ppm_readppmrow(ifP, pixelrow1, cols, maxval, format);
+        ppm_readppmrow(ifP, pixelrow2, cols, maxval, format);
 
-                        u  = (u * 224)/255 ;
-                        v  = (v * 224)/255 ;
-#endif
+        pP1 = &pixelrow1[0]; pP2 = &pixelrow2[0];
+        y1ptr = y1buf; y2ptr = y2buf; vptr = vbuf; uptr = ubuf;
 
+        for (col = 0 ; col < (cols & ~1); col += 2) {
+            pixval r0,g0,b0,r1,g1,b1,r2,g2,b2,r3,g3,b3;
+            myLONG u, v, y0, y1, y2, y3, u0, u1, u2, u3, v0, v1, v2, v3;
 
-                        *y1ptr++  = (y0 >> 16) ;
-                        *y1ptr++  = (y1 >> 16) ;
-                        *y2ptr++  = (y2 >> 16) ;
-                        *y2ptr++  = (y3 >> 16) ;
+            /* first pixel */
+            r0 = PPM_GETR(*pP1);
+            g0 = PPM_GETG(*pP1);
+            b0 = PPM_GETB(*pP1);
+            pP1++;
+            /* 2nd pixel */
+            r1 = PPM_GETR(*pP1);
+            g1 = PPM_GETG(*pP1);
+            b1 = PPM_GETB(*pP1);
+            pP1++;
+            /* 3rd pixel */
+            r2 = PPM_GETR(*pP2);
+            g2 = PPM_GETG(*pP2);
+            b2 = PPM_GETB(*pP2);
+            pP2++;
+            /* 4th pixel */
+            r3 = PPM_GETR(*pP2);
+            g3 = PPM_GETG(*pP2);
+            b3 = PPM_GETB(*pP2);
+            pP2++;
 
 
-                        *uptr++   = (u >> 16)+128 ;
-                        *vptr++   = (v >> 16)+128 ;
+            /* The JFIF RGB to YUV Matrix for $00010000 = 1.0
 
-                }
-                fwrite(y1buf, (cols & ~1), 1, yf);
-                fwrite(y2buf, (cols & ~1), 1, yf);
-                fwrite(ubuf, cols/2, 1, uf);
-                fwrite(vbuf, cols/2, 1, vf);
-        }
+               [Y]   [19595   38469    7471][R]
+               [U] = [-11056  -21712  32768][G]
+               [V]   [32768   -27440  -5328][B]
 
-/* I dunno why pm_close sees an error... get rid of it */
+            */
 
-#ifdef ORIGINAL
-        pm_close(ifp);
-#else
-        if(ifp != stdin) fclose(ifp);
-#endif
-        fclose(yf);
-        fclose(uf);
-        fclose(vf);
-        exit(0);
+            y0 =  19595 * r0 + 38469 * g0 +  7471 * b0;
+            u0 = -11056 * r0 - 21712 * g0 + 32768 * b0;
+            v0 =  32768 * r0 - 27440 * g0 -  5328 * b0;
+
+            y1 =  19595 * r1 + 38469 * g1 +  7471 * b1;
+            u1 = -11056 * r1 - 21712 * g1 + 32768 * b1;
+            v1 =  32768 * r1 - 27440 * g1 -  5328 * b1;
+
+            y2 =  19595 * r2 + 38469 * g2 +  7471 * b2;
+            u2 = -11056 * r2 - 21712 * g2 + 32768 * b2;
+            v2 =  32768 * r2 - 27440 * g2 -  5328 * b2;
+
+            y3 =  19595 * r3 + 38469 * g3 +  7471 * b3;
+            u3 = -11056 * r3 - 21712 * g3 + 32768 * b3;
+            v3 =  32768 * r3 - 27440 * g3 -  5328 * b3;
+
+            /* mean the chroma for subsampling */
+
+            u  = (u0+u1+u2+u3)>>2;
+            v  = (v0+v1+v2+v3)>>2;
+
+            y0 = (y0 * 219)/255 + 1048576;
+            y1 = (y1 * 219)/255 + 1048576;
+            y2 = (y2 * 219)/255 + 1048576;
+            y3 = (y3 * 219)/255 + 1048576;
+
+            u  = (u * 224)/255 ;
+            v  = (v * 224)/255 ;
+
+            *y1ptr++  = (y0 >> 16) ;
+            *y1ptr++  = (y1 >> 16) ;
+            *y2ptr++  = (y2 >> 16) ;
+            *y2ptr++  = (y3 >> 16) ;
+
+
+            *uptr++   = (u >> 16)+128 ;
+            *vptr++   = (v >> 16)+128 ;
+
+        }
+        fwrite(y1buf, (cols & ~1), 1, yf);
+        fwrite(y2buf, (cols & ~1), 1, yf);
+        fwrite(ubuf, cols/2, 1, uf);
+        fwrite(vbuf, cols/2, 1, vf);
+    }
+
+    pm_close(ifP);
+    fclose(yf);
+    fclose(uf);
+    fclose(vf);
+    return 0;
 }
diff --git a/converter/ppm/rawtoppm.c b/converter/ppm/rawtoppm.c
index 8d6c3b2c..44e856da 100644
--- a/converter/ppm/rawtoppm.c
+++ b/converter/ppm/rawtoppm.c
@@ -13,39 +13,148 @@
 #include <math.h>
 #include "ppm.h"
 
-static void dorowskip ARGS(( FILE* ifp, int rowskip ));
+enum order { ORD_RGB, ORD_RBG, ORD_GRB, ORD_GBR, ORD_BRG, ORD_BGR };
+
+enum interleave { INT_PIX, INT_ROW };
+
+static void
+dorowskip(FILE * const ifP,
+          int    const rowskip) {
+
+    unsigned int i;
+
+    for (i = 0; i < rowskip; ++i) {
+        int const val = getc(ifP);
+        if (val == EOF)
+            pm_error("EOF / read error");
+    }
+}
+
+
+
+static void
+doRowPixInterleave(pixel *      const pixrow,
+                   unsigned int const cols,
+                   FILE *       const ifP,
+                   enum order   const order,
+                   int          const rowskip) {
+
+    unsigned int col;
+
+    for (col = 0; col < cols; ++col) {
+        int const val1 = getc(ifP);
+        int const val2 = getc(ifP);
+        int const val3 = getc(ifP);
+
+        if (val1 == EOF)
+            pm_error("EOF / read error");
+        if (val2 == EOF)
+            pm_error("EOF / read error");
+        if (val3 == EOF)
+            pm_error("EOF / read error");
+
+        switch (order) {
+        case ORD_RGB:
+            PPM_ASSIGN(pixrow[col], val1, val2, val3);
+            break;
+        case ORD_RBG:
+            PPM_ASSIGN(pixrow[col], val1, val3, val2);
+            break;
+        case ORD_GRB:
+            PPM_ASSIGN(pixrow[col], val2, val1, val3);
+            break;
+        case ORD_GBR:
+            PPM_ASSIGN(pixrow[col], val3, val1, val2);
+            break;
+        case ORD_BRG:
+            PPM_ASSIGN(pixrow[col], val2, val3, val1);
+            break;
+        case ORD_BGR:
+            PPM_ASSIGN(pixrow[col], val3, val2, val1);
+            break;
+        }
+    }
+    dorowskip(ifP, rowskip);
+}
+
+
+
+static void
+doRowRowInterleave(pixel *      const pixrow,
+                   unsigned int const cols,
+                   FILE *       const ifP,
+                   enum order   const order,
+                   int          const rowskip,
+                   pixval *     const grow1,
+                   pixval *     const grow2,
+                   pixval *     const grow3) {
+
+    unsigned int col;
+
+    for (col = 0; col < cols; ++col) {
+        int const val1 = getc(ifP);
+        if (val1 == EOF)
+            pm_error("EOF / read error");
+        grow1[col] = val1;
+    }
+    dorowskip(ifP, rowskip);
+    for (col = 0; col < cols; ++col) {
+        int const val2 = getc(ifP);
+        if (val2 == EOF)
+            pm_error( "EOF / read error" );
+        grow2[col] = val2;
+    }
+    dorowskip(ifP, rowskip);
+    for (col = 0; col < cols; ++col) {
+        int const val3 = getc(ifP);
+        if (val3 == EOF)
+            pm_error( "EOF / read error" );
+        grow3[col] = val3;
+    }
+    dorowskip(ifP, rowskip);
+
+    for (col = 0; col < cols; ++col) {
+        switch (order) {
+        case ORD_RGB:
+            PPM_ASSIGN(pixrow[col], grow1[col], grow2[col], grow3[col]);
+            break;
+        case ORD_RBG:
+            PPM_ASSIGN(pixrow[col], grow1[col], grow3[col], grow2[col]);
+            break;
+        case ORD_GRB:
+            PPM_ASSIGN(pixrow[col], grow2[col], grow1[col], grow3[col]);
+            break;
+        case ORD_GBR:
+            PPM_ASSIGN(pixrow[col], grow3[col], grow1[col], grow2[col]);
+            break;
+        case ORD_BRG:
+            PPM_ASSIGN(pixrow[col], grow2[col], grow3[col], grow1[col]);
+            break;
+        case ORD_BGR:
+            PPM_ASSIGN(pixrow[col], grow3[col], grow2[col], grow1[col]);
+            break;
+        }
+    }
+}
+
+
 
 int
-main( argc, argv )
-    int argc;
-    char* argv[];
-    {
-    FILE* ifp;
-    pixel* pixrow;
-    register pixel* pP;
+main(int argc, const char * argv[]) {
+
+    pixval const maxval = 255;
+
+    FILE * ifP;
+    pixel * pixrow;
     int argn, headerskip, rowskip, rows, cols, row, i;
-    register int col;
-    int order;
-#define ORD_RGB 1
-#define ORD_RBG 2
-#define ORD_GRB 3
-#define ORD_GBR 4
-#define ORD_BRG 5
-#define ORD_BGR 6
-int interleave;
-#define INT_PIX 1
-#define INT_ROW 2
-    int val1, val2, val3;
-    gray* grow1;
-    gray* grow2;
-    gray* grow3;
-    register gray* g1P;
-    register gray* g2P;
-    register gray* g3P;
+    enum order order;
+    enum interleave interleave;
+    gray * grow1;
+    gray * grow2;
+    gray * grow3;
     const char* const usage = "[-headerskip N] [-rowskip N] [-rgb|-rbg|-grb|-gbr|-brg|-bgr] [-interpixel|-interrow] <width> <height> [rawfile]";
 
-
-    ppm_init( &argc, argv );
+    pm_proginit(&argc, argv);
 
     argn = 1;
     headerskip = 0;
@@ -54,191 +163,90 @@ int interleave;
     interleave = INT_PIX;
 
     while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
-	{
-	if ( pm_keymatch( argv[argn], "-headerskip", 2 ) )
-	    {
-	    ++argn;
-	    if ( argn >= argc )
-		pm_usage( usage );
-	    headerskip = atoi( argv[argn] );
-	    }
-	else if ( pm_keymatch( argv[argn], "-rowskip", 3 ) )
-	    {
-	    ++argn;
-	    if ( argn >= argc )
-		pm_usage( usage );
-	    rowskip = atoi( argv[argn] );
-	    }
-	else if ( pm_keymatch( argv[argn], "-rgb", 3 ) )
-	    order = ORD_RGB;
-	else if ( pm_keymatch( argv[argn], "-rbg", 3 ) )
-	    order = ORD_RBG;
-	else if ( pm_keymatch( argv[argn], "-grb", 3 ) )
-	    order = ORD_GRB;
-	else if ( pm_keymatch( argv[argn], "-gbr", 3 ) )
-	    order = ORD_GBR;
-	else if ( pm_keymatch( argv[argn], "-brg", 3 ) )
-	    order = ORD_BRG;
-	else if ( pm_keymatch( argv[argn], "-bgr", 3 ) )
-	    order = ORD_BGR;
-	else if ( pm_keymatch( argv[argn], "-interpixel", 7 ) )
-	    interleave = INT_PIX;
-	else if ( pm_keymatch( argv[argn], "-interrow", 7 ) )
-	    interleave = INT_ROW;
-	else
-	    pm_usage( usage );
-	++argn;
-	}
+        {
+        if ( pm_keymatch( argv[argn], "-headerskip", 2 ) )
+            {
+            ++argn;
+            if ( argn >= argc )
+                pm_usage( usage );
+            headerskip = atoi( argv[argn] );
+            }
+        else if ( pm_keymatch( argv[argn], "-rowskip", 3 ) )
+            {
+            ++argn;
+            if ( argn >= argc )
+                pm_usage( usage );
+            rowskip = atoi( argv[argn] );
+            }
+        else if ( pm_keymatch( argv[argn], "-rgb", 3 ) )
+            order = ORD_RGB;
+        else if ( pm_keymatch( argv[argn], "-rbg", 3 ) )
+            order = ORD_RBG;
+        else if ( pm_keymatch( argv[argn], "-grb", 3 ) )
+            order = ORD_GRB;
+        else if ( pm_keymatch( argv[argn], "-gbr", 3 ) )
+            order = ORD_GBR;
+        else if ( pm_keymatch( argv[argn], "-brg", 3 ) )
+            order = ORD_BRG;
+        else if ( pm_keymatch( argv[argn], "-bgr", 3 ) )
+            order = ORD_BGR;
+        else if ( pm_keymatch( argv[argn], "-interpixel", 7 ) )
+            interleave = INT_PIX;
+        else if ( pm_keymatch( argv[argn], "-interrow", 7 ) )
+            interleave = INT_ROW;
+        else
+            pm_usage( usage );
+        ++argn;
+        }
 
     if ( argn + 2 > argc )
-	pm_usage( usage );
-
-    cols = atoi( argv[argn++] );
-    rows = atoi( argv[argn++] );
-    if ( cols <= 0 || rows <= 0 )
-	pm_usage( usage );
+        pm_usage( usage );
+    
+    cols = pm_parse_width(argv[argn++]);
+    rows = pm_parse_height(argv[argn++]);
 
     if ( argn < argc )
-	{
-	ifp = pm_openr( argv[argn] );
-	++argn;
-	}
+        {
+        ifP = pm_openr( argv[argn] );
+        ++argn;
+        }
     else
-	ifp = stdin;
+        ifP = stdin;
 
     if ( argn != argc )
-	pm_usage( usage );
-
-    ppm_writeppminit( stdout, cols, rows, (pixval) 255, 0 );
-    pixrow = ppm_allocrow( cols );
-
-    if ( interleave == INT_ROW )
-	{
-	grow1 = pgm_allocrow( cols );
-	grow2 = pgm_allocrow( cols );
-	grow3 = pgm_allocrow( cols );
-	}
-
-    for ( i = 0; i < headerskip; ++i )
-	{
-	val1 = getc( ifp );
-	if ( val1 == EOF )
-	    pm_error( "EOF / read error" );
-	}
-
-    for ( row = 0; row < rows; ++row)
-	{
-	switch ( interleave )
-	    {
-	    case INT_PIX:
-	    for ( col = 0, pP = pixrow; col < cols; ++col, ++pP )
-		{
-		val1 = getc( ifp );
-		if ( val1 == EOF )
-		    pm_error( "EOF / read error" );
-		val2 = getc( ifp );
-		if ( val2 == EOF )
-		    pm_error( "EOF / read error" );
-		val3 = getc( ifp );
-		if ( val3 == EOF )
-		    pm_error( "EOF / read error" );
-		switch ( order )
-		    {
-		    case ORD_RGB:
-		    PPM_ASSIGN( *pP, val1, val2, val3 );
-		    break;
-		    case ORD_RBG:
-		    PPM_ASSIGN( *pP, val1, val3, val2 );
-		    break;
-		    case ORD_GRB:
-		    PPM_ASSIGN( *pP, val2, val1, val3 );
-		    break;
-		    case ORD_GBR:
-		    PPM_ASSIGN( *pP, val3, val1, val2 );
-		    break;
-		    case ORD_BRG:
-		    PPM_ASSIGN( *pP, val2, val3, val1 );
-		    break;
-		    case ORD_BGR:
-		    PPM_ASSIGN( *pP, val3, val2, val1 );
-		    break;
-		    }
-		}
-	    dorowskip( ifp, rowskip );
-	    break;
-
-	    case INT_ROW:
-	    for ( col = 0, g1P = grow1; col < cols; ++col, ++g1P )
-		{
-		val1 = getc( ifp );
-		if ( val1 == EOF )
-		    pm_error( "EOF / read error" );
-		*g1P = val1;
-		}
-	    dorowskip( ifp, rowskip );
-	    for ( col = 0, g2P = grow2; col < cols; ++col, ++g2P )
-		{
-		val2 = getc( ifp );
-		if ( val2 == EOF )
-		    pm_error( "EOF / read error" );
-		*g2P = val2;
-		}
-	    dorowskip( ifp, rowskip );
-	    for ( col = 0, g3P = grow3; col < cols; ++col, ++g3P )
-		{
-		val3 = getc( ifp );
-		if ( val3 == EOF )
-		    pm_error( "EOF / read error" );
-		*g3P = val3;
-		}
-	    dorowskip( ifp, rowskip );
-	    for ( col = 0, pP = pixrow, g1P = grow1, g2P = grow2, g3P = grow3;
-		  col < cols; ++col, ++pP, ++g1P, ++g2P, ++g3P )
-		{
-		switch ( order )
-		    {
-		    case ORD_RGB:
-		    PPM_ASSIGN( *pP, *g1P, *g2P, *g3P );
-		    break;
-		    case ORD_RBG:
-		    PPM_ASSIGN( *pP, *g1P, *g3P, *g2P );
-		    break;
-		    case ORD_GRB:
-		    PPM_ASSIGN( *pP, *g2P, *g1P, *g3P );
-		    break;
-		    case ORD_GBR:
-		    PPM_ASSIGN( *pP, *g3P, *g1P, *g2P );
-		    break;
-		    case ORD_BRG:
-		    PPM_ASSIGN( *pP, *g2P, *g3P, *g1P );
-		    break;
-		    case ORD_BGR:
-		    PPM_ASSIGN( *pP, *g3P, *g2P, *g1P );
-		    break;
-		    }
-		}
-	    break;
-	    }
-	ppm_writeppmrow( stdout, pixrow, cols, (pixval) 255, 0 );
-	}
-
-    pm_close( ifp );
-    pm_close( stdout );
+        pm_usage( usage );
 
-    exit( 0 );
+    ppm_writeppminit(stdout, cols, rows, (pixval) 255, 0);
+    pixrow = ppm_allocrow(cols);
+
+    if (interleave == INT_ROW) {
+        grow1 = pgm_allocrow(cols);
+        grow2 = pgm_allocrow(cols);
+        grow3 = pgm_allocrow(cols);
     }
 
-static void
-dorowskip( ifp, rowskip )
-    FILE* ifp;
-    int rowskip;
-    {
-    int i, val;
-
-    for ( i = 0; i < rowskip; ++i )
-	{
-	val = getc( ifp );
-	if ( val == EOF )
-	    pm_error( "EOF / read error" );
-	}
+    for (i = 0; i < headerskip; ++i) {
+        int const val = getc(ifP);
+        if (val == EOF)
+            pm_error("EOF / read error");
+    }
+
+    for (row = 0; row < rows; ++row) {
+        switch (interleave) {
+        case INT_PIX:
+            doRowPixInterleave(pixrow, cols, ifP, order, rowskip);
+            break;
+
+        case INT_ROW:
+            doRowRowInterleave(pixrow, cols, ifP, order, rowskip,
+                               grow1, grow2, grow3);
+            break;
+        }
+        ppm_writeppmrow(stdout, pixrow, cols, maxval, 0);
+    }
+
+    pm_close( ifP );
+    pm_close( stdout );
+
+    exit( 0 );
     }
diff --git a/converter/ppm/sldtoppm.c b/converter/ppm/sldtoppm.c
index 6b51f291..ad16a649 100644
--- a/converter/ppm/sldtoppm.c
+++ b/converter/ppm/sldtoppm.c
@@ -66,15 +66,9 @@ struct svector {
     struct spoint t;          /* To point */
 };
 
-static int extend ARGS((smallint ch));
-static int sli ARGS((void));
-static int slib ARGS((void));
-static void vscale ARGS((int *px, int *py));
-static void slider ARGS((void (*slvec) ARGS((struct svector *vec, int color)),
-                    void (*slflood) ARGS((struct spolygon *poly, int color)) ));
-static void slidefind ARGS((char *sname, int dironly, int ucasen));
-static void draw ARGS((struct svector *vec, int color));
-static void flood ARGS((struct spolygon *const poly, int const color));
+typedef void (slvecfn)(struct svector * vec, int color);
+typedef void (slfloodfn)(struct spolygon * poly, int color);
+
 
 static int ixdots, iydots;        /* Screen size in dots */
 static FILE *slfile;              /* Slide file descriptor */
@@ -104,21 +98,26 @@ static int adjust = FALSE;        /* Adjust to correct aspect ratio ? */
 static struct slhead slfrof;          /* Slide file header */
 static long xfac, yfac;           /* Aspect ratio scale factors */
 
-static int sdrawkcab;             /* Slide drawing kinematic conversion of
-                     ass-backwards data flag */
+static int sdrawkcab;
+    /* Slide drawing kinematic conversion of ass-backwards data flag */
+
+
 
 /*  EXTEND  --  Turn a smallint into an int with sign extension, whether
-        or not that happens automatically.  */
+    or not that happens automatically.
+*/
 
-static int extend(smallint ch)
-{
+static int
+extend(smallint ch) {
     return ((int) ((ch & 0x80) ? (ch | ~0xFF) : ch));
 }
 
+
+
 /*  SLI  --  Input word from slide file  */
 
-static int sli()
-{
+static int
+sli(void) {
     short wd;
 
     if (fread(&wd, sizeof wd, 1, slfile) != 1) {
@@ -131,10 +130,12 @@ static int sli()
     return wd;
 }
 
+
+
 /*  SLIB  --  Input byte from slide file  */
 
-static int slib()
-{
+static int 
+slib(void) {
     smallint ch = 0;
 
     if (fread(&ch, sizeof ch, 1, slfile) != 1) {
@@ -143,25 +144,171 @@ static int slib()
     return extend(ch);
 }
 
+
+
 /*  VSCALE -- scale screen coordinates for mismatched display.  */
 
-static void vscale(px, py)
-  int *px, *py;
-{
+static void
+vscale(int * const px,
+       int * const py) {
+
     *px = (((unsigned) *px) * xfac) >> 16;
     *py = (((unsigned) *py) * yfac) >> 16;
 }
 
+
+
+/*  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,
+          bool         const ucasen) {
+
+    char uname[32];
+    unsigned char libent[36];
+    long pos;
+
+    if (dironly)
+        pm_message("Slides in library:");
+    else {
+        int i;
+        const char * ip;
+        
+        ip = sname; /* initial value */
+        
+        for (i = 0; i < 31; i++) {
+            char ch = *ip++;
+            if (ch == EOS)
+                break;
+            if (ucasen && ISLOWER(ch))
+                ch = TOUPPER(ch);
+            uname[i] = ch;
+        }
+        uname[i] = EOS;
+    }
+    
+    /* Read slide library header and verify. */
+    
+    if ((fread(libent, 32, 1, slfile) != 1) ||
+        (!streq((char *)libent, "AutoCAD Slide Library 1.0\015\012\32"))) {
+        pm_error("not an AutoCAD slide library file.");
+    }
+    pos = 32;
+    
+    /* Search for a slide with the requested name. */
+    
+    while (TRUE) {
+        if ((fread(libent, 36, 1, slfile) != 1) ||
+            (strlen((char *)libent) == 0)) {
+            if (dironly) {
+                return;
+            }
+            pm_error("slide %s not in library.", sname);
+        }
+        pos += 36;
+        if (dironly) {
+            pm_message("  %s", libent);
+        } else if (streq((char *)libent, uname)) {
+            long 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);
+            }
+            break;
+        }
+    }
+}
+
+
+
+/*  DRAW  --  Draw a vector in the given AutoCAD color.  */
+
+static slvecfn draw;
+
+static void
+draw(struct svector * vec,
+     int              color) {
+
+    pixel rgbcolor;
+
+    if (blither) {
+        pm_message("Vector (%d, %d) - (%d, %d)  Color %d",
+           vec->f.x, vec->f.y, vec->t.x, vec->t.y, color);
+    }
+    assert(vec->f.x >= 0 && vec->f.x < pixcols);
+    assert(vec->f.y >= 0 && vec->f.y < pixrows);
+    assert(vec->t.x >= 0 && vec->t.x < pixcols);
+    assert(vec->t.y >= 0 && vec->t.y < pixrows);
+    PPM_ASSIGN(rgbcolor,
+               acadcol[color][0], acadcol[color][1], acadcol[color][2]);
+    ppmd_line(pixels, pixcols, pixrows, pixmaxval,
+              vec->f.x, iydots - vec->f.y, vec->t.x, iydots - vec->t.y,
+              PPMD_NULLDRAWPROC,
+              (char *) &rgbcolor);
+}
+
+
+
+/*  FLOOD  --  Draw a filled polygon.  */
+
+static slfloodfn flood;
+
+static void
+flood(struct spolygon * const poly,
+      int               const color) {
+
+    unsigned int i;
+    struct fillobj * handle;
+    pixel rgbcolor;
+
+    handle = ppmd_fill_create();
+
+    if (blither) {
+        unsigned int i;
+        pm_message("Polygon: %d points, fill type %d, color %d",
+                   poly->npoints, poly->fill, color);
+        for (i = 0; i < poly->npoints; i++) {
+            pm_message("   Point %d:  (%d, %d)", i + 1,
+                       poly->pt[i].x, poly->pt[i].y);
+        }
+    }
+
+    PPM_ASSIGN(rgbcolor,
+               acadcol[color][0], acadcol[color][1], acadcol[color][2]);
+    for (i = 0; i < poly->npoints; i++) {
+        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 + 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);
+}
+
+
+
 /*  SLIDER  --  Read slide file.  This is called with the name of the
         file to be read and function pointers to the routines
         which process vectors and polygon fill requests
         respectively.
 */
 
-static void slider(slvec, slflood)
-  void (*slvec) ARGS((struct svector *vec, int color));
-  void (*slflood) ARGS((struct spolygon *poly, int color));
-{
+static void
+slider(slvecfn   slvec,
+       slfloodfn slflood) {
+
     int i, rescale;
     unsigned char ubfr[4];        /* Utility character buffer */
     int lx, ly;               /* Last x and y point */
@@ -174,14 +321,14 @@ static void slider(slvec, slflood)
     short rtest;              /* Value to test byte reversal */
     short btest = 0x1234;         /* Value to test byte-reversal */
     static struct slhead slhi =       /* Master slide header sample */
-        {"AutoCAD Slide\r\n\32", 86,2, 0,0, 0.0, 0};
+    {"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);
@@ -194,48 +341,46 @@ static void slider(slvec, slflood)
 
     /* Verify that slide format is compatible with this program. */
 
-    if (STREQ(slfrof.slh, slhi.slh)) {
+    if (streq(slfrof.slh, slhi.slh))
         pm_error("this is not an AutoCAD slide file.");
-    }
 
     /* Verify that the number format and file level in the header  are
        compatible.  All slides written by versions of AutoCAD released
        since September of 1987 are compatible with this format.  */
 
-    if ((slfrof.sntype != slhi.sntype) || (slfrof.slevel != slhi.slevel)) {
+    if ((slfrof.sntype != slhi.sntype) || (slfrof.slevel != slhi.slevel))
         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];
-    }
+    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. */
+       so far.
+    */
 
     if (btest != rtest) {
-    sdrawkcab = TRUE;
+        sdrawkcab = TRUE;
 #define rshort(x) x = ((x >> 8) & 0xFF) | (x << 8)
-    rshort(slfrof.sxdots);
-    rshort(slfrof.sydots);
-    rshort(slfrof.shwfill);
+        rshort(slfrof.sxdots);
+        rshort(slfrof.sydots);
+        rshort(slfrof.shwfill);
 #undef rshort
     }
-
+    
     /* Dump the header if we're blithering. */
 
     if (blither || info) {
         pm_message("Slide file type %d, level %d, hwfill type %d.",
-        slfrof.sntype, slfrof.slevel, slfrof.shwfill);
+                   slfrof.sntype, slfrof.slevel, slfrof.shwfill);
         pm_message("Original screen size %dx%d, aspect ratio %.3f.",
-        slfrof.sxdots + 1, slfrof.sydots + 1, slfrof.sdsar);
+                   slfrof.sxdots + 1, slfrof.sydots + 1, slfrof.sdsar);
         pm_message("Byte order is %s.",
-            sdrawkcab ? "being reversed" : "the same");
+                   sdrawkcab ? "being reversed" : "the same");
     }
 
     /* If the display aspect ratio indicates that the  pixels  on  the
@@ -246,405 +391,284 @@ static void slider(slvec, slflood)
        We  always  correct  the aspect ratio by adjusting the width of
        the image.  This guarantees that output from the SHADE command,
        which  is  essentially  scan-line  data written in vector form,
-       will not be corrupted. */
+       will not be corrupted.
+    */
 
     dsar = ((double) slfrof.sxdots) / slfrof.sydots;
     if (fabs(slfrof.sdsar - dsar) > 0.0001) {
-    if (adjust) {
-        ixdots = slfrof.sxdots * (slfrof.sdsar / dsar) + 0.5;
-        iydots = slfrof.sydots;
-        dsar = ((double) ixdots) / iydots;
+        if (adjust) {
+            ixdots = slfrof.sxdots * (slfrof.sdsar / dsar) + 0.5;
+            iydots = slfrof.sydots;
+            dsar = ((double) ixdots) / iydots;
+        } else {
+            pm_message("Warning - pixels on source screen were non-square.  "
+                       "Specifying -adjust will correct image width "
+                       "to compensate.");
+            ixdots = slfrof.sxdots;
+            iydots = slfrof.sydots;
+            dsar = slfrof.sdsar;
+        }
     } else {
-            pm_message("Warning - pixels on source screen were non-square.");
-            pm_message("          Specifying -adjust will correct image width to compensate.");
+        /* Source pixels were square. */
         ixdots = slfrof.sxdots;
         iydots = slfrof.sydots;
         dsar = slfrof.sdsar;
-    }
-    } else {
-    /* Source pixels were square. */
-    ixdots = slfrof.sxdots;
-    iydots = slfrof.sydots;
-    dsar = slfrof.sdsar;
-    adjust = FALSE;           /* Mark no adjustment needed */
+        adjust = FALSE;           /* Mark no adjustment needed */
     }
 
     /* If there's a uniform scale factor specified, apply it. */
 
     if (uscale > 0) {
-    ixdots = (ixdots * uscale) + 0.5;
-    iydots = (iydots * uscale) + 0.5;
+        ixdots = (ixdots * uscale) + 0.5;
+        iydots = (iydots * uscale) + 0.5;
     }
 
     /* If the image is to be stretched  to  a  given  width,  set  the
        output  image  sizes accordingly.  If only a height or width is
        given, scale the other direction proportionally to preserve the
-       aspect ratio. */
+       aspect ratio.
+    */
 
     if (sxsize > 0) {
-    if (sysize > 0) {
-        iydots = sysize - 1;
-    } else {
-        iydots = ((((long) iydots) * (sxsize - 1)) +
-              (iydots / 2)) / ixdots;
-    }
-    ixdots = sxsize - 1;
-    } else if (sysize > 0) {
-    if (sxsize > 0) {
+        if (sysize > 0) {
+            iydots = sysize - 1;
+        } else {
+            iydots = ((((long) iydots) * (sxsize - 1)) +
+                      (iydots / 2)) / ixdots;
+        }
         ixdots = sxsize - 1;
-    } else {
-        ixdots = ((((long) ixdots) * (sysize - 1)) +
-              (ixdots / 2)) / iydots;
-    }
-    iydots = sysize - 1;
+    } else if (sysize > 0) {
+        if (sxsize > 0) {
+            ixdots = sxsize - 1;
+        } else {
+            ixdots = ((((long) ixdots) * (sysize - 1)) +
+                      (ixdots / 2)) / iydots;
+        }
+        iydots = sysize - 1;
     }
-
+    
     if (adjust) {
-    pm_message(
+        pm_message(
             "Resized from %dx%d to %dx%d to correct pixel aspect ratio.",
-        slfrof.sxdots + 1, slfrof.sydots + 1, ixdots + 1, iydots + 1);
+            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) {
-
+         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;
-    yfac /= (long) (slfrof.sydots + 1);
-    if (dsar < slfrof.sdsar) {
-        yfac = yfac * dsar / slfrof.sdsar;
-       } else {
-        xfac = xfac * slfrof.sdsar / dsar;
-    }
+           right on this display.
+        */
+        
+        xfac = (ixdots + 1) * 0x10000L;
+        xfac /= (long) (slfrof.sxdots + 1);
+        yfac = (iydots + 1) * 0x10000L;
+        yfac /= (long) (slfrof.sydots + 1);
+        if (dsar < slfrof.sdsar) {
+            yfac = yfac * dsar / slfrof.sdsar;
+        } else {
+            xfac = xfac * slfrof.sdsar / dsar;
+        }
     }
 
     poly.npoints = 0;             /* No flood in progress. */
-
+    
     while ((cw = sli()) != 0xFC00) {
-    switch (cw & 0xFF00) {
+        switch (cw & 0xFF00) {
         case 0xFB00:          /*  Short vector compressed  */
-        vec.f.x = lx + extend(cw & 0xFF);
-        vec.f.y = ly + slib();
-        vec.t.x = lx + slib();
-        vec.t.y = ly + slib();
-        lx = vec.f.x;
-        ly = vec.f.y;
-        if (rescale) {
-            vscale(&vec.f.x, &vec.f.y);
-            vscale(&vec.t.x, &vec.t.y);
-        }
-        (*slvec)(&vec, curcolor);/* Draw vector on screen */
-        slx = vec.f.x;        /* Save scaled point */
-        sly = vec.f.y;
-        break;
-
+            vec.f.x = lx + extend(cw & 0xFF);
+            vec.f.y = ly + slib();
+            vec.t.x = lx + slib();
+            vec.t.y = ly + slib();
+            lx = vec.f.x;
+            ly = vec.f.y;
+            if (rescale) {
+                vscale(&vec.f.x, &vec.f.y);
+                vscale(&vec.t.x, &vec.t.y);
+            }
+            (*slvec)(&vec, curcolor);/* Draw vector on screen */
+            slx = vec.f.x;        /* Save scaled point */
+            sly = vec.f.y;
+            break;
+            
         case 0xFC00:          /*  End of file  */
-        break;
-
+            break;
+            
         case 0xFD00:          /*  Flood command  */
-        vec.f.x = sli();
-        vec.f.y = sli();
-        if ((int) vec.f.y < 0) { /* start or end */
-            if (poly.npoints != 0) { /* end?  */
-            if (poly.npoints > 2 && poly.npoints < 11) {
-                (*slflood)(&poly, curcolor);
-            } else {
-                            pm_error("Bad polygon vertex count (%d)",
-                   poly.npoints);
-            }
-            poly.npoints = 0;
-            } else {
-            poly.fill = -vec.f.y;  /* Start */
+            vec.f.x = sli();
+            vec.f.y = sli();
+            if ((int) vec.f.y < 0) { /* start or end */
+                if (poly.npoints != 0) { /* end?  */
+                    if (poly.npoints > 2 && poly.npoints < 11) {
+                        (*slflood)(&poly, curcolor);
+                    } else {
+                        pm_error("Bad polygon vertex count (%d)",
+                                 poly.npoints);
+                    }
+                    poly.npoints = 0;
+                } else {
+                    poly.fill = -vec.f.y;  /* Start */
+                }
+            } else {          /* Polygon vertex */
+                if (poly.npoints < 10) {
+                    if (rescale) {
+                        vscale(&vec.f.x, &vec.f.y);
+                    }
+                    poly.pt[poly.npoints].x = vec.f.x;
+                    poly.pt[poly.npoints].y = vec.f.y;
+                }
+                poly.npoints++;
             }
-        } else {          /* Polygon vertex */
-            if (poly.npoints < 10) {
+            break;
+            
+        case 0xFE00:          /*  Common endpoint compressed  */
+            vec.f.x = lx + extend(cw & 0xFF);
+            vec.f.y = ly + slib();
+            lx = vec.f.x;
+            ly = vec.f.y;
+            vec.t.x = slx;
+            vec.t.y = sly;
             if (rescale) {
                 vscale(&vec.f.x, &vec.f.y);
             }
-            poly.pt[poly.npoints].x = vec.f.x;
-            poly.pt[poly.npoints].y = vec.f.y;
-            }
-            poly.npoints++;
-        }
-        break;
-
-        case 0xFE00:          /*  Common endpoint compressed  */
-        vec.f.x = lx + extend(cw & 0xFF);
-        vec.f.y = ly + slib();
-        lx = vec.f.x;
-        ly = vec.f.y;
-        vec.t.x = slx;
-        vec.t.y = sly;
-        if (rescale) {
-            vscale(&vec.f.x, &vec.f.y);
-        }
-        (*slvec)(&vec, curcolor);/* Draw vector */
-        slx = vec.f.x;        /* Save scaled point */
-        sly = vec.f.y;
-        break;
-
+            (*slvec)(&vec, curcolor);/* Draw vector */
+            slx = vec.f.x;        /* Save scaled point */
+            sly = vec.f.y;
+            break;
+            
         case 0xFF00:          /*  Change color  */
-        curcolor = cw & 0xFF;
-        break;
+            curcolor = cw & 0xFF;
+            break;
 
         default:              /*  Co-ordinates  */
-        lx = vec.f.x = cw;
-        ly = vec.f.y = sli();
-        vec.t.x = sli();
-        vec.t.y = sli();
-        if (rescale) {
-           vscale(&vec.f.x, &vec.f.y);
-           vscale(&vec.t.x, &vec.t.y);
-        }
-        (*slvec)(&vec, curcolor);
-        slx = vec.f.x;        /* Save scaled point */
-        sly = vec.f.y;
-        break;
-    }
-    }
-}
-
-/*  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(sname, dironly, ucasen)
-  char *sname;
-  int dironly, ucasen;
-{
-    char uname[32];
-    unsigned char libent[36];
-    long pos;
-
-    if (dironly) {
-        pm_message("Slides in library:");
-    } else {
-    int i;
-    char *ip = sname;
-
-    for (i = 0; i < 31; i++) {
-        char ch = *ip++;
-        if (ch == EOS) {
-        break;
-        }
-        if (ucasen && ISLOWER(ch)) {
-        ch = TOUPPER(ch);
-        }
-        uname[i] = ch;
-    }
-    uname[i] = EOS;
-    }
-
-    /* Read slide library header and verify. */
-
-    if ((fread(libent, 32, 1, slfile) != 1) ||
-        (!STREQ((char *)libent, "AutoCAD Slide Library 1.0\015\012\32"))) {
-        pm_error("not an AutoCAD slide library file.");
-    }
-    pos = 32;
-
-    /* Search for a slide with the requested name. */
-
-    while (TRUE) {
-    if ((fread(libent, 36, 1, slfile) != 1) ||
-        (strlen((char *)libent) == 0)) {
-        if (dironly) {
-        return;
-        }
-            pm_error("slide %s not in library.", sname);
-    }
-    pos += 36;
-    if (dironly) {
-            pm_message("  %s", libent);
-    } else if (STREQ((char *)libent, uname)) {
-        long 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) {
-            (void) getc(slfile);
-        }
+            lx = vec.f.x = cw;
+            ly = vec.f.y = sli();
+            vec.t.x = sli();
+            vec.t.y = sli();
+            if (rescale) {
+                vscale(&vec.f.x, &vec.f.y);
+                vscale(&vec.t.x, &vec.t.y);
+            }
+            (*slvec)(&vec, curcolor);
+            slx = vec.f.x;        /* Save scaled point */
+            sly = vec.f.y;
+            break;
         }
-        break;
-    }
     }
 }
 
-/*  DRAW  --  Draw a vector in the given AutoCAD color.  */
-
-static void draw(vec, color)
-  struct svector *vec;
-  int color;
-{
-    pixel rgbcolor;
-
-    if (blither) {
-        pm_message("Vector (%d, %d) - (%d, %d)  Color %d",
-           vec->f.x, vec->f.y, vec->t.x, vec->t.y, color);
-    }
-    assert(vec->f.x >= 0 && vec->f.x < pixcols);
-    assert(vec->f.y >= 0 && vec->f.y < pixrows);
-    assert(vec->t.x >= 0 && vec->t.x < pixcols);
-    assert(vec->t.y >= 0 && vec->t.y < pixrows);
-    PPM_ASSIGN(rgbcolor,
-           acadcol[color][0], acadcol[color][1], acadcol[color][2]);
-    ppmd_line(pixels, pixcols, pixrows, pixmaxval,
-          vec->f.x, iydots - vec->f.y, vec->t.x, iydots - vec->t.y,
-          PPMD_NULLDRAWPROC,
-          (char *) &rgbcolor);
-}
-
-/*  FLOOD  --  Draw a filled polygon.  */
-
-static void
-flood(struct spolygon * const poly,
-      int               const color) {
-
-    int i;
-    struct fillobj * handle;
-    pixel rgbcolor;
-
-    handle = ppmd_fill_create();
-
-    if (blither) {
-        pm_message("Polygon: %d points, fill type %d, color %d",
-                   poly->npoints, poly->fill, color);
-        for (i = 0; i < poly->npoints; i++) {
-            pm_message("   Point %d:  (%d, %d)", i + 1,
-                       poly->pt[i].x, poly->pt[i].y);
-        }
-    }
-
-    PPM_ASSIGN(rgbcolor,
-               acadcol[color][0], acadcol[color][1], acadcol[color][2]);
-    for (i = 0; i < poly->npoints; i++) {
-        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 + 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);
-}
 
 /*  Main program. */
 
-int main(argc, argv)
-  int argc;
-  char *argv[];
-{
+int
+main(int    argc,
+     char * argv[]) {
+
     int argn;
     const char * const usage = "[-verbose] [-info] [-adjust] [-scale <s>]\n\
-                 [-dir] [-lib|-Lib <name>]\n\
-                 [-xsize|-width <x>] [-ysize|-height <y>] [sldfile]";
+[-dir] [-lib|-Lib <name>]\n\
+[-xsize|-width <x>] [-ysize|-height <y>] [sldfile]";
     int scalespec = FALSE, widspec = FALSE, hgtspec = FALSE, dironly = FALSE,
-    ucasen;
-    char *slobber = (char *) 0;       /* Slide library item */
+        ucasen;
+    const char * slobber;       /* Slide library item */
 
 
+    slobber = NULL;
+
     ppm_init(&argc, argv);
     argn = 1;
 
     while (argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0') {
         if (pm_keymatch(argv[argn], "-verbose", 2)) {
-        blither = TRUE;
+            blither = TRUE;
         } else if (pm_keymatch(argv[argn], "-adjust", 2)) {
-        adjust = TRUE;
+            adjust = TRUE;
         } else if (pm_keymatch(argv[argn], "-dir", 2)) {
-        dironly = TRUE;
+            dironly = TRUE;
         } else if (pm_keymatch(argv[argn], "-info", 2)) {
-        info = TRUE;
+            info = TRUE;
         } else if (pm_keymatch(argv[argn], "-lib", 2)) {
-        if (slobber != (char *) 0) {
+            if (slobber)
                 pm_error("already specified a library item");
-        }
             ucasen = argv[argn][1] != 'L';
-        argn++;
-        if (argn == argc) {
-        pm_usage(usage);
-        }
-        slobber = argv[argn];
+            argn++;
+            if (argn == argc) {
+                pm_usage(usage);
+            }
+            slobber = argv[argn];
         } else if (pm_keymatch(argv[argn], "-scale", 2)) {
-        if (scalespec) {
+            if (scalespec) {
                 pm_error("already specified a scale factor");
-        }
-        argn++;
+            }
+            argn++;
             if ((argn == argc) || (sscanf(argv[argn], "%lf", &uscale) != 1))
-        pm_usage(usage);
-        if (uscale <= 0.0) {
+                pm_usage(usage);
+            if (uscale <= 0.0) {
                 pm_error("scale factor must be greater than 0");
-        }
-        scalespec = TRUE;
+            }
+            scalespec = TRUE;
         } else if (pm_keymatch(argv[argn], "-xsize", 2) ||
                    pm_keymatch(argv[argn], "-width", 2)) {
-        if (widspec) {
+            if (widspec) {
                 pm_error("already specified a width/xsize");
-        }
-        argn++;
+            }
+            argn++;
             if ((argn == argc) || (sscanf(argv[argn], "%d", &sxsize) != 1))
-        pm_usage(usage);
-        widspec = TRUE;
+                pm_usage(usage);
+            widspec = TRUE;
         } else if (pm_keymatch(argv[argn], "-ysize", 2) ||
                    pm_keymatch(argv[argn], "-height", 2)) {
-        if (hgtspec) {
+            if (hgtspec) {
                 pm_error("already specified a height/ysize");
+            }
+            argn++;
+            if ((argn == argc) || (sscanf(argv[argn], "%d", &sysize) != 1))
+                pm_usage(usage);
+            hgtspec = TRUE;
+        } else {
+            pm_usage(usage);
         }
         argn++;
-            if ((argn == argc) || (sscanf(argv[argn], "%d", &sysize) != 1))
-        pm_usage(usage);
-        hgtspec = TRUE;
-    } else {
-        pm_usage(usage);
-    }
-    argn++;
     }
 
     /* If a file name is specified, open it.  Otherwise read from
-       standard input. */
+       standard input. 
+    */
 
     if (argn < argc) {
-    slfile = pm_openr(argv[argn]);
-    argn++;
+        slfile = pm_openr(argv[argn]);
+        argn++;
     } else {
-    slfile = stdin;
+        slfile = stdin;
     }
-
+    
     if (argn != argc) {           /* Extra bogus arguments ? */
-    pm_usage(usage);
+        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 != (char *) 0)) {
-    slidefind(slobber, dironly, ucasen);
-    }
-
+       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, FALSE);
+        slider(draw, flood);
+        ppm_writeppm(stdout, pixels, pixcols, pixrows, pixmaxval, FALSE);
     }
     pm_close(slfile);
     pm_close(stdout);
-    exit(0);
+    
+    return 0;
 }
diff --git a/converter/ppm/spctoppm.c b/converter/ppm/spctoppm.c
index 3eea7821..d2a48187 100644
--- a/converter/ppm/spctoppm.c
+++ b/converter/ppm/spctoppm.c
@@ -165,15 +165,8 @@ DoBitmap( ifp )
 	sscreen[i] = ( screen[i<<1] << 8 ) + ( 0xff & screen[(i<<1)+1] );
     }
 
-#if __STDC__
 static void
 DoChar( int n, char c )
-#else /*__STDC__*/
-static void
-DoChar( n, c )
-    int n;
-    char c;
-#endif /*__STDC__*/
     {
     int i;
 
diff --git a/converter/ppm/tgatoppm.c b/converter/ppm/tgatoppm.c
index 8a559848..f8538214 100644
--- a/converter/ppm/tgatoppm.c
+++ b/converter/ppm/tgatoppm.c
@@ -16,6 +16,8 @@
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
 #include <string.h>
+
+#include "pm_c_util.h"
 #include "ppm.h"
 #include "tga.h"
 #include "shhopt.h"
@@ -81,7 +83,7 @@ parseCommandLine(int argc, char ** argv,
                  "is the input file specification");
 
     if (alpha_spec &&
-        STREQ(cmdlineP->alpha_filename, "-"))
+        streq(cmdlineP->alpha_filename, "-"))
         cmdlineP->alpha_stdout = 1;
     else 
         cmdlineP->alpha_stdout = 0;
diff --git a/converter/ppm/winicontoppm.c b/converter/ppm/winicontoppm.c
index 2d9de567..ad859c94 100644
--- a/converter/ppm/winicontoppm.c
+++ b/converter/ppm/winicontoppm.c
@@ -21,6 +21,7 @@
 #include <string.h>
 #include <assert.h>
 
+#include "pm_c_util.h"
 #include "ppm.h"
 #include "shhopt.h"
 #include "nstring.h"
@@ -572,7 +573,7 @@ trimOutputName(const char inputName[])
      * oh, for =~ ... :)
      */
     char * outFile = strdup(inputName);
-    if (STREQ(outFile + (strlen (outFile) - 4), ".ppm")) {
+    if (streq(outFile + (strlen (outFile) - 4), ".ppm")) {
         *(outFile + (strlen (outFile) - 4)) = 0;
     }
     return outFile;
@@ -828,7 +829,7 @@ main(int argc, char *argv[]) {
         pm_message("-bestqual doesn't make sense with -allicons.  "
                    "Ignoring -bestqual.");
    
-    if (STREQ(cmdline.outputFilespec, "-"))
+    if (streq(cmdline.outputFilespec, "-"))
         outputFileBase = NULL;
     else
         outputFileBase = trimOutputName(cmdline.outputFilespec);
diff --git a/converter/ppm/ximtoppm.c b/converter/ppm/ximtoppm.c
index 28c9b674..15589c16 100644
--- a/converter/ppm/ximtoppm.c
+++ b/converter/ppm/ximtoppm.c
@@ -14,6 +14,8 @@
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
 #include <string.h>
+
+#include "pm_c_util.h"
 #include "ppm.h"
 #include "xim.h"
 #include "shhopt.h"
@@ -69,7 +71,7 @@ parseCommandLine(int argc, char ** argv,
                  "is the input file specification");
 
     if (cmdlineP->alpha_filename && 
-        STREQ(cmdlineP->alpha_filename, "-"))
+        streq(cmdlineP->alpha_filename, "-"))
         cmdlineP->alpha_stdout = TRUE;
     else 
         cmdlineP->alpha_stdout = FALSE;
diff --git a/converter/ppm/xpmtoppm.c b/converter/ppm/xpmtoppm.c
index bb6a7a0d..235c3867 100644
--- a/converter/ppm/xpmtoppm.c
+++ b/converter/ppm/xpmtoppm.c
@@ -33,6 +33,7 @@
 
 #include <string.h>
 
+#include "pm_c_util.h"
 #include "ppm.h"
 #include "shhopt.h"
 #include "nstring.h"
@@ -104,7 +105,7 @@ parse_command_line(int argc, char ** argv,
                  "is the input file specification");
 
     if (cmdline_p->alpha_filename && 
-        STREQ(cmdline_p->alpha_filename, "-"))
+        streq(cmdline_p->alpha_filename, "-"))
         cmdline_p->alpha_stdout = TRUE;
     else 
         cmdline_p->alpha_stdout = FALSE;
@@ -293,7 +294,7 @@ interpretXpm3ColorTableLine(char line[], int const seqNum,
                number if so)
             */
             for (key = 1; 
-                 key <= NKEYS && !STREQ(xpmColorKeys[key - 1], str2); 
+                 key <= NKEYS && !streq(xpmColorKeys[key - 1], str2); 
                  key++);
             isKey = (key <= NKEYS);
 
@@ -484,15 +485,15 @@ readXpm1Header(FILE * const stream, int * const widthP, int * const heightP,
                 t1 = str1;
             else
                 ++t1;
-            if (STREQ(t1, "format"))
+            if (streq(t1, "format"))
                 format = v;
-            else if (STREQ(t1, "width"))
+            else if (streq(t1, "width"))
                 *widthP = v;
-            else if (STREQ(t1, "height"))
+            else if (streq(t1, "height"))
                 *heightP = v;
-            else if (STREQ(t1, "ncolors"))
+            else if (streq(t1, "ncolors"))
                 *ncolorsP = v;
-            else if (STREQ(t1, "pixel"))
+            else if (streq(t1, "pixel"))
                 *chars_per_pixelP = v;
         } else if (!strncmp(line, "static char", 11)) {
             if ((t1 = strrchr(line, '_')) == NULL)
diff --git a/converter/ppm/xvminitoppm.c b/converter/ppm/xvminitoppm.c
index 935ab177..d76bea87 100644
--- a/converter/ppm/xvminitoppm.c
+++ b/converter/ppm/xvminitoppm.c
@@ -12,6 +12,7 @@
 
 #include <assert.h>
 #include <string.h>
+#include <errno.h>
 
 #include "pm_c_util.h"
 #include "nstring.h"
@@ -61,7 +62,8 @@ getLine(FILE * const ifP,
     rc = fgets(buf, size, ifP);
     if (rc == NULL) {
         if (ferror(ifP))
-            pm_perror("read error");
+            pm_error("read error.  fgets() failed, errno=%d (%s)",
+                     errno, strerror(errno));
         else
             pm_error("unexpected EOF");
     }
@@ -107,16 +109,16 @@ readXvHeader(FILE *         const ifP,
     
     getLine(ifP, buf, sizeof(buf));
 
-    if (!STRNEQ(buf, "P7 332", 6))
+    if (!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));
-        if (STRNEQ(buf, "#END_OF_COMMENTS", 16))
+        if (strneq(buf, "#END_OF_COMMENTS", 16))
             endOfComments = TRUE;
-        else if (STRNEQ(buf, "#BUILTIN", 8))
+        else if (strneq(buf, "#BUILTIN", 8))
             pm_error("This program does not know how to "
                      "convert builtin XV thumbnail pictures");
     }
diff --git a/converter/ppm/yuvsplittoppm.c b/converter/ppm/yuvsplittoppm.c
index 0f5e19a3..b3088812 100644
--- a/converter/ppm/yuvsplittoppm.c
+++ b/converter/ppm/yuvsplittoppm.c
@@ -20,6 +20,8 @@
 */
 
 #include <string.h>
+
+#include "pm_c_util.h"
 #include "ppm.h"
 #include "nstring.h"
 #include "shhopt.h"
diff --git a/doc/HISTORY b/doc/HISTORY
index 3071a207..c70595c9 100644
--- a/doc/HISTORY
+++ b/doc/HISTORY
@@ -4,19 +4,30 @@ Netpbm.
 CHANGE HISTORY 
 --------------
 
-15.12.25 BJH  Release 10.35.98
+15.12.25 BJH  Release 10.47.59
+
+              anytopnm: use --mime-type option instead of --mime on newer
+              'file' program (on which --mime has a new meaning so that
+              'anytopnm' cannot recognize some file types).
+
+              anytopnm: recognize "Netpbm PAM" non-mime output from 'file'
+              program as indicating PAM.
 
               pnmtops: fix bug: always things -level=N is -level=1.
               Introduced after Netpbm 10.26 (January 2005) but before 10.35
               (August 2006).  Manifests only with recent compilers.
 
+              pnmpaste: fix bug with PBM: incorrect output.  Introduced in
+              Netpbm 10.44 (September 2008).
+
               Build: fix superfluous error message when USER environment
               variable contains whitespace.  Broken since primordial Netpbm.
 
-15.09.26 BJH  Release 10.35.97
+15.08.15 BJH  Release 10.47.58
 
-              sbigtopgm: Fix detection of camera type.  Broken in
-              Netpbm 10.35.96 (June 2015).
+              fitstopnm: Add message saying you're probably making a mistake
+              if you have a 3-D image and don't specify -image.  The third
+              dimension is almost always time in practice.
 
               fitstopnm: Fix -min and -max.  Broken in Netpbm 10.39 (June
               2007).
@@ -35,7 +46,13 @@ CHANGE HISTORY
               ppmtoilbm: Fix failure with -hamforce and -nocompression.
               Broken in Netpbm 9.12 (March 2001).
 
-15.06.28 BJH  Release 10.35.96
+              Build: fix compile error in sbigtopnm introduced in 10.47.57
+              (June 2015).
+
+              Build: fix link errors in pbmtomacp, ppmtoyuvsplit introduced
+              in 10.47.57 (June 2015).
+
+15.06.28 BJH  Release 10.47.57
 
               palmtopnm: Fix distorted output with PackBits compressed input.
               Always broken (Ability to convert PackBits input was new in
@@ -68,9 +85,13 @@ CHANGE HISTORY
               pbmtoescp2: Fix bug: overrun on certain input.  Always broken
               (pbmtoescp2 was new in Netpbm 10.18 (September 2003)).
 
+              Build: improve text of pointer man pages.
+
               escp2topbm: Fix buffer overrun on certain input.  Always broken
               (escp2topbm was new in Netpbm 10.18 (September 2003)).
 
+15.05.24 BJH  Release 10.47.56
+
               libnetpbm: pm_stripeq: fix bug: wild pointer access when
               comparator is shorter than comparand.  Doesn't affect function,
               but could cause crash or privacy exposure.  Affects reading of a
@@ -80,9 +101,15 @@ CHANGE HISTORY
               pbmtog3: Fix buffer overrun.  Introduced in Netpbm 10.23
               (July 2004).
 
+              pbmtog3: Fix buffer overrun.  Introduced in Netpbm 10.23
+              (July 2004).
+
               cameratopam: Fix arithmetic underflow in JPEG processing;
               unknown effect.
 
+              pbmtoxbm: Fix bug: crash with zero width input.  Broken in
+              Netpbm 10.37 (December 2006).
+
               sbigtopgm: fix buffer overrun with invalid input image.  Always
               present (sbigtopgm was new in Netpbm 8.3 (March 2000)).
 
@@ -116,29 +143,43 @@ CHANGE HISTORY
               pbmtoatk: Fix crash with very long input file name argument.
               Always broken (pbmtoatk was new in 1991).
 
-              pnmgamma -srgbtobt709, -bt709tosrgb: fix bug; incorrect output
-              nearly always.  Always broken (These options were new in
-              Netpbm 10.32 (February 2006)).
+              Build: fix 'make package' where config.mk sets a subdirectory
+              other than 'man' for the manual.
+
+15.03.29 BJH  Release 10.47.55
 
               pamtosvg: fix use of unset variable; probably results in a
               crash.  Always present (pamtosvg was new in Netpbm 10.33 (March
               2006).
 
-              Install: make backward compatibility link ppmtotga -> pamtotga .
-
-              Install: mention in instructions that it doesn't build on
-              modern Macs.
+15.01.25 BJH  Release 10.47.54
 
-              Make clean: remove .deb file
+              pnmgamma -srgbtobt709, -bt709tosrgb: fix bug; incorrect output
+              nearly always.  Always broken (These options were new in
+              Netpbm 10.32 (February 2006)).
 
-14.12.25 BJH  Release 10.35.95
+              build: fix incompatible type compilation error in giftopnm.
+              Broken in Netpbm 10.38 (March 2007) (but obviously manifests
+              only in recent build environments).
 
               Fix bogus message from ppmXXX programs when the input is not
               (per the magic number) a Netpbm image.  Introduced after
               Netpbm 10.26 (January 2005) but before Netpbm 10.35 (August
               2006).
 
-14.09.26 BJH  Release 10.35.94
+              Install: make backward compatibility link ppmtotga -> pamtotga .
+
+14.11.23 BJH  Release 10.47.53
+
+              pambackground: fix bug: segfault or incorrect results in most
+              cases.  Thanks Ludolf Holzheid (ludolf.holzheid@gmx.de).
+              Introduced in Netpbm 10.37 (December 2006).
+
+14.11.01 BJH  Release 10.47.52
+
+              Fix 'make package': missing pkgconfig_template file.
+
+14.09.07 BJH  Release 10.47.51
 
               Fix incorrect option parsing when there are multiple common
               options (e.g. -plain -quiet).  Always broken.  (Possibility of
@@ -146,10 +187,6 @@ CHANGE HISTORY
               to ignore every other option (like -version) was new in Netpbm
               10.10 (October 2002).
 
-              libnetpbm whole-image functions: Fix undefined behavior due to
-              arithmetic overrun when size of image exceeds the capacity of
-              a signed integer.
-
               cameratopam: fix buffer overflow.  Always present.  (cameratopam
               was new in Netpbm 10.28 (June 2005)).
 
@@ -160,7 +197,7 @@ CHANGE HISTORY
               Build: Fix failure to compile lib/libsystem.c because of
               nonexistent signal classes on some systems.
 
-14.06.29 BJH  Release 10.35.93
+14.04.30 BJH  Release 10.47.50
 
               pamsharpness: put primary output on Standard Output instead of
               on Standard Error as a Netpbm message.
@@ -177,10 +214,7 @@ CHANGE HISTORY
               Always broken.  (-dumpexif was new in Netpbm 9.18 September
               2001).
 
-              Build: fix dependencies in .deb package so they work with Debian
-              6 at least.  Always broken (.deb capability was new in 10.35.92).
-
-14.03.29 BJH  Release 10.35.92
+14.03.29 BJH  Release 10.47.49
 
               ppmrelief: fix out-of-bound values in output.  Always broken.
               Thanks Prophet of the Way <afu@wta.att.ne.jp>.
@@ -196,9 +230,15 @@ CHANGE HISTORY
               Netpbm 9.21 (January 2001)).  Thanks Prophet of the Way
               <afu@wta.att.ne.jp>.
 
-              build/install: add tools for creating a Debian package.
+              make package: Include template for pkgconfig file.
+
+              Windows build: fix Ppmtompeg build failure in non-Cygwin build
+              due to missing sys/utsname.h.  (But other things still fail).
+
+              Windows build: fix missing .exe on copies of programs under
+              their old names.
 
-13.12.24 BJH  Release 10.35.91
+13.12.15 BJH  Release 10.47.48
 
               pbmtoepsi: fix handling of all-white image.  Always broken.
               Thanks Prophet of the Way <afu@wta.att.ne.jp>.
@@ -213,13 +253,24 @@ CHANGE HISTORY
               > 25:1. Thanks Prophet of the Way <afu@wta.att.ne.jp>.
               Always broken.
 
-              pamgauss: Fix typo in message.
+13.12.03 BJH  Release 10.47.47
 
-13.09.26 BJH  Release 10.35.90
+              Fix wild pointer dereference when memory allocation for a string
+              fails.  Broken since 10.36 (September 2006).
 
-              ppmtoxpm: fix bug: ignores inability to open the specified color
-              dictionary file (-rgb) and just doesn't use color names.
-              Introduced in Netpbm 10.15 (April 2003).
+13.09.26 BJH  Release 10.47.46
+
+              Fixes for Mingw build with MSYS shell.
+
+              libnetpbm, pnmpsnr, ppmcie on Mac OS X: fix bogus printing of
+              floating point numbers.
+
+13.06.27 BJH  Release 10.47.45
+
+              giftopnm: fix bug: erroneously claims GIF ends prematurely.
+              Broken in Netpbm 10.38 (March 2007).  This affects all GIFs, but
+              the problem does not manifest when Netpbm was built with Gcc
+              from 2007 and later.
 
               ppmforge. fix crash when -mesh is 1 or less.  Always broken
               (program was added in Pbmplus (October 1991).  Thanks Prophet of
@@ -229,7 +280,26 @@ CHANGE HISTORY
               was added in Pbmplus (October 1991).  Thanks Prophet of the Way
               <afu@wta.att.ne.jp>.
 
-13.05.03 BJH  Release 10.35.89
+              pnmtopng: fix bug: output bigger than it needs to be when the
+              input is a color format image that contains only gray.  Broken
+              since at least Netpbm 10.26 (May 2001), but after 10.18
+              (September 2003).  Thanks Ludolf Holzheid
+              (lholzheid@bihl-wiedemann.de).
+
+              pnmremap (ergo pnmquant): fix bug: incorrect output with
+              -norandom and floyd-steinberg dithering.  Always broken.
+              (-norandom was introduced in Netpbm 10.39 (June 2007)).
+
+              ppmtoxpm: fix bug: ignores inability to open the specified color
+              dictionary file (-rgb) and just doesn't use color names.
+              Introduced in Netpbm 10.15 (April 2003).
+
+              libnetpbm: fix bug: ppm_freecolornames() has wild pointer
+              dereference when the color name table was generated empty
+              because the color dictionary file was not openable.  ppmtoxpm
+              suffers from this.  Broken in 10.15 (April 2003).
+
+13.05.03 BJH  Release 10.47.44
 
               ppmtowinicon: fix bug: sometimes creates image truncated in the
               middle of the AND mask.  Always broken (program was new in
@@ -239,12 +309,15 @@ CHANGE HISTORY
               crash.  Always broken (program was new in Neptbm 10.30
               (October 2005)).
 
-13.02.20 BJH  Release 10.35.88
+13.02.20 BJH  Release 10.47.43
 
-              ppmpat: fix wrong output with -poles.
+              pngtopam -alphapam with grayscale input: fix bug: generates
+              invalid output: tuple type GRAYSCALE_ALPHA, but depth 1.  Depth
+              should be 2.  Always broken (pngtopam was created in 
+              Netpbm 10.44 (September 2008)).
               
-              Windows build: fix compile error on urt/rle_open_f.c due to
-              Unix process management stuff.
+              ppmpat: Fix bug: wrong output with -poles.  Broken in 10.47
+              (June 2009).
 
               leaftoppm: fix incorrect determination of whether input is
               Interleaf on 64 bit systems.  Always broken.
@@ -252,42 +325,56 @@ CHANGE HISTORY
               cmuwmtopbm: fix incorrect determination of whether input is
               a CMU window manager bitmap on 64 bit systems.  Always broken.
 
-12.12.05 BJH  Release 10.35.87
+12.12.04 BJH  Release 10.47.42
 
               pamtotiff: fix bug: XRESOLUTION, YRESOLUTION, and RESOLUTIONUNIT
-              not allowed in -tags.  Broken at least since 10.35.00.
-
-              pnmmontage: fix random stuff placed in unoccupied space in the
-              output image.  Always broken (program was new in 9.10 (January
-              2001).
+              not allowed in -tags.  Broken at least since 10.35.
 
               pnmpsnr: fix crash when invoked (invalidly) with only one
               argument.  Always broken.
 
-              pampick: fix wild pointer in command line parsing.  Bug has
-              always been there.
+              pgmhist, pampick, pamtompfont: fix wild pointer in command line
+              parsing.  Bug has always been there in pampick, pamtompfont,
+              since 10.44 (September 2008) in pgmhist.
 
-              pamgauss: Fix bug: erroneously says -maxval is too big.
-              Always broken (Pamgauss was added in 10.23 (July 2004).
+              Xbmtopbm: fix incorrect output, memory leak.  Thanks Prophet of
+              the Way <afu@wta.att.ne.jp>.
 
-12.07.23 BJH  Release 10.35.86
+12.10.03 BJH  Release 10.47.41
 
-              ppmtobmp: fix failure with "internal error" message on all
-              uses.  Broken in 10.35.78.
+              pamgauss: Fix bug: erroneously says -maxval is too big.
+              Always broken (Pamgauss was added in 10.23 (July 2004)).
+
+12.08.20 BJH  Release 10.47.40
 
               Build: rename getline() in xpmtoppm.c to avoid collision
               with some libc.  Always broken.
 
-12.06.21 BJH  Release 10.35.85
+12.07.04 BJH  Release 10.47.39
+
+              ppmtobmp: fix failure with "internal error" message on all
+              uses.  Broken in 10.47.23.
+
+12.06.21 BJH  Release 10.47.38
 
               configure: work around Perl bug that causes 'configure' to
               falsely conclude Svgalib is installed.
 
               Build: move -lm to end of -l's.  Broken at least since 10.35.
 
-12.02.14 BJH  Release 10.35.84
+12.02.15 BJH  Release 10.47.37
 
-              pamscale: fix all black output with resampling.  Always broken.
+              Fix compile error from 10.47.36.
+
+12.02.15 BJH  Release 10.47.36
+
+              pm_make_tmpfile_fd: fix improper handling when unable to get
+              memory to construct file name.
+
+              Windows/Mingw: fix bug: temporary file creation fails
+              consistently.  Broken in 10.34.
+
+12.01.14 BJH  Release 10.47.35
 
               pgmtexture: fix integer overflow in difference variance.
               Always broken.
@@ -295,14 +382,28 @@ CHANGE HISTORY
               pgmtexture: fix array bounds violations in various calculations.
               Always broken.
 
-              Build: always put Netpbm header files before external library
-              header files in search path.
+11.12.12 BJH  Release 10.47.34
+
+              pamscale: fix all black output with resampling.  Always broken.
+
+11.11.22 BJH  Release 10.47.33
+
+              pngtopam, pngtopnm: fix crash with invalid tIME chunk.  Always
+              broken.
+
+11.11.08 BJH  Release 10.47.32
 
-11.11.25 BJH  Release 10.35.83
+              pamarith: fix wrong result with -multiply.  Broken in 10.41.
 
-              pngtopnm: fix crash with invalid tIME chunk.  Always broken.
+11.09.25 BJH  Release 10.47.31
 
-11.09.25 BJH  Release 10.35.82
+              bmptopnm: Fail properly with BMP that indicates an illegal bits
+              per pixel.
+
+              pnmtops: fix message: says "from top edge" where it means
+              "from bottom edge."
+
+11.08.22 BJH  Release 10.47.30
 
               pnmtopng: fix bug: with -alpha specifying a mask which contains
               no fully transparent area, output PNG is fully opaque.
@@ -310,29 +411,44 @@ CHANGE HISTORY
 
               pnmquant: work with older Perl that doesn't have 3-argument open.
 
-              pnmtops: fix message: says "from top edge" where it means
-              "from bottom edge."
-
-11.07.10 BJH  Release 10.35.81
+11.07.10 BJH  Release 10.47.29
 
               pgmtexture: fix wrong sum variance result.  Wrong since the
               beginning.  Thanks Francois P. S. Luus <fpsluus@gmail.com>.
 
-11.02.18 BJH  Release 10.35.80
+11.06.25 BJH  Release 10.47.28
+
+              pnmpsnr: fix bug: says PGM images differ when they don't
+              and vice versa.
+
+              libnetpbm: pm_system(): fix bug - program always takes
+              Standard Input from invoker's Standard Input when you don't
+              supply a Standard Output accepter.
+
+11.03.27 BJH  Release 10.47.27
+
+              g3topbm: correct error message: codes go up to 13 bits.
+
+11.01.31 BJH  Release 10.47.26
 
               asciitopgm: fix bug: memory corruption on too-long lines.
 
               asciitopgm: fix bug: improper handling of blank lines.
               
-11.01.15 BJH  Release 10.35.79
+              Build: compiles with libpng 1.4.  (This was done in 10.47.04
+              too, but apparently didn't work).
 
-              configure: don't default to /usr/X11R6/lib/libX11.so just because
-              /usr/X11R6 exists.  /usr/X11R6/lib must exist.
+11.01.15 BJH  Release 10.47.25
 
-              pngtopnm: fix bug: -verbose reports history chunk present when
+              pngtopam: fix bug: -verbose reports history chunk present when
               it's really a palette.
 
-10.12.10 BJH  Release 10.35.78
+10.12.30 BJH  Release 10.47.24
+
+              configure: don't default to /usr/X11R6/lib/libX11.so just because
+              /usr/X11R6 exists.  /usr/X11R6/lib must exist.
+
+10.12.10 BJH  Release 10.47.23
 
               bmptopnm: Don't crash on invalid zero value of image height in
               the BMP header of a compressed file.  Always broken.
@@ -340,50 +456,111 @@ CHANGE HISTORY
               bmptopnm: don't crash on large invalid value of 'colorsused' in
               the BMP header.
 
+              pngtopam: include tuple type in output.  Thanks
+              Thomas Henlich <thenlich@users.sourceforge.net>.
+
+10.11.01 BJH  Release 10.47.22
+
+              pnmtops: Fix incorrect output with -flate.  Thanks Prophet of
+              the Way <afu@wta.att.ne.jp>.
+
               ilbmtoppm: Don't crash on image that has a transparent color
               index, but no color map.
 
-10.09.18 BJH  Release 10.35.77
+10.10.16 BJH  Release 10.47.21
+
+              configure: fix crash when libpng test compile fails.
+
+10.09.30 BJH  Release 10.47.20
+
+              pm_floatFromBigendFloat, pm_doubleFromBigendFloat, fitstopnm:
+              fix corrupted output.  Broken in 10.46.
 
               ppmtompeg: fix crash with free of unallocated memory.
 
+10.08.28 BJH  Release 10.47.19
+
               Build: don't expect snprintf() to exist.
 
               Build: don't use <strings.h> or bzero().
 
+              Build: fix PNGVER.  Thanks Matthew Fischer
+              <futhark@users.sourceforge.net>.
+
+10.07.27 BJH  Release 10.47.18
+
               Pnmtopng:  -libversion doesn't report level of linked libz.
               It was a modularity violation and caused build failures on
               Mac OS X, because Pnmtopng per se doesn't know about libz --
               it's used by libpng.
 
+10.07.06 BJH  Release 10.47.17
+
               Build: don't fail due to SIGRTMIN, SIGRTMAX being undefined.
 
-10.05.31 BJH  Release 10.35.75
+10.06.17 BJH  Release 10.47.16
+
+              pbmtext/libpbmfont: Fix wild pointer; probably asymptomatic.
+              Introduced in 10.39.
+
+              pbmtext/libpbmfont: Fix some error messages for bad fonts.
+
+              pbmtext/libpbmfont: fix crash with a BDF font with negative
+              left or bottom border.  Introduced in 10.39.
+
+10.06.03 BJH  Release 10.47.15
+
+              libnetpbm line drawing: fix bogus assertion, introduced in 10.47.
+
+              libnetpbm text drawing: fix incorrect output in all cases.
+              Introduced in 10.47.
+
+              build: fix incorrect determination of when vasprintf() exists
+              in libc.
+
+10.05.20 BJH  Release 10.47.14
+
+              pamarith: fix memory leak.
+
+10.05.04 BJH  Release 10.47.13
+
+              pnmtops: fix bug: 12 bits per sample output when 8 would do.
+              Introduced in 10.40.
+
+10.04.20 BJH  Release 10.47.12
 
               palmtopnm: fix for pixel size 16.  Thanks Paul Bolle
               <pebolle@tiscali.nl>.
 
               pamscale: fix -reduce.  Introduced in 10.27.
 
-              configure: default to 'none' for Svgalib if it doesn't appear to
-              be installed (per 'ldconfig').  Ported from 10.38, released
-              March 2007.
+              pbmtext: fix crash when BDF font file contains spurious
+              blank line.  Ignore such blank lines.
 
-10.03.24 BJH  Release 10.35.74
-
-              pbmtext: don't crash when font file contains a blank line.
-              Fail cleanly.
+10.03.17 BJH  Release 10.47.11
 
               ppmtoilbm: fix arithmetic overflow with image dimension
               represented as 16 bit integer.
 
               pbmpage: fix garbage output.
 
+              Build: don't fail due to SIGPWR being undefined.
+
+10.02.23 BJH  Release 10.47.10
+
               pnmhistmap: Fix crash with -width.  Always broken.
 
-              Build: don't fail due to SIGPWR being undefined.
+              pm_system(): Close extraneous file descriptors that, among
+              other things, prevent child from seeing EOF.
 
-10.01.23 BJH  Release 10.35.73
+10.01.25 BJH  Release 10.47.09
+
+              pamtosvg: fix bug: occasional crash with out of range error.
+
+10.01.11 BJH  Release 10.47.08
+
+              libppmd/ppmpat: fix wild pointer in ppmd_fill_drawprocp();
+              broken in 10.47.00.
 
               Restore ability of Pnmconvol convolution matrix to be a
               pseudo-plain-PNM with samples that exceed the maxval.  Lost in
@@ -396,7 +573,7 @@ CHANGE HISTORY
 
               palmtopnm: fix incorrect output with version 3 direct color.
 
-09.12.29 BJH  Release 10.35.72
+09.12.29 BJH  Release 10.47.07
 
               libnetpbm: When reading plain format PNM with PAM routines,
               validate pixel against maxval (necessary for integer non-overrun
@@ -404,13 +581,21 @@ CHANGE HISTORY
 
               xpmtoppm: fix wild pointer with color index > 127.
 
-              pm_system*: fix various bugs.
+              pnmsmooth: fix incorrect call to pm_system_lp() that makes
+              it never work (but it wouldn't anyway because pm_system_lp()
+              was broken -- see below).
 
-09.12.10 BJH  Release 10.35.71
+              pm_system*: fix various bugs that make it never work.
 
-              pamtosvg: fix compile failure from 10.35.70
+09.12.10 BJH  Release 10.47.06
 
-09.12.10 BJH  Release 10.35.70
+              pamtosvg: fix compile failure from 10.47.05.
+
+09.12.10 BJH  Release 10.47.05
+
+              ppmrainbow: Make new directory for temporary files.
+              Prevents interference by other user who shares the main
+              temporary file directory.
 
               pamtouil: fix crash when -name option doesn't contain an
               underscore.
@@ -419,14 +604,7 @@ CHANGE HISTORY
 
               pnmtofiasco, fiascotopnm: fix bug on bigendian machine.
 
-              Fix bug in ppm_readcolordict(), ppm_readcolornamefile() where
-              the file is not readable.
-
-09.10.18 BJH  Release 10.35.69
-
-              Build: fix build failure with no-network build of Ppmtompeg.
-
-09.09.18 BJH  Release 10.35.68
+09.09.18 BJH  Release 10.47.04
 
               pambayer: fix unconditional crash/failure when you aren't using
               Standard Input.
@@ -434,33 +612,46 @@ CHANGE HISTORY
               Build: use AR from config.mk instead of "ar" to build static
               libraries: libnetpbm, librle, libjasper.
 
+              Build: compiles with libpng 1.4 beta.
+
               Build: don't use "uint".
 
               Package: fix installation of pnmplain on Windows (.exe suffix).
 
-              Configure: fix wrong variable name for HP-UX.
-
-09.09.03 BJH  Release 10.35.67
+09.09.03 BJH  Release 10.47.03
 
               Build: fix failure to recognize static library in omitting
               -R from link.
 
-              Build: Make compile of pnmtopng and pngtopnm fail with helpful
-              error message when PNG library is incompatibly new (version 1.4
-              or later).
+09.08.17 BJH  Release 10.47.02
+
+              tifftopnm: wait for Pamflip processes to terminate before
+              exiting.
+
+              Build: Compiles with libpng 1.4 beta.
 
               Build: work with JPEG library Version 7.
 
-09.07.23 BJH  Release 10.35.66
+09.07.23 BJH  Release 10.47.01
 
               Build: fix failure of a merge build on a system that doesn't
               have the PNG library.
 
-09.06.26 BJH  Release 10.35.65
+09.06.27 BJH  Release 10.47.00
 
-              configure: offer Gcc as an optional compiler for HP-UX.
+              Add pamsistoaglyph.  Thanks Scott Pakin.
 
-09.05.16 BJH  Release 10.35.64
+              pnmpsnr: Use more precise math to conclude that images are
+              identical.
+
+              Add /usr/share/netpbm/rgb.txt to search path for color
+              dictionary.
+
+              Remove nstring.h from exported interfaces.
+
+              giftopnm: improve efficiency.
+
+              libppmd: Add ppmd_point-based functions (ppmd_linep(), etc).
 
               pnmremap: fix: -firstisdefault uses an arbitrary color from the
               map as default.
@@ -468,16 +659,23 @@ CHANGE HISTORY
               pnmremap: fix -missingcolor:  where map file (ergo output) is not
               depth 3, uses an arbitrary color.
 
+              pnmremap: fix loss of dithering when the map (and thus the
+              output) has lower maxval than the input.  (Old code scales down
+              to the new maxval before dithering).
+
               giftopnm: fix for unlikely case of a block smaller than a code.
 
               ppmpat: fix crash in -squig.
 
               ppmpat: fix crash when width or height is zero.
 
-              libppmd (ppmdraw, ppmpat): Fix bugs with coordinates too large.
-              Thanks Prophet of the Way <afu@wta.att.ne.jp>.
+              ppmpat: fix wrong output with -poles.
+
+              libppmd (ppmpat, ppmdraw): Fix various out of bounds number
+              problems.
 
-09.04.28 BJH  Release 10.35.63
+              ilbmtoppm: fix some bug in interpreting ILBM input.
+              (due to wrong pm_uintFromBigend16()).  From 10.46.
 
               ximtoppm: fix crash in command line processing.
 
@@ -487,37 +685,61 @@ CHANGE HISTORY
 
               ppmquantall: don't use 'set' to set Bourne shell variable.
 
-              pbmtomacp: fix buffer overflow.
+              pnmtile: fix reference to arbitrary storage in option
+              processing.  Introduced in 10.42.
 
-              pamtosvg: some fix.
+              pamstereogram: fix tuple type in output file (and crash
+              with -verbose) with -patfile .
 
-              ppmpat: fix usage statement.
+              pamstereogram: fix uninitialized bytes per sample value.
+
+              pbmtomacp: fix buffer overflow.
 
               libnetpbm (ppmd): fix: don't ignore failure to read font file.
 
-              build: rename getline() in xvminitoppm.c to avoid collision
-              with libc.
+              Fix: remove inclusion of pm_c_util.h from sshopt.h, which is
+              an exported interface header file, whereas pm_c_util.h is not.
 
-09.04.12 BJH  Release 10.35.62
+              Lots of changes to integer code, bit access, word access, etc.
+              None functional.  Some speedup.
 
-              pamstereogram: fix tuple type in output file (and crash
-              with -verbose) with -patfile .
+              make distclean: remove lib/compile.h.
+              
+              configure: use TEMP environment variable for TMPDIR if TMPDIR
+              is not set.  Don't try different temp file names forever; the
+              problem may not be the filename.
 
-              pamstereogram: fix uninitialized bytes per sample value.
+              configure: offer Gcc as an optional compiler for HP-UX.
 
-09.03.22 BJH  Release 10.35.61
+09.03.29 BJH  Release 10.46.00
 
-              g3topbm: fix array bound violation.  Introduced in 10.32.
+              xwdtopnm: Add -cmapdump .
 
-09.02.14 BJH  Release 10.35.60
+              xwdtopnm: Change maxval calculation for truecolor to fit
+              X11 spec.  Now it's determined by bits_per_rgb.
 
-              xwdtopnm: Fix for at least some direct color 24/32 images.
+              xwdtopnm: Add warning about 2 byte sample output.
+         
+              pnmcrop: fast path for PBM.
 
-              pnm_alloctupletable, pnm_tuplehashtotable,
-              pnm_computetuplefreqtable3: fix crash when out of memory,
-              memory leak with uncomputably large numbers.
+              pnmcrop: Use border file instead of original image to determine
+              the background color (i.e. the color of added margins).
+
+              pnmscale: finally make it just an alias of pamscale, which
+              obsoleted it in 10.20.
+              
+              pnmcut: finally make it just an alias of pamcut, which
+              obsoleted it in 9.20.
+
+              tifftopnm: fix orientation problem on big-endian machines.
+              Introduced in 10.42.
+
+              pnmcrop: various fixes.
+
+              g3topbm: fix array bound violation.  Introduced in 10.32.
 
-09.01.29 BJH  Release 10.35.59
+              pnmcat: fix array bound violation with PBM top/bottom
+              concatenation.  Introduced in 10.44.
 
               ilbmtoppm: Fix array bound violation with compressed ILBM.
               Introduced in 10.18.
@@ -525,158 +747,203 @@ CHANGE HISTORY
               fitstopnm: fix garbage output when input is little endian
               floating point FITS.
 
-09.01.15 BJH  Release 10.35.58
+              picttoppm: Fix crash with -fullres and all-text PICT image.
+              Also issue warning that program omits text with -fullres.
 
-              picttoppm: fix crash when image is all text with -fullres.
+              picttoppm: Improve error/informational messages.
+
+              picttoppm: Don't fail if 'fontdir' file doesn't exist.  Bug
+              from 10.44.
+
+              ppmtopict: Use two-byte length field when image width > 200
+              instead of > 250.  Former is what Picttoppm has been assuming
+              since March 2004, when it was found to work better than 250,
+              which is the number documented as right.
 
               ilbmtoppm: fix bug: appends color map PPM to output if input
               has color map; fails if input doesn't have color map.  Broken
               in 10.18.
 
               leaftoppm: fix bug: uses red channel as all three channels;
-              (produces grayscale output).
+              (produces grayscale output).  Broken between 10.19 and 10.26,
+              inclusive.
 
               pbmtomrf, mrftopbm: fix crashes, incorrect output in all
               cases.  Broken forever.
 
-08.12.07 BJH  Release 10.35.57
+              pnm_alloctupletable, pnm_tuplehashtotable,
+              pnm_computetuplefreqtable3: fix crash when out of memory,
+              memory leak with uncomputably large numbers.
 
-              Build: Move CFLAGS later in libjasper build so that a -I in
-              CFLAGS doesn't interfere with finding the local libjasper
-              header files.
+              build: rename getline() in xvminitoppm.c to avoid collision
+              with libc.
 
-              Build: Move LDFLAGS later so that a -L in LDFLAGS doesn't
-              interfere with finding the built libnetpbm.  (The common
-              link rule is already this way in 10.35.56; all the other
-              link rules are now the same).
+              Source tree: split out some of editor/ directory into new
+              editor/specialty directory.
 
-              Build: Add source type declarations to make -ansi compile work.
+08.12.28 BJH  Release 10.45.00
 
-              Build: Add -fPIC to all Gcc compiles of library modules, so it
-              works on AMD64, IA64.
+              pgmnoise: Add -randomseed.
 
-08.11.09 BJH  Release 10.35.56
+              ppmtobmp: Add -mapfile option.
 
-              Add -fPIC to all Gcc compiles of library modules, so it
-              works on AMD64, IA64.
+              pnm_createBlackTuple(): Fix array bounds violation with
+              PBM, PGM.
 
-08.10.31 BJH  Release 10.35.55
+              pnmtoddif: Fix crash with any PGM input.
 
-              pamcomp: Fix mysterious doubling of code introduced in 10.35.50
-              which causes build to fail.
+              pgmnoise: Fix bug: never generates full white pixel.
 
-08.10.27 BJH  Release 10.35.54
+              pamditherbw: Fix -value other than .5 with -fs, -atkinson.
 
-              pnm_createBlackTuple(): Fix array bounds violation with
-              PBM, PGM.
+              Configure: fix crash when Libxml2 is installed, but not new
+              enough version.
 
-              ppmforge, pgmnoise, pgmcrater: better randomization;
-              won't produce the same image if you run it twice within
-              the same second.
+              Build: Use GNU compile options for Darwin.
 
-              pnmtoddif: Fix crash with any PGM input.
+              Build: Move CFLAGS later in libjasper and librle build so that a
+              -I in CFLAGS doesn't interfere with finding the local libjasper
+              and Netpbm header files.
 
-              pgmnoise: Fix bug: never generates full white pixel.
+              Build: Move LDFLAGS later so that a -L in LDFLAGS doesn't
+              interfere with finding the built libnetpbm.  (The common
+              link rule is already this way in 10.44.00; all the other
+              link rules are now the same).
 
-08.10.12 BJH  Release 10.35.53
+              Build: Rename Makefile.common, Makefile.config, to common.mk,
+              etc.
 
-              pamditherbw: Fix -value other than .5 with -fs .
+              Build: Add source type declarations to make -ansi compile work.
 
-08.09.27 BJH  Release 10.35.52
+08.09.27 BJH  Release 10.44.00
 
-              libppmd/ppmdraw: fix crash when line is completely out of
-              frame.
+              Add pngtopam as a replacement for pngtopnm.  Has ability
+              to generate image with alpha channel.  Thanks 
+              David Jones <drj@ravenbrook.com>.
 
-              Build: add dependencies so parallel make of top level
-              "all" works.
+              pnmcolormap (pnmquant): round instead of truncating when
+              computing means.
 
-08.09.12 BJH  Release 10.35.51
+              pamcut, pnmcat, pnmpaste, pnmpad, g3topbm: Add fast PBM
+              path.  Thanks Prophet of the Way <afu@wta.att.ne.jp>.
 
-              pamgradient: fix bug: always produces color, not grayscale.
+              pnmpaste: fail if user specified stdin for both images.
 
-08.08.29 BJH  Release 10.35.50
+              libppmd/ppmdraw: fix crash when line is completely out of
+              frame.
+
+              pamgradient: fix bug: always produces color, not grayscale.
 
               pnm_backgroundxel(), pnm_backgroundxelrow() (affects
               pnmrotate, pnmshear, pnmcrop, pnmcat: correctly average
               corner colors to determine background (fill) color.
 
+              pamcut: don't crash when cutting a region entirely to
+              the left or right of the input image, with -pad.  Thanks
+              Prophet of the Way <afu@wta.att.ne.jp>.
+
+              pamcut: don't crash when left > right or top > bottom with
+              -pad.  Thanks Prophet of the Way <afu@wta.att.ne.jp>.
+
               pgmhist: arbitrary output when total pixels doesn't fit in an
               integer.
 
               pamcomp: fix bug: arbitrary output when combined number of rows
               doesn't fit in an integer.
 
-              pamcut: don't crash when left > right or top > bottom with
-              -pad.  Thanks Prophet of the Way <afu@wta.att.ne.jp>.
-
-08.08.13 BJH  Release 10.35.49
+              pamtosvg: remove "needed exchange" debug trace.
 
-              pamcut: don't crash when cutting a region entirely to the
-              left or right of the input image, with -pad.
+              Add pbmminkowski (source code has been in package since 10.12
+              but not documented or built by default).
 
-08.07.27 BJH  Release 10.35.48
+              pnmmontage: don't corrupt file names when they contain
+              colons.
 
-              pamperspective: change calculation of window height to avoid
-              buffer overrun.
+              pamflip: fix bug: garbage output for PBMs, since 10.42.
 
               pngtopnm: fix gamma correction.
 
-08.07.13 BJH  Release 10.35.47
-
               pngtopnm: fix bug: when background is color, output
               should be PPM.
 
-08.06.22 BJH  Release 10.35.46
+              pamperspective: fix bug: array bounds violation crash.
 
-              pbmtext: fix buffer overrun with tab characters.
+              Build: add dependencies so parallel make of top level
+              "all" works.
 
-              pamtotga: fix free of unallocated memory with no -name option.
+08.06.27 BJH  Release 10.43.00
 
-              pamtouil: fix double free of memory.
+              Add pamtompfont: Mplayer bitmap font.
 
-              pnmtopclxl: fix crash with Standard Input input.
+              pamthreshold: copy transparency information from input to
+              output.
+
+              pnmnorm: adjust bvalue, wvalue when -bpercent and/or -wpercent
+              would cause an overlap, instead of failing.
 
-08.06.08 BJH  Release 10.35.45
+              pamscale: fix PBM input + -nomix.
 
               anytopnm: fix parsing of type description.
 
+              pamtilt: fix crash with excessive angle.
+
+              pbmtext: remove meaningless "no output" from error messages.
+
+              pbm_loadbdffont(): handle zero size glyph.  Affects pbmtext.
+
+              pbmtext: fail properly if input contains no text.
+
+              pbmtext: fix buffer overrun with tab characters.
+
+              pnmnorm: fix -colorvalue, -saturation, -keephues options.
+
+              pnmnorm: fix resolution of conflicting -wpercent and -wvalue.
+
               pamtohtmltbl: fix output error: contains holes due to
               uninitialized memory.
 
               xvminitoppm: fix.  Totally broken since 10.34.
 
-              pbmtogo: fix buffer overrun on too wide image.
+              pnmtopclxl: fix crash with Standard Input input.
 
-              tgatoppm: fix standard input invocation.
+              pbmtogo: fix buffer overrun with too wide image.
 
-08.05.24 BJH  Release 10.35.44
+              tgatoppm: fix standard input invocation.
 
-              pamscale: fix PBM input + -nomix.
+              pamtotga: fix free of unallocated memory with no -name option.
 
-              pamtilt: fix crash with excessive angle.
+              pamtouil: fix double free of memory.
 
-              ppmd_filledrectangle(): fix crash with rectangle that is
-              entirely left or right of the image.
+              ppmd_filledrectangle: fail properly on negative image
+              height, width.
 
               Add back ppmd_fill_init() for backward compatibility;
               removed in 10.29.
 
-08.05.10 BJH  Release 10.35.43
+              Build: make it work with Gcc 4.3 and -sse.
 
-              pbmtext: fail properly if input contains no text.
+08.03.26 BJH  Release 10.42.00
 
-08.04.11 BJH  Release 10.35.42
+              anytopnm: Recognize xwd image by 'file' type.
 
-              pnmnorm: fix resolution of conflicting -wpercent and -wvalue.
+              pamflip: Rewrite low-memory algorithm; use temp files and
+              conserve virtual as well as real memory.
 
-08.03.28 BJH  Release 10.35.41
+              tifftopnm: orient raster properly in row-by-row operation
+              when TIFF uses nonstandard raster orientation.  (ORIENTATION
+              tag).
 
-              pnmnorm: fix -colorvalue, -saturation, -keephues options.
+              pbmtext: fail cleanly if -width, -space, or -lspace is
+              too large for computation.  Thanks Prophet of the Way
+              <afu@wta.att.ne.jp>.
+
+              pnmtile: deal with zero width/height.
+
+              pbmtext: fix negative -space.
 
-              Build: make it work with Gcc 4.3 and -sse by omitting
-              MMX/SSE optimizations.
+              pbmtext: fail cleanly if input line is > 5000 characters.
 
-08.03.08 BJH  Release 10.35.40
+              pamundice: Fix bogus error about missing "-instem" option.
 
               pgmdeshadow: fix crash in option processing.
 
@@ -684,71 +951,109 @@ CHANGE HISTORY
 
               pgmbentley: fix output; black areas not black.
 
+              pamtosvg: fix various bugs: crash, poor tracing, memory leaks.
+
               pamtosvg: fix log file name with Standard Input.
               
-08.02.22 BJH  Release 10.35.39
+              ppmtoarbtxt: fix array bounds violation with really large
+              replacement sequence.
 
-              pamtosvg: fix crash with -centerline.
+              ppmtoarbtxt: fix missing right parenthesis with impostor
+              replacement sequence.
 
-08.02.01 BJH  Release 10.35.38
+              pnmnlfilt: fix internal error "impossible alpha value".
 
-              ppmtoarbtxt: fix array overflow.
+              Add pm_tmpfile_fd() and pm_make_tmpfile_fd().
 
-              pbmtext: fail cleanly if input line is > 5000 characters.
+              Various things to make it build on Windows without POSIX
+              emulation.
 
-              pbmtext: fix negative -space .
+              Build: Include LDFLAGS environment variable in link of
+              build tools.
+              
+07.12.27 BJH  Release 10.41.00
 
-08.01.15 BJH  Release 10.35.37
+              pamenlarge: much faster for PBM.  Thanks Prophet of the
+              Way <afu@wta.att.ne.jp>.
 
-              Fix fast endianness stuff for 64 bit LE; bypass a compiler
-              bug that causes compile failure.
+              pamenlarge: remove arithmetic overflow.
 
-              Build: remove use of "uint" type in pamtosvg.
+              pnmmontage: new search algorithm to choose square
+              output quickly.  Thanks "Nic Roets" <nroets@gmail.com>.
 
-07.12.27 BJH  Release 10.35.36
+              bmptopnm: fix crash with 16 bit images.
 
-              pamperspective: Eliminate build-time symbol conflict
-              with 'nearest' on AIX.
+              palmcolor8.map: Make it a 256 color map sorted by index,
+              per Palm's spec.  Thanks Paul Bolle <pebolle@tiscali.nl>.
+
+              xwdtopnm: deal with too-large bits per pixel value in XWD 
+              header.
+
+              pamditherbw, pamsharpness, pamsharpmask, pamtopfm:
+              fix crash due to write to arbitrary memory.
+
+              pamtotiff: fix incorrect pixels with PNM maxval != TIFF
+              maxval.
+
+              pnmcrop: fix -verbose message about background color with
+              -white.
+
+              pbmmake: handle ridiculously large height, width arguments.
+
+              pnmcat: fix arithmetic overflow.
+
+              libnetpbm: Add arithmetic overflow protection to PBM
+              routines, like PGM/PPM/PNM have had for a long time.
+
+              libnetpbm: make all row free operations go through
+              pm_freerow(); change row buffer type from char * to void *
+              for pm_allocrow(), pm_freerow().
 
               set _XOPEN_SOURCE=600 so jpeg2ktopam and pamtojpeg2k
               compile on AIX (_XOPEN_SOURCE=500 keeps int_fast32_t from
               being defined).
 
-07.12.11 BJH  Release 10.35.35
+              pamperspective: Eliminate build-time symbol conflict
+              with 'nearest' on AIX.
 
-              pamtotiff: fix incorrect pixels with PNM maxval != TIFF
-              maxval.
+              Fix bug: ppmdraw.h, ppmdfont.h not installed.
 
-07.11.26 BJH  Release 10.35.34
+              Add -fPIC to all Gcc compiles of library modules, so it works
+              on AMD64, IA64.
 
-              xwdtopnm: deal with too-large bits per pixel value in XWD 
-              header.
+              Add Windows version of gethostname to ppmtompeg; normal build
+              does not use it.
 
-              pamditherbw, pamsharpness, pamsharpmask, pamtopfm:
-              fix crash due to write to arbitrary memory.
+              package, install: install interface header files as
+              <netpbm/xxx.h> instead of just <xxx.h>.
 
-07.11.13 BJH  Release 10.35.33
+07.09.26 BJH  Release 10.40.00
 
-              pnmcrop: fix -verbose message about background color with
-              -white.
+              pamfunc: Add -andmask, -ormask, -xormask, -not,
+              -shiftleft, -shiftright.
 
-07.10.12 BJH  Release 10.35.32
+              pbmtonokia: fail if input is > 255 x 255.  Thanks Paul Bolle
+              <pebolle@tiscali.nl>.
 
-              bmptopnm: fix crash with 16 bit images.
+              pnmmargin: add -plain option.    Thanks
+              Prophet of the Way <afu@wta.att.ne.jp>.
 
-07.09.03 BJH  Release 10.35.31
+              pnmgamma: improve error messages.
 
               pamstack: accept Standard Input properly.
 
-              pnmtopalm: fix typo in error message.
+              pnmmargin: recognize invalid options better.  Thanks
+              Prophet of the Way <afu@wta.att.ne.jp>.
+
+              anytopnm, pnmmargin, pamstretch-gen, ppmquantall: fix
+              small temporary file security exposure.
 
-07.07.30 BJH  Release 10.35.30
+              pnmtopalm: fix typo in error message.  Thanks Paul Bolle
+              <pebolle@tiscali.nl>.
 
               pnmtops: fix crash when program attempts to write 12 bits per
               sample Postscript.  It doesn't really know how to do that, so
-              now it just writes 8.
-
-07.07.10 BJH  Release 10.35.29
+              now it just writes 8 unless you do -psfilter.
 
               pnmtopng: Sort palette properly when alpha maxval != 255.
 
@@ -758,181 +1063,309 @@ CHANGE HISTORY
 
               pamtopfm: Fix wrong selection of default endianness.
 
-07.06.25 BJH  Release 10.35.28
+              libnetpbm: Add pm_system_lp(), pm_system_vp().
 
-              fitstopnm: fix BITPIX = -32.
+              libnetpbm: Make pbm_readpbminit() and pgm_readpgminit()
+              recognize incompatible Netpbm formats and issue a
+              helpful error message.
+
+              libnetpbm: pbm_readppminit: read PBM as maxval of 255, not 1.
+
+              libnetpbm: Fix pm_drain() to use the specified limit.
+              Thanks Paul Bolle <pebolle@tiscali.nl>.
+
+              libnetpbm: Fix multi-line TUPLTYPE interpretation:
+              separate lines with blank, not newline.  Thanks Paul
+              Bolle <pebolle@tiscali.nl>.
+
+              libnetpbm: Reject empty TUPLTYPE header.
+
+              libnetpbm: Fix uninitialized variable in pnm_allocpamrown().
+
+              libnetpbm: fix crash in REALLOCARRAY with advanced optimization.
+
+              libnetpbm: REALLOCARRAY frees memory if it fails.
+
+              Rename pamtooctave to pamtooctaveimg.
+
+              Build: don't build pstopnm on system without unix process
+              management.
+
+              Build: put -l's after .o's in test links.
+
+              Build: don't use 'uint' type.  Solaris 10 apparently doesn't
+              have it.
+
+              Build: Add missing <stdlib.h> to ppmrough.c.
+
+07.06.26 BJH  Release 10.39.00
+
+              Add pamtooctave.  Thanks Scott Pakin (scott@pakin.org).
+
+              Add pamundice.
+
+              fitstopnm: add -omaxval.
+
+              pnmremap: add -norand.
+
+              pbmtext: improve error messages about fonts.
 
               pamtofits: fix -min, -max.
 
-              PAM_STRUCT_SIZE: cast pointer to ulong instead of uint.
+              fitstopnm: fix BITPIX = -32.
 
-07.05.20 BJH  Release 10.35.27
+              PAM_STRUCT_SIZE: cast pointer to ulong instead of uint.
 
               pamthreshold: fix totally bogus threshold selection with
               simple thresholding.
 
-07.04.07 BJH  Release 10.35.26
+              Configure: do test compile for missing Libxml2 and too old
+              Libxml2.
 
-              picttoppm: Fix wild memory reference in all use cases.
+              Configure: fix bug detecting presence of libvga with
+              Ldconfig.
 
-              picttoppm: Fix for multi-pixel-per-byte image in which
-              it says the image has a row that is too long (because of
-              padding).
+              Configure: build properly for Mac OSX when user says
+              libnetpbm will be in the default search path.
 
-              configure: fix choice of default library suffix.
+              Build: don't use 'uint' type.  Mac OSX apparently doesn't
+              have it.
+
+07.03.30 BJH  Release 10.38.00
+    
+              Add pamfixtrunc.
+
+              pamtogif: Add -aspect.  Thanks
+              Prophet of the Way <afu@wta.att.ne.jp>.
+
+              pamditherbw: Add -atkinson.
+
+              pammixinterlace: Add -filter and fir and ffmpeg filters.
+              Thanks Bruce Guenter <bruce@untroubled.org>.
+
+              pammixinterlace: Add -adaptive.
+              Thanks Bruce Guenter <bruce@untroubled.org>.
+
+              pambackground: recognize mid-row background.
+
+              ppm3d: Change default offset to zero columns.
+
+              ppm3d: Add -color option.
+
+              ppm3d: Add -offset option as alternative to offset argument.
+
+              jpegtopnm: Add -repair option.
 
-07.03.23 BJH  Release 10.35.25
+              giftopnm: Add -repair option.
+
+              xwdtopnm: use pm_drain() to catch some format
+              misinterpretations.
+
+              pamtogif: Speed up for monochrome images.  Thanks
+              Prophet of the Way <afu@wta.att.ne.jp>.
+
+              pamtogif: Speed up for small images by using smaller
+              hash table (so smaller memory footprint).  Thanks
+              Prophet of the Way <afu@wta.att.ne.jp>.
+
+              libnetpbm: add pm_drain().
+              
+              libnetpbm: shhopt: reject signed number as value for
+              OPT_UINT option.
+
+              libnetpbm: in the "no such option" error message, say what
+              the valid options are.
+
+              libnetpbm: Embellish "bad magic number" error message.
 
               pnmremap: fix incorrect output with map file deeper than
               input file.
 
-              Pnmtopclxl: fix -feeder, -outtray options.  Thanks
+              xwdtopnm: fix right edge padding for 24 bit per pixel,
+              32 bit per item images.
+
+              xwdtopnm: update assumptions about format for direct color
+              images to match an image we found.
+
+              pnmtopclxl: fix -feeder, -outtray options.  Thanks
               "Eric K. Olson" <olson@mauicomputing.com>.
 
-07.03.09 BJH  Release 10.35.24
+              picttoppm: Fix wild memory reference in all use cases.
 
-              libnetpbm: fix crash with PBM images < 8 columns on MMX/SSE
-              machine.
+              picttoppm: Fix for multi-pixel-per-byte image in which
+              it says the image has a row that is too long (because of
+              padding).
 
-              pamtoxvmini: fix maxval != 255 bugs.
+              pamtogif: fix crash with -mapfile.
 
-07.02.21 BJH  Release 10.35.23
+              ppmtogif: handle case that map file and input file are not
+              the same depth, as was the case with the original ppmtogif.
+              Run the input through pnmremap.
+
+              pamtoxvmini: fix bug: produces garbage when maxval is
+              not 255.
 
               pamtoxvmini: fix crash.
 
-              ppmtogif: fail properly if image to wide or high for GIF.
+              libnetpbm: fix buffer overrun with PBM images < 8 columns.
 
-07.01.15 BJH  Release 10.35.22
+              libnetpbm: fix crash with PBM images < 8 columns on MMX/SSE
+              machine.
 
-              libnetpbm: fix buffer overrun with PBM images < 8 columns.
+              pamtogif: fail properly if image to wide or high for GIF.
 
               ppmdraw: fix crash with use of freed storage.  Thanks
               John Walker <kelvin@fourmilab.ch>.
 
-              installnetpbm: use 2-argument open() for old Perl.
-    
-06.12.31 BJH  Release 10.35.21
-
               libnetpbm: fix crash with PAM read as PNM.
 
-              pbmtextps: Fix buffer overrun -- typical symptom: extra
-              text generated.
-
-06.12.18 BJH  Release 10.35.20
-
-              ppmchange: Fix bug with -closeness and not -remainder or
-              vice versa.
+              installnetpbm: use 2-argument open() for old Perl.
+    
+              configure: fix choice of default library suffix.
 
-              pgmtoppm: Fix garbage output with -map and input maxval
-              != map maxval.
+              configure: default to 'none' for Svgalib if it doesn't appear
+              to be installed.
 
-06.12.03 BJH  Release 10.35.19
+06.12.31 BJH  Release 10.37.00
 
-              pbmtext: Fix crash when there is only one character.
+              Add pambackground.  But doesn't find mid-row background yet.
 
-              pnmtopng: fix transparency optimization on PPM input.
-    
-              pnmrotate: fix -background option with PGM/PBM input.
+              pnmcrop, pamtopnm: work on multi-image stream.
+              Thanks Erik Auerswald <auerswal@unix-ag.uni-kl.de>.
 
-              pamx: fix bug with X depth = 0.
+              pnmshear: add -background.  Thanks Erik Auerswald
+              <auerswal@unix-ag.uni-kl.de>.
 
-              pamx: fix bug: incorrect display of one-plane input image.
+              ppmtogif: replace with pamtogif.  Fix garbage output with
+              -interlace.
 
-06.11.27 BJH  Release 10.35.18
+              pbmtoxbm: add -name option.
+              Thanks Prophet of the Way <afu@wta.att.ne.jp>.
 
-              Configure: Fix incorrect .dylib suffix in shared library default.
+              ppmshift, ppmspread, ppmforge, pgmnoise, pgmcrater: better
+              randomization; won't produce the same image if you run it
+              twice within the same second.
 
-              libnetpbm: fix bogus results from ppm_parsecolor() of
-              rgb:0/0/0 style color name.
+              pbmtoxbm: Use packed PBM functions for efficiency.
+              Thanks Prophet of the Way <afu@wta.att.ne.jp>.
+              
+              xbmtopbm: Use packed PBM functions for efficiency.
+              Thanks Prophet of the Way <afu@wta.att.ne.jp>.
 
-06.11.18 BJH  Release 10.35.17
+              cmuwmtopbm, mgrtopbm, pbmtocmuwm, pbmtoicon, pbmtomgr:
+              Use packed PBM functions for efficiency.
+              Thanks Prophet of the Way <afu@wta.att.ne.jp>.
 
-              ppmtogif: fix garbage output with non-ppm-raw input;
-              restore to 10.34 version.
+              libnetpbm: add pnm_colorname().
 
-06.11.14 BJH  Release 10.35.16
+              libnetpbm: add pnm_computetuplefreqtable3().  Has ability
+              to limit number of planes of input considered.
 
-              ppmtogif: fix garbage output with -interlace.
+              libpam: better checking of sufficient size of struct pam
+              in pnm_writepaminit().
 
-06.11.13 BJH  Release 10.35.15
+              pnm_readpaminit(): abort instead of crash if width == 0.
 
-              Configure: fix default for X library location.
+              pnmtopng: fix transparency optimization on PPM input.
+    
+              pnmrotate: fix -background option with PGM/PBM input.
 
-06.11.13 BJH  Release 10.35.14
+              pamx: fix bug with X depth = 0.
 
-              Configure: fix default for X library location.
+              pamx: fix bug: incorrect display of one-plane input image.
 
-06.11.13 BJH  Release 10.35.13
+              libnetpbm: fix bogus results from ppm_parsecolor() of
+              rgb:0/0/0 style color name.
 
-              Build: fix Darwin build.
+              pbmtoxbm, pbmtox10bm: merge.
+              Thanks Prophet of the Way <afu@wta.att.ne.jp>.
 
-06.11.08 BJH  Release 10.35.12
+              pbmtox10bm: Fix generation of name in XBM file.
+              Thanks Prophet of the Way <afu@wta.att.ne.jp>.
 
-              Build: remove unnecessary limit of 200 characters on
-              argument length in Libopt.
+              pbmtextps: Fix buffer overrun -- typical symptom: extra
+              text generated.
 
-06.10.26 BJH  Release 10.35.11
+              ppmchange: Fix bug with -closeness and not -remainder or
+              vice versa.
 
-              pnm_readpaminit(): Don't crash if width == 0.
+              pgmtoppm: Fix garbage output with -map and input maxval
+              != map maxval.
 
-06.10.22 BJH  Release 10.35.10
+              pbmtext: Fix crash when there is only one character.
 
-              ppmtompeg: fix reading of GOP files.  Thanks Alun Jones.
+              pnmnlfilt: Give proper error message for syntax errors.
 
-06.10.21 BJH  Release 10.35.09
+              ppmtompeg: fix reading of GOPs.  Thanks Alun Jones.
 
               Fix pamcomp: due to use of undefined value, sometimes produces
               plain format.
 
-06.10.18 BJH  Release 10.35.08
-
               Fix pamperspective: read all of input.
 
               Fix libnetpbm pm_system(): don't hang if shell command quits
               before taking all the input.
 
-              Fix redefinitions of functions that Irix compiler can't handle.
+              Build: Configure does test links to detect broken
+              libpng-config --ldflags.
 
-06.10.01 BJH  Release 10.35.07
+              Build: Fix some compile failures with Irix IDO compiler.
 
-              Fix some prototypes that don't literally match definitions
-              so that Irix compiler will accept them.
+              Build: fix Darwin (Mac OS X) build.
 
-06.09.29 BJH  Release 10.35.06
+              Configure: fix default for X library location.
+
+              Build: remove unnecessary limit of 200 characters on
+              argument length in Libopt.
 
               Build: fix build with separate build directory.
 
               Build: fix merge build with no X11 library.
 
-06.09.11 BJH  Release 10.35.05
+06.09.21 BJH  Release 10.36.00
 
-              Remove some generated files from release so build works.
-
-              Properly clean thinkjettopbm.c with 'make distclean'.
+              pbmtonokia: Add plain NPM output capability.  Thanks Paul Bolle
+              <pebolle@tiscali.nl>.
 
-06.09.10 BJH  Release 10.35.04
+              pbmtonokia: Pad to 8 bytes.  Thanks Paul Bolle
+              <pebolle@tiscali.nl>.
 
-              Fix version number control.
+              pbmtonokia: Limit text to 120 bytes.  Thanks Paul Bolle
+              <pebolle@tiscali.nl>.
 
-06.09.09 BJH  Release 10.35.03
+              pamthreshold: don't gather global information, and reread
+              file, for local thresholding (to save time, resources).
+              Thanks Erik Auerswald <auerswal@unix-ag.uni-kl.de>.
 
-              Remove general history from change file
+              libnetpbm: BK color matching adjustments, change of
+              enum bk_color.  Thanks "Kenan Kalajdzic"
+              <kalajdzic@gmail.com>.
 
-06.09.03 BJH  Release 10.35.02
+              Fix version number control.
 
               pbmtonokia: fix headers of NGG and NOL to include 3 character
               magic.
 
+              Release allocated memory before longjmping from libnetpbm.
+
+              Eliminate pm_perror() in favor of informative error messages.
+
+              libnetpbm: fix rgb:r/g/b color name parsing for invalid
+              syntax.
+
+              Add pm_errormsg(), pm_setusererrormsg(), pm_setusermessage().
+
               pm_accept_to_pamtuples: fix bug: fill in pam structure.
               Thanks "Christian Schlotter" <schlotter@users.sourceforge.net>.
 
-06.08.26 BJH  Release 10.35.01
-
-              Fix selection of MMX_SEE fastpath based on GNU compiler version
+              Fix selection of MMX_SSE fastpath based on GNU compiler version
               number.
 
-              libnetpbm: fix rgb:r/g/b color name parsing for invalid
-              syntax.
+              Properly clean thinkjettopbm.c with 'make distclean'.
+
+              Remove some generated files from release so build works.
+
+              Remove general history from change file
 
 06.08.19 BJH  Release 10.35.00
 
@@ -1038,6 +1471,11 @@ CHANGE HISTORY
               picttoppm: fix bug - interprets some images wrong because of
               bogus "rowBytes" value.
 
+              libnetpbm: add ppm_fastlumin().  Change ppmtopgm, ppmtoilbm,
+              and ppm3d to use this, which results in higher quality output,
+              because ppm_fastlumin() rounds to nearest, whereas these
+              programs rounded down.  Otherwise, it's the same calculation.
+
               Redo asprintfN(), etc. so as not to use va_list in a way
               that doesn't work on some machines.
 
@@ -1133,7 +1571,7 @@ CHANGE HISTORY
               for superspeed operations.  Thanks
               Prophet of the Way <afu@wta.att.ne.jp>.
 
-              pm_make_tempfile(): Use TEMP and TMP environment variables if
+              pm_make_tmpfile(): Use TEMP and TMP environment variables if
               TMPDIR not set.
 
               pm_make_tempfile(): improve error message.
@@ -1177,8 +1615,6 @@ CHANGE HISTORY
               pamstereogram: Fix crippling bugs.  Thanks Scott Pakin
               <scott@pakin.org>.
 
-              giftopnm: Fix bug with interlaced GIF < 5 rows.
-
               giftopnm: Handle case of a clear code at the end of a block.
 
               ppmtogif: Fix bug with interlaced GIF < 5 rows.
@@ -1482,7 +1918,7 @@ CHANGE HISTORY
 
               anytopnm: remove apparently redundant traps of signals.
 
-              pnm_alloctupletable(): fix bug - returns random value.
+              pnm_alloctupletable(): fix bug from 10.27: returns random value.
               (affects pnmcolormap, pnmremap, pnmquant, others).
          
               jpeg2ktopam: fix array bounds violation on 64 bit machines.
@@ -1696,6 +2132,8 @@ CHANGE HISTORY
               ppmforge: fix bug: crash due to wild pointer with -night.
               Thanks John Walker <kelvin@fourmilab.ch>.
 
+              libnetpbm: add pm_make_tmpfile().
+
 05.01.01 BJH  Release 10.26
 
               pnmhistmap: Add -dots, -nmax, -red, -green, -blue, -lval,
@@ -1791,7 +2229,9 @@ CHANGE HISTORY
               pamcut: major speedup.  Thanks Prophet of the Way
               <afu@wta.att.ne.jp> (Akira Urushibata ("Douso")).
 
-              Add pnm_getopacity().
+              libnetpbm: Add pnm_getopacity().
+
+              libnetpbm: Add pnm_applyopacityrown(), pnm_unapplyopacityrown().
 
               libnetpbm: "pam" read and write routines much more
               efficient.  Thanks Prophet of the Way
@@ -2130,6 +2570,8 @@ CHANGE HISTORY
               Stefan Nordhausen <nordhaus@informatik.hu-berlin.de>.
               
               tifftopnm: Do better validation of number of channels.
+
+              libnetpbm: Add pm_tmpfile().
               
               libnetpbm: Add "normalized" (floating point) read/write
               routines:  pnm_readpamrown(), pnm_writepamrown(), 
@@ -2208,6 +2650,8 @@ CHANGE HISTORY
 
 03.09.19 BJH  Release 10.18
 
+              Add pbmtomrf, mrftopbm.
+
               Add escp2topbm, pbmtoescp2.  Thanks Ulrich Walcher
               <u.walcher@gmx.de>
 
@@ -3775,41 +4219,41 @@ Minor bug fixes and compatibility fixes are not documented in this file.
 
 PBM
 
-libpbm1.c	strstr() added to libpbm1.c.
-libpbm5.c	BDF font support added.
-pbmtext		BDF font support added.
-pbmto4425	New filter.
-pbmtoln03	Command line parsing changed to Pbmplus standard.
+libpbm1.c       strstr() added to libpbm1.c.
+libpbm5.c       BDF font functions added.
+pbmtext         Ability to use BDF fonts added.
+pbmto4425       New filter.
+pbmtoln03       Command line parsing changed to Pbmplus standard.
 
 
 PGM
 
-pgmnoise	New filter.
+pgmnoise        New filter.
 
 
 PPM
 
-picttoppm	Updated
-ppm3d		New facility.
-ppmchange	New filter.
-ppmdim		New filter.
-ppmflash	New filter.
-ppmmix		New filter.
-ppmntsc		New filter.
-ppmqvga		Option parsing changed to Pbmplus standard.
-ppmshift	New filter.
-ppmspread	New filter.
-ppmtoxpm	Prototypes added.
-xpmtoppm	Prototypes added.
-ilbmtoppm	Updated.
-ppmtoilbm	Updated.
+picttoppm       Updated
+ppm3d           New facility.
+ppmchange       New filter.
+ppmdim          New filter.
+ppmflash        New filter.
+ppmmix          New filter.
+ppmntsc         New filter.
+ppmqvga         Option parsing changed to Pbmplus standard.
+ppmshift        New filter.
+ppmspread       New filter.
+ppmtoxpm        Prototypes added.
+xpmtoppm        Prototypes added.
+ilbmtoppm       Updated.
+ppmtoilbm       Updated.
 
 
 PNM
 
-pnmtoddif	New filter.
-pnmhistmap	New facility.
-pnmtops		New option (-nocenter) added.
+pnmtoddif       New filter.
+pnmhistmap      New facility.
+pnmtops         New option (-nocenter) added.
 
 
 Functional changes to Netpbm since 7 December 1993.
@@ -3817,25 +4261,25 @@ Minor bug fixes and compatibility fixes are not documented in this file.
 
 PGM
 
-asciitopgm	New filter.
-fitstopgm	Replaced by fitstopnm.
-pgmtofits	Replaced by pnmtofits.
-pgmtopbm	Upgraded.
-pgmkernel	New filter.
+asciitopgm      New filter.
+fitstopgm       Replaced by fitstopnm.
+pgmtofits       Replaced by pnmtofits.
+pgmtopbm        Upgraded.
+pgmkernel       New filter.
 
 PPM
 
-ppmchange	Upgraded.
-xvminitoppm	New filter.
+ppmchange       Upgraded.
+xvminitoppm     New filter.
 
 PNM
 
-pnmalias	New filter.
-pnmtofits	Replacement for pgmtofits.
-fitstopnm	Replacement for fitstopgm.
-pnmtosgi	New filter.
-sgitopnm	New filter.
-pstopnm		New filter.
+pnmalias        New filter.
+pnmtofits       Replacement for pgmtofits.
+fitstopnm       Replacement for fitstopgm.
+pnmtosgi        New filter.
+sgitopnm        New filter.
+pstopnm         New filter.
 
 
 
@@ -3846,126 +4290,126 @@ The following is new in Netpbm (compared to Pbmplus):
 
 PBM
 
-pbmtext		BDF font support added.
+pbmtext         Ability to use BDF fonts added.
 
-pbmto4425	Display on an AT&T 4425 Ascii terminal.
+pbmto4425       Display on an AT&T 4425 Ascii terminal.
 
-pbmtoascii	A new improved version.
+pbmtoascii      A new improved version.
 
-pbmtoln03	Convert to DEC LN03+.
+pbmtoln03       Convert to DEC LN03+.
 
-pbmtolps	Fast PostScript creator.
+pbmtolps        Fast PostScript creator.
 
-pbmtopk		Conversion to/from a packed (PK) format font.
+pbmtopk         Conversion to/from a packed (PK) format font.
 pktopbm
 
-pbmclean	Flip isolated pixels.
+pbmclean        Flip isolated pixels.
 
-pbmpscale	Enlarge pbm image with edge smoothing.
+pbmpscale       Enlarge pbm image with edge smoothing.
 
 
 PGM
 
-asciitopgm	Convert an ascii image into pgm.
+asciitopgm      Convert an ascii image into pgm.
 
-pbmtopgm	Convert pbm to pgm by averaging areas.
+pbmtopgm        Convert pbm to pgm by averaging areas.
 
-rawtopgm	Handles input files without specification of the file size,
-		assuming the input image is quadratic. It also supports a
-		-tb (top bottom flip) option.
+rawtopgm        Handles input files without specification of the file size,
+                assuming the input image is quadratic. It also has a
+                -tb (top bottom flip) option.
 
-bioradtopgm	Conversion utility for files created by Biorad confocal
-		microscopes.
+bioradtopgm     Conversion utility for files created by Biorad confocal
+                microscopes.
 
-spottopgm	Convert SPOT satellite images to pgm.
+spottopgm       Convert SPOT satellite images to pgm.
 
-pgmkernel	Generate a convolution kernel.
+pgmkernel       Generate a convolution kernel.
 
-pgmnoise	Create a pgm file with random pixels.
+pgmnoise        Create a pgm file with random pixels.
 
 
 PPM
 
-bmptoppm	Conversion to/from windows bitmap format.
+bmptoppm        Conversion to/from windows bitmap format.
 ppmtobmp
 
-ppmtogif	Updated version.
-giftoppm	Removed (see giftopnm).
+ppmtogif        Updated version.
+giftoppm        Removed (see giftopnm).
 
-ppmtoilbm	Updated version.
+ppmtoilbm       Updated version.
 ilbmtoppm
 
-picttoppm	Updated version.
+picttoppm       Updated version.
 ppmtopict
 
-ppmtoxpm	Updated version, which supports xpm version 3.
+ppmtoxpm        Updated version, which understands xpm version 3.
 xpmtoppm
 
-ppmtomap	Extract all colors from a ppm file.
+ppmtomap        Extract all colors from a ppm file.
 
-ppmtomitsu	Convert to Mitsubishi S340-10 printer format.
+ppmtomitsu      Convert to Mitsubishi S340-10 printer format.
 
-xvminitoppm	Convert an XV thumbnail picture to ppm.
+xvminitoppm     Convert an XV thumbnail picture to ppm.
 
-ppmtoyuvsplit	Conversion to/from YUV triplets. (MPEG / JPEG).
+ppmtoyuvsplit   Conversion to/from YUV triplets. (MPEG / JPEG).
 yuvsplittoppm
 
-ppm3d		Create a red/blue stereo image.
+ppm3d           Create a red/blue stereo image.
 
-ppmbrighten	Change image saturation and value on an HSV map.
+ppmbrighten     Change image saturation and value on an HSV map.
 
-ppmchange	Change all pixels of one color to another in a portable pixmap
+ppmchange       Change all pixels of one color to another in a portable pixmap
 
-ppmdim		Dim a ppm file down to total blackness.
+ppmdim          Dim a ppm file down to total blackness.
 
-ppmdist		Simplistic grayscale assignment for machine generated
-		color images.
+ppmdist         Simplistic grayscale assignment for machine generated
+                color images.
 
-ppmflash	Brighten a picture up to complete white-out
+ppmflash        Brighten a picture up to complete white-out
 
-ppmmix		Blend together two portable pixmaps.
+ppmmix          Blend together two portable pixmaps.
 
-ppmnorm		Normalize the contrast in a portable pixmap.
+ppmnorm         Normalize the contrast in a portable pixmap.
 
-ppmntsc		Make a portable pixmap look like taken from an American TV.
+ppmntsc         Make a portable pixmap look like taken from an American TV.
 
-ppmqvga		Eight plane quantization.
+ppmqvga         Eight plane quantization.
 
-ppmshift	Shift lines of a portable pixmap left or right by a random amount.
+ppmshift        Shift lines of a portable pixmap left or right by a random amount.
 
-ppmspread	Displace a portable pixmap's pixels by a random amount.
+ppmspread       Displace a portable pixmap's pixels by a random amount.
 
-ppmtopjxl	Convert a ppm file into an HP PaintJet XL PCL file.
+ppmtopjxl       Convert a ppm file into an HP PaintJet XL PCL file.
 
 
 PNM
 
-pnmtops		New option (-nocenter) added.
+pnmtops         New option (-nocenter) added.
 
-pnmtofits	Replacement for pgmtofits/fitstopgm
+pnmtofits       Replacement for pgmtofits/fitstopgm
 fitstopnm
 
-pnmtosgi	Conversion to/from sgi image format.
+pnmtosgi        Conversion to/from sgi image format.
 sgitopnm
 
-pnmtosir	Conversion to/from Solitaire image recorder format.
+pnmtosir        Conversion to/from Solitaire image recorder format.
 sirtopnm
 
-giftopnm	Replaces giftoppm. Examines the input image and produces
-		a pbm, pgm, or ppm output.
+giftopnm        Replaces giftoppm. Examines the input image and produces
+                a pbm, pgm, or ppm output.
 
-pstopnm		Convert PostScript to pnm. Requires Ghostscript.
+pstopnm         Convert PostScript to pnm. Requires Ghostscript.
 
-zeisstopnm	Conversion utility for files created by Zeiss confocal
-		microscopes (the old standard).
+zeisstopnm      Conversion utility for files created by Zeiss confocal
+                microscopes (the old standard).
 
-pnmalias	Anti aliasing filter.
+pnmalias        Anti aliasing filter.
 
-pnmcomp		Composite two portable anymaps together.
+pnmcomp         Composite two portable anymaps together.
 
-pnmcrop		New options added.
+pnmcrop         New options added.
 
-pnmpad		Add borders to anymap.
+pnmpad          Add borders to anymap.
 
 
 LIBTIFF
@@ -4049,7 +4493,7 @@ Changes during the extended beta test period, starting on 15jan91:
     Added a -pseudodepth flag to pnmtoxwd.
     Updated tifftopnm for libtiff 2.4.
     Added many option flags to pnmtotiff.  (J.T. Conklin)
-    Added support for X11R5's new color specifiers rgb: and rgbi:.
+    Added recognition of X11R5's new color specifiers rgb: and rgbi:.
     Added pgmtexture.  (James Darrell McCauley)
     Added ppmtopj, pjtoppm, and ppmdither.  (Christos Zoulas)
     Added ppmtotga.  (Mark Shand)
@@ -4057,7 +4501,7 @@ Changes during the extended beta test period, starting on 15jan91:
     Added pbmtoatk and atktopbm.  (Bill Janssen)
     Added ppmtoyuv and yuvtoppm.  (Marc Boucher)
     Fixes to picttoppm.  (George Phillips)
-    Added 24-bit support to ilbmtoppm.  (Mark Thompson)
+    Added recognition of 24-bit images to ilbmtoppm.  (Mark Thompson)
 
 Changes since the X.V11R4 / comp.sources.misc distribution of 22nov89:
 
@@ -4098,7 +4542,7 @@ Changes since the X.V11R4 / comp.sources.misc distribution of 22nov89:
     Added -expand flag to pbmmask.
     Speedup to pnmflip - don't buffer if possible.
     Added color-name-to-value routine to ppm - uses X11's rgb.txt if present.
-    Updated Imakefile support to reflect X.V11R4.
+    Updated Imakefile function to reflect X.V11R4.
     Removed picttopbm.
     Improved pnmcut argument syntax so that negative coords work like pnmpaste.
     Added "magic" file, for use with the "file" program.
@@ -4122,7 +4566,7 @@ Changes since the X.V11R4 / comp.sources.misc distribution of 22nov89:
     Added -map flag to ppmquant - user-specifiable colormap.  Also, the
       Floyd-Steinberg error diffusion finally works right.
     Added -map flag to pgmtoppm.
-    Added DirectColor support to xwdtopnm and pnmtoxwd.
+    Added DirectColor capability to xwdtopnm and pnmtoxwd.
     Speedup to pgmtolj from Arthur David Olson: avoid sending whitespace.
     Fix to pbmtogo from Bo Thide': 2D compression now works.
 
@@ -4206,7 +4650,7 @@ Changes since the comp.sources.misc distribution of 31oct88:
 
 Changes since the X.V11R3 distribution of 31aug88:
 
-    The cbm format has been revised to support run-length encoding.
+    The cbm format has been revised to include run-length encoding.
     Pbmtops now does run-length encoding.
 
 Major changes since the X.V11R2 distribution of 28mar88:
diff --git a/doc/INSTALL b/doc/INSTALL
index 0b75607e..ec2cfbed 100644
--- a/doc/INSTALL
+++ b/doc/INSTALL
@@ -3,7 +3,7 @@ HOW TO INSTALL NETPBM
 
 For most typical platforms, you can just do
 
-    configure
+    ./configure
 
 followed by
 
@@ -16,7 +16,7 @@ To build all the programs.  Then
 to gather all the installable parts into a specified directory, and 
 finally
 
-    installnetpbm
+    ./installnetpbm
 
 to install it into your system.  
 
@@ -67,15 +67,23 @@ than you need to do and you will be disappointed with Configure's
 unpredictability, especially from one release to the next.  Configure is
 specifically intended to talk to an intelligent human being.
 
-Rather, just write a program to generate the file Makefile.config.  That's
+Rather, just write a program to generate the file config.mk.  That's
 all Configure does in the end anyway.  Like Configure, your program can
-simply copy Makefile.config.in and add overrides to the bottom.  Or you
-can just package up a complete Makefile.config and not run any program at
-all at build time.  Comments in Makefile.config.in explain the entire
+simply copy config.mk.in and add overrides to the bottom.  Or you
+can just package up a complete config.mk and not run any program at
+all at build time.  Comments in config.mk.in explain the entire
 contents.  You can also run Configure interactively and use its output
 as an example.
 
 
+THE PREREQUISITE LIBRARIES
+--------------------------
+
+Netpbm uses lots of external libraries, but you don't necessarily have
+to install them all.  See http://netpbm.sourceforge.net/prereq.html .
+You do, however, have to tell Configure accurately whether you have the
+library installed and if so, where.
+
 
 INSTALLATION - WINDOWS
 ----------------------
@@ -84,8 +92,7 @@ For notes on building Netpbm on Windows using Cygwin, see the file
 README.CYGWIN.  With Cygwin, you can build Netpbm programs that use
 Cygwin or Netpbm programs that use Mingw.
 
-For notes on building Netpbm on Windows using Djgpp, see the file
-README.DJGPP.
+Netpbm has also been built for Windows using Djgpp, as late as 2001.
 
 See also the general installation instructions above.
 
@@ -105,9 +112,9 @@ just do
 It will build Pnmtojpeg and any of its dependencies, but nothing else.
 You have to install it manually.  
 
-When you build just one program, you should request static libraries
-in the configure step, because for just one program, the shared
-libraries would be pure masochism.
+When you build just one program, you should request a static Netpbm
+library in the configure step, because for just one program, the
+shared library would be pure masochism.
 
 
 CUSTOM BUILDING
@@ -116,19 +123,19 @@ CUSTOM BUILDING
 This section explains how to customize the installation in the case
 that your platform is, or your requirements are, not among the simple
 cases that 'configure' understands.  'configure' is really just a
-convenient way to build the Makefile.config file, and in the most
+convenient way to build the config.mk file, and in the most
 general case, you build that file manually.
 
-Makefile.config contains settings for various things that vary from
+config.mk contains settings for various things that vary from
 one system to the next, like file paths.  Start with the distributed
-file Makefile.config.in.  Copy it as Makefile.config, then edit it.
+file config.mk.in.  Copy it as config.mk, then edit it.
 Search for your platform name (Solaris, SunOS, NetBSD, Cygwin, BeOS,
 and Tru64 are among those mentioned) to see recommended settings for
 your platform.
 
 If your system is even too exotic to accomodate with settings in
-Makefile.config, you may need to modify things in the main make files
-or pm_config.h.
+config.mk, you may need to modify things in the main make files
+or pm_config.h.in.
 
 If you figure out how to install on other platforms, contact the
 Netpbm maintainer to have the 'configure' program or these
@@ -173,6 +180,9 @@ shared libraries (which is kept in /var/ld/ld.config).  Make sure that
 path includes the directory in which you installed the Netpbm shared
 library.  You can also use the LD_LIBRARY_PATH environment variable.
 
+On AIX, the environment variable LIBPATH affects the shared library search
+path.  On AIX 5.3 and newer, you can use LD_LIBRARY_PATH as well.
+
 Besides the Netpbm shared library, libnetpbm, several of the converter
 programs, e.g. Jpegtopnm, use separately distributed libraries that
 understand the graphics format involved.  You need to make sure your
@@ -182,8 +192,8 @@ Netpbm build to statically bind the libraries into the Netpbm programs).
 Another thing you can do is forget about library search paths and just
 build into each Netpbm executable the location of the Netpbm shared
 library.  (I'm talking about the classic -R linker option) You set
-this up with variables in Makefile.config.  If you use Configure to
-build Makefile.config, then for some platforms where this method is
+this up with variables in config.mk.  If you use Configure to
+build config.mk, then for some platforms where this method is
 common, the Configure dialog asks you what directory, if any, you want
 built into Netpbm executables.
 
@@ -203,24 +213,56 @@ Since shared libraries can be such a pain, and in fact some systems
 don't even have them, you can build Netpbm with a static library
 instead.  Just answer "static" to the static/shared question when you
 run 'configure' (if you don't use 'configure', set NETPBMLIBTYPE as
-directed in Makefile.config.in).
+directed in config.mk.in).
 
 If you do this, you probably want to do a merge build instead of the
 normal build (there's a question for that in the Configure program).
 See below.
 
 
+SEPARATE BUILD TREE
+-------------------
+
+While it's traditional to build a Unix package by adding object files
+to the same tree with the source files, it's actually much cleaner to
+keep your source tree exactly as you got it and put the built files in
+a separate directory, called the build tree.
+
+To do this, just create an empty directory and run 'configure' in it,
+then 'make':
+
+  mkdir netpbmbuild
+  cd netpbmbuild
+  /usr/src/netpbm/configure
+  ...
+  make
+
+But if you plan to work on Netpbm source code, you'll probably find it
+more convenient to build the traditional way, with a single tree for
+source and build.
+
+In the source tree, you can type 'make' in any directory to do the
+default make for that directory, or make FILENAME to make the file of
+that name there.  In the separate build tree, there are special
+facilities to allow you to do a simple make from the _top level
+directory_, but if you want to make a subcomponent or individual part,
+you have to have a -f option and set SRCDIR and BLDDIR on your 'make'
+command.
+
+
 MERGE BUILD
 -----------
 
 There are two ways to build Netpbm: the standard or nonmerge build,
 and the merge build.  There are different make file targets for them
 and which one is default is controlled by the DEFAULT_TARGET make
-variable in Makefile.config, and its value is one of the choices you
+variable in config.mk, and its value is one of the choices you
 make via the Configure dialog.
 
 The standard build is the conventional one.  The merge build is a way
 to reduce disk space and other resource usage in some configurations.
+These are rare configurations, mostly antique ones.  The advent of
+shared libraries largely obsoleted the merge build.
 
 In the standard build, hundreds of separate programs get built: ppmtogif,
 pamcomp, etc.
@@ -246,24 +288,3 @@ DOCUMENTATION
 
 Documentation is not packaged with the program source code.  See the
 file doc/USERDOC for information on installing documentation.
-
-
-COMMON PROBLEMS
----------------
-
-Apple Mac: unknown machine mode 'V8QI'
---------------------------------------
-
-Netpbm 10.35 does not work on modern Macs.  These newer systems have a
-different compiler than what 10.35 was designed for, and it reports itself as
-the GNU Compiler but doesn't provide some of the facilities the GNU Compiler
-does.  Because Netpbm 10.35 was the Super Stable release series by the time
-the newer Macs were found to be incompatible and this is a very complex area,
-we cannot fix it in 10.35.
-
-Netpbm 10.51 and later work.
-
-You can work around the problem by adding the option '-mno-sse' to the
-compile, which you can do like this:
-
-  $ make CFLAGS=-mno-sse
diff --git a/doc/README.CYGWIN b/doc/README.CYGWIN
index 6c11ff35..a9867aae 100644
--- a/doc/README.CYGWIN
+++ b/doc/README.CYGWIN
@@ -66,7 +66,7 @@ search for include files and link libraries in a different place,
 which is basically just a -mno-cygwin option and -I and -L options.
 
 You can easily add the compiler options you need at the bottom of
-Makefile.config after you create it with the automatic configurator
+config.mk after you create it with the automatic configurator
 ('configure').
 
 Mingw does not have the Unix process management facilities (fork/wait,
diff --git a/doc/README.DJGPP b/doc/README.DJGPP
deleted file mode 100644
index 773a22e7..00000000
--- a/doc/README.DJGPP
+++ /dev/null
@@ -1,65 +0,0 @@
-Warning: The Netpbm distribution uses long filenames, so you can only
-build the package in a W9x environment, with LFN support.
-
-To build Netpbm with DJGPP v2.0x you need:
-
-        - djgpp: gcc, make, and binutils
-        - bash 
-        - fileutils
-        - sed
-        - groff (to convert man pages to cat format)
-        - man and less (to read the converted man pages)
-
-You can fetch all packages on Simtelnet in the DJGPP tree:
-ftp://ftp.simtel.net/pub/simtelnet/gnu/djgpp/
-
-You need too four external libraries to add tiff, png and jpeg support
-to Netpbm (you can build without them, modifying Makefile.config.djgpp
-but I think it makes Netpbm useless).
-
-        - Libtiff (www.libtiff.org)
-        - Libjpeg (www.ijg.org)
-        - Libpng  (www.libpng.org/pub/png/libpng.html)
-        - Libz    (www.info-zip.org/pub/infozip)
-
-All libraries come with guidelines to compile ok with DJGPP. At this time
-you can get binary packages of libjpeg, libpng and libz on Simtelnet.
-
-
-To build the Netpbm programs do the following:
-
-  Copy Makefile.config.djgpp to Makefile.config
-
-  Run "touch Makefile.config" to prevent Make from updating the file.
-
-  Read the Makefile.config and make any changes you want
-
-  Do "make merge"
-
-  Do "make install-merge" to install binaries and man pages.
-
-  Makefile.config.djgpp is set up to install these in TMPDIR/netpbm
-  (normally /dgjpp/tmp/netpbm).  If everything worked OK, you can
-  move the contents of this directory to your DJGPP production tree.
-
-
-WARNING
-
-There are some problems that have to be addressed if using the
-binaries in pure DOS OS, without long file names, but it works OK
-using "netpbm <utility>" (<utility> is the real Netpbm program to be
-used, for example: 
-
-      netpbm ppmtobmp <testimg.ppm >testimg.bmp
-
-instead of the usual
-
-      ppmtobmp <testimg.ppm >testimg.bmp).
-
-
-
-This procedure and the code to adapt Netpbm to DJGPP was contributed by
-M.Alvarez <malfer@teleline.es> in August 2001.  You can find his 
-current information on using Netpbm with DJGPP and prebuilt binaries
-at <http://www.terra.es/personal/malfer/netpbm/netpbm.htm>.
-
diff --git a/doc/USERDOC b/doc/USERDOC
index 3cd2b383..d1da64e1 100644
--- a/doc/USERDOC
+++ b/doc/USERDOC
@@ -18,18 +18,25 @@ so you can use the current manual with old Netpbm code.
 INSTALLING A LOCAL COPY OF DOCUMENTATION
 ----------------------------------------
 
-If accessing the manual on the World Wide Web is not convenient for
-you (for example, if you want to access it from a computer that is not
-always connected to the Internet), just make a local copy of the web
-site files using GNU Wget:
+If accessing the manual on the World Wide Web is not convenient for you (for
+example, if you want to access it from a computer that is not always connected
+to the Internet), just make a local copy of the HTML files.
+
+You can get the files from the Subversion repository using the Subversion
+client program 'svn':
+
+  URL=https://svn.code.sf.net/p/netpbm/code/userguide
+  svn export $URL
+
+This creates a directory "userguide" in your current directory containing
+all the same files that are on the web site.
 
-  wget --recursive --relative http://netpbm.sourceforge.net/doc/
 
-The above copies all the HTML files from the web site into your
-current directory, under a subdirectory 'netpbm.sourceforge.net/doc'
-that it creates.  You can browse those files directly with a web
-browser.  If you don't have Wget, get it from
-ftp://ftp.gnu.org/gnu/wget.  It is very useful.
+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
+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.
 
 
 GETTING COMMAND HELP WITH A "MAN" COMMAND
@@ -56,7 +63,7 @@ to go straight to the Ppmtogif documentation.
 
 Installnetpbm normally installs Manweb and the netpbm.url file that
 Manweb needs to find the Netpbm documentation.  Through the Configure
-dialog, or editing Makefile.config, you determine whether Manweb
+dialog, or editing config.mk, you determine whether Manweb
 accesses the master web copy or a local copy you installed.
 
 Installnetpbm installs the program as 'manweb'.  If you want to invoke
@@ -121,9 +128,9 @@ Here is an example of making troff pages:
   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/Makefile.manpage manpages
-  make -f /usr/src/netpbm/buildtools/Makefile.manpage manpages
-  make -f /usr/src/netpbm/buildtools/Makefile.manpage installman
+    -f /usr/src/netpbm/buildtools/manpage.mk manpages
+  make -f /usr/src/netpbm/buildtools/manpage.mk manpages
+  make -f /usr/src/netpbm/buildtools/manpage.mk installman
   cd ../../..
   rm -r netpbmdoc
 
diff --git a/editor/Makefile b/editor/Makefile
index 18165666..d5d7633f 100644
--- a/editor/Makefile
+++ b/editor/Makefile
@@ -5,7 +5,9 @@ endif
 SUBDIR = editor
 VPATH=.:$(SRCDIR)/$(SUBDIR)
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
+
+SUBDIRS = specialty
 
 # We tend to separate out the build targets so that we don't have
 # any more dependencies for a given target than it really needs.
@@ -14,25 +16,24 @@ include $(BUILDDIR)/Makefile.config
 # This package is so big, it's useful even when some parts won't 
 # build.
 
-PORTBINARIES = pamaddnoise pamcomp pamcut \
-	       pamdeinterlace pamdice pamditherbw pamedge \
+PORTBINARIES = pamaddnoise pambackground pamcomp pamcut \
+	       pamdice pamditherbw pamedge \
 	       pamenlarge \
-	       pamflip pamfunc pammasksharpen pammixinterlace \
-	       pamoil pamperspective pampop9 \
-	       pamscale pamstretch pamthreshold \
-	       pbmclean pbmlife pbmmask pbmpscale pbmreduce \
-	       pgmabel pgmbentley pgmdeshadow pgmenhance \
-	       pgmmedian pgmmorphconv \
-	       pnmalias pnmcat pnmcomp pnmconvol pnmcrop pnmcut \
+	       pamflip pamfunc pammasksharpen \
+	       pamperspective \
+	       pamscale pamsistoaglyph pamstretch pamthreshold pamundice \
+	       pbmclean pbmmask pbmpscale pbmreduce \
+	       pgmdeshadow pgmenhance \
+	       pgmmedian \
+	       pnmalias pnmcat pnmcomp pnmconvol pnmcrop \
 	       pnmgamma \
-	       pnmhisteq pnmindex pnminvert pnmmontage \
+	       pnmhisteq pnminvert pnmmontage \
 	       pnmnlfilt pnmnorm pnmpad pnmpaste \
 	       pnmremap pnmrotate \
-	       pnmscale pnmscalefixed pnmshear pnmsmooth pnmstitch pnmtile \
-	       ppm3d ppmbrighten ppmchange ppmcolormask \
+	       pnmscalefixed pnmshear pnmsmooth pnmstitch pnmtile \
+	       ppmbrighten ppmchange ppmcolormask \
 	       ppmdim ppmdist ppmdither ppmdraw \
-	       ppmflash ppmglobe ppmlabel ppmmix \
-	       ppmntsc ppmrelief ppmshift ppmspread ppmtv
+	       ppmflash ppmlabel ppmmix \
 
 # We don't include programs that have special library dependencies in the
 # merge scheme, because we don't want those dependencies to prevent us
@@ -51,36 +52,41 @@ OBJECTS = $(BINARIES:%=%.o)
 MERGE_OBJECTS = $(MERGEBINARIES:%=%.o2)
 
 .PHONY: all
-all: $(BINARIES)
+all: $(BINARIES) $(SUBDIRS:%=%/all)
 
-include $(SRCDIR)/Makefile.common
+include $(SRCDIR)/common.mk
 
 install.bin: install.bin.local
 
 .PHONY: install.bin.local
 install.bin.local: $(PKGDIR)/bin
 # Remember that $(SYMLINK) might just be a copy command.
-# backward compatibility: program used to be named pnmnoraw
 # backward compatibility: program used to be pnminterp
 	cd $(PKGDIR)/bin ; \
-	rm -f pnminterp; \
-	$(SYMLINK) pamstretch$(EXE) pnminterp
-# pamoil replaced pgmoil in June 2001.
-	cd $(PKGDIR)/bin ; \
-	rm -f pgmoil ; \
-	$(SYMLINK) pamoil$(EXE) pgmoil
+	rm -f pnminterp$(EXE); \
+	$(SYMLINK) pamstretch$(EXE) pnminterp$(EXE)
 # In March 2002, pnmnorm replaced ppmnorm and pgmnorm
 	cd $(PKGDIR)/bin ; \
-	rm -f ppmnorm ; \
-	$(SYMLINK) pnmnorm$(EXE) ppmnorm 
+	rm -f ppmnorm$(EXE) ; \
+	$(SYMLINK) pnmnorm$(EXE) ppmnorm$(EXE)
 	cd $(PKGDIR)/bin ; \
-	rm -f pgmnorm ; \
-	$(SYMLINK) pnmnorm$(EXE) pgmnorm
+	rm -f pgmnorm$(EXE) ; \
+	$(SYMLINK) pnmnorm$(EXE) pgmnorm$(EXE)
 # In March 2003, pamedge replaced pgmedge
 	cd $(PKGDIR)/bin ; \
-	rm -f pgmedge ; \
-	$(SYMLINK) pamedge$(EXE) pgmedge
+	rm -f pgmedge$(EXE) ; \
+	$(SYMLINK) pamedge$(EXE) pgmedge$(EXE)
 # In October 2004, pamenlarge replaced pnmenlarge
 	cd $(PKGDIR)/bin ; \
-	rm -f pnmenlarge ; \
-	$(SYMLINK) pamenlarge$(EXE) pnmenlarge
+	rm -f pnmenlarge$(EXE) ; \
+	$(SYMLINK) pamenlarge$(EXE) pnmenlarge$(EXE)
+# In March 2009, pamcut replaced pnmcut (but pamcut is much older -- pnmcut
+# was obsoleted by pamcut long before this).
+	cd $(PKGDIR)/bin ; \
+	rm -f pnmcut$(EXE) ; \
+	$(SYMLINK) pamcut$(EXE) pnmcut$(EXE)
+# In March 2009, pamscale replaced pnmscale (but pamscale is much older --
+# pnmscale was obsoleted by pamscale long before this).
+	cd $(PKGDIR)/bin ; \
+	rm -f pnmscale$(EXE) ; \
+	$(SYMLINK) pamscale$(EXE) pnmscale$(EXE)
diff --git a/editor/pamaddnoise.c b/editor/pamaddnoise.c
index 9c2d12f7..cf1af815 100644
--- a/editor/pamaddnoise.c
+++ b/editor/pamaddnoise.c
@@ -208,7 +208,7 @@ main(int argc, char * argv[]) {
     int argn;
     const char * inputFilename;
     int noise_type;
-    int seed;
+    unsigned int seed;
     int i;
     const char * const usage = "[-type noise_type] [-lsigma x] [-mgsigma x] "
         "[-sigma1 x] [-sigma2 x] [-lambda x] [-seed n] "
@@ -247,7 +247,7 @@ main(int argc, char * argv[]) {
 
     pnm_init(&argc, argv);
 
-    seed = time(NULL) ^ getpid();
+    seed = pm_randseed();
     noise_type = GAUSSIAN;
 
     argn = 1;
diff --git a/editor/pambackground.c b/editor/pambackground.c
new file mode 100644
index 00000000..1aa53177
--- /dev/null
+++ b/editor/pambackground.c
@@ -0,0 +1,505 @@
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "nstring.h"
+#include "shhopt.h"
+#include "pam.h"
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * inputFileName;  
+    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;
+        /* Instructions to OptParseOptions2 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0, "verbose",    OPT_FLAG,   NULL, &cmdlineP->verbose,        0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (argc-1 < 1)
+        cmdlineP->inputFileName = "-";
+    else {
+        cmdlineP->inputFileName = argv[1];
+        if (argc-1 > 1)
+            pm_error("There is at most one argument:  input file name.  "
+                     "You specified %d", argc-1);
+    }
+}        
+
+
+
+static void
+initOutpam(const struct pam * const inpamP,
+           struct pam *       const outpamP) {
+
+    outpamP->file             = stdout;
+    outpamP->format           = PAM_FORMAT;
+    outpamP->plainformat      = 0;
+    outpamP->width            = inpamP->width;
+    outpamP->height           = inpamP->height;
+    outpamP->depth            = 1;
+    outpamP->maxval           = 1;
+    outpamP->bytes_per_sample = pnm_bytespersample(outpamP->maxval);
+    outpamP->len              = PAM_STRUCT_SIZE(bytes_per_sample);
+    outpamP->size             = sizeof(*outpamP);
+}
+
+
+
+static void
+allocateOutputPointerRow(unsigned int const width,
+                         tuple **     const tuplerowP) {
+
+    MALLOCARRAY(*tuplerowP, width);
+
+    if (*tuplerowP == NULL)
+        pm_error("Could not allocate a %u-column tuple pointer array", width);
+}
+
+
+
+static void
+createWhiteTuple(const struct pam * const pamP, 
+                 tuple *            const whiteTupleP) {
+/*----------------------------------------------------------------------------
+   Create a "white" tuple.  By that we mean a tuple all of whose elements
+   are zero.  If it's an RGB, grayscale, or b&w pixel, that means it's black.
+-----------------------------------------------------------------------------*/
+    tuple whiteTuple;
+    unsigned int plane;
+
+    whiteTuple = pnm_allocpamtuple(pamP);
+
+    for (plane = 0; plane < pamP->depth; ++plane)
+        whiteTuple[plane] = pamP->maxval;
+
+    *whiteTupleP = whiteTuple;
+}
+
+
+
+static void
+selectBackground(struct pam * const pamP,
+                 tuple        const ul,
+                 tuple        const ur,
+                 tuple        const lr,
+                 tuple        const ll,
+                 tuple *      const bgColorP) {
+
+    tuple bg;  /* Reference to one of ul, ur, ll, lr */
+
+    if (pnm_tupleequal(pamP, ul, ur) &&
+        (pnm_tupleequal(pamP, ul, ll) ||
+         pnm_tupleequal(pamP, ul, lr)))
+        bg = ul;
+    else if (pnm_tupleequal(pamP, ll, lr) &&
+             pnm_tupleequal(pamP, lr, ul))
+        bg = ll;
+    else {
+        /* No 3 corners are same color; look for 2 corners */
+        if (pnm_tupleequal(pamP, ul, ur))  /* top edge */
+            bg = ul;
+        else if (pnm_tupleequal(pamP, ul, ll)) /* left edge */
+            bg = ul;
+        else if (pnm_tupleequal(pamP, ur, lr)) /* right edge */
+            bg = ur;
+        else if (pnm_tupleequal(pamP, ll, lr)) /* bottom edge */
+            bg = ll;
+        else {
+            /* No two corners are same color; just use upper left corner */
+            bg = ul;
+        }
+    }
+    
+    *bgColorP = pnm_allocpamtuple(pamP);
+    pnm_assigntuple(pamP, *bgColorP, bg);
+}
+
+
+
+static void
+determineBackgroundColor(struct pam * const pamP,
+                         bool         const verbose,
+                         tuple *      const bgColorP) {
+/*----------------------------------------------------------------------------
+   Determine what color is the background color of the image in the
+   file represented by *pamP.
+
+   Expect the file to be positioned to the start of the raster, and leave
+   it positioned arbitrarily.
+-----------------------------------------------------------------------------*/
+    unsigned int row;
+    tuple * tuplerow;
+    tuple ul, ur, ll, lr;
+        /* Color of upper left, upper right, lower left, lower right */
+
+    tuplerow  = pnm_allocpamrow(pamP);
+    ul = pnm_allocpamtuple(pamP);
+    ur = pnm_allocpamtuple(pamP);
+    ll = pnm_allocpamtuple(pamP);
+    lr = pnm_allocpamtuple(pamP);
+
+    pnm_readpamrow(pamP, tuplerow);
+
+    pnm_assigntuple(pamP, ul, tuplerow[0]);
+    pnm_assigntuple(pamP, ur, tuplerow[pamP->width-1]);
+
+    for (row = 1; row < pamP->height; ++row)
+        pnm_readpamrow(pamP, tuplerow);
+
+    pnm_assigntuple(pamP, ll, tuplerow[0]);
+    pnm_assigntuple(pamP, lr, tuplerow[pamP->width-1]);
+
+    selectBackground(pamP, ul, ur, ll, lr, bgColorP);
+
+    if (verbose) {
+        int const hexokTrue = 1;
+        const char * const colorname =
+            pnm_colorname(pamP, *bgColorP, hexokTrue);
+        pm_message("Background color is %s", colorname);
+
+        strfree(colorname);
+    }
+
+    pnm_freepamtuple(lr);
+    pnm_freepamtuple(ll);
+    pnm_freepamtuple(ur);
+    pnm_freepamtuple(ul);
+    pnm_freepamrow(tuplerow);
+}
+
+
+static unsigned char const PT_UNKNOWN = 0;
+static unsigned char const PT_BG      = 1;
+static unsigned char const PT_FG      = 2;
+
+
+static unsigned char **
+newPi(unsigned int const width,
+      unsigned int const height) {
+
+    unsigned char ** pi;
+    unsigned int row;
+
+    MALLOCARRAY(pi, height);
+    if (pi == NULL)
+        pm_error("Out of memory allocating %u row pointers", height);
+    for (row = 0; row < height; ++row) {
+        MALLOCARRAY(pi[row], width);
+        if (pi[row] == NULL)
+            pm_error("Out of memory allocating row %u", row);
+    }
+    return pi;
+}
+
+
+
+static void
+destroyPi(const unsigned char *const * const pi,
+          unsigned int                 const height) {
+
+    unsigned int row;
+
+    for (row = 0; row < height; ++row)
+        free((void*)pi[row]);
+
+    free((void*)pi);
+}
+
+
+
+static void
+initPi(unsigned char **   const pi,
+       const struct pam * const pamP,
+       tuple              const backgroundColor) {
+/*----------------------------------------------------------------------------
+  Set the initial info about every pixel in pi[][].
+
+  Read through the image in the file described by *inpamP and set each
+  pixel which is not of background color, and therefore obviously
+  foreground, to type PT_FG.  Set every other pixel to PT_UNKNOWN.
+-----------------------------------------------------------------------------*/
+    tuple * tuplerow;
+    unsigned int row;
+
+    tuplerow  = pnm_allocpamrow(pamP);
+
+    for (row = 0; row < pamP->height; ++row) {
+        unsigned int col;
+
+        pnm_readpamrow(pamP, tuplerow);
+
+        for (col = 0; col < pamP->width; ++col) {
+            pi[row][col] =
+                pnm_tupleequal(pamP, tuplerow[col], backgroundColor) ?
+                PT_UNKNOWN : PT_FG;
+        }
+    }
+    pnm_freepamrow(tuplerow);
+}
+
+
+
+static void
+setEdges(unsigned char ** const pi,
+         unsigned int     const width,
+         unsigned int     const height) {
+/*----------------------------------------------------------------------------
+  Do the four edges.
+
+  Anything of background color in an edge is background.  Pixel type
+  PT_UNKNOWN implies background color, so we change all PT_UNKNOWN pixels
+  to PT_BG.
+-----------------------------------------------------------------------------*/
+    unsigned int row, col;
+
+    for (col = 0; col < width; ++col) {
+        if (pi[0][col] == PT_UNKNOWN)
+            pi[0][col] = PT_BG;
+
+        if (pi[height-1][col] == PT_UNKNOWN)
+            pi[height-1][col] = PT_BG;
+    }
+    for (row = 0; row < height; ++row) {
+        if (pi[row][0] == PT_UNKNOWN)
+            pi[row][0] = PT_BG;
+
+        if (pi[row][width-1] == PT_UNKNOWN)
+            pi[row][width-1] = PT_BG;
+    }
+}
+
+
+
+static void
+expandBackgroundHoriz(unsigned char ** const pi,
+                      unsigned int     const width,
+                      unsigned int     const height,
+                      bool *           const expandedP) {
+/*----------------------------------------------------------------------------
+   In every row, expand the background rightward from any known background
+   pixel through all consecutive unknown pixels.
+
+   Then do the same thing leftward.
+
+   Iff we managed to expand the background at all, return *expandedP == TRUE.
+-----------------------------------------------------------------------------*/
+    unsigned int row;
+    bool expanded;
+
+    for (row = 1, expanded = FALSE; row < height-1; ++row) {
+        int col;
+
+        for (col = 1; col < width - 1; ++col) {
+            if (pi[row][col] == PT_UNKNOWN && pi[row][col-1] == PT_BG) {
+                expanded = TRUE;
+                /* Set the whole consecutive row of unknown to background */
+                for (; pi[row][col] == PT_UNKNOWN && col < width - 1; ++col)
+                    pi[row][col] = PT_BG;
+            }
+        }
+
+        for (col = width-2; col > 0; --col) {
+            if (pi[row][col] == PT_UNKNOWN && pi[row][col+1] == PT_BG) {
+                expanded = TRUE;
+                /* Set the whole consecutive row of unknown to background */
+                for (; pi[row][col] == PT_UNKNOWN && col > 0; --col)
+                    pi[row][col] = PT_BG;
+            }
+        }
+    }
+    *expandedP = expanded;
+}
+
+
+
+static void
+expandBackgroundVert(unsigned char ** const pi,
+                     unsigned int     const width,
+                     unsigned int     const height,
+                     bool *           const expandedP) {
+/*----------------------------------------------------------------------------
+   In every column, expand the background downward from any known background
+   pixel through all consecutive unknown pixels.
+
+   Then do the same thing upward.
+
+   Iff we managed to expand the background at all, return *expandedP == TRUE.
+-----------------------------------------------------------------------------*/
+    unsigned int col;
+    bool expanded;
+
+    for (col = 1, expanded = FALSE; col < width-1; ++col) {
+        int row;
+
+        for (row = 1; row < height - 1; ++row) {
+            if (pi[row][col] == PT_UNKNOWN && pi[row-1][col] == PT_BG) {
+                expanded = TRUE;
+                /* Set the whole consecutive col of unknown to background */
+                for (; pi[row][col] == PT_UNKNOWN && row < height - 1; ++row)
+                    pi[row][col] = PT_BG;
+            }
+        }
+
+        for (row = height-2; row > 0; --row) {
+            if (pi[row][col] == PT_UNKNOWN && pi[row+1][col] == PT_BG) {
+                expanded = TRUE;
+                /* Set the whole consecutive col of unknown to background */
+                for (; pi[row][col] == PT_UNKNOWN && row > 0; --row)
+                    pi[row][col] = PT_BG;
+            }
+        }
+    }
+    *expandedP = expanded;
+}
+
+
+
+static void
+findBackgroundPixels(struct pam *                   const inpamP,
+                     tuple                          const backgroundColor,
+                     bool                           const verbose,
+                     const unsigned char * const ** const piP) {
+/*----------------------------------------------------------------------------
+   Figure out which pixels of the image are background.  Read the
+   image from the input file described by *inpamP (positioned now to
+   the start of the raster) and generate the matrix *piP telling which
+   pixels are background and which are foreground, given that the
+   background color is 'backgroundColor'.
+
+   Note that it isn't as simple as finding which pixels are of color
+   'backgroundColor'.  They have to be part of a contiguous region that
+   touches one of the 4 edges of the image.
+
+   In the matrix we return, there is one element for each pixel in the
+   image, and it has value PT_BG or PT_FG.
+-----------------------------------------------------------------------------*/
+    unsigned char ** pi;
+    bool backgroundComplete;
+    unsigned int passes;
+
+    pi = newPi(inpamP->width, inpamP->height);
+
+    initPi(pi, inpamP, backgroundColor);
+
+    setEdges(pi, inpamP->width, inpamP->height);
+
+    backgroundComplete = FALSE;
+    passes = 0;
+    
+    while (!backgroundComplete) {
+        bool expandedHoriz, expandedVert;
+
+        expandBackgroundHoriz(pi, inpamP->width, inpamP->height,
+                              &expandedHoriz);
+    
+        expandBackgroundVert(pi, inpamP->width, inpamP->height,
+                             &expandedVert);
+
+        backgroundComplete = !expandedHoriz && !expandedVert;
+
+        ++passes;
+    }
+
+    if (verbose)
+        pm_message("Background found in %u passes", passes);
+
+    *piP = (const unsigned char * const *)pi;
+}
+
+                     
+
+static void
+writeOutput(const struct pam *            const inpamP,
+            const unsigned char * const * const pi) {
+
+    tuple black, white;
+    tuple * outputTuplerow;
+        /* This is not a normal tuplerow; it is just pointers to either
+           'black' or 'white'
+        */
+    unsigned int row;
+    struct pam outpam;
+
+    initOutpam(inpamP, &outpam);
+
+    allocateOutputPointerRow(outpam.width, &outputTuplerow);
+    pnm_createBlackTuple(&outpam, &black);
+    createWhiteTuple(&outpam, &white);
+
+    pnm_writepaminit(&outpam);
+
+    for (row = 0; row < outpam.height; ++row) {
+        unsigned int col;
+        for (col = 0; col < outpam.width; ++col)
+            outputTuplerow[col] = pi[row][col] == PT_BG ? white : black;
+
+        pnm_writepamrow(&outpam, outputTuplerow);
+    }
+    pnm_freepamtuple(white);
+    pnm_freepamtuple(black);
+    free(outputTuplerow);
+}
+
+
+
+int
+main(int argc, char *argv[]) {
+
+    struct cmdlineInfo cmdline;
+    struct pam inpam;
+    FILE * ifP;
+    pm_filepos rasterpos;
+    tuple backgroundColor;
+    const unsigned char * const * pi;
+    
+    pnm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr_seekable(cmdline.inputFileName);
+
+    pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
+
+    pm_tell2(ifP, &rasterpos, sizeof(rasterpos));
+
+    determineBackgroundColor(&inpam, cmdline.verbose, &backgroundColor);
+
+    pm_seek2(ifP, &rasterpos, sizeof(rasterpos));
+
+    findBackgroundPixels(&inpam, backgroundColor, cmdline.verbose, &pi);
+
+    writeOutput(&inpam, pi);
+
+    destroyPi(pi, inpam.height);
+
+    pm_close(ifP);
+
+    pnm_freepamtuple(backgroundColor);
+    
+    return 0;
+}
diff --git a/editor/pamcomp.c b/editor/pamcomp.c
index c9c389b7..e89509cc 100644
--- a/editor/pamcomp.c
+++ b/editor/pamcomp.c
@@ -22,14 +22,17 @@
    additional work by multiple authors.
 -----------------------------------------------------------------------------*/
 
-#define _BSD_SOURCE    /* Make sure strcasecmp() is in string.h */
+#define _BSD_SOURCE    /* Make sure strcaseceq() is in nstring.h */
+#include <assert.h>
 #include <string.h>
 #include <math.h>
 
-#include "pam.h"
-#include "pm_gamma.h"
-#include "shhopt.h"
+#include "pm_c_util.h"
 #include "mallocvar.h"
+#include "nstring.h"
+#include "shhopt.h"
+#include "pm_gamma.h"
+#include "pam.h"
 
 enum horizPos {BEYONDLEFT, LEFT, CENTER, RIGHT, BEYONDRIGHT};
 enum vertPos {ABOVE, TOP, MIDDLE, BOTTOM, BELOW};
@@ -64,7 +67,7 @@ struct cmdlineInfo {
 
 static void
 parseCommandLine(int                        argc, 
-                 char **                    argv,
+                 const char **              argv,
                  struct cmdlineInfo * const cmdlineP ) {
 /*----------------------------------------------------------------------------
    Parse program command line described in Unix standard form by argc
@@ -111,7 +114,7 @@ parseCommandLine(int                        argc,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
 
@@ -123,15 +126,15 @@ parseCommandLine(int                        argc,
         cmdlineP->alphaFilespec = NULL;
 
     if (alignSpec) {
-        if (strcasecmp(align, "BEYONDLEFT") == 0)
+        if (strcaseeq(align, "BEYONDLEFT"))
             cmdlineP->align = BEYONDLEFT;
-        else if (strcasecmp(align, "LEFT") == 0)
+        else if (strcaseeq(align, "LEFT"))
             cmdlineP->align = LEFT;
-        else if (strcasecmp(align, "CENTER") == 0)
+        else if (strcaseeq(align, "CENTER"))
             cmdlineP->align = CENTER;
-        else if (strcasecmp(align, "RIGHT") == 0)
+        else if (strcaseeq(align, "RIGHT"))
             cmdlineP->align = RIGHT;
-        else if (strcasecmp(align, "BEYONDRIGHT") == 0)
+        else if (strcaseeq(align, "BEYONDRIGHT"))
             cmdlineP->align = BEYONDRIGHT;
         else
             pm_error("Invalid value for align option: '%s'.  Only LEFT, "
@@ -141,15 +144,15 @@ parseCommandLine(int                        argc,
         cmdlineP->align = LEFT;
 
     if (valignSpec) {
-        if (strcasecmp(valign, "ABOVE") == 0)
+        if (strcaseeq(valign, "ABOVE"))
             cmdlineP->valign = ABOVE;
-        else if (strcasecmp(valign, "TOP") == 0)
+        else if (strcaseeq(valign, "TOP"))
             cmdlineP->valign = TOP;
-        else if (strcasecmp(valign, "MIDDLE") == 0)
+        else if (strcaseeq(valign, "MIDDLE"))
             cmdlineP->valign = MIDDLE;
-        else if (strcasecmp(valign, "BOTTOM") == 0)
+        else if (strcaseeq(valign, "BOTTOM"))
             cmdlineP->valign = BOTTOM;
-        else if (strcasecmp(valign, "BELOW") == 0)
+        else if (strcaseeq(valign, "BELOW"))
             cmdlineP->valign = BELOW;
         else
             pm_error("Invalid value for valign option: '%s'.  Only TOP, "
@@ -268,12 +271,12 @@ determineOutputType(struct pam * const composedPamP,
 
 
 static void
-warnOutOfFrame( int const originLeft,
-                int const originTop, 
-                int const overCols,
-                int const overRows,
-                int const underCols,
-                int const underRows ) {
+warnOutOfFrame(int const originLeft,
+               int const originTop, 
+               int const overCols,
+               int const overRows,
+               int const underCols,
+               int const underRows) {
 
     if (originLeft >= underCols)
         pm_message("WARNING: the overlay is entirely off the right edge "
@@ -339,6 +342,10 @@ computeOverlayPosition(int                const underCols,
    The origin may be outside the underlying image (so e.g. *originLeftP may
    be negative or > image width).  That means not all of the overlay image
    actually gets used.  In fact, there may be no overlap at all.
+
+   But we insist that the span from the topmost row of the two images
+   to the bottommost row be less than INT_MAX so that Caller can
+   use an integer for a row number in the combination.
 -----------------------------------------------------------------------------*/
     int xalign, yalign;
 
@@ -470,6 +477,44 @@ adaptRowToOutputFormat(struct pam * const inpamP,
 
 
 static void
+composeRow(int              const originleft, 
+           struct pam *     const underlayPamP,
+           struct pam *     const overlayPamP,
+           bool             const invertAlpha,
+           float            const masterOpacity,
+           bool             const overlayHasOpacity,
+           unsigned int     const opacityPlane,
+           struct pam *     const composedPamP,
+           enum sampleScale const sampleScale,
+           const tuple *    const underlayTuplerow,
+           const tuple *    const overlayTuplerow,
+           const tuplen *   const alphaTuplerown,
+           tuple *          const composedTuplerow) {
+
+    unsigned int col;
+    for (col = 0; col < composedPamP->width; ++col) {
+        int const ovlcol = col - originleft;
+
+        if (ovlcol >= 0 && ovlcol < overlayPamP->width) {
+            tuplen const alphaTuplen = 
+                alphaTuplerown ? alphaTuplerown[ovlcol] : NULL;
+
+            overlayPixel(overlayTuplerow[ovlcol], overlayPamP,
+                         underlayTuplerow[col], underlayPamP,
+                         alphaTuplen, invertAlpha,
+                         overlayHasOpacity, opacityPlane,
+                         composedTuplerow[col], composedPamP,
+                         masterOpacity, sampleScale);
+        } else
+            /* Overlay image does not touch this column. */
+            pnm_assigntuple(composedPamP, composedTuplerow[col],
+                            underlayTuplerow[col]);
+    }
+}
+
+
+
+static void
 composite(int          const originleft, 
           int          const origintop, 
           struct pam * const underlayPamP,
@@ -495,6 +540,9 @@ composite(int          const originleft,
    go.  It is not necessarily inside the underlying image (in fact,
    may be negative).  Only the part of the overlay that actually
    intersects the underlying image, if any, gets into the output.
+
+   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 = 
         assumeLinear ? INTENSITY_SAMPLE : GAMMA_SAMPLE;
@@ -515,9 +563,13 @@ composite(int          const originleft,
     overlayTuplerow  = pnm_allocpamrow(overlayPamP);
     if (alphaPamP)
         alphaTuplerown = pnm_allocpamrown(alphaPamP);
+    else
+        alphaTuplerown = NULL;
 
     pnm_writepaminit(composedPamP);
 
+    assert(INT_MAX - overlayPamP->height > origintop); /* arg constraint */
+
     for (underlayRow = MIN(0, origintop), overlayRow = MIN(0, -origintop);
          underlayRow < MAX(underlayPamP->height, 
                            origintop + overlayPamP->height);
@@ -541,25 +593,12 @@ composite(int          const originleft,
 
                 pnm_writepamrow(composedPamP, underlayTuplerow);
             } else {
-                unsigned int col;
-                for (col = 0; col < composedPamP->width; ++col) {
-                    int const ovlcol = col - originleft;
-
-                    if (ovlcol >= 0 && ovlcol < overlayPamP->width) {
-                        tuplen const alphaTuplen = 
-                            alphaPamP ? alphaTuplerown[ovlcol] : NULL;
-
-                        overlayPixel(overlayTuplerow[ovlcol], overlayPamP,
-                                     underlayTuplerow[col], underlayPamP,
-                                     alphaTuplen, invertAlpha,
-                                     overlayHasOpacity, opacityPlane,
-                                     composedTuplerow[col], composedPamP,
-                                     masterOpacity, sampleScale);
-                    } else
-                        /* Overlay image does not touch this column. */
-                        pnm_assigntuple(composedPamP, composedTuplerow[col],
-                                        underlayTuplerow[col]);
-                }
+                composeRow(originleft, underlayPamP, overlayPamP,
+                           invertAlpha, masterOpacity, overlayHasOpacity,
+                           opacityPlane, composedPamP, sampleScale,
+                           underlayTuplerow, overlayTuplerow, alphaTuplerown,
+                           composedTuplerow);
+                
                 pnm_writepamrow(composedPamP, composedTuplerow);
             }
         }
@@ -574,7 +613,7 @@ composite(int          const originleft,
 
 
 int
-main(int argc, char *argv[]) {
+main(int argc, const char *argv[]) {
 
     struct cmdlineInfo cmdline;
     FILE * underlayFileP;
@@ -586,7 +625,7 @@ main(int argc, char *argv[]) {
     struct pam composedPam;
     int originLeft, originTop;
 
-    pnm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
diff --git a/editor/pamcut.c b/editor/pamcut.c
index c5d53f44..8d4c2240 100644
--- a/editor/pamcut.c
+++ b/editor/pamcut.c
@@ -12,6 +12,8 @@
 
 #include <limits.h>
 #include <assert.h>
+
+#include "pm_c_util.h"
 #include "pam.h"
 #include "shhopt.h"
 #include "mallocvar.h"
@@ -94,8 +96,10 @@ parseCommandLine(int argc, char ** const argv,
         pm_error("-height may not be negative.");
 
     if ((argc-1) != 0 && (argc-1) != 1 && (argc-1) != 4 && (argc-1) != 5)
-        pm_error("Wrong number of arguments.  "
-                 "Must be 0, 1, 4, or 5 arguments.");
+        pm_error("Wrong number of arguments: %u.  The only argument in "
+                 "the preferred syntax is an optional input file name.  "
+                 "In older syntax, there are also forms with 4 and 5 "
+                 "arguments.", argc-1);
 
     switch (argc-1) {
     case 0:
@@ -269,44 +273,51 @@ computeCutBounds(const int cols, const int rows,
 
 
 static void
-rejectOutOfBounds(const int cols, const int rows, 
-                  const int leftcol, const int rightcol, 
-                  const int toprow, const int bottomrow) {
-
-    /* Reject coordinates off the edge */
-
-    if (leftcol < 0)
-        pm_error("You have specified a left edge (%d) that is beyond\n"
-                 "the left edge of the image (0)", leftcol);
-    if (leftcol > cols-1)
-        pm_error("You have specified a left edge (%d) that is beyond\n"
-                 "the right edge of the image (%d)", leftcol, cols-1);
-    if (rightcol < 0)
-        pm_error("You have specified a right edge (%d) that is beyond\n"
-                 "the left edge of the image (0)", rightcol);
-    if (rightcol > cols-1)
-        pm_error("You have specified a right edge (%d) that is beyond\n"
-                 "the right edge of the image (%d)", rightcol, cols-1);
-    if (leftcol > rightcol) 
-        pm_error("You have specified a left edge (%d) that is to the right\n"
-                 "of the right edge you specified (%d)", 
+rejectOutOfBounds(unsigned int const cols,
+                  unsigned int const rows,
+                  int          const leftcol,
+                  int          const rightcol,
+                  int          const toprow,
+                  int          const bottomrow,
+                  bool         const pad) {
+
+     /* Reject coordinates off the edge */
+
+    if (!pad) {
+        if (leftcol < 0)
+            pm_error("You have specified a left edge (%d) that is beyond "
+                     "the left edge of the image (0)", leftcol);
+        if (leftcol > (int)(cols-1))
+            pm_error("You have specified a left edge (%d) that is beyond "
+                     "the right edge of the image (%u)", leftcol, cols-1);
+        if (rightcol < 0)
+            pm_error("You have specified a right edge (%d) that is beyond "
+                     "the left edge of the image (0)", rightcol);
+        if (rightcol > (int)(cols-1))
+            pm_error("You have specified a right edge (%d) that is beyond "
+                     "the right edge of the image (%u)", rightcol, cols-1);
+        if (toprow < 0)
+            pm_error("You have specified a top edge (%d) that is above "
+                     "the top edge of the image (0)", toprow);
+        if (toprow > (int)(rows-1))
+            pm_error("You have specified a top edge (%d) that is below "
+                     "the bottom edge of the image (%u)", toprow, rows-1);
+        if (bottomrow < 0)
+            pm_error("You have specified a bottom edge (%d) that is above "
+                     "the top edge of the image (0)", bottomrow);
+        if (bottomrow > (int)(rows-1))
+            pm_error("You have specified a bottom edge (%d) that is below "
+                     "the bottom edge of the image (%u)", bottomrow, rows-1);
+    }
+
+    if (leftcol > rightcol)
+        pm_error("You have specified a left edge (%d) that is to the right of "
+                 "the right edge you specified (%d)",
                  leftcol, rightcol);
-    
-    if (toprow < 0)
-        pm_error("You have specified a top edge (%d) that is above the top "
-                 "edge of the image (0)", toprow);
-    if (toprow > rows-1)
-        pm_error("You have specified a top edge (%d) that is below the\n"
-                 "bottom edge of the image (%d)", toprow, rows-1);
-    if (bottomrow < 0)
-        pm_error("You have specified a bottom edge (%d) that is above the\n"
-                 "top edge of the image (0)", bottomrow);
-    if (bottomrow > rows-1)
-        pm_error("You have specified a bottom edge (%d) that is below the\n"
-                 "bottom edge of the image (%d)", bottomrow, rows-1);
-    if (toprow > bottomrow) 
-        pm_error("You have specified a top edge (%d) that is below\n"
-                 "the bottom edge you specified (%d)", 
+
+    if (toprow > bottomrow)
+        pm_error("You have specified a top edge (%d) that is below "
+                 "the bottom edge you specified (%d)",
                  toprow, bottomrow);
 }
 
@@ -398,13 +409,15 @@ struct rowCutter {
    create a new one then.
 */
 
+
+
 static void
-createRowCutter(struct pam *        const inpamP,
-                struct pam *        const outpamP,
+createRowCutter(const struct pam *  const inpamP,
+                const struct pam *  const outpamP,
                 int                 const leftcol,
                 int                 const rightcol,
                 struct rowCutter ** const rowCutterPP) {
-    
+
     struct rowCutter * rowCutterP;
     tuple * inputPointers;
     tuple * outputPointers;
@@ -412,7 +425,7 @@ createRowCutter(struct pam *        const inpamP,
     tuple blackTuple;
     tuple discardTuple;
     int col;
-    
+
     assert(inpamP->depth >= outpamP->depth);
         /* Entry condition.  If this weren't true, we could not simply
            treat an input tuple as an output tuple.
@@ -483,15 +496,144 @@ destroyRowCutter(struct rowCutter * const rowCutterP) {
 
 
 static void
+extractRowsGen(const struct pam * const inpamP,
+               const struct pam * const outpamP,
+               int                const leftcol,
+               int                const rightcol,
+               int                const toprow,
+               int                const bottomrow) {
+
+    struct rowCutter * rowCutterP;
+    int row;
+
+    /* Write out top padding */
+    if (0 - toprow > 0)
+        writeBlackRows(outpamP, 0 - toprow);
+
+    createRowCutter(inpamP, outpamP, leftcol, rightcol, &rowCutterP);
+
+    /* Read input and write out rows extracted from it */
+    for (row = 0; row < inpamP->height; ++row) {
+        if (row >= toprow && row <= bottomrow){
+            pnm_readpamrow(inpamP, rowCutterP->inputPointers);
+            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.
+        */
+    }
+
+    destroyRowCutter(rowCutterP);
+    
+    /* Write out bottom padding */
+    if ((bottomrow - (inpamP->height-1)) > 0)
+        writeBlackRows(outpamP, bottomrow - (inpamP->height-1));
+}
+
+
+
+static void
+makeBlackPBMRow(unsigned char * const bitrow,
+                unsigned int    const cols) {
+
+    unsigned int const colByteCnt = pbm_packed_bytes(cols);
+
+    unsigned int i;
+
+    for (i = 0; i < colByteCnt; ++i)
+        bitrow[i] = PBM_BLACK * 0xff;
+
+    if (PBM_BLACK != 0 && cols % 8 > 0)
+        bitrow[colByteCnt-1] <<= (8 - cols % 8);
+}
+
+
+
+static void
+extractRowsPBM(const struct pam * const inpamP,
+               const struct pam * const outpamP,
+               int                const leftcol,
+               int                const rightcol,
+               int                const toprow,
+               int                const bottomrow) {
+
+    unsigned char * bitrow;
+    int             readOffset, writeOffset;
+    int             row;
+    unsigned int    totalWidth;
+
+    assert(leftcol <= rightcol);
+    assert(toprow <= bottomrow);
+
+    if (leftcol > 0) {
+        totalWidth = MAX(rightcol+1, inpamP->width) + 7;
+        if (totalWidth > INT_MAX)
+            /* 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 {
+        totalWidth = -leftcol + MAX(rightcol+1, inpamP->width);
+        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;
+    }
+
+    bitrow = pbm_allocrow_packed(totalWidth);
+
+    if (toprow < 0 || leftcol < 0 || rightcol >= inpamP->width){
+        makeBlackPBMRow(bitrow, totalWidth);
+        if (toprow < 0) {
+            int row;
+            for (row=0; row < 0 - toprow; ++row)
+                pbm_writepbmrow_packed(outpamP->file, bitrow,
+                                       outpamP->width, 0);
+        }
+    }
+
+    for (row = 0; row < inpamP->height; ++row){
+        if (row >= toprow && row <= bottomrow) {
+            pbm_readpbmrow_bitoffset(inpamP->file, bitrow, inpamP->width,
+                                     inpamP->format, readOffset);
+
+            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] =
+                    0xff * PBM_BLACK;
+        } else
+            pnm_readpamrow(inpamP, NULL);    /* read and discard */
+    }
+
+    if (bottomrow - (inpamP->height-1) > 0) {
+        int row;
+        makeBlackPBMRow(bitrow, outpamP->width);
+        for (row = 0; row < bottomrow - (inpamP->height-1); ++row)
+            pbm_writepbmrow_packed(outpamP->file, bitrow, outpamP->width, 0);
+    }
+    pbm_freerow_packed(bitrow);
+}
+
+
+
+static void
 cutOneImage(FILE *             const ifP,
             struct cmdlineInfo const cmdline,
             FILE *             const ofP) {
 
-    int row;
     int leftcol, rightcol, toprow, bottomrow;
     struct pam inpam;   /* Input PAM image */
     struct pam outpam;  /* Output PAM image */
-    struct rowCutter * rowCutterP;
 
     pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
     
@@ -501,22 +643,11 @@ cutOneImage(FILE *             const ifP,
                      cmdline.width, cmdline.height, 
                      &leftcol, &rightcol, &toprow, &bottomrow);
 
-    if (!cmdline.pad)
-        rejectOutOfBounds(inpam.width, inpam.height, leftcol, rightcol, 
-                          toprow, bottomrow);
-    else {
-        if (cmdline.left > cmdline.right) 
-            pm_error("You have specified a left edge (%d) that is to the right\n"
-                     "of the right edge you specified (%d)", 
-                     cmdline.left, cmdline.right);
-        
-        if (cmdline.top > cmdline.bottom) 
-            pm_error("You have specified a top edge (%d) that is below\n"
-                     "the bottom edge you specified (%d)", 
-                     cmdline.top, cmdline.bottom);
-    }
+    rejectOutOfBounds(inpam.width, inpam.height, leftcol, rightcol, 
+                      toprow, bottomrow, cmdline.pad);
+
     if (cmdline.verbose) {
-        pm_message("Image goes from Row 0, Column 0 through Row %d, Column %d",
+        pm_message("Image goes from Row 0, Column 0 through Row %u, Column %u",
                    inpam.height-1, inpam.width-1);
         pm_message("Cutting from Row %d, Column %d through Row %d Column %d",
                    toprow, leftcol, bottomrow, rightcol);
@@ -524,36 +655,15 @@ cutOneImage(FILE *             const ifP,
 
     outpam = inpam;    /* Initial value -- most fields should be same */
     outpam.file   = ofP;
-    outpam.width  = rightcol-leftcol+1;
-    outpam.height = bottomrow-toprow+1;
+    outpam.width  = rightcol - leftcol + 1;
+    outpam.height = bottomrow - toprow + 1;
 
     pnm_writepaminit(&outpam);
 
-    /* Write out top padding */
-    if (0 - toprow > 0)
-        writeBlackRows(&outpam, 0 - toprow);
-
-    createRowCutter(&inpam, &outpam, leftcol, rightcol, &rowCutterP);
-
-    /* Read input and write out rows extracted from it */
-    for (row = 0; row < inpam.height; ++row) {
-        if (row >= toprow && row <= bottomrow){
-            pnm_readpamrow(&inpam, rowCutterP->inputPointers);
-            pnm_writepamrow(&outpam, rowCutterP->outputPointers);
-        } else  /* row < toprow || row > bottomrow */
-            pnm_readpamrow(&inpam, 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.
-        */
-    }
-
-    destroyRowCutter(rowCutterP);
-    
-    /* Write out bottom padding */
-    if ((bottomrow - (inpam.height-1)) > 0)
-        writeBlackRows(&outpam, bottomrow - (inpam.height-1));
+    if (PNM_FORMAT_TYPE(outpam.format) == PBM_TYPE)
+        extractRowsPBM(&inpam, &outpam, leftcol, rightcol, toprow, bottomrow);
+    else
+        extractRowsGen(&inpam, &outpam, leftcol, rightcol, toprow, bottomrow);
 }
 
 
diff --git a/editor/pamdice.c b/editor/pamdice.c
index 062e05e3..f72420ab 100644
--- a/editor/pamdice.c
+++ b/editor/pamdice.c
@@ -11,6 +11,7 @@
 
 #include <string.h>
 
+#include "pm_c_util.h"
 #include "pam.h"
 #include "shhopt.h"
 #include "nstring.h"
@@ -23,8 +24,8 @@ 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 */
-    char * outstem; 
+    const char * inputFileName;  /* '-' if stdin */
+    const char * outstem; 
         /* null-terminated string, max MAXFILENAMELEN-10 characters */
     unsigned int sliceVertically;    /* boolean */
     unsigned int sliceHorizontally;  /* boolean */
@@ -39,8 +40,8 @@ struct cmdlineInfo {
 
 
 static void
-parseCommandLine ( int argc, char ** argv,
-                   struct cmdlineInfo * const cmdlineP ) {
+parseCommandLine(int argc, char ** argv,
+                 struct cmdlineInfo * const cmdlineP ) {
 /*----------------------------------------------------------------------------
    parse program command line described in Unix standard form by argc
    and argv.  Return the information in the options as *cmdlineP.  
@@ -63,17 +64,17 @@ parseCommandLine ( int argc, char ** argv,
 
     option_def_index = 0;   /* incremented by OPTENT3 */
     OPTENT3(0, "width",       OPT_UINT,    &cmdlineP->width,       
-            &cmdlineP->sliceVertically,       0 );
+            &cmdlineP->sliceVertically,       0);
     OPTENT3(0, "height",      OPT_UINT,    &cmdlineP->height,
-            &cmdlineP->sliceHorizontally,     0 );
+            &cmdlineP->sliceHorizontally,     0);
     OPTENT3(0, "hoverlap",    OPT_UINT,    &cmdlineP->hoverlap,
-            &hoverlapSpec,                    0 );
+            &hoverlapSpec,                    0);
     OPTENT3(0, "voverlap",    OPT_UINT,    &cmdlineP->voverlap,
-            &voverlapSpec,                    0 );
+            &voverlapSpec,                    0);
     OPTENT3(0, "outstem",     OPT_STRING,  &cmdlineP->outstem,
-            &outstemSpec,                     0 );
+            &outstemSpec,                     0);
     OPTENT3(0, "verbose",     OPT_FLAG,    NULL,
-            &cmdlineP->verbose,               0 );
+            &cmdlineP->verbose,               0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
@@ -104,13 +105,14 @@ parseCommandLine ( int argc, char ** argv,
     if (!outstemSpec)
         pm_error("You must specify the -outstem option to indicate where to "
                  "put the output images.");
+
     if (argc-1 < 1)
-        cmdlineP->inputFilespec = "-";
+        cmdlineP->inputFileName = "-";
     else if (argc-1 == 1)
-        cmdlineP->inputFilespec = argv[1];
+        cmdlineP->inputFileName = argv[1];
     else 
         pm_error("Progam takes at most 1 parameter: the file specification.  "
-                 "You specified %d", argc-1);
+                 "You specified %u", argc-1);
 }
 
 
@@ -451,7 +453,7 @@ main(int argc, char ** argv) {
     
     parseCommandLine(argc, argv, &cmdline);
         
-    ifP = pm_openr(cmdline.inputFilespec);
+    ifP = pm_openr(cmdline.inputFileName);
 
     pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
 
diff --git a/editor/pamdither.c b/editor/pamdither.c
index 5eb931a6..e084892c 100644
--- a/editor/pamdither.c
+++ b/editor/pamdither.c
@@ -8,6 +8,7 @@
   This is meant to replace Ppmdither by Christos Zoulas, 1991.
 =============================================================================*/
 
+#include "pm_c_util.h"
 #include "mallocvar.h"
 #include "shhopt.h"
 #include "pam.h"
diff --git a/editor/pamditherbw.c b/editor/pamditherbw.c
index f9e159f1..90e2abd5 100644
--- a/editor/pamditherbw.c
+++ b/editor/pamditherbw.c
@@ -13,13 +13,19 @@
 #include <assert.h>
 #include <string.h>
 
+#include "pm_c_util.h"
 #include "pam.h"
 #include "dithers.h"
 #include "mallocvar.h"
 #include "shhopt.h"
 #include "pm_gamma.h"
 
-enum halftone {QT_FS, QT_THRESH, QT_DITHER8, QT_CLUSTER, QT_HILBERT};
+enum halftone {QT_FS,
+               QT_ATKINSON,
+               QT_THRESH,
+               QT_DITHER8,
+               QT_CLUSTER,
+               QT_HILBERT};
 
 enum ditherType {DT_REGULAR, DT_CLUSTER};
 
@@ -53,7 +59,7 @@ parseCommandLine(int argc, char ** argv,
     optStruct3 opt;
 
     unsigned int option_def_index;
-    unsigned int floydOpt, hilbertOpt, thresholdOpt, dither8Opt,
+    unsigned int floydOpt, atkinsonOpt, hilbertOpt, thresholdOpt, dither8Opt,
         cluster3Opt, cluster4Opt, cluster8Opt;
     unsigned int valueSpec, clumpSpec;
 
@@ -62,6 +68,7 @@ parseCommandLine(int argc, char ** argv,
     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, "atkinson",  OPT_FLAG,  NULL, &atkinsonOpt,     0);
     OPTENT3(0, "threshold", OPT_FLAG,  NULL, &thresholdOpt, 0);
     OPTENT3(0, "hilbert",   OPT_FLAG,  NULL, &hilbertOpt,   0);
     OPTENT3(0, "dither8",   OPT_FLAG,  NULL, &dither8Opt,   0);
@@ -84,15 +91,17 @@ parseCommandLine(int argc, char ** argv,
     optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
-    if (floydOpt + thresholdOpt + hilbertOpt + dither8Opt + 
+    if (floydOpt + atkinsonOpt + thresholdOpt + hilbertOpt + dither8Opt + 
         cluster3Opt + cluster4Opt + cluster8Opt == 0)
         cmdlineP->halftone = QT_FS;
-    else if (floydOpt + thresholdOpt + dither8Opt + 
+    else if (floydOpt + atkinsonOpt + thresholdOpt + dither8Opt + 
         cluster3Opt + cluster4Opt + cluster8Opt > 1)
-        pm_error("No cannot specify more than one halftoning type");
+        pm_error("Cannot specify more than one halftoning type");
     else {
         if (floydOpt)
             cmdlineP->halftone = QT_FS;
+        else if (atkinsonOpt)
+            cmdlineP->halftone = QT_ATKINSON;
         else if (thresholdOpt)
             cmdlineP->halftone = QT_THRESH;
         else if (hilbertOpt) {
@@ -390,10 +399,22 @@ struct converter {
 
 struct fsState {
     float * thiserr;
+        /* thiserr[N] is the power from previous pixels to include in 
+           future column N of the current row.
+        */
     float * nexterr;
+        /* nexterr[N] is the power from previous pixels to include in 
+           future column N of the next row.
+        */
     bool fs_forward;
-    samplen threshval;
-        /* The power value we consider to be half white */
+        /* We're going forward (left to right) through the current row */
+    samplen white;
+        /* The power value we consider to be white (normally 1.0).
+           Constant. */
+    samplen halfWhite;
+        /* The power value we consider to be half white (always half of
+           'white'; carried separately to save computation)
+        */
 };
 
 
@@ -423,37 +444,39 @@ fsConvertRow(struct converter * const converterP,
     }
 
     do {
-        samplen sum;
+        samplen const thisPixelPower =
+            MIN(pm_ungamma709(grayrow[col][0]), stateP->white);
+        samplen accum;
+
+        accum = thisPixelPower + thiserr[col + 1];
 
-        sum = MIN(2*stateP->threshval, pm_ungamma709(grayrow[col][0])) +
-            thiserr[col + 1];
-        if (sum >= stateP->threshval) {
-            /* We've accumulated enough light to justify a white output
-               pixel.
+        if (accum >= stateP->halfWhite) {
+            /* We've accumulated enough light (power) to justify a
+               white output pixel.
             */
             bitrow[col][0] = PAM_BW_WHITE;
-            /* Remove from sum the power of the white output pixel */
-            sum -= 2*stateP->threshval;
+            /* Remove from sum the power of this white output pixel */
+            accum -= stateP->white;
         } else
             bitrow[col][0] = PAM_BLACK;
-        
-        /* Forward the power from current input pixel and the power
-           forwarded from previous input pixels to the current pixel,
-           to future output pixels, but subtract out any power we put
-           into the current output pixel.  
+
+        /* Forward to future output pixels the power from current
+           input pixel and the power forwarded from previous input
+           pixels to the current pixel, less any power we put into the
+           current output pixel.
         */
         if (stateP->fs_forward) {
-            thiserr[col + 2] += (sum * 7) / 16;
-            nexterr[col    ] += (sum * 3) / 16;
-            nexterr[col + 1] += (sum * 5) / 16;
-            nexterr[col + 2] += (sum    ) / 16;
+            thiserr[col + 2] += (accum * 7) / 16;
+            nexterr[col    ] += (accum * 3) / 16;
+            nexterr[col + 1] += (accum * 5) / 16;
+            nexterr[col + 2] += (accum * 1) / 16;
             
             ++col;
         } else {
-            thiserr[col    ] += (sum * 7) / 16;
-            nexterr[col + 2] += (sum * 3) / 16;
-            nexterr[col + 1] += (sum * 5) / 16;
-            nexterr[col    ] += (sum    ) / 16;
+            thiserr[col    ] += (accum * 7) / 16;
+            nexterr[col + 2] += (accum * 3) / 16;
+            nexterr[col + 1] += (accum * 5) / 16;
+            nexterr[col    ] += (accum * 1) / 16;
             
             --col;
         }
@@ -468,7 +491,13 @@ fsConvertRow(struct converter * const converterP,
 
 static void
 fsDestroy(struct converter * const converterP) {
-    free(converterP->stateP);
+
+    struct fsState * const stateP = converterP->stateP;
+
+    free(stateP->thiserr);
+    free(stateP->nexterr);
+
+    free(stateP);
 }
 
 
@@ -489,7 +518,7 @@ createFsConverter(struct pam * const graypamP,
     /* Initialize Floyd-Steinberg error vectors. */
     MALLOCARRAY_NOFAIL(stateP->thiserr, graypamP->width + 2);
     MALLOCARRAY_NOFAIL(stateP->nexterr, graypamP->width + 2);
-    srand((int)(time(NULL) ^ getpid()));
+    srand(pm_randseed());
 
     {
         /* (random errors in [-1/8 .. 1/8]) */
@@ -498,7 +527,8 @@ createFsConverter(struct pam * const graypamP,
             stateP->thiserr[col] = ((float)rand()/RAND_MAX - 0.5) / 4;
     }
 
-    stateP->threshval  = threshFraction;
+    stateP->halfWhite = threshFraction;
+    stateP->white = 2 * threshFraction;
 
     stateP->fs_forward = TRUE;
 
@@ -509,6 +539,150 @@ createFsConverter(struct pam * const graypamP,
 
 
 
+struct atkinsonState {
+    float * error[3];
+        /* error[R][C] is the power from previous pixels to include
+           in column C of the Rth row down from the current row
+           (0th row is the current row).
+
+           No error propagates down more than two rows.
+
+           For R == 0, C is a column we haven't done yet.
+        */
+    samplen white;
+        /* The power value we consider to be white (normally 1.0).
+           Constant. */
+    samplen halfWhite;
+        /* The power value we consider to be half white (always half of
+           'white'; carried separately to save computation)
+        */
+};
+
+
+static void
+moveAtkinsonErrorWindowDown(struct converter * const converterP) {
+                            
+    struct atkinsonState * const stateP = converterP->stateP;
+
+    float * const oldError0 = stateP->error[0];
+
+    unsigned int relRow;
+    unsigned int col;
+
+    for (relRow = 0; relRow < 2; ++relRow)
+        stateP->error[relRow] = stateP->error[relRow+1];
+
+    for (col = 0; col < converterP->cols + 2; ++col)
+        oldError0[col] = 0.0;
+
+    stateP->error[2] = oldError0;
+}
+
+
+
+static void
+atkinsonConvertRow(struct converter * const converterP,
+                   unsigned int       const row,
+                   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;
+
+    unsigned int col;
+
+    for (col = 0; col < converterP->cols; ++col) {
+        samplen accum;
+
+        accum = pm_ungamma709(grayrow[col][0]) + error[0][col + 1];
+        if (accum >= stateP->halfWhite) {
+            /* We've accumulated enough light (power) to justify a
+               white output pixel.
+            */
+            bitrow[col][0] = PAM_BW_WHITE;
+            /* Remove from accum the power of this white output pixel */
+            accum -= stateP->white;
+        } else
+            bitrow[col][0] = PAM_BLACK;
+        
+        /* Forward to future output pixels 3/4 of the power from current
+           input pixel and the power forwarded from previous input
+           pixels to the current pixel, less any power we put into the
+           current output pixel.
+        */
+        error[0][col+1] += accum/8;
+        error[0][col+2] += accum/8;
+        if (col > 0)
+            error[1][col-1] += accum/8;
+        error[1][col  ] += accum/8;
+        error[1][col+1] += accum/8;
+        error[2][col  ] += accum/8;
+    }
+    
+    moveAtkinsonErrorWindowDown(converterP);
+}
+
+
+
+static void
+atkinsonDestroy(struct converter * const converterP) {
+
+    struct atkinsonState * const stateP = converterP->stateP;
+
+    unsigned int relRow;
+
+    for (relRow = 0; relRow < 3; ++relRow)
+        free(stateP->error[relRow]);
+
+    free(stateP);
+}
+
+
+
+static struct converter
+createAtkinsonConverter(struct pam * const graypamP,
+                        float        const threshFraction) {
+
+    struct atkinsonState * stateP;
+    struct converter converter;
+    unsigned int relRow;
+    
+    converter.cols       = graypamP->width;
+    converter.convertRow = &atkinsonConvertRow;
+    converter.destroy    = &atkinsonDestroy;
+
+    MALLOCVAR_NOFAIL(stateP);
+
+    for (relRow = 0; relRow < 3; ++relRow)
+        MALLOCARRAY_NOFAIL(stateP->error[relRow], graypamP->width + 2);
+
+    srand(pm_randseed());
+
+    {
+        /* (random errors in [-1/8 .. 1/8]) */
+        unsigned int col;
+        for (col = 0; col < graypamP->width + 2; ++col) {
+            stateP->error[0][col] = ((float)rand()/RAND_MAX - 0.5) / 4;
+            stateP->error[1][col] = 0.0;
+            stateP->error[2][col] = 0.0;
+        }
+    }
+
+    stateP->halfWhite = threshFraction;
+    stateP->white = 2 * threshFraction;
+
+    converter.stateP = stateP;
+
+    return converter;
+}
+
+
+
 struct threshState {
     samplen threshval;
 };
@@ -705,6 +879,9 @@ main(int argc, char *argv[]) {
         case QT_FS:
             converter = createFsConverter(&graypam, cmdline.threshval);
             break;
+        case QT_ATKINSON:
+            converter = createAtkinsonConverter(&graypam, cmdline.threshval);
+            break;
         case QT_THRESH:
             converter = createThreshConverter(&graypam, cmdline.threshval);
             break;
diff --git a/editor/pamedge.c b/editor/pamedge.c
index e73c9d17..ac94e2ae 100644
--- a/editor/pamedge.c
+++ b/editor/pamedge.c
@@ -188,7 +188,7 @@ main(int argc, char *argv[]) {
     pnm_writepaminit(&outpam);
 
     /* First row is black: */
-    writeBlackRow(&outpam      );
+    writeBlackRow(&outpam);
 
     writeMiddleRows(&inpam, &outpam);
 
diff --git a/editor/pamenlarge.c b/editor/pamenlarge.c
index 15b91b4f..b3039424 100644
--- a/editor/pamenlarge.c
+++ b/editor/pamenlarge.c
@@ -5,8 +5,10 @@
   author.
 =============================================================================*/
 
-#include "pam.h"
-#include "mallocvar.h"
+#include "netpbm/mallocvar.h"
+#include "netpbm/pm_c_util.h"
+#include "netpbm/pam.h"
+#include "netpbm/pbm.h"
 
 struct cmdlineInfo {
     /* All the information the user supplied in the command line,
@@ -19,7 +21,8 @@ struct cmdlineInfo {
 
 
 static void
-parseCommandLine(int argc, char ** const argv,
+parseCommandLine(int                  const argc,
+                 const char **        const argv,
                  struct cmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec array we return is stored in the storage that
@@ -69,45 +72,231 @@ makeOutputRowMap(tuple **     const outTupleRowP,
 
 
 
-int
-main(int    argc, 
-     char * argv[]) {
+static void
+validateComputableDimensions(unsigned int const width,
+                             unsigned int const height,
+                             unsigned int const scaleFactor) {
+/*----------------------------------------------------------------------------
+   Make sure that multiplication for output image width and height do not
+   overflow.
+   See validateComputetableSize() in libpam.c
+   and pbm_readpbminitrest() in libpbm2.c
+-----------------------------------------------------------------------------*/
+    unsigned int const maxWidthHeight = INT_MAX - 2;
+    unsigned int const maxScaleFactor = maxWidthHeight / MAX(height, width);
 
-    struct cmdlineInfo cmdline;
-    FILE * ifP;
-    struct pam inpam;
-    struct pam outpam; 
-    tuple * tuplerow;
-    tuple * newtuplerow;
-    int row;
+    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);
+}
 
-    pnm_init(&argc, argv);
 
-    parseCommandLine(argc, argv, &cmdline);
 
-    ifP = pm_openr(cmdline.inputFilespec);
- 
-    pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
+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, 
+        0xC0, 0xC3, 0xCC, 0xCF, 0xF0, 0xF3, 0xFC, 0xFF };
+
+    static unsigned char const trp1[8] = { 
+        0x00, 0x03, 0x1C, 0x1F, 0xE0, 0xE3, 0xFC, 0xFF };
+        
+    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] = { 
+        0x00, 0x07, 0x38, 0x3F, 0xC0, 0xC7, 0xF8, 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 };
+
+    unsigned int colChar;
+
+    switch (scaleFactor) {
+    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];
+            /* 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] & 0x07];
+        }
+        break;  
+    case 5:
+        for (colChar = 0; colChar < inColChars; ++colChar) { 
+            outrow[colChar*5]  = pair[ (inrow[colChar] >>6) & 0x03 ] >> 5; 
+            outrow[colChar*5+1]= quin2[(inrow[colChar] >>4) & 0x07 ]; 
+            outrow[colChar*5+2]= pair[ (inrow[colChar] >>3) & 0x03 ] >> 4; 
+            outrow[colChar*5+3]= quin4[(inrow[colChar] >>1) & 0x07 ];
+            outrow[colChar*5+4]= pair[ inrow[colChar] & 0x03 ] >>3; 
+        }
+        break;
+    case 4: default:
+        /*  Unlike the above cases, we iterate through outrow.  The color
+            composition of each outrow byte is computed by consulting
+            a single bit or two consecutive bits in inrow. 
+            Color changes never happen twice in a single outrow byte.
+        */
+        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;
+        }
+    }
+}
+
+
+
+static void
+enlargePbm(struct pam * const inpamP,
+           unsigned int const scaleFactor,
+           FILE *       const ofP) {
+
+    unsigned char * inrow;
+    unsigned char * outrow;
+
+    unsigned int row;
+
+    unsigned int const outcols = inpamP->width * scaleFactor;
+    unsigned int const outrows = inpamP->height * scaleFactor;
+    unsigned int const inColChars  = pbm_packed_bytes(inpamP->width);
+    unsigned int const outColChars = pbm_packed_bytes(outcols);
+
+    inrow  = pbm_allocrow_packed(inpamP->width);
+    
+    if (scaleFactor == 1)
+        outrow = inrow;
+    else  
+        outrow = pbm_allocrow_packed(outcols + 32);
+            /* The 32 (=4 bytes) is to allow writes beyond outrow data
+               end when scaleFactor is 2, 3, 5.  (max 4 bytes when
+               scaleFactor is 5)
+            */
 
-    outpam = inpam; 
-    outpam.file   = stdout;
-    outpam.width  = inpam.width * cmdline.scaleFactor;
-    outpam.height = inpam.height * cmdline.scaleFactor; 
+    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 (inpamP->width % 8 > 0) {  /* clean final partial byte */ 
+            inrow[inColChars-1] >>= 8 - inpamP->width % 8;
+            inrow[inColChars-1] <<= 8 - inpamP->width % 8;
+        }
+
+        enlargePbmRowHorizontally(inpamP, inrow, inColChars, outColChars,
+                                  scaleFactor, outrow);
+
+        for (i = 0; i < scaleFactor; ++i)  
+            pbm_writepbmrow_packed(ofP, outrow, outcols, 0);
+    }
+    
+    if (outrow != inrow)
+        pbm_freerow(outrow);
+
+    pbm_freerow(inrow);
+}
+
+
+
+static void
+enlargeGeneral(struct pam * const inpamP,
+               unsigned int const scaleFactor,
+               FILE *       const ofP) {
+/*----------------------------------------------------------------------------
+   Enlarge the input image described by *pamP.
+
+   Assume the dimensions won't cause an arithmetic overflow.
+
+   This works on all kinds of images, but is slower than enlargePbm on
+   PBM.
+-----------------------------------------------------------------------------*/
+    struct pam outpam; 
+    tuple * tuplerow;
+    tuple * newtuplerow;
+    unsigned int row;
+
+    outpam = *inpamP; 
+    outpam.file   = ofP;
+    outpam.width  = inpamP->width  * scaleFactor;
+    outpam.height = inpamP->height * scaleFactor; 
 
     pnm_writepaminit(&outpam);
 
-    tuplerow = pnm_allocpamrow(&inpam);
+    tuplerow = pnm_allocpamrow(inpamP);
 
-    makeOutputRowMap(&newtuplerow, &outpam, &inpam, tuplerow);
+    makeOutputRowMap(&newtuplerow, &outpam, inpamP, tuplerow);
 
-    for (row = 0; row < inpam.height; ++row) {
-        pnm_readpamrow(&inpam, tuplerow);
-        pnm_writepamrowmult(&outpam, newtuplerow, cmdline.scaleFactor);
+    for (row = 0; row < inpamP->height; ++row) {
+        pnm_readpamrow(inpamP, tuplerow);
+        pnm_writepamrowmult(&outpam, newtuplerow, scaleFactor);
     }
 
     free(newtuplerow);
 
     pnm_freepamrow(tuplerow);
+}
+
+
+
+int
+main(int           argc, 
+     const char ** argv) {
+
+    struct cmdlineInfo cmdline;
+    FILE * ifP;
+    struct pam inpam;
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFilespec);
+ 
+    pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
+    
+    validateComputableDimensions(inpam.width, inpam.height,
+                                 cmdline.scaleFactor); 
+    
+    if (PNM_FORMAT_TYPE(inpam.format) == PBM_TYPE)
+        enlargePbm(&inpam, cmdline.scaleFactor, stdout);
+    else
+        enlargeGeneral(&inpam, cmdline.scaleFactor, stdout);
 
     pm_close(ifP);
     pm_close(stdout);
diff --git a/editor/pamenlarge.test b/editor/pamenlarge.test
index a2221d4d..7584af01 100644
--- a/editor/pamenlarge.test
+++ b/editor/pamenlarge.test
@@ -1,6 +1,6 @@
 echo Test 1.  Should print 3424505894 913236
 ./pamenlarge 3 ../testimg.ppm | cksum
-echo Test 2.  Should print 2940246561 304422
+echo Test 2.  Should print 4152147096 304422
 ppmtopgm ../testimg.ppm | ./pamenlarge 3 | cksum
 echo Test 3.  Should print 3342398172 297
 ./pamenlarge 3 ../testgrid.pbm | cksum
diff --git a/editor/pamflip.c b/editor/pamflip.c
index 59b60b56..1c479e54 100644
--- a/editor/pamflip.c
+++ b/editor/pamflip.c
@@ -11,90 +11,57 @@
 */
 
 /*
-   transformGen() is the general transformation function.
+   transformNonPbmChunk() is the general transformation function.
+   It can tranform anything, albeit slowly and expensively.
    
    The following are enhancements for specific cases:
    
-     transformRowByRowPbm()
+     transformRowByRowPbm():
+       PBM image with left-right or null transformation
      transformRowsBottomTopPbm()
+       PBM image with bottom-top transformation
      transformRowByRowNonPbm()
+       non-PBM image with left-right or null transformation
+       (also works for PBM, but we don't use it)
      transformRowsBottomTopNonPbm()
+       non-PBM image with bottom-top transformation
+       (also works for PBM, but we don't use it)
      transformPbm()
-   
-   Although we use transformGen() only when none of the enhancement
-   functions apply, it is capable of handling all cases.  (Only that it
-   is slow, and uses more memory.)  In the same manner, transformPbm() is
-   capable of handling all pbm transformations and transformRowByRowNonPbm()
-   transformRowsBottomTomNonPbm() are capable of handling pbm.
-
-
-   There is some fancy virtual memory management in transformGen() to avoid
-   page thrashing when you flip a large image in a columns-for-rows
-   way (e.g. -transpose).
-   
-   The page thrashing we're trying to avoid could happen because the
-   output of the transformation is stored in an array of tuples in
-   virtual memory.  A tuple array is stored in column-first order,
-   meaning that all the columns of particular row are contiguous, the
-   next row is next to that, etc.  If you fill up that array by
-   filling in Column 0 sequentially in every row from top to bottom,
-   you will touch a lot of different virtual memory pages, and every
-   one has to be paged in as you touch it.
-
-   If the number of virtual memory pages you touch exceeds the amount
-   of real memory the process can get, then by the time you hit the bottom
-   of the tuple array, the pages that hold the top are already paged out.
-   So if you go back and do Column 1 from top to bottom, you will again
-   touch lots of pages and have to page in every one of them.  Do this 
-   for 100 columns, and you might page in every page in the array 100 times
-   each, putting a few bytes in the page each time.
-
-   That is very expensive.  Instead, you'd like to keep the same pages in
-   real memory as long as possible and fill them up as much as you can 
-   before paging them out and working on a new set of pages.  You can do
-   that by doing Column 0 from top to say Row 10, then Column 1 from top
-   to Row 10, etc. all the way across the image.  Assuming 10 rows fits
-   in real memory, you will keep the virtual memory for the first 10 rows
-   of the tuple array in real memory until you've filled them in completely.
-   Now you go back and do Column 0 from Row 11 to Row 20, then Column 1
-   from Row 11 to Row 20, and so on.
-
-   So why are we even trying to fill in column by column instead of just
-   filling in row by row?  Because we're reading an input image row by row
-   and transforming it in such a way that a row of the input becomes
-   a column of the output.  In order to fill in a whole row of the output,
-   we'd have to read a whole column of the input, and then we have the same
-   page thrashing problem in the input.
-
-   So the optimal procedure is to do N output rows in each pass, where
-   N is the largest number of rows we can fit in real memory.  In each
-   pass, we read certain columns of every row of the input and output
-   every column of certain rows of the output.  The output area for
-   the rows in the pass gets paged in once during the pass and then
-   never again.  Note that some pages of every row of the input get
-   paged in once in each pass too.  As each input page is referenced
-   only in one burst, input pages do not compete with output pages for
-   real memory -- the working set is the output pages, which get referenced
-   cyclically.
-
-   This all worked when we used the pnm xel format, but now that we
-   use the pam tuple format, there's an extra memory reference that
-   could be causing trouble.  Because tuples have varying depth, a pam
-   row is an array of pointers to the tuples.  To access a tuple, we
-   access the tuple pointer, then the tuple.  We could probably do better,
-   because the samples are normally in the memory immediately following
-   the tuple pointers, so we could compute where a tuple's samples live
-   without actually loading the tuple address from memory.  I.e. the 
-   address of the tuple for Column 5 of Row 9 of a 3-deep 100-wide
-   image is (void*)tuples[9] + 100 * sizeof(tuple*) + 5*(3*sizeof(sample)).
+       PBM image with column-for-row transformation
+       (also works for all other transformations, but we don't use it)
+     transformNonPbmWhole()
+       non-PBM column-for-row transformation
+       (also works for PBM, but we don't use it)
+
+   Except we don't use any of the above if we can't fit the entire image
+   into the amount of memory the user gave us to work with.  In that
+   case, we fall back to transformNonPbmChunk().
+
+   Note that while virtual memory can be limited (e.g. sometimes the
+   machine's 32 bit address space itself is a limit), real memory is
+   also a concern.  With a row-for-column transformation
+   (e.g. -transpose), if we can't fit the entire image into _real_
+   memory, we will page thrash within an inch our lives.  So we count
+   on the user to tell us how much real memory we should expect to
+   get -- his -memsize option is the lesser of the available virtual
+   and real memory.
+
+   Before Netpbm 10.42 (March 2008), we had a different method for
+   dealing with memory shortage.  We didn't worry about virtual memory
+   at all, and always kept the whole target image in memory.  We did
+   not use temporary files.  But to avoid page thrashing in a
+   column-for-row transformation, we transformed in column stripes of
+   the source image, reading the input image through multiple times.
 */
 
 #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 <limits.h>
 #include <string.h>
 
+#include "pm_c_util.h"
 #include "pam.h"
 #include "shhopt.h"
 #include "mallocvar.h"
@@ -103,22 +70,6 @@
 
 enum xformType {LEFTRIGHT, TOPBOTTOM, TRANSPOSE};
 
-struct cmdlineInfo {
-    /* All the information the user supplied in the command line,
-       in a form easy for the program to use.
-    */
-    const char *inputFilespec;  /* Filespec of input file */
-    unsigned int xformCount;
-        /* Number of transforms in the 'xformType' array */
-    enum xformType xformList[10];
-        /* Array of transforms to be applied, in order */
-    unsigned int availableMemory;
-    unsigned int pageSize;
-    unsigned int verbose;
-};
-
-
-
 static void
 parseXformOpt(const char *     const xformOpt,
               unsigned int  *  const xformCountP,
@@ -164,6 +115,175 @@ parseXformOpt(const char *     const xformOpt,
 
 
 
+/* See transformPoint() for an explanation of the transform matrix types.
+   The difference between the two types is that 'xformCore' is particular
+   to the source image dimensions and can be used to do the transformation,
+   while 'xformCore' is independent of the source image and just
+   tells what kind of transformation.
+*/
+
+struct xformCore {
+    /* a b
+       c d
+    */
+    int a;  /* -1, 0, or 1 */
+    int b;  /* -1, 0, or 1 */
+    int c;  /* -1, 0, or 1 */
+    int d;  /* -1, 0, or 1 */
+};
+
+
+
+struct xformMatrix {
+    /* a b 0
+       c d 0
+       e f 1
+    */
+    int a;  /* -1, 0, or 1 */
+    int b;  /* -1, 0, or 1 */
+    int c;  /* -1, 0, or 1 */
+    int d;  /* -1, 0, or 1 */
+    int e;  /* 0 or maximum column number in target image */
+    int f;  /* 0 or maximum row number in target image */
+};
+
+
+
+static void
+leftright(struct xformCore * const xformP) {
+    xformP->a = - xformP->a;
+    xformP->c = - xformP->c;
+}
+
+
+
+static void
+topbottom(struct xformCore * const xformP) {
+    xformP->b = - xformP->b;
+    xformP->d = - xformP->d;
+}
+
+
+
+static void
+swap(int * const xP, int * const yP) {
+
+    int const t = *xP;
+
+    *xP = *yP;
+    *yP = t;
+}
+
+
+
+static void
+transpose(struct xformCore * const xformP) {
+    swap(&xformP->a, &xformP->b);
+    swap(&xformP->c, &xformP->d);
+}
+
+
+
+static void
+computeXformCore(unsigned int       const xformCount,
+                 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: 
+            leftright(xformP);
+            break;
+        case TOPBOTTOM:
+            topbottom(xformP);
+            break;
+        case TRANSPOSE:
+            transpose(xformP);
+            break;
+        }
+    }
+}
+
+
+
+static void
+xformDimensions(struct xformCore const xform,
+                unsigned int     const inCols,
+                unsigned int     const inRows,
+                unsigned int *   const outColsP,
+                unsigned int *   const outRowsP) {
+/*----------------------------------------------------------------------------
+   Compute the output dimensions from the input dimensions given a
+   matrix that describes a transformation.
+
+   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);
+}
+
+
+
+static void
+computeXformMatrix(struct xformMatrix * const xformP, 
+                   unsigned int         const sourceCols,
+                   unsigned int         const sourceRows,
+                   struct xformCore     const xformCore) {
+
+    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->e = colMax < 0 ? -colMax : 0;
+    xformP->f = rowMax < 0 ? -rowMax : 0;
+}
+
+
+
+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 */
+    struct xformCore xform;
+        /* Handy mathematical representation of all of transform options */
+    size_t availableMemory;
+    unsigned int pageSize;
+    unsigned int verbose;
+};
+
+
+
+static void
+interpretMemorySize(unsigned int const memsizeSpec,
+                    unsigned int const memsizeOpt,
+                    size_t *     const availableMemoryP) {
+
+    size_t const sizeMax = (size_t)(-1);
+    unsigned int const Meg = 1024 * 1024;
+
+    if (memsizeSpec) {
+        if (memsizeOpt > sizeMax / Meg)
+            pm_error("-memsize value too large: %u MiB.  Maximum this program "
+                     "can handle is %u MiB", 
+                     memsizeOpt, sizeMax / Meg);
+        *availableMemoryP = memsizeOpt * Meg;
+    } else
+        *availableMemoryP = sizeMax;
+}
+
+
+
 static void
 parseCommandLine(int argc, char ** const argv,
                  struct cmdlineInfo * const cmdlineP) {
@@ -171,7 +291,7 @@ parseCommandLine(int argc, char ** const argv,
    Note that the file spec array we return is stored in the storage that
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
-    optEntry *option_def = malloc(100*sizeof(optEntry));
+    optEntry * option_def;
         /* Instructions to OptParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
@@ -181,7 +301,13 @@ parseCommandLine(int argc, char ** const argv,
     unsigned int lr, tb, xy, r90, r270, r180, null;
     unsigned int memsizeSpec, pagesizeSpec, xformSpec;
     unsigned int memsizeOpt;
-    const char *xformOpt;
+    const char * xformOpt;
+    unsigned int xformCount;
+        /* Number of transforms in the 'xformType' array */
+    enum xformType xformList[10];
+        /* Array of transforms to be applied, in order */
+
+    MALLOCARRAY(option_def, 100);
 
     option_def_index = 0;   /* incremented by OPTENTRY */
     OPTENT3(0, "lr",        OPT_FLAG,    NULL, &lr,      0);
@@ -218,43 +344,38 @@ parseCommandLine(int argc, char ** const argv,
         pm_error("You may specify only one type of flip.");
     if (lr + tb + xy + r90 + r180 + r270 + null == 1) {
         if (lr) {
-            cmdlineP->xformCount = 1;
-            cmdlineP->xformList[0] = LEFTRIGHT;
+            xformCount = 1;
+            xformList[0] = LEFTRIGHT;
         } else if (tb) {
-            cmdlineP->xformCount = 1;
-            cmdlineP->xformList[0] = TOPBOTTOM;
+            xformCount = 1;
+            xformList[0] = TOPBOTTOM;
         } else if (xy) {
-            cmdlineP->xformCount = 1;
-            cmdlineP->xformList[0] = TRANSPOSE;
+            xformCount = 1;
+            xformList[0] = TRANSPOSE;
         } else if (r90) {
-            cmdlineP->xformCount = 2;
-            cmdlineP->xformList[0] = TRANSPOSE;
-            cmdlineP->xformList[1] = TOPBOTTOM;
+            xformCount = 2;
+            xformList[0] = TRANSPOSE;
+            xformList[1] = TOPBOTTOM;
         } else if (r180) {
-            cmdlineP->xformCount = 2;
-            cmdlineP->xformList[0] = LEFTRIGHT;
-            cmdlineP->xformList[1] = TOPBOTTOM;
+            xformCount = 2;
+            xformList[0] = LEFTRIGHT;
+            xformList[1] = TOPBOTTOM;
         } else if (r270) {
-            cmdlineP->xformCount = 2;
-            cmdlineP->xformList[0] = TRANSPOSE;
-            cmdlineP->xformList[1] = LEFTRIGHT;
+            xformCount = 2;
+            xformList[0] = TRANSPOSE;
+            xformList[1] = LEFTRIGHT;
         } else if (null) {
-            cmdlineP->xformCount = 0;
+            xformCount = 0;
         }
     } else if (xformSpec) 
-        parseXformOpt(xformOpt, &cmdlineP->xformCount, cmdlineP->xformList);
+        parseXformOpt(xformOpt, &xformCount, xformList);
     else
         pm_error("You must specify an option such as -topbottom to indicate "
                  "what kind of flip you want.");
 
-    if (memsizeSpec) {
-        if (memsizeOpt > UINT_MAX / 1024 / 1024)
-            pm_error("-memsize value too large: %u MiB.  Maximum this program "
-                     "can handle is %u MiB", 
-                     memsizeOpt, UINT_MAX / 1024 / 1024);
-        cmdlineP->availableMemory = memsizeOpt * 1024 *1024;
-    } else
-        cmdlineP->availableMemory = UINT_MAX;
+    computeXformCore(xformCount, xformList, &cmdlineP->xform);
+    
+    interpretMemorySize(memsizeSpec, memsizeOpt, &cmdlineP->availableMemory);
 
     if (!pagesizeSpec)
         cmdlineP->pageSize = 4*1024;         
@@ -270,83 +391,6 @@ parseCommandLine(int argc, char ** const argv,
 
 
 
-struct xformMatrix {
-    int a;
-    int b;
-    int c;
-    int d;
-    int e;
-    int f;
-};
-
-
-
-static void
-leftright(struct xformMatrix * const xformP) {
-    xformP->a = - xformP->a;
-    xformP->c = - xformP->c;
-    xformP->e = - xformP->e + 1;
-}
-
-
-
-static void
-topbottom(struct xformMatrix * const xformP) {
-    xformP->b = - xformP->b;
-    xformP->d = - xformP->d;
-    xformP->f = - xformP->f + 1;
-}
-
-
-
-static void
-swap(int * const xP, int * const yP) {
-
-    int const t = *xP;
-
-    *xP = *yP;
-    *yP = t;
-}
-
-
-
-static void
-transpose(struct xformMatrix * const xformP) {
-    swap(&xformP->a, &xformP->b);
-    swap(&xformP->c, &xformP->d);
-    swap(&xformP->e, &xformP->f);
-}
-
-
-
-static void
-computeXformMatrix(struct xformMatrix * const xformP, 
-                   unsigned int         const xformCount,
-                   enum xformType             xformType[]) {
-
-    struct xformMatrix const nullTransform = {1, 0, 0, 1, 0, 0};
-
-    unsigned int i;
-
-    *xformP = nullTransform;   /* initial value */
-
-    for (i = 0; i < xformCount; ++i) {
-        switch (xformType[i]) {
-        case LEFTRIGHT: 
-            leftright(xformP);
-            break;
-        case TOPBOTTOM:
-            topbottom(xformP);
-            break;
-        case TRANSPOSE:
-            transpose(xformP);
-            break;
-        }
-    }
-}
-
-
-
 static void
 bitOrderReverse(unsigned char * const bitrow, 
                 unsigned int    const cols) {
@@ -400,8 +444,9 @@ transformRowByRowPbm(struct pam * const inpamP,
                      struct pam * const outpamP,
                      bool         const reverse) {
 /*----------------------------------------------------------------------------
-  Transform a PBM image either by flipping it left for right, or just leaving
-  it alone, as indicated by 'reverse'.
+  Transform a PBM raster either by flipping it left for right, or just
+  leaving it alone, as indicated by 'reverse'.  Read the raster from
+  *inpamP; write the transformed raster to *outpamP.
 
   Process the image one row at a time and use fast packed PBM bit
   reverse algorithm (where required).
@@ -411,8 +456,6 @@ transformRowByRowPbm(struct pam * const inpamP,
 
     bitrow = pbm_allocrow_packed(outpamP->width); 
 
-    pbm_writepbminit(outpamP->file, outpamP->width, outpamP->height, 0);
-
     for (row = 0; row < inpamP->height; ++row) {
         pbm_readpbmrow_packed(inpamP->file,  bitrow, inpamP->width,
                               inpamP->format);
@@ -432,7 +475,8 @@ transformRowByRowNonPbm(struct pam * const inpamP,
                         struct pam * const outpamP,
                         bool         const reverse) {
 /*----------------------------------------------------------------------------
-  Flip an image left for right or leave it alone.
+  Flip a raster left for right or leave it alone.  Read the raster
+  from *inpamP; write the transformed raster to *outpamP.
 
   Process one row at a time.
 
@@ -466,8 +510,6 @@ transformRowByRowNonPbm(struct pam * const inpamP,
         scratchTuplerow = NULL;
         newtuplerow = tuplerow;
     }
-    pnm_writepaminit(outpamP);
-
     for (row = 0; row < inpamP->height ; ++row) {
         pnm_readpamrow(inpamP, tuplerow);
         pnm_writepamrow(outpamP, newtuplerow);
@@ -481,32 +523,12 @@ transformRowByRowNonPbm(struct pam * const inpamP,
 
 
 static void
-transformRowByRow(struct pam * const inpamP,
-                  struct pam * const outpamP,
-                  bool         const reverse,
-                  bool         const verbose) {
-
-    if (verbose)
-        pm_message("Transforming row by row, top to bottom");
-
-    switch (PNM_FORMAT_TYPE(inpamP->format)) {
-    case PBM_TYPE:
-        transformRowByRowPbm(inpamP, outpamP, reverse);
-        break;
-    default:
-        transformRowByRowNonPbm(inpamP, outpamP, reverse);
-        break;
-    }
-} 
-
-
-
-static void
 transformRowsBottomTopPbm(struct pam * const inpamP,
                           struct pam * const outpamP,
                           bool         const reverse) { 
 /*----------------------------------------------------------------------------
-  Flip a PBM image top for bottom.  Iff 'reverse', also flip it left for right.
+  Flip a PBM raster top for bottom; read the raster from *inpamP;
+  write the flipped raster to *outpamP.
 
   Read complete image into memory in packed PBM format; Use fast
   packed PBM bit reverse algorithm (where required).
@@ -514,7 +536,7 @@ transformRowsBottomTopPbm(struct pam * const inpamP,
     unsigned int const rows=inpamP->height;
 
     unsigned char ** bitrow;
-    int row;
+    unsigned int row;
         
     bitrow = pbm_allocarray_packed(outpamP->width, outpamP->height);
         
@@ -522,8 +544,6 @@ transformRowsBottomTopPbm(struct pam * const inpamP,
         pbm_readpbmrow_packed(inpamP->file, bitrow[row], 
                               inpamP->width, inpamP->format);
 
-    pbm_writepbminit(outpamP->file, outpamP->width, outpamP->height, 0);
-
     for (row = 0; row < rows; ++row) {
         if (reverse) 
             bitOrderReverse(bitrow[rows-row-1], inpamP->width);
@@ -538,82 +558,43 @@ transformRowsBottomTopPbm(struct pam * const inpamP,
 
 static void
 transformRowsBottomTopNonPbm(struct pam * const inpamP, 
-                             struct pam * const outpamP,
-                             bool         const reverse) {
+                             struct pam * const outpamP) {
 /*----------------------------------------------------------------------------
-  Read complete image into memory as a tuple array.
+  Do a simple vertical flip.  Read the raster from *inpamP; write the
+  flipped raster to *outpamP.
+
+  We do this faster than the more general subroutines because we just
+  move the row pointers.
+
+  But we must fit the entire image into memory at once.
 
-  This can do any transformation except a column-for-row transformation,
-  on any type of image, but is slower and uses more memory than the 
-  PBM-only transformRowsBottomTopPbm().
+  This works on PBM, but the PBM-specific subroutine is faster.
 -----------------------------------------------------------------------------*/
-    tuple** tuplerows;
-    tuple * scratchTuplerow;
-        /* This is not a full tuple row -- just an array of pointers to
-           the tuples in 'tuplerows'.
-        */
+    tuple ** tuplerows;
     unsigned int row;
 
-    if (reverse)
-        MALLOCARRAY_NOFAIL(scratchTuplerow, inpamP->width);
-    else
-        scratchTuplerow = NULL;
-  
     tuplerows = pnm_allocpamarray(outpamP);
 
     for (row = 0; row < inpamP->height ; ++row)
         pnm_readpamrow(inpamP, tuplerows[row]);
 
-    pnm_writepaminit(outpamP);
-
-    for (row = 0; row < inpamP->height ; ++row) {
-        tuple * newtuplerow;
+    for (row = 0; row < inpamP->height; ++row) {
         tuple * const tuplerow = tuplerows[inpamP->height - row - 1];
-        if (reverse) {
-            unsigned int col;
-            newtuplerow = scratchTuplerow;
-            for (col = 0; col < inpamP->width; ++col) 
-                newtuplerow[col] = tuplerow[inpamP->width - col - 1];
-        } else
-            newtuplerow = tuplerow;
-        pnm_writepamrow(outpamP, newtuplerow);
-    }
 
-    if (scratchTuplerow)
-        free(scratchTuplerow);
+        pnm_writepamrow(outpamP, tuplerow);
+    }
     
     pnm_freepamarray(tuplerows, outpamP);
 }
 
 
 
-static void
-transformRowsBottomTop(struct pam * const inpamP,
-                       struct pam * const outpamP,
-                       bool         const reverse,
-                       bool         const verbose) {
-
-    if (PNM_FORMAT_TYPE(inpamP->format) == PBM_TYPE) {
-        if (verbose)
-            pm_message("Transforming PBM row by row, bottom to top");
-        transformRowsBottomTopPbm(inpamP, outpamP, reverse);
-    } else {
-        if (verbose)
-            pm_message("Transforming non-PBM row by row, bottom to top");
-        transformRowsBottomTopNonPbm(inpamP, outpamP, reverse);
-    }
-}
-
-
-
 static void __inline__
 transformPoint(int                const col, 
-               int                const newcols,
                int                const row, 
-               int                const newrows, 
                struct xformMatrix const xform, 
-               int *              const newcolP, 
-               int *              const newrowP ) {
+               unsigned int *     const newcolP, 
+               unsigned int *     const newrowP ) {
 /*----------------------------------------------------------------------------
    Compute the location in the output of a pixel that is at row 'row',
    column 'col' in the input.  Assume the output image is 'newcols' by
@@ -626,17 +607,39 @@ transformPoint(int                const col,
                  [ a b 0 ]
        [ x y 1 ] [ c d 0 ] = [ x2 y2 1 ]
                  [ e f 1 ]
+
+       Where (x, y) is the source pixel location and (x2, y2) is the
+       target pixel location.
+
+       Note that this is more of a logical computation than an arithmetic
+       one: a, b, c, and d are -1, 0, or 1.  e is the maximum column number
+       in the target image or 0; f is the maximum row number or 0.
     */
-    *newcolP = xform.a * col + xform.c * row + xform.e * (newcols - 1);
-    *newrowP = xform.b * col + xform.d * row + xform.f * (newrows - 1);
+    *newcolP = xform.a * col + xform.c * row + xform.e * 1;
+    *newrowP = xform.b * col + xform.d * row + xform.f * 1;
+
+    assert(*newcolP >= 0);
+    assert(*newrowP >= 0);
+}
+
+
+
+static void
+writeRaster(struct pam *    const pamP,
+            tuple * const * const tuples) {
+
+    unsigned int outRow;
+
+    for (outRow = 0; outRow < pamP->height; ++ outRow)
+        pnm_writepamrow(pamP, tuples[outRow]);
 }
 
 
 
 static void
-transformPbm(struct pam *       const inpamP,
-             struct pam *       const outpamP,
-             struct xformMatrix const xform) { 
+transformPbmGen(struct pam *     const inpamP,
+                struct pam *     const outpamP,
+                struct xformCore const xformCore) { 
 /*----------------------------------------------------------------------------
    This is the same as transformGen, except that it uses less 
    memory, since the PBM buffer format uses one bit per pixel instead
@@ -646,37 +649,37 @@ transformPbm(struct pam *       const inpamP,
    memory than the more restricted transformRowByRowPbm() and
    transformRowsBottomTopPbm().
 -----------------------------------------------------------------------------*/
-    bit* bitrow;
-    bit** newbits;
-    int row;
+    bit * bitrow;
+    bit ** newbits;
+    struct xformMatrix xform;
+    unsigned int row;
             
+    computeXformMatrix(&xform, inpamP->width, inpamP->height, xformCore);
+    
     bitrow = pbm_allocrow(inpamP->width);
     newbits = pbm_allocarray(pbm_packed_bytes(outpamP->width), 
                              outpamP->height);
             
     /* Initialize entire array to zeroes.  One bits will be or'ed in later */
     for (row = 0; row < outpamP->height; ++row) {
-        int col;
+        unsigned int col;
         for (col = 0; col < pbm_packed_bytes(outpamP->width); ++col) 
              newbits[row][col] = 0; 
     }
     
     for (row = 0; row < inpamP->height; ++row) {
-        int col;
+        unsigned int col;
         pbm_readpbmrow(inpamP->file, bitrow, inpamP->width, inpamP->format);
         for (col = 0; col < inpamP->width; ++col) {
-            int newcol, newrow;
-            transformPoint(col, outpamP->width, row, outpamP->height, xform,
-                           &newcol, &newrow);
+            unsigned int newcol, newrow;
+            transformPoint(col, row, xform, &newcol, &newrow);
             newbits[newrow][newcol/8] |= bitrow[col] << (7 - newcol % 8);
                 /* Use of "|=" patterned after pbm_readpbmrow_packed. */
          }
     }
     
-    pbm_writepbminit(outpamP->file, outpamP->width, outpamP->height, 0);
     for (row = 0; row < outpamP->height; ++row)
-        pbm_writepbmrow_packed(outpamP->file, newbits[row], outpamP->width, 
-                               0);
+        pbm_writepbmrow_packed(outpamP->file, newbits[row], outpamP->width, 0);
     
     pbm_freearray(newbits, outpamP->height);
     pbm_freerow(bitrow);
@@ -684,131 +687,53 @@ transformPbm(struct pam *       const inpamP,
 
 
 
-static unsigned int 
-optimalSegmentSize(struct xformMatrix  const xform, 
-                   struct pam *        const pamP,
-                   unsigned int        const availableMemory,
-                   unsigned int        const pageSize) {
-/*----------------------------------------------------------------------------
-   Compute the maximum number of columns that can be transformed, one row
-   at a time, without causing page thrashing.
-
-   See comments at the top of this file for an explanation of the kind
-   of page thrashing using segments avoids.
-
-   'availableMemory' is the amount of real memory in bytes that this
-   process should expect to be able to use.
-
-   'pageSize' is the size of a page in bytes.  A page means the unit that
-   is paged in or out.
-   
-   'pamP' describes the storage required to represent a row of the
-   output array.
------------------------------------------------------------------------------*/
-    unsigned int segmentSize;
-
-    if (xform.b == 0)
-        segmentSize = pamP->width;
-    else {
-        unsigned int const otherNeeds = 200*1024;
-            /* A wild guess at how much real memory is needed by the program
-               for purposes other than the output tuple array.
-            */
-        if (otherNeeds > availableMemory)
-            segmentSize = pamP->width;  /* Can't prevent thrashing */
-        else {
-            unsigned int const availablePages = 
-                (availableMemory - otherNeeds) / pageSize;
-            if (availablePages <= 1)
-                segmentSize = pamP->width; /* Can't prevent thrashing */
-            else {
-                unsigned int const bytesPerRow = 
-                    pamP->width * pamP->depth * pamP->bytes_per_sample;
-                unsigned int rowsPerPage = 
-                    MAX(1, (pageSize + (pageSize/2)) / bytesPerRow);
-                    /* This is how many consecutive rows we can touch
-                       on average while staying within the same page.  
-                    */
-                segmentSize = availablePages * rowsPerPage;
-            }
-        }
-    }    
-    return segmentSize;
-}
-
-
-
 static void
-transformNonPbm(struct pam *       const inpamP,
-                struct pam *       const outpamP,
-                struct xformMatrix const xform,
-                unsigned int       const segmentSize,
-                bool               const verbose) {
+transformNonPbmWhole(struct pam *     const inpamP,
+                     struct pam *     const outpamP,
+                     struct xformCore const xformCore,
+                     bool             const verbose) {
 /*----------------------------------------------------------------------------
   Do the transform using "pam" library functions, as opposed to "pbm"
   ones.
 
+  Read the raster from *inpamP; write the transformed raster to *outpamP.
+
   Assume input file is positioned to the raster (just after the
   header).
 
-  'segmentSize' is the number of columns we are to process in each
-  pass.  We do each segment going from left to right.  For each
-  segment, we do each row, going from top to bottom.  For each row of
-  the segment, we do each column, going from left to right.  (The
-  reason Caller wants it done by segments is to improve virtual memory
-  reference locality.  See comments at the top of this file).
-
-  if 'segmentSize' is less than the whole image, ifP must be a seekable
-  file.
-
   This can do any transformation, but is slower and uses more memory
-  than the PBM-only transformPbm().
+  than some of the alternatives which are usable only for certain
+  cases.  But we do require a certain amount of virtual and real memory;
+  transformNonPbmChunk() is even more general in that it doesn't.
 -----------------------------------------------------------------------------*/
-    pm_filepos imagepos;
-        /* The input file position of the raster.  But defined only if
-           segment size is less than whole image.
-        */
-    tuple* tuplerow;
-    tuple** newtuples;
-    unsigned int startCol;
+    tuple * tuplerow;
+    tuple ** newtuples;
+    struct xformMatrix xform;
+    unsigned int row;
 
+    computeXformMatrix(&xform, inpamP->width, inpamP->height, xformCore);
+    
     tuplerow = pnm_allocpamrow(inpamP);
     newtuples = pnm_allocpamarray(outpamP);
     
-    if (segmentSize < inpamP->width)
-        pm_tell2(inpamP->file, &imagepos, sizeof(imagepos));
+    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;
 
-    for (startCol = 0; startCol < inpamP->width; startCol += segmentSize) {
-        /* Do one set of columns which is small enough not to cause
-           page thrashing.
-        */
-        unsigned int const endCol = MIN(inpamP->width, startCol + segmentSize);
-        unsigned int row;
+            transformPoint(col, row, xform, &newcol, &newrow);
 
-        if (verbose)
-            pm_message("Transforming Columns %u up to %u", 
-                       startCol, endCol);
-
-        if (startCol > 0)
-            /* Go back to read from Row 0 again */
-            pm_seek2(inpamP->file, &imagepos, sizeof(imagepos));
-
-        for (row = 0; row < inpamP->height; ++row) {
-            unsigned int col;
-            pnm_readpamrow(inpamP, tuplerow);
-
-            for (col = startCol; col < endCol; ++col) {
-                int newcol, newrow;
-                transformPoint(col, outpamP->width, row, outpamP->height, 
-                               xform,
-                               &newcol, &newrow);
-                pnm_assigntuple(inpamP, newtuples[newrow][newcol],
-                                tuplerow[col]);
-            }
+            assert(newcol < outpamP->width);
+            assert(newrow < outpamP->height);
+
+            pnm_assigntuple(inpamP, newtuples[newrow][newcol],
+                            tuplerow[col]);
         }
     }
     
-    pnm_writepam(outpamP, newtuples);
+    writeRaster(outpamP, newtuples);
     
     pnm_freepamarray(newtuples, outpamP);
     pnm_freepamrow(tuplerow);
@@ -816,40 +741,396 @@ transformNonPbm(struct pam *       const inpamP,
 
 
 
+typedef struct {
+/*----------------------------------------------------------------------------
+   A description of the quilt of cells that make up the output image.
+-----------------------------------------------------------------------------*/
+    unsigned int ranks, files;
+        /* Dimensions of the output image in cells */
+    struct pam ** pam;
+        /* pam[y][x] is the pam structure for the cell at rank y, file x
+           in the output.
+        */
+
+    /* Each of the cells corresponds to a temporary file; the 'file'
+       member of its pam structure identifies it.  But it is not a normal
+       Netpbm file; it contains only the raster portion.  The program
+       writes the raster to the file, starting at offset zero, then rewinds
+       and reads it out later.  The header is unnecessary because the pam
+       structure is still available at readback time.
+    */
+} outputMap;
+
+
+
 static void
-transformGen(struct pam *       const inpamP,
-             struct pam *       const outpamP,
-             struct xformMatrix const xform,
-             unsigned int       const availableMemory,
-             unsigned int       const pageSize,
-             bool               const verbose) {
+initOutCell(struct pam *     const outCellPamP,
+            unsigned int     const inCellWidth,
+            unsigned int     const inCellHeight,
+            struct pam *     const inpamP,
+            struct xformCore const xformCore) {
+
+    unsigned int outCellFiles, outCellRanks;
+
+    *outCellPamP = *inpamP;  /* initial value */
+
+    outCellPamP->len  = PAM_STRUCT_SIZE(tuple_type);
+
+    outCellPamP->file = pm_tmpfile();
+
+    xformDimensions(xformCore, inCellWidth, inCellHeight,
+                    &outCellFiles, &outCellRanks);
+
+    outCellPamP->width = outCellFiles;
+    outCellPamP->height = outCellRanks;
+}
+
+
+
+static outputMap *
+createOutputMap(struct pam *       const inpamP,
+                unsigned int       const maxRows,
+                struct xformMatrix const cellXform,
+                struct xformCore   const xformCore) {
+
+    unsigned int const inCellFiles = 1;
+    unsigned int const inCellRanks = (inpamP->height + maxRows - 1) / maxRows;
+
+    outputMap * mapP;
+    unsigned int outCellRank;
+    unsigned int inCellRank;
+
+    MALLOCVAR_NOFAIL(mapP);
+
+    xformDimensions(xformCore, inCellFiles, inCellRanks,
+                    &mapP->files, &mapP->ranks);
+
+    MALLOCARRAY(mapP->pam, mapP->ranks);
+    if (mapP->pam == NULL)
+        pm_error("Could not allocate a cell array for %u ranks of cells.",
+                 mapP->ranks);
+
+    for (outCellRank = 0; outCellRank < mapP->ranks; ++outCellRank) {
+
+        MALLOCARRAY(mapP->pam[outCellRank], mapP->files);
+
+        if (mapP->pam[outCellRank] == NULL)
+            pm_error("Failed to allocate rank %u of the cell array, "
+                     "%u cells wide", outCellRank, mapP->files);
+    }
+
+    for (inCellRank = 0; inCellRank < inCellRanks; ++inCellRank) {
+        unsigned int const inCellFile = 0;
+        unsigned int const inCellStartRow = inCellRank * maxRows;
+        unsigned int const inCellRows =
+            MIN(inpamP->height - inCellStartRow, maxRows);
+
+        unsigned int outCellFile, outCellRank;
+        transformPoint(inCellFile, inCellRank, cellXform,
+                       &outCellFile, &outCellRank);
+    
+        initOutCell(&mapP->pam[outCellRank][outCellFile],
+                    inpamP->width, inCellRows,
+                    inpamP, xformCore);
+    }
+    return mapP;
+}
+                
+
+
+static void
+destroyOutputMap(outputMap * const mapP) {
+
+    unsigned int outCellRank;
+
+    for (outCellRank = 0; outCellRank < mapP->ranks; ++outCellRank)
+        free(mapP->pam[outCellRank]);
+
+    free(mapP->pam);
+
+    free(mapP);
+}
+
+
+
+static void
+rewindCellFiles(outputMap * const outputMapP) {
+
+    unsigned int outCellRank;
+
+    for (outCellRank = 0; outCellRank < outputMapP->ranks; ++outCellRank) {
+        unsigned int outCellFile;
+        for (outCellFile = 0; outCellFile < outputMapP->files; ++outCellFile)
+            pm_seek(outputMapP->pam[outCellRank][outCellFile].file, 0);
+    }
+}
+
+
+
+static void
+closeCellFiles(outputMap * const outputMapP) {
+
+    unsigned int outCellRank;
+
+    for (outCellRank = 0; outCellRank < outputMapP->ranks; ++outCellRank) {
+        unsigned int outCellFile;
+        for (outCellFile = 0; outCellFile < outputMapP->files; ++outCellFile)
+            pm_close(outputMapP->pam[outCellRank][outCellFile].file);
+    }
+}
+
+
+
+static void
+transformCell(struct pam *     const inpamP,
+              struct pam *     const outpamP,
+              struct xformCore const xformCore) {
+
+    struct xformMatrix xform;
+    tuple * inTupleRow;
+    tuple ** outTuples;
+    unsigned int inRow;
+
+    computeXformMatrix(&xform, inpamP->width, inpamP->height, xformCore);
+
+    inTupleRow = pnm_allocpamrow(inpamP);
+
+    outTuples = pnm_allocpamarray(outpamP);
+
+    for (inRow = 0; inRow < inpamP->height; ++inRow) {
+        unsigned int inCol;
+
+        pnm_readpamrow(inpamP, inTupleRow);
+        
+        for (inCol = 0; inCol < inpamP->width; ++inCol) {
+            unsigned int outCol, outRow;
+
+            transformPoint(inCol, inRow, xform, &outCol, &outRow);
+
+            assert(outCol < outpamP->width);
+            assert(outRow < outpamP->height);
+
+            pnm_assigntuple(inpamP,
+                            outTuples[outRow][outCol], inTupleRow[inCol]);
+        }
+    }
+    pnm_freepamrow(inTupleRow);
+
+    writeRaster(outpamP, outTuples);
+
+    pnm_freepamarray(outTuples, outpamP);
+}
+
+
+
+static void
+stitchCellsToOutput(outputMap *  const outputMapP,
+                    struct pam * const outpamP) {
+
+    unsigned int outRank;
+    tuple * tupleRow;
+
+    tupleRow = pnm_allocpamrow(outpamP);
+
+    for (outRank = 0; outRank < outputMapP->ranks; ++outRank) {
+        unsigned int const cellRows = outputMapP->pam[outRank][0].height;
+            /* Number of rows in every cell in this rank */
+
+        unsigned int cellRow;
+
+        for (cellRow = 0; cellRow < cellRows; ++cellRow) {
+            unsigned int outFile;
+            unsigned int outCol;
+
+            outCol = 0;
+
+            for (outFile = 0; outFile < outputMapP->files; ++outFile) {
+                struct pam * const outCellPamP = 
+                    &outputMapP->pam[outRank][outFile];
+
+                assert(outCellPamP->height == cellRows);
+
+                assert(outCol < outpamP->width);
+                pnm_readpamrow(outCellPamP, &tupleRow[outCol]);
+
+                outCol += outCellPamP->width;
+            }
+
+            assert(outCol = outpamP->width);
+
+            pnm_writepamrow(outpamP, tupleRow);
+        }
+    }
+    pnm_freepamrow(tupleRow);
+}
+
+
+
+static void
+transformNonPbmChunk(struct pam *     const inpamP,
+                     struct pam *     const outpamP,
+                     struct xformCore const xformCore,
+                     unsigned int     const maxRows,
+                     bool             const verbose) {
 /*----------------------------------------------------------------------------
-  Produce the transformed output on Standard Output.
+  Same as transformNonPbmChunk(), except we read 'maxRows' rows of the
+  input into memory at a time, storing intermediate results in temporary
+  files, to limit our use of virtual and real memory.
 
   Assume input file is positioned to the raster (just after the
   header).
 
-  This can transform any image in any way, but is slower and uses more
-  memory than the more restricted transformRowByRow() and
-  transformRowsBottomTop().
+  We call the strip of 'maxRows' rows that we read a source cell.  We
+  transform that cell according to 'xformCore' to create to create a
+  target cell.  We store all the target cells in temporary files.
+  We consider the target cells to be arranged in a column matrix the
+  same as the source cells within the source image; we transform that
+  matrix according to 'xformCore'.  The resulting cell matrix is the
+  target image.
 -----------------------------------------------------------------------------*/
-    unsigned int const segmentSize = 
-        optimalSegmentSize(xform, outpamP, availableMemory, pageSize);
+    unsigned int const inCellFiles = 1;
+    unsigned int const inCellRanks = (inpamP->height + maxRows - 1) / maxRows;
+
+    struct xformMatrix cellXform;
+    unsigned int inCellRank;
+    outputMap * outputMapP;
+
+    if (verbose)
+        pm_message("Transforming in %u chunks, using temp files", inCellRanks);
+
+    computeXformMatrix(&cellXform, inCellFiles, inCellRanks, xformCore);
+
+    outputMapP = createOutputMap(inpamP, maxRows, cellXform, xformCore);
+
+    for (inCellRank = 0; inCellRank < inCellRanks; ++inCellRank) {
+        unsigned int const inCellFile = 0;
+        unsigned int const inCellStartRow = inCellRank * maxRows;
+        unsigned int const inCellRows =
+            MIN(inpamP->height - inCellStartRow, maxRows);
+
+        struct pam inCellPam;
+        struct pam * outCellPamP;
+        unsigned int outCellFile, outCellRank;
+
+        transformPoint(inCellFile, inCellRank, cellXform,
+                       &outCellFile, &outCellRank);
     
-    switch (PNM_FORMAT_TYPE(inpamP->format)) {
-    case PBM_TYPE: 
-        transformPbm(inpamP, outpamP, xform);
-        break;
-    default:
-        if (segmentSize < outpamP->width) {
-            if (verbose && xform.b !=0)
-                pm_message("Transforming %u columns of %u total at a time", 
-                           segmentSize, outpamP->width);
-            else
-                pm_message("Transforming entire image at once");
-        }
-        transformNonPbm(inpamP, outpamP, xform, segmentSize, verbose);
-        break;
+        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);
+    }    
+
+    rewindCellFiles(outputMapP);
+
+    stitchCellsToOutput(outputMapP, outpamP);
+
+    closeCellFiles(outputMapP);
+
+    destroyOutputMap(outputMapP);
+}
+
+
+
+static unsigned int
+maxRowsThatFit(struct pam * const pamP,
+               size_t       const availableMemory) {
+
+    unsigned long const otherNeeds = 100*1024;
+        /* A wild guess at how much memory (from the same pool as the
+           input rows) is needed for things other than the input rows
+        */
+    unsigned long const availForRows =
+        availableMemory > otherNeeds ? availableMemory - otherNeeds : 0;
+    unsigned int const bytesPerTuple =
+        pamP->depth * sizeof(sample) + sizeof(tuple *);
+    unsigned int const bytesPerRow =
+        pamP->width * bytesPerTuple + sizeof(tuple **);
+
+    unsigned long const maxRows = availForRows / bytesPerRow;
+
+    if (maxRows < 1)
+        pm_error("You haven't allowed enough memory to fit even one row "
+                 "of the source image in memory.  The minimum chunk size "
+                 "is one row; we need at least %lu bytes.",
+                 otherNeeds + bytesPerRow);
+
+    return (unsigned int)MIN(maxRows, UINT_MAX);
+}
+
+
+
+static void
+transformPbm(struct pam *     const inpamP,
+             struct pam *     const outpamP,
+             struct xformCore const xform,
+             bool             const verbose) {
+
+    if (xform.b == 0 && xform.c == 0) {
+        /* Rows of input map to rows of target; no column-for-row */
+        if (xform.d == 1)
+            /* Row N of the output is based only on Row N of the
+               input, so we can transform row by row and avoid
+               in-memory buffering altogether.
+            */
+            transformRowByRowPbm(inpamP, outpamP, xform.a == -1);
+        else
+            /* Row N of the output is based only on Row ~N of the
+               input.  We need all the rows in memory, but have to pass
+               through them only twice, so there is no page thrashing concern.
+            */
+            transformRowsBottomTopPbm(inpamP, outpamP, xform.a == -1);
+    } else
+        /* This is a column-for-row type of transformation, which requires
+           complex traversal of an in-memory image.
+        */
+        transformPbmGen(inpamP, outpamP, xform);
+}
+
+
+
+static void
+transformNonPbm(struct pam *     const inpamP,
+                struct pam *     const outpamP,
+                struct xformCore const xform,
+                unsigned int     const availableMemory,
+                bool             const verbose) {
+
+    if (xform.b == 0 && xform.c == 0 && xform.d == 1) {
+        /* Row N of the output is based only on Row N of the
+           input, so we can transform row by row and avoid
+           in-memory buffering altogether.
+        */
+        if (verbose)
+            pm_message("Transforming row by row");
+        transformRowByRowNonPbm(inpamP, outpamP, xform.a == -1);
+    } else {
+        unsigned int const maxRows = maxRowsThatFit(inpamP, availableMemory);
+        if (maxRows >= inpamP->height) {
+            /* We can fit the whole image in memory at once and avoid
+               temporary files.
+            */
+            if (xform.b == 0 && xform.c == 0 && xform.d == -1 &&
+                xform.a == 1) {
+                /* This is just a vertical flip;  We can move whole rows
+                   instead of individual pixels and save time.
+                */
+                if (verbose)
+                    pm_message("Transforming whole rows, all in memory");
+
+                transformRowsBottomTopNonPbm(inpamP, outpamP);
+            } else {
+                if (verbose)
+                    pm_message("Transforming whole image at once, "
+                               "pixel by pixel");
+                transformNonPbmWhole(inpamP, outpamP, xform, verbose);
+            }
+        } else
+            /* No optimizations possible */
+            transformNonPbmChunk(inpamP, outpamP, xform, maxRows, verbose);
     }
 }
 
@@ -860,8 +1141,8 @@ main(int argc, char * argv[]) {
     struct cmdlineInfo cmdline;
     struct pam inpam;
     struct pam outpam;
-    FILE* ifP;
-    struct xformMatrix xform;
+    unsigned int cols, rows;
+    FILE * ifP;
 
     pnm_init(&argc, argv);
 
@@ -874,35 +1155,22 @@ main(int argc, char * argv[]) {
     
     pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
 
-    computeXformMatrix(&xform, cmdline.xformCount, cmdline.xformList);
-
     outpam = inpam;  /* initial value */
 
     outpam.file = stdout;
-    outpam.width  = abs(xform.a) * inpam.width + abs(xform.c) * inpam.height;
-    outpam.height = abs(xform.b) * inpam.width + abs(xform.d) * inpam.height;
-    
-    if (xform.b == 0 && xform.d == 1 && xform.f == 0)
-        /* In this case Row N of the output is based only on Row N of
-           the input, so we can transform row by row and avoid
-           in-memory buffering altogether.  
-        */
-        transformRowByRow(&inpam, &outpam, xform.a == -1, cmdline.verbose);
-    else if (xform.b == 0 && xform.c == 0) 
-        /* In this case, Row N of the output is based only on Row ~N of the
-           input.  We need all the rows in memory, but have to pass
-           through them only twice, so there is no page thrashing concern.
-        */
-        transformRowsBottomTop(&inpam, &outpam, xform.a == -1, 
-                               cmdline.verbose);
-    else
-        /* This is a colum-for-row type of transformation, which requires
-           complex traversal of an in-memory image.
-        */
-        transformGen(&inpam, &outpam, xform,
-                     cmdline.availableMemory, cmdline.pageSize, 
-                     cmdline.verbose);
+    xformDimensions(cmdline.xform, inpam.width, inpam.height, &cols, &rows);
+    outpam.width = cols; outpam.height = rows;
+
+    pnm_writepaminit(&outpam);
 
+    switch (PNM_FORMAT_TYPE(inpam.format)) {
+    case PBM_TYPE:
+        transformPbm(&inpam, &outpam, cmdline.xform, cmdline.verbose);
+        break;
+    default:
+        transformNonPbm(&inpam, &outpam, cmdline.xform,
+                        cmdline.availableMemory, cmdline.verbose);
+    }
     pm_close(inpam.file);
     pm_close(outpam.file);
     
diff --git a/editor/pamfunc.c b/editor/pamfunc.c
index dbb1ca70..b6e56e17 100644
--- a/editor/pamfunc.c
+++ b/editor/pamfunc.c
@@ -18,10 +18,25 @@
 
 ******************************************************************************/
 
-#include "pam.h"
+#include "pm_c_util.h"
+#include "mallocvar.h"
 #include "shhopt.h"
+#include "pam.h"
 
-enum function {FN_MULTIPLY, FN_DIVIDE, FN_ADD, FN_SUBTRACT, FN_MIN, FN_MAX};
+enum function {
+    FN_MULTIPLY,
+    FN_DIVIDE,
+    FN_ADD,
+    FN_SUBTRACT,
+    FN_MIN,
+    FN_MAX,
+    FN_AND,
+    FN_OR,
+    FN_XOR,
+    FN_NOT,
+    FN_SHIFTLEFT,
+    FN_SHIFTRIGHT
+};
 
 /* Note that when the user specifies a minimum, that means he's requesting
    a "max" function.
@@ -40,11 +55,30 @@ struct cmdlineInfo {
         int subtractor;
         unsigned int max;
         unsigned int min;
+        unsigned int mask;
+        unsigned int shiftCount;
     } u;
     unsigned int verbose;
 };
 
 
+
+static unsigned int
+parseHex(const char * const hexString) {
+
+    unsigned int retval;
+    char * tail;
+
+    retval = strtol(hexString, &tail, 16);
+
+    if (*tail != '\0')
+        pm_error("Invalid hex string '%s'.  Junk: '%s'", hexString, tail);
+
+    return retval;
+}
+
+         
+
 static void
 parseCommandLine(int argc, char ** const argv,
                  struct cmdlineInfo * const cmdlineP) {
@@ -52,29 +86,46 @@ parseCommandLine(int argc, char ** const argv,
    Note that the file spec array we return is stored in the storage that
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
-    optEntry *option_def = malloc(100*sizeof(optEntry));
-        /* Instructions to OptParseOptions2 on how to parse our options.
-         */
+    optEntry * option_def;
+        /* Instructions to OptParseOptions3 on how to parse our options. */
     optStruct3 opt;
 
     unsigned int option_def_index;
 
     unsigned int multiplierSpec, divisorSpec, adderSpec, subtractorSpec;
     unsigned int maxSpec, minSpec;
+    unsigned int andmaskSpec, ormaskSpec, xormaskSpec, notSpec;
+    unsigned int shiftleftSpec, shiftrightSpec;
+
+    const char * mask;
+
+    MALLOCARRAY(option_def, 100);
 
-    option_def_index = 0;   /* incremented by OPTENTRY */
+    option_def_index = 0;   /* incremented by OPTENT3 */
     OPTENT3(0,   "multiplier", OPT_FLOAT,  &cmdlineP->u.multiplier, 
             &multiplierSpec, 0);
     OPTENT3(0,   "divisor",    OPT_FLOAT,  &cmdlineP->u.divisor,
-            &divisorSpec, 0);
+            &divisorSpec,    0);
     OPTENT3(0,   "adder",      OPT_INT,    &cmdlineP->u.adder,
-            &adderSpec, 0);
+            &adderSpec,      0);
     OPTENT3(0,   "subtractor", OPT_INT,    &cmdlineP->u.subtractor,
             &subtractorSpec, 0);
     OPTENT3(0,   "min",        OPT_UINT,   &cmdlineP->u.min,
-            &minSpec, 0);
+            &minSpec,        0);
     OPTENT3(0,   "max",        OPT_UINT,   &cmdlineP->u.max,
-            &maxSpec, 0);
+            &maxSpec,        0);
+    OPTENT3(0,   "andmask",    OPT_STRING, &mask,
+            &andmaskSpec,    0);
+    OPTENT3(0,   "ormask",     OPT_STRING, &mask,
+            &ormaskSpec,     0);
+    OPTENT3(0,   "xormask",    OPT_STRING, &mask,
+            &xormaskSpec,    0);
+    OPTENT3(0,   "not",        OPT_FLAG,   NULL,
+            &notSpec,        0);
+    OPTENT3(0,   "shiftleft",  OPT_UINT,   &cmdlineP->u.shiftCount,
+            &shiftleftSpec,  0);
+    OPTENT3(0,   "shiftright", OPT_UINT,   &cmdlineP->u.shiftCount,
+            &shiftrightSpec, 0);
     OPTENT3(0,   "verbose",    OPT_FLAG,   NULL, &cmdlineP->verbose,       0);
 
     opt.opt_table = option_def;
@@ -85,9 +136,12 @@ parseCommandLine(int argc, char ** const argv,
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (multiplierSpec + divisorSpec + adderSpec + subtractorSpec +
-        minSpec + maxSpec > 1)
+        minSpec + maxSpec + andmaskSpec + ormaskSpec + xormaskSpec + notSpec +
+        shiftleftSpec + shiftrightSpec > 1)
         pm_error("You may specify at most one of -multiplier, -divisor,"
-                 "-adder, -subtractor, -min, and -max");
+                 "-adder, -subtractor, -min, -max, "
+                 "-andmask, -ormask, -xormask, -not, "
+                 "-shiftleft, and -shiftright");
 
     if (multiplierSpec) {
         cmdlineP->function = FN_MULTIPLY;
@@ -107,9 +161,25 @@ parseCommandLine(int argc, char ** const argv,
         cmdlineP->function = FN_MAX;
     } else if (maxSpec) {
         cmdlineP->function = FN_MIN;
+    } else if (andmaskSpec) {
+        cmdlineP->function = FN_AND;
+        cmdlineP->u.mask = parseHex(mask);
+    } else if (ormaskSpec) {
+        cmdlineP->function = FN_OR;
+        cmdlineP->u.mask = parseHex(mask);
+    } else if (xormaskSpec) {
+        cmdlineP->function = FN_XOR;
+        cmdlineP->u.mask = parseHex(mask);
+    } else if (notSpec) {
+        cmdlineP->function = FN_NOT;
+    } else if (shiftleftSpec) {
+        cmdlineP->function = FN_SHIFTLEFT;
+    } else if (shiftrightSpec) {
+        cmdlineP->function = FN_SHIFTRIGHT;
     } else 
         pm_error("You must specify one of -multiplier, -divisor, "
-                 "-adder, -subtractor, -min, or -max");
+                 "-adder, -subtractor, -min, -max, "
+                 "-and, -or, -xor, -not, -shiftleft, or -shiftright");
         
     if (argc-1 > 1)
         pm_error("Too many arguments (%d).  File spec is the only argument.",
@@ -124,6 +194,70 @@ parseCommandLine(int argc, char ** const argv,
 
 
 
+static bool
+isDyadicMaskFunction(enum function const fn) {
+
+    return (fn == FN_AND || fn == FN_OR || fn == FN_XOR);
+}
+
+
+
+static bool
+isMaskFunction(enum function const fn) {
+
+    return (isDyadicMaskFunction(fn) || fn == FN_NOT);
+}
+
+
+
+static bool
+isShiftFunction(enum function const fn) {
+
+    return (fn == FN_SHIFTLEFT || fn == FN_SHIFTRIGHT);
+}
+
+
+
+static bool
+isBitstringFunction(enum function const fn) {
+
+    return isMaskFunction(fn) || isShiftFunction(fn);
+}
+
+
+
+static void
+validateFunction(struct cmdlineInfo const cmdline,
+                 const struct pam * const pamP) {
+
+    if (isBitstringFunction(cmdline.function)) {
+        if (pm_bitstomaxval(pm_maxvaltobits(pamP->maxval)) != pamP->maxval)
+            pm_error("For a bit string function, the maxval must be a full "
+                     "binary count, i.e. a power of two minus one such as "
+                     "0xff or 0x1.  You have 0x%x",
+                     (unsigned)pamP->maxval);
+
+        if (isDyadicMaskFunction(cmdline.function)) {
+            if ((cmdline.u.mask & pamP->maxval) != cmdline.u.mask)
+                pm_error("Your bit string mask 0x%x is wider than the samples "
+                         "of the image (%u bits, according to the maxval %lu",
+                         cmdline.u.mask, pm_maxvaltobits(pamP->maxval),
+                         pamP->maxval);
+        }
+
+        if (isShiftFunction(cmdline.function)) {
+            if (cmdline.u.shiftCount > pm_maxvaltobits(pamP->maxval))
+                pm_error("Your shift count (%u) is greater than the width "
+                         "of the samples of the image (%u bits, according "
+                         "to the maxval %lu)",
+                         cmdline.u.shiftCount, pm_maxvaltobits(pamP->maxval),
+                         pamP->maxval);
+        }
+    }
+}
+
+
+
 static void
 applyFunction(struct cmdlineInfo const cmdline,
               struct pam         const inpam,
@@ -168,6 +302,25 @@ applyFunction(struct cmdlineInfo const cmdline,
             case FN_MIN:
                 outSample = MIN(inSample, cmdline.u.max);
                 break;
+            case FN_AND:
+                outSample = inSample & cmdline.u.mask;
+                break;
+            case FN_OR:
+                outSample = inSample | cmdline.u.mask;
+                break;
+            case FN_XOR:
+                outSample = inSample ^ cmdline.u.mask;
+                break;
+            case FN_NOT:
+                outSample = ~inSample;
+                break;
+            case FN_SHIFTLEFT:
+                outSample =
+                    (inSample << cmdline.u.shiftCount) & outpam.maxval;
+                break;
+            case FN_SHIFTRIGHT:
+                outSample = inSample >> cmdline.u.shiftCount;
+                break;
             }
             outputRow[col][plane] = MIN(outpam.maxval, outSample);
         }
@@ -179,15 +332,15 @@ applyFunction(struct cmdlineInfo const cmdline,
 int
 main(int argc, char *argv[]) {
 
-    FILE* ifP;
-    tuple* inputRow;   /* Row from input image */
-    tuple* outputRow;  /* Row of output image */
+    FILE * ifP;
+    tuple * inputRow;   /* Row from input image */
+    tuple * outputRow;  /* Row of output image */
     int row;
     struct cmdlineInfo cmdline;
     struct pam inpam;   /* Input PAM image */
     struct pam outpam;  /* Output PAM image */
 
-    pnm_init( &argc, argv );
+    pnm_init(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
@@ -195,6 +348,8 @@ main(int argc, char *argv[]) {
 
     pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
 
+    validateFunction(cmdline, &inpam);
+
     inputRow = pnm_allocpamrow(&inpam);
 
     outpam = inpam;    /* Initial value -- most fields should be same */
@@ -216,6 +371,6 @@ main(int argc, char *argv[]) {
     pm_close(inpam.file);
     pm_close(outpam.file);
     
-    exit(0);
+    return 0;
 }
 
diff --git a/editor/pammasksharpen.c b/editor/pammasksharpen.c
index 87b928be..e61237ca 100644
--- a/editor/pammasksharpen.c
+++ b/editor/pammasksharpen.c
@@ -1,6 +1,7 @@
-#include "pam.h"
-#include "shhopt.h"
+#include "pm_c_util.h"
 #include "mallocvar.h"
+#include "shhopt.h"
+#include "pam.h"
 
 struct cmdlineInfo {
     /* All the information the user supplied in the command line,
@@ -62,7 +63,6 @@ parseCommandLine(int argc, char ** const argv,
         if (cmdlineP->threshold > 1.0)
             pm_error("-threshold greater than unity doesn't make sense.  "
                      "You specified %f", cmdlineP->threshold);
-        
     } else
         cmdlineP->threshold = 0.0;
 
diff --git a/editor/pammixinterlace.c b/editor/pammixinterlace.c
deleted file mode 100644
index 1421c7a2..00000000
--- a/editor/pammixinterlace.c
+++ /dev/null
@@ -1,173 +0,0 @@
-/******************************************************************************
-                             pammixinterlace
-*******************************************************************************
-  De-interlace an image by merging adjacent rows.
-   
-  Copyright (C) 2005 Bruce Guenter, FutureQuest, Inc.
-
-  Permission to use, copy, modify, and distribute this software and its
-  documentation for any purpose and without fee is hereby granted,
-  provided that the above copyright notice appear in all copies and that
-  both that copyright notice and this permission notice appear in
-  supporting documentation.  This software is provided "as is" without
-  express or implied warranty.
-
-******************************************************************************/
-
-#include "pam.h"
-#include "shhopt.h"
-#include "mallocvar.h"
-
-struct cmdlineInfo {
-    /* All the information the user supplied in the command line,
-       in a form easy for the program to use.
-    */
-    const char *inputFilespec;  /* Filespecs of input files */
-};
-
-
-static void
-parseCommandLine(int argc, char ** argv,
-                 struct cmdlineInfo *cmdlineP) {
-/*----------------------------------------------------------------------------
-   Note that the file spec array we return is stored in the storage that
-   was passed to us as the argv array.
------------------------------------------------------------------------------*/
-    optStruct3 opt;  /* set by OPTENT3 */
-    optEntry *option_def;
-    unsigned int option_def_index;
-
-    MALLOCARRAY_NOFAIL(option_def, 100);
-
-    option_def_index = 0;   /* incremented by OPTENT3 */
-
-    opt.opt_table = option_def;
-    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
-    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
-
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
-    /* Uses and sets argc, argv, and some of *cmdlineP and others. */
-
-    if (argc-1 < 1)
-        cmdlineP->inputFilespec = "-";
-    else if (argc-1 == 1)
-        cmdlineP->inputFilespec = argv[1];
-    else
-        pm_error("You specified too many arguments (%d).  The only "
-                 "argument is the optional input file specification.",
-                 argc-1);
-}
-
-
-
-static void
-allocateRowWindowBuffer(struct pam * const pamP,
-                        tuple **     const tuplerow) {
-
-    unsigned int row;
-
-    for (row = 0; row < 3; ++row)
-        tuplerow[row] = pnm_allocpamrow(pamP);
-}
-
-
-
-static void
-freeRowWindowBuffer(tuple ** const tuplerow) {
-
-    unsigned int row;
-
-    for (row = 0; row < 3; ++row)
-        pnm_freepamrow(tuplerow[row]);
-
-}
-
-
-
-static void
-slideWindowDown(tuple ** const tuplerow) {
-/*----------------------------------------------------------------------------
-  Slide the 3-line tuple row window tuplerow[] down one row by moving
-  pointers.
-
-  tuplerow[2] ends up an uninitialized buffer.
------------------------------------------------------------------------------*/
-    tuple * const oldrow0 = tuplerow[0];
-    tuplerow[0] = tuplerow[1];
-    tuplerow[1] = tuplerow[2];
-    tuplerow[2] = oldrow0;
-}
-
-
-
-int
-main(int argc, char *argv[]) {
-
-    FILE * ifP;
-    struct cmdlineInfo cmdline;
-    struct pam inpam;  
-    struct pam outpam;
-    tuple * tuplerow[3];
-    tuple * outputrow;
-    
-    pnm_init( &argc, argv );
-
-    parseCommandLine(argc, argv, &cmdline);
-
-    ifP = pm_openr(cmdline.inputFilespec);
-    
-    pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
-
-    outpam = inpam;    /* Initial value -- most fields should be same */
-    outpam.file = stdout;
-
-    pnm_writepaminit(&outpam);
-
-    allocateRowWindowBuffer(&inpam, tuplerow);
-    outputrow = pnm_allocpamrow(&outpam);
-
-    if (inpam.height < 3) {
-        unsigned int row;
-        pm_message("WARNING: Image height less than 3.  No mixing done.");
-        for (row = 0; row < inpam.height; ++row) {
-            pnm_readpamrow(&inpam, tuplerow[0]);
-            pnm_writepamrow(&outpam, tuplerow[0]);
-        }
-    } else {
-        unsigned int row;
-
-        pnm_readpamrow(&inpam, tuplerow[0]);
-        pnm_readpamrow(&inpam, tuplerow[1]);
-
-        /* Pass through first row */
-        pnm_writepamrow(&outpam, tuplerow[0]);
-
-        for (row = 2; row < inpam.height; ++row) {
-            unsigned int col;
-            pnm_readpamrow(&inpam, tuplerow[2]);
-            for (col = 0; col < inpam.width; ++col) {
-                unsigned int plane;
-
-                for (plane = 0; plane < inpam.depth; ++plane) {
-                    outputrow[col][plane] =
-                        (tuplerow[0][col][plane]
-                         + tuplerow[1][col][plane] * 2
-                         + tuplerow[2][col][plane]) / 4;
-                }
-            }
-            pnm_writepamrow(&outpam, outputrow);
-            
-            slideWindowDown(tuplerow);
-        }
-
-        /* Pass through last row */
-        pnm_writepamrow(&outpam, tuplerow[1]);
-    }
-
-    freeRowWindowBuffer(tuplerow);
-    pnm_freepamrow(outputrow);
-    pm_close(inpam.file);
-    pm_close(outpam.file);
-    
-    return 0;
-}
diff --git a/editor/pamperspective.c b/editor/pamperspective.c
index a655443e..6bf8314e 100644
--- a/editor/pamperspective.c
+++ b/editor/pamperspective.c
@@ -20,12 +20,15 @@
 
 #define _BSD_SOURCE   /* Make sure strdup is int string.h */
 
+#include <assert.h>
+#include <stdlib.h>
 #include <math.h>
 #include <string.h>
 
-#include "pam.h"
-#include "shhopt.h"
+#include "pm_c_util.h"
 #include "mallocvar.h"
+#include "shhopt.h"
+#include "pam.h"
 
 typedef double number;
 
@@ -164,9 +167,9 @@ typedef struct {
          xw_ll, yw_ll, zw_ll,  xw_lr, yw_lr, zw_lr;
 
   /* Originally I planned to include the possibility to move the
-     centre of projection, that is the pixel the camera "looks at".  It
+     center of projection, that is the pixel the camera "looks at".  It
      turned out, maybe surprisingly, that this does not have any
-     effect. So now this centre is moved to (0,0).
+     effect. So now this center is moved to (0,0).
      
      Another original plan was to correct the output parameters
      depending on the lengths of the paralellograms sides or its
@@ -184,26 +187,36 @@ typedef struct {
 } world_data;
 
 
-/*
-  Internal infile buffer
-
-  This is a cyclic in random access out buffer, just large enough
-  to store all input lines that are still in use.
-*/
 
 typedef struct {
-
-  int num_rows, last_physical, last_logical;
-  tuple** rows;
-  const struct pam* inpam;
-
+/*----------------------------------------------------------------------------
+   A buffer of image input.  This holds a vertical window of the input.
+-----------------------------------------------------------------------------*/
+    unsigned int numRows;
+        /* Height of buffer window */
+    unsigned int nextImageRow;
+        /* Row number of the next image row that will go into the buffer.
+           The 'numRows' rows before (above) that are in the buffer now.
+        */
+    unsigned int nextBufferRow;
+        /* Row number in the physical buffer (index of rows[]) where
+           the next row read will go (hence where the oldest/highest
+           row in the buffer is now).
+        */
+    tuple ** rows;
+        /* The rows of the window, as a cyclic buffer */
+    const struct pam * inpamP;
+        /* The image from which we fill the buffer */
 } buffer;
 
 
 
+typedef void interpolateFn(tuple, number, number);
+
 /*
   The following are like MALLOCARRAY_NOFAIL and MALLOCVAR_NOFAIL,
-  but issue an error message instead of aborting.
+  but abort (fail) the program instead of killing the process with an
+  abort signal.
 */
 
 #define MALLOCARRAY_SAFE(handle,length) \
@@ -306,45 +319,53 @@ static int parse_enum (const char *const text,
 
 
 
-static number parse_float (char *const text)
+static number
+parseFloat(const char * const text) {
 /*----------------------------------------------------------------------------
   Parse an argument given to a float command line option.  We cannot
   just call strtod, because we want to parse fractions like "5/3"
 -----------------------------------------------------------------------------*/
-{
-  bool error;
-  char* end;
-  char* denstart;
-  number num,den;
-
-  error = FALSE;
-  num = strtod (text, &end);    /* try strtod anyway */
-  switch (*end) {
-  case 0:           /* It is a plain number */
-    break;
-  case '/':         /* It might be a fraction */
-    /* (Try to) parse the numerator */
-    *end = 0;
-    num = strtod (text, &end);
-    error = (*end) != 0;
-    if (!error) {
-      /* Undo the above change */
-      *end = '/';
-      /* (Try to) parse the denominator */
-      denstart = end+1;
-      den = strtod (denstart, &end);
-      error = (fabs(den)<eps) || ((*end) != 0);
-      if (!error)
-    num /= den;
+    bool error;
+    char * end;
+    number num;
+    char * buffer;
+    
+    buffer = strdup(text);
+    if (!buffer)
+        pm_error("Out of memory");
+
+    error = FALSE;
+    num = strtod(buffer, &end);    /* try strtod anyway */
+    switch(*end) {
+    case 0:           /* It is a plain number */
+        break;
+    case '/':         /* It might be a fraction */
+        /* (Try to) parse the numerator */
+        *end = 0;
+        num = strtod(text, &end);
+        error = (*end != '\0');
+        if (!error) {
+            char * const denStart = end + 1;
+            number denominator;
+
+            /* Undo the above change */
+            *end = '/';
+            /* (Try to) parse the denominator */
+            denominator = strtod(denStart, &end);
+            error = (fabs(denominator) < eps) || (*end != '\0');
+            if (!error)
+                num /= denominator;
+        };
+        break;
+    default:          /* It is no number format we know */
+        error = TRUE;
     };
-    break;
-  default:          /* It is no number format we know */
-    error = TRUE;
-  };
-  if (error)
-    pm_error ("Invalid number format: %s", text);
+    if (error)
+        pm_error("Invalid number format: %s", text);
 
-  return num;
+    free(buffer);
+
+    return num;
 }
 
 
@@ -373,8 +394,8 @@ static void parse_include_point(char * specification,
   if (*comma_seek == 0)
     pm_error ("Invalid format for --include point: '%s'", specification);
   *comma_seek = 0;      /* separate the two parts for parsing purposes */
-  new_point->xi = (number) parse_float(specification);
-  new_point->yi = (number) parse_float(comma_seek+1);
+  new_point->xi = (number) parseFloat(specification);
+  new_point->yi = (number) parseFloat(comma_seek+1);
   *comma_seek = ',';
 }
 
@@ -422,9 +443,12 @@ static void parse_include_points(const char * const include_opt,
 }
 
 
-static void parse_command_line (int argc, char* argv[], option *const options)
-{
-  char* float_text[num_float_options];
+
+static void
+parseCommandLine(int argc, const char * argv[],
+                 option *  const options) {
+
+  const char* float_text[num_float_options];
   unsigned int float_spec[num_float_options];
   char* enum_text[num_enum_options];
   unsigned int enum_spec[num_enum_options];
@@ -437,6 +461,8 @@ static void parse_command_line (int argc, char* argv[], option *const options)
   unsigned int option_def_index;
   optEntry* option_def;
 
+  set_command_line_defaults(options);
+
   /* Let shhopt try its best */
 
   option_def_index = 0;
@@ -460,7 +486,7 @@ static void parse_command_line (int argc, char* argv[], option *const options)
   opt.opt_table = option_def;
   opt.short_allowed = FALSE;
   opt.allowNegNum = TRUE;
-  optParseOptions3 (&argc, argv, opt, sizeof(opt), 0);
+  optParseOptions3 (&argc, (char **)argv, opt, sizeof(opt), 0);
 
   /* The non-option arguments are optionally all eight coordinates
      and optionally the input filename
@@ -493,7 +519,7 @@ static void parse_command_line (int argc, char* argv[], option *const options)
 
   for (i=0; i<num_float_options; i++)
     if (float_spec[i])
-      options->floats[i] = parse_float (float_text[i]);
+      options->floats[i] = parseFloat (float_text[i]);
 
   /* Parse enum options -- shhopt retrieved them as strings */
 
@@ -611,7 +637,7 @@ static bool solve_3_linear_equations (number* x1, number* x2, number* x3,
     a11*x1 + a12*x2 + a13*x3 = b1
     a21*x1 + a22*x2 + a23*x3 = b2
     a31*x1 + a32*x2 + a33*x3 = b3
-  The return value is wether the system is solvable
+  The return value is whether the system is solvable
 ----------------------------------------------------------------------------*/
 {
   number c11,c12,d1,c21,c22,d2,e,f;
@@ -706,18 +732,18 @@ static bool solve_3_linear_equations (number* x1, number* x2, number* x3,
 static void determine_world_parallelogram (world_data *const world,
                                            const option *const options)
 /*----------------------------------------------------------------------------
-  constructs xw_ul,...,zw_lr from xi_ul,...,yi_lr
+  Construct xw_ul,...,zw_lr from xi_ul,...,yi_lr
      
-  Actually this is a solution of a linear equation system.
+  This is a solution of a linear equation system.
   
-  We first solve 4 variables (the 4 z-coordinates) against 4
-  equations: Each z-coordinate determines the corresponding x- and
-  y-coordinates in a linear fashion, where the coefficients are taken
-  from the image coordinates. This corresponds to the fact that a
-  point of an image determines a line in the world.
+  We first solve 4 equations for 4 variables (the 4 z-coordinates):
+  Each z-coordinate determines the corresponding x- and y-coordinates
+  in a linear fashion, where the coefficients are taken from the image
+  coordinates.  This corresponds to the fact that a point of an image
+  determines a line in the world.
   
   3 equations state that the 4 points form a parallelogram.  The 4th
-  equation is for normalization and states, that the centre of the
+  equation is for normalization and states that the center of the
   parallelogram has a z-coordinate of 1.
 -----------------------------------------------------------------------------*/
 {
@@ -885,9 +911,11 @@ static void determine_world_parallelogram (world_data *const world,
 
 
 
-static int diff (int const a, int const b)
-{
-  return MAX (b-a, a-b);
+static unsigned int
+distance(unsigned int const a,
+         unsigned int const b) {
+
+    return a > b ? a - b : b - a;
 }
 
 
@@ -1024,8 +1052,8 @@ static void determine_coefficients_pixel (world_data *const world,
   Constructs ax,...,cz from xw_ul,...,zw_lr
      
   The calculations assume pixel coordinates, that is the point ul
-  corresponds to the centre of the pixel (0,0) and the point lr
-  corresponds to the centre of the pixel (width-1,height-1)
+  corresponds to the center of the pixel (0,0) and the point lr
+  corresponds to the center of the pixel (width-1,height-1)
 -----------------------------------------------------------------------------*/
 {
   number width,height;
@@ -1054,40 +1082,46 @@ static void determine_coefficients_pixel (world_data *const world,
 
 
 
-static void outpixel_to_inpixel (int const xo, int const yo, 
-                                 number* const xi, number* const yi,
-                                 const world_data *const world)
-{
-  number xof,yof,xw,yw,zw;
-
-  xof = (number) xo;
-  yof = (number) yo;
-  xw = world->ax + world->bx*xof + world->cx*yof;
-  yw = world->ay + world->by*xof + world->cy*yof;
-  zw = world->az + world->bz*xof + world->cz*yof;
-  *xi = xw/zw;
-  *yi = yw/zw;
+static void
+outpixelToInPos(int                const outCol,
+                int                const outRow, 
+                number *           const inColP,
+                number *           const inRowP,
+                const world_data * const worldP) {
+/*----------------------------------------------------------------------------
+   For a pixel of the output image at Column 'outCol', row 'outRow',
+   determine the position in the input image that corresponds to the
+   center of that pixel.
+
+   This position is not a pixel position -- it's a position in
+   continuous space, for example Row 9.2, Column 0.1.  And it isn't
+   necessarily within the input image, for example Column 600 even though
+   the input image is only 500 pixels wide, and a coordinate might even
+   be negative.
+-----------------------------------------------------------------------------*/
+    number const outColF = (number) outCol;
+    number const outRowF = (number) outRow;
+
+    number const xw = worldP->ax + worldP->bx * outColF + worldP->cx * outRowF;
+    number const yw = worldP->ay + worldP->by * outColF + worldP->cy * outRowF;
+    number const zw = worldP->az + worldP->bz * outColF + worldP->cz * outRowF;
+
+    *inColP = xw/zw;
+    *inRowP = yw/zw;
 }
 
-static int outpixel_to_iny (int xo, int yo, const world_data *const world)
-{
-  number xi,yi;
 
-  outpixel_to_inpixel (xo,yo,&xi,&yi,world);
 
-  return (int) yi;
-}
+static int
+outpixelToInRow(int                const outCol,
+                int                const outRow,
+                const world_data * const worldP) {
 
-static int clean_y (int const y,  const struct pam *const outpam)
-{
-  return MIN(MAX(0, y), outpam->height-1);
-}
+    number xi, yi;
 
-static unsigned int
-distance(unsigned int const a,
-         unsigned int const b) {
+    outpixelToInPos(outCol, outRow, &xi, &yi, worldP);
 
-    return a > b ? a - b : b - a;
+    return (int) yi;
 }
 
 
@@ -1101,6 +1135,71 @@ boundedRow(int                const unboundedRow,
 
 
 
+#if 0
+/* This is the original calculation of window height.  It's
+   mysterious, and doesn't work.  It looks like it basically wants to
+   take the greater of vertical displacement of the top edge of the
+   input quadrilateral and that of the bottom edge.  In simple
+   scenarios, that is in fact what it does, and I can see how those
+   edges might be where the most stretching takes place.  However, it
+   the calculation is obviously more complex than that.
+
+   It doesn't work because the actual image generation produces rows
+   in the middle that are derived from lines in the input quadrilateral
+   with greater slope than either the top or bottom edge.  I.e. to
+   compute one output row, it needs more rows of input than this 
+   calculation provides.
+
+   I don't know if that means the computation of the output is wrong
+   or the computation of the window height is wrong.  The code is too
+   opaque.  But just to make a viable computation, I replaced the
+   window height calculation with the brute force computation you
+   see below: it determines the vertical displacement of every line
+   of the input quadrilateral that is used to generate an output row
+   and takes the greatest of them for the window height.
+
+   - Bryan Henderson 08.07.27.
+*/
+   
+
+static unsigned int
+windowHeight(const world_data * const worldP,
+             const struct pam * const inpamP,
+             const struct pam * const outpamP,
+             const option *     const optionsP) {
+
+    unsigned int numRows;
+    int yul, yur, yll, ylr, y_min;
+
+    yul = outpixelToInRow(0, 0, worldP);
+    yur = outpixelToInRow(outpamP->width-1, 0, worldP);
+    yll = outpixelToInRow(0, outpamP->height-1, worldP);
+    ylr = outpixelToInRow(outpamP->width-1, outpamP->height-1, worldP);
+    
+    y_min = MIN(MIN(yul, yur), MIN(yll, ylr));
+    numRows = MAX(MAX(diff(yul, yur),
+                      diff(yll, ylr)),
+                  MAX(diff(boundedRow(yul, outpamP),
+                           boundedRow(y_min, outpamP)),
+                      diff(boundedRow(yur, outpamP),
+                           boundedRow(y_min, outpamP))))
+        + 2;
+    switch (optionsP->enums[3]) {  /* --interpolation */
+    case interp_nearest:
+        break;
+    case interp_linear:
+        numRows += 1;
+        break;
+    }
+    if (numRows > inpamP->height)
+        numRows = inpamP->height;
+
+    return numRows;
+}
+#endif
+
+
+
 static unsigned int
 windowHeight(const world_data * const worldP,
              const struct pam * const inpamP,
@@ -1116,9 +1215,9 @@ windowHeight(const world_data * const worldP,
         unsigned int const leftCol = 0;
         unsigned int const rghtCol = outpamP->width - 1;
         unsigned int const leftInRow =
-            boundedRow(outpixel_to_iny(leftCol, outRow, worldP), outpamP);
+            boundedRow(outpixelToInRow(leftCol, outRow, worldP), outpamP);
         unsigned int const rghtInRow =
-            boundedRow(outpixel_to_iny(rghtCol, outRow, worldP), outpamP);
+            boundedRow(outpixelToInRow(rghtCol, outRow, worldP), outpamP);
         
         unsigned int const rowWindowHeight = distance(leftInRow, rghtInRow);
 
@@ -1133,249 +1232,305 @@ windowHeight(const world_data * const worldP,
 
 
 static void
-init_buffer(buffer *           const bufferP,
+buffer_init(buffer *           const bufferP,
             const world_data * const worldP,
             const option *     const optionsP,
             const struct pam * const inpamP,
             const struct pam * const outpamP) {
 
-    unsigned int const num_rows =
+    unsigned int const numRows =
         windowHeight(worldP, inpamP, outpamP, optionsP);
 
-    MALLOCARRAY_SAFE(bufferP->rows, num_rows);
-    bufferP->num_rows = num_rows;
-    {
-        unsigned int row;
-        for (row = 0; row < num_rows; ++row) {
-            bufferP->rows[row] = pnm_allocpamrow(inpamP);
-            pnm_readpamrow(inpamP, bufferP->rows[row]);
-        }
+    unsigned int row;
+
+    MALLOCARRAY_SAFE(bufferP->rows, numRows);
+
+    for (row = 0; row < numRows; ++row) {
+        bufferP->rows[row] = pnm_allocpamrow(inpamP);
+        pnm_readpamrow(inpamP, bufferP->rows[row]);
     }
-    bufferP->last_logical = num_rows-1;
-    bufferP->last_physical = num_rows-1;
-    bufferP->inpam = inpamP;
+
+    bufferP->nextImageRow  = numRows;
+    bufferP->nextBufferRow = 0;
+    bufferP->numRows       = numRows;
+
+    bufferP->inpamP = inpamP;
 }
 
 
 
+static const tuple *
+buffer_getRow(buffer *     const bufferP,
+              unsigned int const imageRow) {
+/*----------------------------------------------------------------------------
+   Return row 'imageRow' of an image.
 
-static tuple* read_buffer (buffer *const b, int const logical_y)
-{
-  int y;
-
-  while (logical_y > b->last_logical) {
-    b->last_physical++;
-    if (b->last_physical == b->num_rows)
-      b->last_physical = 0;
-    pnm_readpamrow (b->inpam, b->rows[b->last_physical]);
-    b->last_logical++;
-  }
+   The return value is a pointer into storage that belongs to *bufferP.
 
-  y = logical_y - b->last_logical + b->last_physical;
-  if (y<0)
-    y += b->num_rows;
+   *bufferP remembers only a window of the image, and the window
+   cannot move up, so 'imageRow' cannot be higher in the image than
+   the lowest row read so far through *bufferP plus *bufferP's maximum
+   window height.  We assume that.
+-----------------------------------------------------------------------------*/
+    unsigned int bufferRow;
+        /* The row of the buffer that holds row 'imageRow' of the image */
+    unsigned int n;
+        /* Number of rows our row is before the bottom of the window */
+
+    assert(imageRow >= bufferP->nextImageRow - bufferP->numRows);
+        /* The requested row is not one that's already been bumped out
+           of the buffer.
+        */
+
+    while (imageRow >= bufferP->nextImageRow) {
+        pnm_readpamrow(bufferP->inpamP, bufferP->rows[bufferP->nextBufferRow]);
+
+        ++bufferP->nextBufferRow;
+        if (bufferP->nextBufferRow == bufferP->numRows)
+            bufferP->nextBufferRow = 0;
+
+        ++bufferP->nextImageRow;
+    }
+
+    n = bufferP->nextImageRow - imageRow;
+
+    assert(n <= bufferP->numRows);
+    
+    if (n <= bufferP->nextBufferRow)
+        bufferRow = bufferP->nextBufferRow - n;
+    else
+        bufferRow = bufferP->nextBufferRow + bufferP->numRows - n;
 
-  return b->rows[y];
+    assert(bufferRow < bufferP->numRows);
+
+    return bufferP->rows[bufferRow];
 }
 
-static void free_buffer (buffer *const b)
-{
-  int i;
 
-  /* We have to read through the end of the input image even if we
-     didn't use all the rows, because if the input is a pipe, the
-     guy writing into the pipe may require all the data to go
-     through.
-  */
-  
-  while (b->last_logical < b->inpam->height-1) {
-      pnm_readpamrow(b->inpam, b->rows[0]);
-      ++b->last_logical;
-  }
 
-  for (i=0; i<b->num_rows; i++)
-    pnm_freepamrow (b->rows[i]);
-  free (b->rows);
+static void
+buffer_term(buffer * const bufferP) {
+
+    unsigned int i;
+
+    /* We have to read through the end of the input image even if we
+       didn't use all the rows, because if the input is a pipe, the
+       guy writing into the pipe may require all the data to go
+       through.
+    */
+
+    while (bufferP->nextImageRow < bufferP->inpamP->height) {
+        pnm_readpamrow(bufferP->inpamP, bufferP->rows[0]);
+        ++bufferP->nextImageRow;
+    }
+
+    for (i = 0; i < bufferP->numRows; ++i)
+        pnm_freepamrow(bufferP->rows[i]);
+    
+    free(bufferP->rows);
 }
 
 
 
 
-/* The following variables are global for speed reasons.
-   In this way they do not have to be passed to each call of the
+struct interpContext {
+    tuple background;
+    buffer* indata;
+    int width,height,depth;
+};
+
+/* The following is global for speed reasons.
+   In this way it does not have to be passed to each call of the
    interpolation functions
 
    Think of this as Sch&ouml;nfinkeling (aka Currying).
 */
 
-static tuple background;
-static buffer* indata;
-static int width,height,depth;
+static struct interpContext ictx;
 
-static void init_interpolation_global_vars (buffer* const inbuffer,
-                                            const struct pam *const inpam,
-                                            const struct pam *const outpam)
-{
-  pnm_createBlackTuple (outpam, &background);
-  indata = inbuffer;
-  width = inpam->width;
-  height = inpam->height;
-  depth = outpam->depth;
+static void
+init_interpolation_global_vars(buffer *           const inbufferP,
+                               const struct pam * const inpamP,
+                               const struct pam * const outpamP) {
+
+    pnm_createBlackTuple(outpamP, &ictx.background);
+    ictx.indata = inbufferP;
+    ictx.width  = inpamP->width;
+    ictx.height = inpamP->height;
+    ictx.depth  = outpamP->depth;
 }
 
 
 
-static void clean_interpolation_global_vars (void)
-{
-  free (background);
+static void
+clean_interpolation_global_vars(void) {
+
+    free(ictx.background);
 }
 
 
 
 /* These functions perform the interpolation */
 
-static tuple attempt_read (int const x, int const y)
-{
-  if ((x<0) || (x>=width) || (y<0) || (y>=height))
-    return background;
-  else
-    return read_buffer(indata, y)[x];
+static tuple
+getPixel(int const col,
+         int const row) {
+/*----------------------------------------------------------------------------
+   Get the pixel at Row 'row', Column 'col' of the image which is the
+   context of the interpolation in which we are called.
+
+   Consider the image to go on forever in all directions (even negative
+   column/row numbers), being the background color everywhere outside
+   the actual image.
+-----------------------------------------------------------------------------*/
+    if ((col < 0) || (col >= ictx.width) || (row < 0) || (row >= ictx.height))
+        return ictx.background;
+    else
+        return buffer_getRow(ictx.indata, row)[col];
 }
 
 
 
-static void take_nearest (tuple const dest, number const x, number const y)
-{
-  int xx,yy,entry;
-  tuple p;
-
-  xx = (int)floor(x+0.5);
-  yy = (int)floor(y+0.5);
-  p = attempt_read (xx, yy);
-  for (entry=0; entry<depth; entry++) {
-    dest[entry]=p[entry];
-  }
-}
+static void
+takeNearest(tuple  const dest,
+            number const x,
+            number const y) {
 
+    int   const xx = (int)floor(x+0.5);
+    int   const yy = (int)floor(y+0.5);
+    tuple const p  = getPixel(xx, yy);
 
+    unsigned int entry;
 
-static void linear_interpolation (tuple const dest, 
-                                  number const x, number const y)
-{
-  int xx,yy,entry;
-  number xf,yf,a,b,c,d;
-  tuple p1,p2,p3,p4;
-
-  xx = (int)floor(x);
-  yy = (int)floor(y);
-  xf = x-(number)xx;
-  yf = y-(number)yy;
-  p1 = attempt_read (xx, yy);
-  p2 = attempt_read (xx+1, yy);
-  p3 = attempt_read (xx, yy+1);
-  p4 = attempt_read (xx+1, yy+1);
-  a = (1.0-xf)*(1.0-yf);
-  b = xf*(1.0-yf);
-  c = (1.0-xf)*yf;
-  d = xf*yf;
-  for (entry=0; entry<depth; entry++) {
-    dest[entry]=(sample) floor(
-      a*((number) p1[entry]) +
-      b*((number) p2[entry]) +
-      c*((number) p3[entry]) +
-      d*((number) p4[entry]) +
-      0.5);
-  }
+    for (entry = 0; entry < ictx.depth; ++entry) {
+        dest[entry] = p[entry];
+    }
 }
 
 
 
-int main (int argc, char* argv[])
-{
-  FILE* infp;
-  struct pam inpam;
-  buffer inbuffer;
-  FILE* outfp;
-  struct pam outpam;
-  tuple* outrow;
-  option options;
-  world_data world;
-  int row,col;
-  number xi,yi;
-  void (*interpolate) (tuple, number, number);
+static void
+linearInterpolation(tuple  const dest, 
+                    number const x,
+                    number const y) {
+
+    int    const xx = (int)floor(x);
+    int    const yy = (int)floor(y);
+    number const xf = x - (number)xx;
+    number const yf = y - (number)yy;
+    tuple  const p1 = getPixel(xx, yy);
+    tuple  const p2 = getPixel(xx+1, yy);
+    tuple  const p3 = getPixel(xx, yy+1);
+    tuple  const p4 = getPixel(xx+1, yy+1);
+    number const a  = (1.0-xf) * (1.0-yf);
+    number const b  = xf * (1.0-yf);
+    number const c  = (1.0-xf) * yf;
+    number const d  = xf * yf;
+
+    unsigned int entry;
+
+    for (entry=0; entry < ictx.depth; ++entry) {
+        dest[entry] = floor(
+            a * (number) p1[entry] +
+            b * (number) p2[entry] +
+            c * (number) p3[entry] +
+            d * (number) p4[entry] +
+            0.5);
+    }
+}
 
-  /* The usual initializations */
 
-  pnm_init (&argc, argv);
-  set_command_line_defaults (&options);
-  parse_command_line (argc, argv, &options);
-  infp = pm_openr (options.infilename);
-  pnm_readpaminit (infp, &inpam, PAM_STRUCT_SIZE(tuple_type));
 
-  /* Our own initializations */
+static void
+perspective(struct pam * const outpamP,
+            world_data * const worldP,
+            interpolateFn *    interpolater) {
 
-  init_world (&options, &inpam, &world);
-  determine_world_parallelogram (&world, &options);
-  determine_output_width_and_height (&world, &options);
-  switch (options.enums[1]) {   /* --output_system */
-  case lattice:
-    determine_coefficients_lattice (&world, &options);
-    break;
-  case pixel_s:
-    determine_coefficients_pixel (&world, &options);
-    break;
-  };
+    tuple * outrow;
+    unsigned int row;
 
-  /* Initialize outpam */
-
-  outfp = pm_openw ("-");
-  outpam.size = sizeof (outpam);
-  outpam.len = PAM_STRUCT_SIZE(bytes_per_sample);
-  outpam.file = outfp;
-  outpam.format = inpam.format;
-  outpam.plainformat = inpam.plainformat;
-  outpam.height = options.height;
-  outpam.width = options.width;
-  outpam.depth = inpam.depth;
-  outpam.maxval = inpam.maxval;
-  outpam.bytes_per_sample = inpam.bytes_per_sample;
-  pnm_writepaminit (&outpam);
-
-  /* Initialize the actual calculation */
-
-  init_buffer (&inbuffer, &world, &options, &inpam, &outpam);
-  outrow = pnm_allocpamrow (&outpam);
-  init_interpolation_global_vars (&inbuffer,&inpam,&outpam);
-  switch (options.enums[3]) {   /* --interpolation */
-  case interp_nearest:
-    interpolate = take_nearest;
-    break;
-  case interp_linear:
-    interpolate = linear_interpolation;
-    break;
-  };
+    outrow = pnm_allocpamrow(outpamP);
 
-  /* Perform the actual calculation */
+    for (row = 0; row < outpamP->height; ++row) {
+        unsigned int col;
 
-  for (row=0; row<outpam.height; row++) {
-    for (col=0; col<outpam.width; col++) {
-      outpixel_to_inpixel (col,row,&xi,&yi,&world);
-      interpolate(outrow[col],xi,yi);
+        for (col = 0; col < outpamP->width; ++col) {
+            number xi, yi;
+            outpixelToInPos(col, row, &xi, &yi, worldP);
+            interpolater(outrow[col], xi, yi);
+        }
+        pnm_writepamrow(outpamP, outrow);
     }
-    pnm_writepamrow (&outpam, outrow);
-  }
+    pnm_freepamrow(outrow);
+}
 
-  /* Close everything down nicely */
 
-  clean_interpolation_global_vars ();
-  free_buffer (&inbuffer);
-  pnm_freepamrow (outrow);
-  free_option (&options);
-  pm_close (infp);
-  pm_close (outfp);
-  return 0;
-}
 
+int
+main(int argc, const char * argv[]) {
+
+    FILE * ifP;
+    struct pam inpam;
+    buffer inbuffer;
+    struct pam outpam;
+    option options;
+    world_data world;
+    interpolateFn * interpolater;
+
+    pm_proginit(&argc, argv);
 
+    parseCommandLine(argc, argv, &options);
 
+    ifP = pm_openr(options.infilename);
 
+    pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
+
+    /* Our own initializations */
+
+    init_world(&options, &inpam, &world);
+    determine_world_parallelogram(&world, &options);
+    determine_output_width_and_height(&world, &options);
+    switch (options.enums[1]) {   /* --output_system */
+    case lattice:
+        determine_coefficients_lattice(&world, &options);
+        break;
+    case pixel_s:
+        determine_coefficients_pixel(&world, &options);
+        break;
+    };
+
+    outpam.size             = sizeof(outpam);
+    outpam.len              = PAM_STRUCT_SIZE(bytes_per_sample);
+    outpam.file             = stdout;
+    outpam.format           = inpam.format;
+    outpam.plainformat      = FALSE;
+    outpam.height           = options.height;
+    outpam.width            = options.width;
+    outpam.depth            = inpam.depth;
+    outpam.maxval           = inpam.maxval;
+    outpam.bytes_per_sample = inpam.bytes_per_sample;
+    pnm_writepaminit(&outpam);
+
+    /* Initialize the actual calculation */
+
+    buffer_init(&inbuffer, &world, &options, &inpam, &outpam);
+    init_interpolation_global_vars(&inbuffer, &inpam, &outpam);
+    switch (options.enums[3]) {   /* --interpolation */
+    case interp_nearest:
+        interpolater = takeNearest;
+        break;
+    case interp_linear:
+        interpolater = linearInterpolation;
+        break;
+    };
+
+    perspective(&outpam, &world, interpolater);
+
+    clean_interpolation_global_vars();
+    buffer_term(&inbuffer);
+    free_option(&options);
+    pm_close(ifP);
+    pm_close(stdout);
+
+    return 0;
+}
diff --git a/editor/pamscale.c b/editor/pamscale.c
index 16bd8819..0abbc205 100644
--- a/editor/pamscale.c
+++ b/editor/pamscale.c
@@ -30,6 +30,7 @@
 #include <string.h>
 #include <assert.h>
 
+#include "pm_c_util.h"
 #include "pam.h"
 #include "shhopt.h"
 #include "mallocvar.h"
@@ -2054,6 +2055,9 @@ scaleWithoutMixing(const struct pam * const inpamP,
     int row;
     int rowInInput;
 
+    assert(outpamP->maxval == inpamP->maxval);
+    assert(outpamP->depth  == inpamP->depth);
+
     tuplerow = pnm_allocpamrow(inpamP); 
     rowInInput = -1;
 
@@ -2136,7 +2140,7 @@ main(int argc, char **argv ) {
         scaleWithoutMixing(&inpam, &outpam, xscale, yscale);
     } else if (!cmdline.filterFunction) {
         if (cmdline.verbose)
-            pm_message("Using regular rescaling method");
+            pm_message("Using simple pixel mixing rescaling method");
         scaleWithMixing(&inpam, &outpam, xscale, yscale, 
                         cmdline.linear, cmdline.verbose);
     } else {
diff --git a/editor/pamsistoaglyph.c b/editor/pamsistoaglyph.c
new file mode 100644
index 00000000..1c92658c
--- /dev/null
+++ b/editor/pamsistoaglyph.c
@@ -0,0 +1,421 @@
+/* ----------------------------------------------------------------------
+ *
+ * Convert a single-image stereogram to a red/cyan anaglyphic image
+ *
+ * By Scott Pakin <scott+pbm@pakin.org>
+ *
+ * ----------------------------------------------------------------------
+ *
+ * Copyright (C) 2009 Scott Pakin <scott+pbm@pakin.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see http://www.gnu.org/licenses/.
+ *
+ * ----------------------------------------------------------------------
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+
+#include "mallocvar.h"
+#include "nstring.h"
+#include "shhopt.h"
+#include "pam.h"
+
+
+struct cmdlineInfo {
+    /* This structure represents all of the information the user
+       supplied in the command line but in a form easy for the program
+       to use.
+    */
+    int separation;
+        /* Exact separation in pixels between the left and right eye,
+           or -1
+        */
+    int minSeparation;
+        /* Minimum separation in pixels between the left and right eye */
+    gray maxGrayVal;
+        /* Maximum grayscale value to which to scale the image */
+    int swapEyes;
+        /* 0=left red, right cyan; 1=left cyan, right red */
+    const char *inputFilename;   /* '-' if stdin */
+};
+
+
+
+static void
+parseCommandLine( int argc, const char ** const argv,
+                  struct cmdlineInfo * const cmdlineP ) {
+/*--------------------------------------------------------------------
+  Parse the command line into a structure.
+----------------------------------------------------------------------*/
+
+    optEntry     * option_def;
+        /* Instructions to OptParseOptions3 on how to parse our options */
+    optStruct3     opt;
+    unsigned int   option_def_index;
+    int            maxgrayval;
+
+    maxgrayval = 63;  /* default */
+
+    MALLOCARRAY(option_def, 100);
+    option_def_index = 0;          /* Incremented by OPTENTRY */
+    MEMSZERO(cmdlineP);
+    cmdlineP->separation = -1;
+
+    OPTENT3('s', "sep",    OPT_INT,  &cmdlineP->separation,    NULL, 0);
+    OPTENT3('g', "gray",   OPT_INT,  &maxgrayval,              NULL, 0);
+    OPTENT3('i', "invert", OPT_FLAG, &cmdlineP->swapEyes,      NULL, 0);
+    OPTENT3('m', "minsep", OPT_INT,  &cmdlineP->minSeparation, NULL, 0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = 1;
+    opt.allowNegNum = 0;
+
+    optParseOptions3( &argc, (char **)argv, opt, sizeof(opt), 0 );
+
+    if (argc-1 < 1)
+        cmdlineP->inputFilename = "-";
+    else {
+        cmdlineP->inputFilename = argv[1];
+        if (argc-1 > 1)
+            pm_error("Too many arguments: %u.  The only argument is the "
+                     "optional input file name", argc-1);
+    }
+    cmdlineP->maxGrayVal = (gray) maxgrayval;
+}
+
+
+
+static gray **
+readAsGray( const char * const fileName,
+            gray         const maxGrayVal,
+            struct pam * const pamP) {
+/*--------------------------------------------------------------------
+  Read the input image and convert it to grayscale to reduce the
+  number of "almost but not quite" equal pixels.  Return the
+  grayscale array and the initialized PAM structure.
+  ----------------------------------------------------------------------*/
+
+    FILE       *  fileP;
+    tuple      *  tuplerow;
+    gray       ** grayArray;
+    unsigned int  row;
+
+    fileP = pm_openr( fileName );
+
+    pnm_readpaminit( fileP, pamP, PAM_STRUCT_SIZE(tuple_type) );
+
+    tuplerow = pnm_allocpamrow( pamP );
+
+    grayArray = pgm_allocarray( pamP->width, pamP->height );
+
+    for (row = 0; row < pamP->height; ++row) {
+        unsigned int col;
+        pnm_readpamrow( pamP, tuplerow );
+        for (col = 0; col < pamP->width; ++col) {
+            double YP, CbP, CrP;
+
+            pnm_YCbCrtuple( tuplerow[col], &YP, &CbP, &CrP );
+            grayArray[row][col] = (gray)
+                (YP * maxGrayVal / (double)pamP->maxval);
+        }
+    }
+    pnm_freepamrow( tuplerow );
+    pm_close( fileP );
+    return grayArray;
+}
+
+
+
+static int
+bestEyeSepWeEncountered(int const bestSeparation[3],
+                        int const altBestSeparation) {
+
+    int i;
+
+    for (i = 2; i >= 0; --i) {
+        if (bestSeparation[i] != 0)
+            return bestSeparation[i];
+    }    
+    return altBestSeparation;
+}
+
+
+
+static int
+findRegionEyeSeparation( gray ** const grayArray,
+                         int     const width,
+                         int     const height ) {
+/*----------------------------------------------------------------------
+  Determine the number of pixels that corresponds to the separation
+  between the viewer's left eye and right eye.  We do this by counting
+  the number of pixels that match N pixels ahead in the image for all
+  N in [1, W/2].  The first big spike in the number of matched pixels
+  determines the N to use for the eye separation.  More specifically,
+  if a spike that exceeds 3*stdev+mean is found, the corresponding
+  value of N is taken as the eye separation; otherwise, a spike
+  exceeding 2*stdev+mean is used, then 1*stdev+mean, and finally, the
+  eye separation that produces the minimum average distance between
+  matched pixels.  A return value of zero indicates that no eye
+  separation could be determined.
+------------------------------------------------------------------------*/
+    int              bestSeparation[3];
+        /* Eye separation corresponding to spikes of N+1 standard deviations */
+    int              hShift;
+        /* Current horizontal shift */
+    double           sumMatches;
+        /* Sum of all matches seen so far */
+    double           sumSqMatches;
+        /* Sum of the squares of all matches seen so far */
+    double           meanMatches;
+        /* Mean of all matches seen so far */
+    double           stdMatches;
+        /* Standard deviation of all matches seen so far */
+    double           minAvgDist;
+        /* Min. average distance between matches */
+    int              altBestSeparation;
+        /* Shift distance corresponding to the above */
+    unsigned int     i;
+
+    /* Try in turn each horizontal shift value from 1 to width/2.  A
+       shift of 0 is defined to be a perfect match.  A shift of more
+       than width/2 implies that the right-eye image is truncated, which
+       is an unnatural way to construct a crosseyed stereogram.
+    */
+    for (i = 0; i < 3; ++i)
+        bestSeparation[i] = 0;
+
+    altBestSeparation = 0;
+    sumMatches = sumSqMatches = 0.0;
+    meanMatches = stdMatches = minAvgDist = width * height;
+
+    for (hShift = 1; hShift <= width/2; ++hShift) {
+        unsigned int row;
+        unsigned long numMatches;      /* Number of matched pixels */
+        double avgDist;                /* Average distance between matches */
+
+        numMatches = 0;  /* initial value */
+
+        /* Tally the number of matches for this shift distance. */
+        for (row = 0; row < height; ++row) {
+            unsigned int col;
+            for (col = 0; col < width - hShift; ++col)
+                if (grayArray[row][col] == grayArray[row][col + hShift])
+                    ++numMatches;
+
+            /* See if the number of matches exceeds the running mean plus N
+               standard deviations.  Also, keep track of the shortest average
+               distance between matches seen so far.
+            */
+            if (hShift > 1) {
+                int i;
+                for (i = 2; i >= 0; --i)
+                    if (bestSeparation[i] == 0 &&
+                        numMatches > meanMatches + (i+1)*stdMatches) {
+                        bestSeparation[i] = hShift;
+                        break;
+                    }
+            }
+            avgDist = (height * (width-hShift)) / (double)numMatches;
+            if (minAvgDist > avgDist) {
+                minAvgDist = avgDist;
+                altBestSeparation = hShift;
+            }
+
+            /* Compute the new mean and standard deviation. */
+            sumMatches   += (double)numMatches;
+            sumSqMatches += (double)numMatches * (double)numMatches;
+            meanMatches = sumMatches / (double)hShift;
+            stdMatches  = sqrt(sumSqMatches/hShift - meanMatches*meanMatches);
+        }
+    }
+
+    return bestEyeSepWeEncountered(bestSeparation, altBestSeparation);
+}
+
+
+
+static int
+compare_ints( const void * const firstP,
+              const void * const secondP ) {
+
+    int const first  = *(int *)firstP;
+    int const second = *(int *)secondP;
+
+    int retval;
+
+    if (first < second)
+        retval = -1;
+    else if (first > second)
+        retval = +1;
+    else
+        retval = 0;
+
+    return retval;
+}
+
+
+
+static int
+findEyeSeparation( struct pam *  const pamP,
+                   gray       ** const grayArray,
+                   int           const minSeparation ) {
+/*----------------------------------------------------------------------
+  Compute the eye separation for each row of the grayscale image.
+  Ignore rows for which the eye separation could not be determined and
+  return the median of the remaining rows, aborting with an error
+  message if there are no remaining rows.  Out of laziness we use
+  qsort() to help find the median; if this turns out to be a
+  performance problem, it should be replaced with a linear-time median
+  finder.
+------------------------------------------------------------------------*/
+    int bestSeparation;      /* Best eye separation found */
+
+    /* First attempt: Find the best eye separation across the image as a
+       whole.  This works well when the image consists of relatively
+       small foreground objects in front of a comparatively large
+       background plane.
+    */
+    bestSeparation =
+        findRegionEyeSeparation( grayArray, pamP->width, pamP->height );
+
+    /* Second attempt: Compute the best eye separation for each row
+       independently and return the median of the best eye
+       separations.
+    */
+    if (bestSeparation < minSeparation) {
+        int * rowSeparation;   /* Per-row best separation distance */
+        unsigned int numValidRows;
+            /* Number of entries in the above (<= #rows) */
+        unsigned int row;
+
+        numValidRows = 0;  /* initial value */
+
+        MALLOCARRAY_NOFAIL( rowSeparation, pamP->height );
+        for (row = 0; row < pamP->height; ++row) {
+            int const sep =
+                findRegionEyeSeparation( &grayArray[row], pamP->width, 1);
+            if (sep >= minSeparation)
+                rowSeparation[numValidRows++] = sep;
+        }
+        if (numValidRows > 0) {
+            qsort( rowSeparation, numValidRows, sizeof(int), compare_ints );
+            bestSeparation = rowSeparation[numValidRows/2];
+        }
+        free( rowSeparation );
+    }
+
+    if (bestSeparation < minSeparation)
+        pm_error("Failed to determine the separation between "
+                 "the left and right views");
+
+    return bestSeparation;
+}
+
+
+
+static void
+writeAnaglyph( FILE *       const ofP,
+               gray **      const grayArray,
+               gray         const maxGrayVal,
+               int          const eyeSep,
+               int          const swapEyes,
+               struct pam * const pamP) {
+/*----------------------------------------------------------------------
+  Output an anaglyphic stereogram from the given grayscale array and
+  eye-separation value.
+------------------------------------------------------------------------*/
+    struct pam   outPam;
+    tuple      * tuplerow;
+
+    outPam.size        = sizeof(struct pam);
+    outPam.len         = PAM_STRUCT_SIZE(tuple_type);
+    outPam.file        = ofP;
+    outPam.format      = PAM_FORMAT;
+    outPam.plainformat = 0;
+    outPam.height      = pamP->height;
+    outPam.width       = pamP->width - eyeSep;
+        /* Avoid color bands on the left/right edges. */
+    outPam.depth       = 3;
+    outPam.maxval      = (sample) maxGrayVal;
+    strcpy(outPam.tuple_type, PAM_PPM_TUPLETYPE);
+
+    pnm_writepaminit( &outPam );
+
+    tuplerow = pnm_allocpamrow( &outPam );
+
+    if (swapEyes) {
+        unsigned int row;
+
+        for (row = 0; row < outPam.height; ++row) {
+            unsigned int col;
+            for (col = 0; col < outPam.width; ++col) {
+                tuplerow[col][PAM_RED_PLANE] = grayArray[row][col+eyeSep];
+                tuplerow[col][PAM_GRN_PLANE] = grayArray[row][col];
+                tuplerow[col][PAM_BLU_PLANE] = grayArray[row][col];
+            }
+            pnm_writepamrow( &outPam, tuplerow );
+        }
+    } else {
+        unsigned int row;
+        for (row = 0; row < outPam.height; ++row) {
+            unsigned int col;
+            for (col = 0; col < outPam.width; ++col) {
+                tuplerow[col][PAM_RED_PLANE] = grayArray[row][col];
+                tuplerow[col][PAM_GRN_PLANE] = grayArray[row][col+eyeSep];
+                tuplerow[col][PAM_BLU_PLANE] = grayArray[row][col+eyeSep];
+            }
+            pnm_writepamrow( &outPam, tuplerow );
+        }
+    }
+    pnm_freepamrow( tuplerow );
+}
+
+
+
+int
+main(int argc, const char *argv[]) {
+    struct pam            inPam;
+    gray               ** inImage;
+    int                   eyeSep;
+    struct cmdlineInfo    cmdline;
+
+    pm_proginit( &argc, argv );
+    parseCommandLine( argc, argv, &cmdline );
+
+    inImage = readAsGray( cmdline.inputFilename, cmdline.maxGrayVal, &inPam );
+
+    if (cmdline.separation >= 0)
+        eyeSep = cmdline.separation;
+    else {
+        int const minSeparation =
+            cmdline.minSeparation > 0
+            ? cmdline.minSeparation : inPam.width / 10;
+
+            /* Minimum separation in pixels between eyes.
+               Heuristic: Eye separation must be at least 10% of image width.
+            */
+        eyeSep = findEyeSeparation ( &inPam, inImage, minSeparation );
+    }
+
+    pm_message( "Separation between left/right views = %d pixels", eyeSep );
+
+    writeAnaglyph ( stdout, inImage, cmdline.maxGrayVal,
+                    eyeSep, cmdline.swapEyes,
+                    &inPam );
+
+    return 0;
+}
+
diff --git a/editor/pamstretch-gen b/editor/pamstretch-gen
index cd59a36b..ba0e8188 100755
--- a/editor/pamstretch-gen
+++ b/editor/pamstretch-gen
@@ -32,12 +32,12 @@ if [ "$1" = "" ]; then
 fi
 
 tempdir="${TMPDIR-/tmp}/pamstretch-gen.$$"
-mkdir $tempdir || { echo "Could not create temporary file. Exiting."; exit 1;}
-chmod 700 $tempdir
-tempfile=$tempdir/pnmig
-
+mkdir -m 0700 $tempdir || \
+  { echo "Could not create temporary file. Exiting."; exit 1;}
 trap 'rm -rf $tempdir' 0 1 3 15
 
+tempfile=$tempdir/pnmig
+
 if ! cat $2 >$tempfile 2>/dev/null; then
   echo 'pamstretch-gen: error reading file' 1>&2
   exit 1
diff --git a/editor/pamstretch.c b/editor/pamstretch.c
index 0e9e6abf..87c105f9 100644
--- a/editor/pamstretch.c
+++ b/editor/pamstretch.c
@@ -24,6 +24,8 @@
 #include <string.h>
 #include <stdlib.h>
 #include <ctype.h>
+
+#include "pm_c_util.h"
 #include "pam.h"
 #include "shhopt.h"
 
diff --git a/editor/pamthreshold.c b/editor/pamthreshold.c
index a5635790..c5f48147 100644
--- a/editor/pamthreshold.c
+++ b/editor/pamthreshold.c
@@ -23,6 +23,7 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include "pm_c_util.h"
 #include "mallocvar.h"
 #include "nstring.h"
 #include "shhopt.h"
@@ -44,10 +45,18 @@ struct cmdlineInfo {
         /* geometry of local subimage.  Defined only if 'local' or 'dual'
            is true.
         */
+    unsigned int verbose;
 };
 
 
 
+static __inline__ bool
+betweenZeroAndOne(float const arg) {
+    return (arg >= 0.0 && arg <= 1.0);
+}
+
+
+
 struct range {
     /* A range of sample values, normalized to [0, 1] */
     samplen min;
@@ -146,6 +155,8 @@ parseCommandLine(int                 argc,
             &thresholdSpec,         0);
     OPTENT3(0, "contrast",  OPT_FLOAT,  &cmdlineP->contrast,
             &contrastSpec,          0);
+    OPTENT3(0, "verbose",    OPT_FLAG,   NULL,               
+            &cmdlineP->verbose,     0);
 
     /* set the defaults */
     cmdlineP->width = cmdlineP->height = 0U;
@@ -212,6 +223,21 @@ parseCommandLine(int                 argc,
 
 
 
+static void
+thresholdPixel(struct pam * const outpamP,
+               tuplen       const inTuplen,
+               tuple        const outTuple,
+               float        const threshold) {
+
+    outTuple[0] = inTuplen[0] >= threshold ? PAM_BW_WHITE : PAM_BLACK;
+    if (outpamP->depth > 1) {
+        /* Do alpha */
+        outTuple[1] = inTuplen[1] > 0.5 ? 1 : 0;
+    }
+}
+
+
+
 /* simple thresholding (the same as in pamditherbw) */
 
 static void
@@ -230,9 +256,9 @@ thresholdSimple(struct pam * const inpamP,
     for (row = 0; row < inpamP->height; ++row) {
         unsigned int col;
         pnm_readpamrown(inpamP, inrow);
-        for (col = 0; col < inpamP->width; ++col)
-            outrow[col][0] =
-                inrow[col][0] >= threshold ? PAM_BW_WHITE : PAM_BLACK;
+        for (col = 0; col < inpamP->width; ++col) {
+            thresholdPixel(outpamP, inrow[col], outrow[col], threshold);
+        }
         pnm_writepamrow(outpamP, outrow);
     }
 
@@ -244,6 +270,7 @@ thresholdSimple(struct pam * const inpamP,
 
 static void
 analyzeDistribution(struct pam *          const inpamP,
+                    bool                  const verbose,
                     const unsigned int ** const histogramP,
                     struct range *        const rangeP) {
 /*----------------------------------------------------------------------------
@@ -255,7 +282,8 @@ analyzeDistribution(struct pam *          const inpamP,
    distribution as *histogramP, an array such that histogram[i] is the
    number of pixels that have sample value i.
 
-   Leave the file positioned to the raster.
+   Assume the file is positioned to the raster upon entry and leave
+   it positioned at the same place.
 -----------------------------------------------------------------------------*/
     unsigned int row;
     tuple * inrow;
@@ -295,6 +323,10 @@ analyzeDistribution(struct pam *          const inpamP,
     pnm_freepamrown(inrown);
 
     pm_seek2(inpamP->file, &rasterPos, sizeof(rasterPos));
+
+    if (verbose)
+        pm_message("Pixel values range from %f to %f",
+                   rangeP->min, rangeP->max);
 }
 
 
@@ -342,9 +374,7 @@ computeGlobalThreshold(struct pam *         const inpamP,
                        float *              const thresholdP) {
 /*----------------------------------------------------------------------------
    Compute the proper threshold to use for the image described by
-   *inpamP, whose file is positioned to the raster.
-
-   For our convenience:
+   *inpamP, and:
 
      'histogram' describes the frequency of occurence of the various sample
      values in the image.
@@ -475,6 +505,7 @@ thresholdLocalRow(struct pam *       const inpamP,
                   struct cmdlineInfo const cmdline,
                   struct range       const globalRange,
                   samplen            const globalThreshold,
+                  struct pam *       const outpamP,
                   tuple *            const outrow) {
 
     tuplen * const inrow = inrows[row % windowHeight];
@@ -494,7 +525,7 @@ thresholdLocalRow(struct pam *       const inpamP,
                           cmdline.threshold, minSpread, globalThreshold,
                           &threshold);
         
-        outrow[col][0] = inrow[col][0] >= threshold ? PAM_BW_WHITE : PAM_BLACK;
+        thresholdPixel(outpamP, inrow[col], outrow[col], threshold);
     }
 }
 
@@ -552,9 +583,16 @@ thresholdLocal(struct pam *       const inpamP,
 
     windowHeight = MIN(oddLocalHeight, inpamP->height);
 
-    analyzeDistribution(inpamP, &histogram, &globalRange);
-
-    computeGlobalThreshold(inpamP, histogram, globalRange, &globalThreshold);
+    /* global information is needed for dual thresholding */
+    if (cmdline.dual) {
+        analyzeDistribution(inpamP, cmdline.verbose, &histogram, &globalRange);
+        computeGlobalThreshold(inpamP, histogram, globalRange,
+                               &globalThreshold);
+    } else {
+        histogram = NULL;
+        initRange(&globalRange);
+        globalThreshold = 1.0;
+    }
 
     outrow = pnm_allocpamrow(outpamP);
 
@@ -574,7 +612,8 @@ thresholdLocal(struct pam *       const inpamP,
 
     for (row = 0; row < inpamP->height; ++row) {
         thresholdLocalRow(inpamP, inrows, oddLocalWidth, windowHeight, row,
-                          cmdline, globalRange, globalThreshold, outrow);
+                          cmdline, globalRange, globalThreshold,
+                          outpamP, outrow);
 
         pnm_writepamrow(outpamP, outrow);
         
@@ -595,13 +634,14 @@ thresholdLocal(struct pam *       const inpamP,
 
 static void
 thresholdIterative(struct pam * const inpamP,
-                   struct pam * const outpamP) {
+                   struct pam * const outpamP,
+                   bool         const verbose) {
 
     const unsigned int * histogram;
     struct range globalRange;
     samplen threshold;
 
-    analyzeDistribution(inpamP, &histogram, &globalRange);
+    analyzeDistribution(inpamP, verbose, &histogram, &globalRange);
 
     computeGlobalThreshold(inpamP, histogram, globalRange, &threshold);
 
@@ -624,17 +664,17 @@ main(int argc, char **argv) {
 
     parseCommandLine(argc, argv, &cmdline);
 
-    if (cmdline.simple)
+    if (cmdline.simple || cmdline.local)
         ifP = pm_openr(cmdline.inputFileName);
     else
         ifP = pm_openr_seekable(cmdline.inputFileName);
 
-    /* threshold each image in the PAM file */
+    /* Threshold each image in the PAM file */
     eof = FALSE;
     while (!eof) {
         pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
 
-        /* set output image parameters for a bilevel image */
+        /* Set output image parameters for a bilevel image */
         outpam.size        = sizeof(outpam);
         outpam.len         = PAM_STRUCT_SIZE(tuple_type);
         outpam.file        = stdout;
@@ -642,21 +682,27 @@ main(int argc, char **argv) {
         outpam.plainformat = 0;
         outpam.height      = inpam.height;
         outpam.width       = inpam.width;
-        outpam.depth       = 1;
         outpam.maxval      = 1;
         outpam.bytes_per_sample = 1;
-        strcpy(outpam.tuple_type, "BLACKANDWHITE");
+
+        if (inpam.depth > 1) {
+            strcpy(outpam.tuple_type, "BLACKANDWHITE_ALPHA");
+            outpam.depth = 2;
+        } else {
+            strcpy(outpam.tuple_type, "BLACKANDWHITE");
+            outpam.depth = 1;
+        }
 
         pnm_writepaminit(&outpam);
 
-        /* do the thresholding */
+        /* Do the thresholding */
 
         if (cmdline.simple)
             thresholdSimple(&inpam, &outpam, cmdline.threshold);
         else if (cmdline.local || cmdline.dual)
             thresholdLocal(&inpam, &outpam, cmdline);
         else
-            thresholdIterative(&inpam, &outpam);
+            thresholdIterative(&inpam, &outpam, cmdline.verbose);
 
         pnm_nextimage(ifP, &eof);
     }
diff --git a/editor/pamundice.c b/editor/pamundice.c
new file mode 100644
index 00000000..89d8b6b5
--- /dev/null
+++ b/editor/pamundice.c
@@ -0,0 +1,702 @@
+/*****************************************************************************
+                                  pamundice
+******************************************************************************
+  Assemble a grid of images into one.
+
+  By Bryan Henderson, San Jose CA 2001.01.31
+
+  Contributed to the public domain.
+
+******************************************************************************/
+
+#include <assert.h>
+#include <string.h>
+
+#include "pm_c_util.h"
+#include "pam.h"
+#include "shhopt.h"
+#include "nstring.h"
+#include "mallocvar.h"
+
+#define MAXFILENAMELEN 80
+    /* Maximum number of characters we accept in filenames */
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * inputFilePattern;
+        /* null-terminated string, max MAXFILENAMELEN-10 characters */
+    unsigned int across;
+    unsigned int down;
+    unsigned int hoverlap; 
+    unsigned int voverlap; 
+    unsigned int verbose;
+};
+
+
+
+static void
+parseCommandLine(int argc, char ** argv,
+                 struct cmdlineInfo * const cmdlineP ) {
+/*----------------------------------------------------------------------------
+   parse program command line described in Unix standard form by argc
+   and argv.  Return the information in the options as *cmdlineP.  
+
+   If command line is internally inconsistent (invalid options, etc.),
+   issue error message to stderr and abort program.
+
+   Note that the strings we return are stored in the storage that
+   was passed to us as the argv array.  We also trash *argv.
+-----------------------------------------------------------------------------*/
+    optEntry *option_def;
+        /* Instructions to optParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+    
+    unsigned int acrossSpec, downSpec;
+    unsigned int hoverlapSpec, voverlapSpec;
+    unsigned int option_def_index;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "across",      OPT_UINT,    &cmdlineP->across,
+            &acrossSpec,                      0);
+    OPTENT3(0, "down",        OPT_UINT,    &cmdlineP->down,
+            &downSpec,                        0);
+    OPTENT3(0, "hoverlap",    OPT_UINT,    &cmdlineP->hoverlap,
+            &hoverlapSpec,                    0);
+    OPTENT3(0, "voverlap",    OPT_UINT,    &cmdlineP->voverlap,
+            &voverlapSpec,                    0);
+    OPTENT3(0, "verbose",     OPT_FLAG,    NULL,
+            &cmdlineP->verbose,               0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
+        /* Uses and sets argc, argv, and some of *cmdline_p and others. */
+
+    if (!acrossSpec)
+        cmdlineP->across = 1;
+    
+    if (!downSpec)
+        cmdlineP->down = 1;
+
+    if (!hoverlapSpec)
+        cmdlineP->hoverlap = 0;
+
+    if (!voverlapSpec)
+        cmdlineP->voverlap = 0;
+
+    if (argc-1 < 1)
+        pm_error("You must specify one argument: the input file name "
+                 "pattern (e.g. 'myimage%%2a%%2d.pnm')");
+    else {
+        cmdlineP->inputFilePattern = argv[1];
+
+        if (argc-1 > 1)
+            pm_error("Progam takes at most one parameter: input file name.  "
+                     "You specified %u", argc-1);
+    }
+}
+
+
+
+/*------------------ string buffer -----------------------------------*/
+struct buffer {
+    char * string;
+    unsigned int allocSize;
+    unsigned int length;
+};
+
+
+static void
+buffer_init(struct buffer * const bufferP) {
+
+    bufferP->length = 0;
+    bufferP->allocSize = 1024;
+    MALLOCARRAY(bufferP->string, bufferP->allocSize);
+
+    if (bufferP->string == NULL)
+        pm_error("Out of memory allocating buffer to compute file name");
+}
+
+
+
+static void
+buffer_term(struct buffer * const bufferP) {
+    
+    free(bufferP->string);
+}
+
+
+
+static void
+buffer_addChar(struct buffer * const bufferP,
+               char            const newChar) {
+
+    if (bufferP->length + 1 + 1 > bufferP->allocSize)
+        pm_error("Ridiculously long input file name.");
+    else {
+        bufferP->string[bufferP->length++] = newChar;
+        bufferP->string[bufferP->length] = '\0';
+    }
+}
+
+
+
+static void
+buffer_addString(struct buffer * const bufferP,
+                 const char *    const newString) {
+
+    if (bufferP->length + 1 + strlen(newString) > bufferP->allocSize)
+        pm_error("Ridiculously long input file name.");
+    else {
+        strcat(&bufferP->string[bufferP->length], newString);
+        bufferP->length += strlen(newString);
+    }
+}
+/*------------------ end of string buffer ----------------------------*/
+
+
+
+/*------------------ computeInputFileName ----------------------------*/
+static unsigned int
+digitValue(char const digitChar) {
+
+    return digitChar - '0';
+}
+
+
+
+static void
+getPrecision(const char *   const pattern,
+             unsigned int   const startInCursor,
+             unsigned int * const precisionP,
+             unsigned int * const newInCursorP) {
+
+    unsigned int precision;
+    unsigned int inCursor;
+
+    inCursor = startInCursor;  /* Start right after the '%' */
+
+    precision = 0;
+                
+    while (isdigit(pattern[inCursor])) {
+        precision = 10 * precision + digitValue(pattern[inCursor]);
+        ++inCursor;
+    }
+
+    if (precision == 0)
+        pm_error("Zero (or no) precision in substitution "
+                 "specification in file name pattern '%s'.  "
+                 "A proper substitution specification is like "
+                 "'%%3a'.", pattern);
+
+    *precisionP = precision;
+    *newInCursorP = inCursor;
+}
+
+
+
+static void
+doSubstitution(const char *    const pattern,
+               unsigned int    const startInCursor,
+               unsigned int    const rank,
+               unsigned int    const file,
+               struct buffer * const bufferP,
+               unsigned int *  const newInCursorP) {
+
+    unsigned int inCursor;
+
+    inCursor = startInCursor;  /* Start right after the '%' */
+
+    if (pattern[inCursor] == '%') {
+        buffer_addChar(bufferP, '%');
+        ++inCursor;
+    } else {
+        unsigned int precision;
+        
+        getPrecision(pattern, inCursor, &precision, &inCursor);
+
+        if (pattern[inCursor] == '\0')
+            pm_error("No format character follows '%%' in input "
+                     "file name pattern '%s'.  A proper substitution "
+                     "specification is like '%%3a'", pattern);
+        else {
+            const char * substString;
+            const char * desc;
+
+            switch (pattern[inCursor]) {
+            case 'a':
+                asprintfN(&substString, "%0*u", precision, file);
+                asprintfN(&desc, "file (across)");
+                break;
+            case 'd':
+                asprintfN(&substString, "%0*u", precision, rank);
+                asprintfN(&desc, "rank (down)");
+                break;
+            default:
+                pm_error("Unknown format specifier '%c' in input file "
+                         "pattern '%s'.  Recognized format specifier s are "
+                         "'%%a' (across) and '%%d (down)'",
+                         pattern[inCursor], pattern);
+            }
+            if (strlen(substString) > precision)
+                pm_error("%s number %u is wider than "
+                         "the %u characters specified in the "
+                         "input file pattern",
+                         desc, strlen(substString), precision);
+            else
+                buffer_addString(bufferP, substString);
+            
+            strfree(desc);
+            strfree(substString);
+
+            ++inCursor;
+        }
+    }
+    *newInCursorP = inCursor;
+}
+
+
+
+static void
+computeInputFileName(const char *  const pattern,
+                     unsigned int  const rank,
+                     unsigned int  const file,
+                     const char ** const fileNameP) {
+
+    struct buffer buffer;
+    unsigned int inCursor, outCursor;
+
+    buffer_init(&buffer);
+
+    inCursor = 0;
+    outCursor = 0;
+
+    while (pattern[inCursor] != '\0') {
+        if (pattern[inCursor] == '%') {
+            ++inCursor;
+
+            doSubstitution(pattern, inCursor, rank, file, &buffer, &inCursor);
+
+        } else
+            buffer_addChar(&buffer, pattern[inCursor++]);
+    }
+
+    asprintfN(fileNameP, "%s", buffer.string);
+
+    buffer_term(&buffer);
+}
+/*------------------ end of computeInputFileName ------------------------*/
+
+
+
+static void
+getCommonInfo(const char *   const inputFilePattern,
+              int *          const formatP,
+              unsigned int * const depthP,
+              sample *       const maxvalP,
+              char *         const tupleType) {
+/*----------------------------------------------------------------------------
+   Get from the top left input image all the information which is common
+   among all input images and the output image.  I.e. everything except
+   width and height.
+-----------------------------------------------------------------------------*/
+    const char * fileName;
+        /* Name of top left input image */
+    FILE * ifP;
+        /* Top left input image stream */
+    struct pam inpam00;
+        /* Description of top left input image */
+
+    computeInputFileName(inputFilePattern, 0, 0, &fileName);
+
+    ifP = pm_openr(fileName);
+
+    pnm_readpaminit(ifP, &inpam00, PAM_STRUCT_SIZE(tuple_type));
+
+    *formatP = inpam00.format;
+    *depthP  = inpam00.depth;
+    *maxvalP = inpam00.maxval;
+    strcpy(tupleType, inpam00.tuple_type);
+
+    pm_close(ifP);
+
+    strfree(fileName);
+}
+
+
+
+static FILE *
+openInputImage(const char * const inputFilePattern,
+               unsigned int const rank,
+               unsigned int const file) {
+
+    FILE * retval;
+    const char * fileName;
+        
+    computeInputFileName(inputFilePattern, rank, file, &fileName);
+
+    retval = pm_openr(fileName);
+    
+    strfree(fileName);
+
+    return retval;
+}
+
+               
+
+static void
+getImageInfo(const char * const inputFilePattern,
+             unsigned int const rank,
+             unsigned int const file,
+             struct pam * const pamP) {
+
+    FILE * ifP;
+
+    ifP = openInputImage(inputFilePattern, rank, file);
+
+    pnm_readpaminit(ifP, pamP, PAM_STRUCT_SIZE(tuple_type));
+
+    pm_close(ifP);
+    pamP->file = NULL;  /* for robustness */
+}
+
+
+
+static void
+getOutputWidth(const char * const inputFilePattern,
+               unsigned int const nFile,
+               unsigned int const hoverlap,
+               int *        const widthP) {
+/*----------------------------------------------------------------------------
+   Get the output width by adding up the widths of all 'nFile' images of
+   the top rank, and allowing for overlap of 'hoverlap' pixels.
+-----------------------------------------------------------------------------*/
+    unsigned int totalWidth;
+    unsigned int file;
+
+    for (file = 0, totalWidth = 0; file < nFile; ++file) {
+        struct pam inpam;
+
+        getImageInfo(inputFilePattern, 0, file, &inpam);
+
+        if (inpam.width < hoverlap)
+            pm_error("Rank 0, file %u image has width %u, "
+                     "which is less than the horizontal overlap of %u pixels",
+                     file, inpam.width, hoverlap);
+        else {
+            totalWidth += inpam.width;
+
+            if (file < nFile-1)
+                totalWidth -= hoverlap;
+        }
+    }
+    *widthP = totalWidth;
+}
+
+
+
+static void
+getOutputHeight(const char *  const inputFilePattern,
+                unsigned int  const nRank,
+                unsigned int  const voverlap,
+                int *         const heightP) {
+/*----------------------------------------------------------------------------
+   Get the output height by adding up the widths of all 'nRank' images of
+   the left file, and allowing for overlap of 'voverlap' pixels.
+-----------------------------------------------------------------------------*/
+    unsigned int totalHeight;
+    unsigned int rank;
+
+    for (rank = 0, totalHeight = 0; rank < nRank; ++rank) {
+        struct pam inpam;
+
+        getImageInfo(inputFilePattern, rank, 0, &inpam);
+
+        if (inpam.height < voverlap)
+            pm_error("Rank %u, file 0 image has height %u, "
+                     "which is less than the vertical overlap of %u pixels",
+                     rank, inpam.height, voverlap);
+        
+        totalHeight += inpam.height;
+        
+        if (rank < nRank-1)
+            totalHeight -= voverlap;
+    }
+    *heightP = totalHeight;
+}
+
+
+
+static void
+initOutpam(const char * const inputFilePattern,
+           unsigned int const nFile,
+           unsigned int const nRank,
+           unsigned int const hoverlap,
+           unsigned int const voverlap,
+           FILE *       const ofP,
+           bool         const verbose,
+           struct pam * const outpamP) {
+/*----------------------------------------------------------------------------
+   Figure out the attributes of the output image and return them as
+   *outpamP.
+
+   Do this by examining the top rank and left file of the input images,
+   which are in files named by 'inputFilePattern', 'nFile', and 'nRank'.
+
+   In computing dimensions, assume 'hoverlap' pixels of horizontal
+   overlap and 'voverlap' pixels of vertical overlap.
+
+   We overlook any inconsistencies among the images.  E.g. if two images
+   have different depths, we just return one of them.  If two images in
+   the top rank have different heights, we use just one of them.
+
+   Therefore, Caller must check all the input images to make sure they are
+   consistent with the information we return.
+-----------------------------------------------------------------------------*/
+    assert(nFile >= 1);
+    assert(nRank >= 1);
+
+    outpamP->size        = sizeof(*outpamP);
+    outpamP->len         = PAM_STRUCT_SIZE(tuple_type);
+    outpamP->file        = ofP;
+    outpamP->plainformat = 0;
+    
+    getCommonInfo(inputFilePattern, &outpamP->format, &outpamP->depth,
+                  &outpamP->maxval, outpamP->tuple_type);
+
+    getOutputWidth(inputFilePattern, nFile, hoverlap, &outpamP->width);
+
+    getOutputHeight(inputFilePattern, nRank, voverlap, &outpamP->height);
+
+    if (verbose) {
+        pm_message("Output width = %u pixels", outpamP->width);
+        pm_message("Output height = %u pixels", outpamP->height);
+    }
+}
+
+
+
+static void
+openInStreams(struct pam         inpam[],
+              unsigned int const rank,
+              unsigned int const fileCount,
+              char         const inputFilePattern[]) {
+/*----------------------------------------------------------------------------
+   Open the input files for a single horizontal slice (there's one file
+   for each vertical slice) and read the Netpbm headers from them.  Return
+   the pam structures to describe each.
+-----------------------------------------------------------------------------*/
+    unsigned int file;
+
+    for (file = 0; file < fileCount; ++file) {
+        FILE * const ifP = openInputImage(inputFilePattern, rank, file);
+
+        pnm_readpaminit(ifP, &inpam[file], PAM_STRUCT_SIZE(tuple_type));
+    }        
+}
+
+
+
+static void
+closeInFiles(struct pam         pam[],
+             unsigned int const fileCount) {
+
+    unsigned int file;
+    
+    for (file = 0; file < fileCount; ++file)
+        pm_close(pam[file].file);
+}
+
+
+
+static void
+assembleRow(tuple              outputRow[], 
+            struct pam         inpam[], 
+            unsigned int const fileCount,
+            unsigned int const hOverlap) {
+/*----------------------------------------------------------------------------
+   Assemble the row outputRow[] from the 'fileCount' input files
+   described out inpam[].
+
+   'hOverlap', which is meaningful only when fileCount is greater than 1,
+   is the amount by which files overlap each other.  We assume every
+   input image is at least that wide.
+
+   We assume that outputRow[] is allocated wide enough to contain the
+   entire assembly.
+-----------------------------------------------------------------------------*/
+    tuple * inputRow;
+    unsigned int file;
+
+    for (file = 0, inputRow = &outputRow[0]; 
+         file < fileCount; 
+         ++file) {
+
+        unsigned int const overlap = file == fileCount - 1 ? 0 : hOverlap;
+
+        assert(hOverlap <= inpam[file].width);
+
+        pnm_readpamrow(&inpam[file], inputRow);
+
+        inputRow += inpam[file].width - overlap;
+    }
+}
+
+
+
+static void
+allocInpam(unsigned int  const rankCount,
+           struct pam ** const inpamArrayP) {
+
+    struct pam * inpamArray;
+
+    MALLOCARRAY(inpamArray, rankCount);
+
+    if (inpamArray == NULL)
+        pm_error("Unable to allocate array for %u input pam structures.",
+                 rankCount);
+
+    *inpamArrayP = inpamArray;
+}
+
+
+
+static void
+verifyRankFileAttributes(struct pam *       const inpam,
+                         unsigned int       const nFile,
+                         const struct pam * const outpamP,
+                         unsigned int       const hoverlap,
+                         unsigned int       const rank) {
+/*----------------------------------------------------------------------------
+   Verify that the 'nFile' images that make up a rank, which are described
+   by inpam[], are consistent with the properties of the assembled image
+   *outpamP.
+
+   I.e. verify that each image has the depth, maxval, format, and tuple
+   type of *outpamP and their total width is the width given by
+   *outpamP.
+
+   Also verify that every image has the same height.
+
+   Abort the program if verification fails.
+-----------------------------------------------------------------------------*/
+    unsigned int file;
+    unsigned int totalWidth;
+
+    for (file = 0, totalWidth = 0; file < nFile; ++file) {
+        struct pam * const inpamP = &inpam[file];
+
+        if (inpamP->depth != outpamP->depth)
+            pm_error("Rank %u, File %u image has depth %u, "
+                     "which differs from others (%u)",
+                     rank, file, inpamP->depth, outpamP->depth);
+        else if (inpamP->maxval != outpamP->maxval)
+            pm_error("Rank %u, File %u image has maxval %lu, "
+                     "which differs from others (%lu)",
+                     rank, file, inpamP->maxval, outpamP->maxval);
+        else if (inpamP->format != outpamP->format)
+            pm_error("Rank %u, File %u image has format 0x%x, "
+                     "which differs from others (0x%x)",
+                     rank, file, inpamP->format, outpamP->format);
+        else if (!streq(inpamP->tuple_type, outpamP->tuple_type))
+            pm_error("Rank %u, File %u image has tuple type '%s', "
+                     "which differs from others ('%s')",
+                     rank, file, inpamP->tuple_type, outpamP->tuple_type);
+
+        else if (inpamP->height != inpam[0].height)
+            pm_error("Rank %u, File %u image has height %u, "
+                     "which differs from that of File 0 in the same rank (%u)",
+                     rank, file, inpamP->height, inpam[0].height);
+        else {
+            totalWidth += inpamP->width;
+        
+            if (file < nFile-1)
+                totalWidth -= hoverlap;
+        }
+    }
+
+    if (totalWidth != outpamP->width)
+        pm_error("Rank %u has a total width (%u) different from that of "
+                 "other ranks (%u)", rank, totalWidth, outpamP->width);
+}
+
+
+
+static void
+assembleTiles(struct pam * const outpamP,
+              const char * const inputFilePattern,
+              unsigned int const nFile,
+              unsigned int const nRank,
+              unsigned int const hoverlap,
+              unsigned int const voverlap,
+              struct pam         inpam[],
+              tuple *      const tuplerow) {
+
+    unsigned int rank;
+        /* Number of the current rank (horizontal slice).  Ranks are numbered
+           sequentially starting at 0.
+        */
+    
+    for (rank = 0; rank < nRank; ++rank) {
+        unsigned int row;
+        unsigned int rankHeight;
+
+        openInStreams(inpam, rank, nFile, inputFilePattern);
+
+        verifyRankFileAttributes(inpam, nFile, outpamP, hoverlap, rank);
+
+        rankHeight = inpam[0].height - (rank == nRank-1 ? 0 : voverlap);
+
+        for (row = 0; row < rankHeight; ++row) {
+            assembleRow(tuplerow, inpam, nFile, hoverlap);
+
+            pnm_writepamrow(outpamP, tuplerow);
+        }
+        closeInFiles(inpam, nFile);
+    }
+}
+
+
+
+int
+main(int argc, char ** argv) {
+
+    struct cmdlineInfo cmdline;
+    struct pam outpam;
+    struct pam * inpam;
+        /* malloc'ed.  inpam[x] is the pam structure that controls the
+           current rank of file x. 
+        */
+    tuple * tuplerow;
+
+    pnm_init(&argc, argv);
+    
+    parseCommandLine(argc, argv, &cmdline);
+        
+    allocInpam(cmdline.across, &inpam);
+
+    initOutpam(cmdline.inputFilePattern, cmdline.across, cmdline.down,
+               cmdline.hoverlap, cmdline.voverlap, stdout, cmdline.verbose,
+               &outpam);
+    
+    tuplerow = pnm_allocpamrow(&outpam);
+
+    pnm_writepaminit(&outpam);
+
+    assembleTiles(&outpam,
+                  cmdline.inputFilePattern, cmdline.across, cmdline.down,
+                  cmdline.hoverlap, cmdline.voverlap, inpam, tuplerow);
+
+    pnm_freepamrow(tuplerow);
+
+    free(inpam);
+
+    return 0;
+}
diff --git a/editor/pbmclean.c b/editor/pbmclean.c
index 3ae3acfc..65b53e1c 100644
--- a/editor/pbmclean.c
+++ b/editor/pbmclean.c
@@ -6,6 +6,8 @@
  */
 
 #include <stdio.h>
+
+#include "pm_c_util.h"
 #include "pbm.h"
 #include "shhopt.h"
 
diff --git a/editor/pbmpscale.c b/editor/pbmpscale.c
index 63f203ed..2e24f3cd 100644
--- a/editor/pbmpscale.c
+++ b/editor/pbmpscale.c
@@ -81,119 +81,125 @@ void nextrow_pscale(ifd, row)
 
 }
 
+
+
 int
-main(argc, argv)
-     int argc;
-     char *argv[];
-{
-   FILE *ifd;
-   register bit *outrow;
-   register int row, col, i, k;
-   int scale, cutoff, ucutoff ;
-   unsigned char *flags;
-
-   pbm_init( &argc, argv );
-
-   if (argc < 2)
-      pm_usage("scale [pbmfile]");
-
-   scale = atoi(argv[1]);
-   if (scale < 1)
-      pm_perror("bad scale (< 1)");
-
-   if (argc == 3)
-      ifd = pm_openr(argv[2]);
-   else
-      ifd = stdin ;
-
-   inrow[0] = inrow[1] = inrow[2] = NULL;
-   pbm_readpbminit(ifd, &columns, &rows, &format) ;
-
-   outrow = pbm_allocrow(columns*scale) ;
-   MALLOCARRAY(flags, columns);
-   if (flags == NULL) 
-       pm_error("out of memory") ;
-
-   pbm_writepbminit(stdout, columns*scale, rows*scale, 0) ;
-
-   cutoff = scale / 2;
-   ucutoff = scale - 1 - cutoff;
-   nextrow_pscale(ifd, 0);
-   for (row = 0; row < rows; row++) {
-      nextrow_pscale(ifd, row+1);
-      for (col = 0; col < columns; col++) {
-         flags[col] = 0 ;
-         for (i = 0; i != 8; i += 2) {
-            int vec = inrow[thisrow][col] != PBM_WHITE;
-            for (k = 0; k < 7; k++) {
-               int x = col + xd_pscale[(k+i)&7] ;
-               int y = thisrow + yd_pscale[(k+i)&7] ;
-               vec <<= 1;
-               if (x >=0 && x < columns && inrow[y])
-                  vec |= (inrow[y][x] != PBM_WHITE) ;
+main(int argc, char ** argv) {
+
+    FILE * ifP;
+    bit * outrow;
+    unsigned int row;
+    int scale, cutoff, ucutoff ;
+    unsigned char *flags;
+
+    pbm_init( &argc, argv );
+
+    if (argc < 2)
+        pm_usage("scale [pbmfile]");
+
+    scale = atoi(argv[1]);
+    if (scale < 1)
+        pm_error("Scale argument must be at least one.  You specified '%s'",
+                 argv[1]);
+
+    if (argc == 3)
+        ifP = pm_openr(argv[2]);
+    else
+        ifP = stdin ;
+
+    inrow[0] = inrow[1] = inrow[2] = NULL;
+    pbm_readpbminit(ifP, &columns, &rows, &format) ;
+
+    outrow = pbm_allocrow(columns*scale) ;
+    MALLOCARRAY(flags, columns);
+    if (flags == NULL) 
+        pm_error("out of memory") ;
+
+    pbm_writepbminit(stdout, columns*scale, rows*scale, 0) ;
+
+    cutoff = scale / 2;
+    ucutoff = scale - 1 - cutoff;
+    nextrow_pscale(ifP, 0);
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+        unsigned int i;
+        nextrow_pscale(ifP, row+1);
+        for (col = 0; col < columns; ++col) {
+            unsigned int i;
+            flags[col] = 0 ;
+            for (i = 0; i != 8; i += 2) {
+                int vec = inrow[thisrow][col] != PBM_WHITE;
+                unsigned int k;
+                for (k = 0; k < 7; ++k) {
+                    int x = col + xd_pscale[(k+i)&7] ;
+                    int y = thisrow + yd_pscale[(k+i)&7] ;
+                    vec <<= 1;
+                    if (x >=0 && x < columns && inrow[y])
+                        vec |= (inrow[y][x] != PBM_WHITE) ;
+                }
+                flags[col] |= corner(vec)<<i ;
             }
-            flags[col] |= corner(vec)<<i ;
-         }
-      }
-      for (i = 0; i < scale; i++) {
-         bit *ptr = outrow ;
-         int zone = (i > ucutoff) - (i < cutoff) ;
-         int cut = (zone < 0) ? (cutoff - i) :
-                   (zone > 0) ? (i - ucutoff) : 0 ;
-
-         for (col = 0; col < columns; col++) {
-            int pix = inrow[thisrow][col] ;
-            int flag = flags[col] ;
-            int cutl, cutr ;
-
-            switch (zone) {
-            case -1:
-               switch (NW(flag)) {
-               case 0: cutl = 0; break;
-               case 1: cutl = cut; break;
-               case 2: cutl = cut ? cut-1 : 0; break;
-               case 3: cutl = (cut && cutoff > 1) ? cut-1 : cut; break;
-               default: cutl = 0;  /* Should never reach here */
-               }
-               switch (NE(flag)) {
-               case 0: cutr = 0; break;
-               case 1: cutr = cut; break;
-               case 2: cutr = cut ? cut-1 : 0; break;
-               case 3: cutr = (cut && cutoff > 1) ? cut-1 : cut; break;
-               default: cutr = 0;  /* Should never reach here */
-              }
-               break;
-            case 0:
-               cutl = cutr = 0;
-               break ;
-            case 1:
-               switch (SW(flag)) {
-               case 0: cutl = 0; break;
-               case 1: cutl = cut; break;
-               case 2: cutl = cut ? cut-1 : 0; break;
-               case 3: cutl = (cut && cutoff > 1) ? cut-1 : cut; break;
-               default: cutl = 0;  /* should never reach here */
-               }
-               switch (SE(flag)) {
-               case 0: cutr = 0; break;
-               case 1: cutr = cut; break;
-               case 2: cutr = cut ? cut-1 : 0; break;
-               case 3: cutr = (cut && cutoff > 1) ? cut-1 : cut; break;
-               default: cutr = 0;  /* should never reach here */
-               }
-               break;
-             default: cutl = 0; cutr = 0;  /* Should never reach here */
+        }
+        for (i = 0; i < scale; i++) {
+            bit *ptr = outrow ;
+            int zone = (i > ucutoff) - (i < cutoff) ;
+            int cut = (zone < 0) ? (cutoff - i) :
+                (zone > 0) ? (i - ucutoff) : 0 ;
+
+            for (col = 0; col < columns; ++col) {
+                int pix = inrow[thisrow][col] ;
+                int flag = flags[col] ;
+                int cutl, cutr;
+                unsigned int k;
+
+                switch (zone) {
+                case -1:
+                    switch (NW(flag)) {
+                    case 0: cutl = 0; break;
+                    case 1: cutl = cut; break;
+                    case 2: cutl = cut ? cut-1 : 0; break;
+                    case 3: cutl = (cut && cutoff > 1) ? cut-1 : cut; break;
+                    default: cutl = 0;  /* Should never reach here */
+                    }
+                    switch (NE(flag)) {
+                    case 0: cutr = 0; break;
+                    case 1: cutr = cut; break;
+                    case 2: cutr = cut ? cut-1 : 0; break;
+                    case 3: cutr = (cut && cutoff > 1) ? cut-1 : cut; break;
+                    default: cutr = 0;  /* Should never reach here */
+                    }
+                    break;
+                case 0:
+                    cutl = cutr = 0;
+                    break ;
+                case 1:
+                    switch (SW(flag)) {
+                    case 0: cutl = 0; break;
+                    case 1: cutl = cut; break;
+                    case 2: cutl = cut ? cut-1 : 0; break;
+                    case 3: cutl = (cut && cutoff > 1) ? cut-1 : cut; break;
+                    default: cutl = 0;  /* should never reach here */
+                    }
+                    switch (SE(flag)) {
+                    case 0: cutr = 0; break;
+                    case 1: cutr = cut; break;
+                    case 2: cutr = cut ? cut-1 : 0; break;
+                    case 3: cutr = (cut && cutoff > 1) ? cut-1 : cut; break;
+                    default: cutr = 0;  /* should never reach here */
+                    }
+                    break;
+                default: cutl = 0; cutr = 0;  /* Should never reach here */
+                }
+                for (k = 0; k < cutl; ++k) /* left part */
+                    *ptr++ = !pix ;
+                for (k = 0; k < scale-cutl-cutr; ++k)  /* center part */
+                    *ptr++ = pix ;
+                for (k = 0; k < cutr; ++k) /* right part */
+                    *ptr++ = !pix ;
             }
-            for (k = 0; k < cutl; k++) /* left part */
-               *ptr++ = !pix ;
-            for (k = 0; k < scale-cutl-cutr; k++)  /* centre part */
-               *ptr++ = pix ;
-            for (k = 0; k < cutr; k++) /* right part */
-               *ptr++ = !pix ;
-         }
-         pbm_writepbmrow(stdout, outrow, scale*columns, 0) ;
-      }
-   }
-   pm_close(ifd);
-   exit(0);
+            pbm_writepbmrow(stdout, outrow, scale*columns, 0) ;
+        }
+    }
+    pm_close(ifP);
+    return 0;
 }
diff --git a/editor/pbmreduce.c b/editor/pbmreduce.c
index 15ec2a1b..f49c8d9a 100644
--- a/editor/pbmreduce.c
+++ b/editor/pbmreduce.c
@@ -91,21 +91,22 @@ main( argc, argv )
     pbm_writepbminit( stdout, newcols, newrows, 0 );
     newbitrow = pbm_allocrow( newcols );
 
-    if ( halftone == QT_FS ) {
+    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" );
+        if (thiserr == NULL || nexterr == NULL)
+            pm_error("out of memory");
 
-        srand( (int) ( time( 0 ) ^ getpid( ) ) );
-        for ( col = 0; col < newcols + 2; ++col )
-          thiserr[col] = ( rand( ) % SCALE - HALFSCALE ) / 4;
+        srand(pm_randseed());
+        for (col = 0; col < newcols + 2; ++col)
+            thiserr[col] = (rand() % SCALE - HALFSCALE) / 4;
 	    /* (random errors in [-SCALE/8 .. SCALE/8]) */
 	} else {
         /* These variables are meaningless in this case, and the values
            should never be used.
-           */
+        */
         thiserr = NULL;
         nexterr = NULL;
     }
diff --git a/editor/pgmbentley.c b/editor/pgmbentley.c
deleted file mode 100644
index a008ed84..00000000
--- a/editor/pgmbentley.c
+++ /dev/null
@@ -1,68 +0,0 @@
-/* pgmbentley.c - read a portable graymap and smear it according to brightness
-**
-** Copyright (C) 1990 by Wilson Bent (whb@hoh-2.att.com)
-**
-** Permission to use, copy, modify, and distribute this software and its
-** documentation for any purpose and without fee is hereby granted, provided
-** that the above copyright notice appear in all copies and that both that
-** copyright notice and this permission notice appear in supporting
-** documentation.  This software is provided "as is" without express or
-** implied warranty.
-*/
-
-#include <stdio.h>
-#include "pgm.h"
-
-int
-main( argc, argv )
-    int argc;
-    char* argv[];
-    {
-    FILE* ifp;
-    gray maxval;
-    gray** gin;
-    gray** gout;
-    int argn, rows, cols, row;
-    register int brow, col;
-    const char* const usage = "[pgmfile]";
-
-
-    pgm_init( &argc, argv );
-
-    argn = 1;
-
-    if ( argn < argc )
-	{
-	ifp = pm_openr( argv[argn] );
-	++argn;
-	}
-    else
-	ifp = stdin;
-
-    if ( argn != argc )
-	pm_usage( usage );
-
-    gin = pgm_readpgm( ifp, &cols, &rows, &maxval );
-    pm_close( ifp );
-    gout = pgm_allocarray( cols, rows );
-
-#define N 4
-    for (row = 0; row < rows; ++row)
-        for (col = 0; col < cols; ++col)
-            gout[row][col] = 0;
-
-    for ( row = 0; row < rows; ++row )
-	for ( col = 0; col < cols; ++col )
-	    {
-	    brow = row + (int) (gin[row][col]) / N;
-	    if ( brow >= rows )
-		brow = rows - 1;
-	    gout[brow][col] = gin[row][col];
-	    }
-
-    pgm_writepgm( stdout, gout, cols, rows, maxval, 0 );
-    pm_close( stdout );
-    pgm_freearray( gout, rows );
-
-    exit( 0 );
-    }
diff --git a/editor/pgmdeshadow.c b/editor/pgmdeshadow.c
index 05eda944..948c6cba 100644
--- a/editor/pgmdeshadow.c
+++ b/editor/pgmdeshadow.c
@@ -85,7 +85,7 @@ parseCommandLine(int argc, char ** argv,
 
     option_def_index = 0;   /* incremented by OPTENT3 */
 
-    option_def[0].type = OPT_END;
+    OPTENTINIT;
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
diff --git a/editor/pgmmedian.c b/editor/pgmmedian.c
index 65d2eeb0..f911475d 100644
--- a/editor/pgmmedian.c
+++ b/editor/pgmmedian.c
@@ -26,6 +26,7 @@
 */
 
 
+#include "pm_c_util.h"
 #include "pgm.h"
 #include "shhopt.h"
 #include "mallocvar.h"
@@ -52,7 +53,6 @@ static int format;
 static gray maxval;
 static gray **grays;
 static gray *grayrow;
-static gray **rowptr;
 static int ccolso2, crowso2;
 static int row;
 
@@ -102,9 +102,9 @@ parseCommandLine(int argc, char ** argv,
         cmdlineP->cutoff = 250;
 
     if (typeSpec) {
-        if (STREQ(type, "histogram_sort"))
+        if (streq(type, "histogram_sort"))
             cmdlineP->type = HISTOGRAM_SORT_MEDIAN;
-        else if (STREQ(type, "select"))
+        else if (streq(type, "select"))
             cmdlineP->type = SELECT_MEDIAN;
         else
             pm_error("Invalid value '%s' for -type.  Valid values are "
@@ -125,10 +125,10 @@ parseCommandLine(int argc, char ** argv,
 
 
 static void
-select_489(gray * const a,
-           int *  const parray,
-           int    const n,
-           int    const k) {
+select489(gray * const a,
+          int *  const parray,
+          int    const n,
+          int    const k) {
 
     gray t;
     int i, j, l, r;
@@ -180,206 +180,220 @@ select_489(gray * const a,
 
 
 static void
-select_median(FILE * const ifp,
-              int    const ccols,
-              int    const crows,
-              int    const cols,
-              int    const rows,
-              int    const median) {
-
-    int ccol, col;
-    int crow;
-    int rownum, irow, temprow;
-    gray *temprptr;
-    int i, leftcol;
-    int num_values;
-    gray *garray;
-
-    int *parray;
-    int addcol;
-    int *subcol;
-    int tsum;
-
-    /* Allocate storage for array of the current gray values. */
-    garray = pgm_allocrow( crows * ccols );
+selectMedian(FILE *       const ifP,
+             unsigned int const ccols,
+             unsigned int const crows,
+             unsigned int const cols,
+             unsigned int const rows,
+             unsigned int const median) {
+
+    unsigned int const numValues = crows * ccols;
+
+    unsigned int col;
+    gray * garray;
+        /* Array of the currenty gray values */
+    int * parray;
+    int * subcol;
+    gray ** rowptr;
+    
+    garray = pgm_allocrow(numValues);
 
-    num_values = crows * ccols;
+    MALLOCARRAY(rowptr, crows);
+    MALLOCARRAY(parray, numValues);
+    MALLOCARRAY(subcol, cols);
 
-    parray = (int *) pm_allocrow( crows * ccols, sizeof(int) );
-    subcol = (int *) pm_allocrow( cols, sizeof(int) );
+    if (rowptr == NULL || parray == NULL || subcol == NULL)
+        pm_error("Unable to allocate memory");
 
-    for ( i = 0; i < cols; ++i )
-        subcol[i] = ( i - (ccolso2 + 1) ) % ccols;
+    for (col = 0; col < cols; ++col)
+        subcol[col] = (col - (ccolso2 + 1)) % ccols;
 
     /* Apply median to main part of image. */
-    for ( ; row < rows; ++row ) {
-        temprow = row % crows;
-        pgm_readpgmrow( ifp, grays[temprow], cols, maxval, format );
+    for ( ; row < rows; ++row) {
+        int crow;
+        int rownum, irow, temprow;
+        unsigned int col;
+    
+        pgm_readpgmrow(ifP, grays[row % crows], cols, maxval, format);
 
         /* Rotate pointers to rows, so rows can be accessed in order. */
-        temprow = ( row + 1 ) % crows;
+        temprow = (row + 1) % crows;
         rownum = 0;
-        for ( irow = temprow; irow < crows; ++rownum, ++irow )
+        for (irow = temprow; irow < crows; ++rownum, ++irow)
             rowptr[rownum] = grays[irow];
-        for ( irow = 0; irow < temprow; ++rownum, ++irow )
+        for (irow = 0; irow < temprow; ++rownum, ++irow)
             rowptr[rownum] = grays[irow];
 
-        for ( col = 0; col < cols; ++col ) {
-            if ( col < ccolso2 || col >= cols - ccolso2 ) {
+        for (col = 0; col < cols; ++col) {
+            if (col < ccolso2 || col >= cols - ccolso2) {
                 grayrow[col] = rowptr[crowso2][col];
-            } else if ( col == ccolso2 ) {
-                leftcol = col - ccolso2;
+            } else if (col == ccolso2) {
+                unsigned int const leftcol = col - ccolso2;
+                unsigned int i;
                 i = 0;
-                for ( crow = 0; crow < crows; ++crow ) {
-                    temprptr = rowptr[crow] + leftcol;
-                    for ( ccol = 0; ccol < ccols; ++ccol ) {
-                        garray[i] = *( temprptr + ccol );
+                for (crow = 0; crow < crows; ++crow) {
+                    gray * const temprptr = rowptr[crow] + leftcol;
+                    unsigned int ccol;
+                    for (ccol = 0; ccol < ccols; ++ccol) {
+                        garray[i] = *(temprptr + ccol);
                         parray[i] = i;
                         ++i;
                     }
                 }
-                select_489( garray, parray, num_values, median );
+                select489(garray, parray, numValues, median);
                 grayrow[col] = garray[parray[median]];
             } else {
-                addcol = col + ccolso2;
+                unsigned int const addcol = col + ccolso2;
+                unsigned int crow;
+                unsigned int tsum;
                 for (crow = 0, tsum = 0; crow < crows; ++crow, tsum += ccols)
                     garray[tsum + subcol[col]] = *(rowptr[crow] + addcol );
-                select_489( garray, parray, num_values, median );
+                select489( garray, parray, numValues, median );
                 grayrow[col] = garray[parray[median]];
             }
         }
         pgm_writepgmrow( stdout, grayrow, cols, maxval, forceplain );
     }
 
-    /* Write out remaining unchanged rows. */
-    for ( irow = crowso2 + 1; irow < crows; ++irow )
-        pgm_writepgmrow( stdout, rowptr[irow], cols, maxval, forceplain );
-
-    pgm_freerow( garray );
-    pm_freerow( (char *) parray );
-    pm_freerow( (char *) subcol );
+    {
+        unsigned int irow;
+        /* Write out remaining unchanged rows. */
+        for (irow = crowso2 + 1; irow < crows; ++irow)
+            pgm_writepgmrow(stdout, rowptr[irow], cols, maxval, forceplain);
+    }
+    free(subcol);
+    free(parray);
+    free(rowptr);
+    pgm_freerow(garray);
 }
 
 
 
 static void
-histogram_sort_median(FILE * const ifp,
-                      int    const ccols,
-                      int    const crows,
-                      int    const cols,
-                      int    const rows,
-                      int    const median) {
+histogramSortMedian(FILE *       const ifP,
+                    unsigned int const ccols,
+                    unsigned int const crows,
+                    unsigned int const cols,
+                    unsigned int const rows,
+                    unsigned int const median) {
 
-    int const histmax = maxval + 1;
+    unsigned int const histmax = maxval + 1;
 
-    int *hist;
-    int mdn, ltmdn;
-    gray *left_col, *right_col;
+    unsigned int * hist;
+    unsigned int mdn, ltmdn;
+    gray * leftCol;
+    gray * rghtCol;
+    gray ** rowptr;
 
-    hist = (int *) pm_allocrow( histmax, sizeof( int ) );
-    left_col = pgm_allocrow( crows );
-    right_col = pgm_allocrow( crows );
+    MALLOCARRAY(rowptr, crows);
+    MALLOCARRAY(hist, histmax);
+
+    if (rowptr == NULL || hist == NULL)
+        pm_error("Unable to allocate memory");
+
+    leftCol = pgm_allocrow(crows);
+    rghtCol = pgm_allocrow(crows);
 
     /* Apply median to main part of image. */
-    for ( ; row < rows; ++row ) {
-        int col;
-        int temprow;
-        int rownum;
-        int irow;
-        int i;
+    for ( ; row < rows; ++row) {
+        unsigned int col;
+        unsigned int temprow;
+        unsigned int rownum;
+        unsigned int irow;
+        unsigned int i;
         /* initialize hist[] */
-        for ( i = 0; i < histmax; ++i )
+        for (i = 0; i < histmax; ++i)
             hist[i] = 0;
 
-        temprow = row % crows;
-        pgm_readpgmrow( ifp, grays[temprow], cols, maxval, format );
+        pgm_readpgmrow(ifP, grays[row % crows], cols, maxval, format);
 
         /* Rotate pointers to rows, so rows can be accessed in order. */
-        temprow = ( row + 1 ) % crows;
+        temprow = (row + 1) % crows;
         rownum = 0;
-        for ( irow = temprow; irow < crows; ++rownum, ++irow )
+        for (irow = temprow; irow < crows; ++rownum, ++irow)
             rowptr[rownum] = grays[irow];
-        for ( irow = 0; irow < temprow; ++rownum, ++irow )
+        for (irow = 0; irow < temprow; ++rownum, ++irow)
             rowptr[rownum] = grays[irow];
 
-        for ( col = 0; col < cols; ++col ) {
-            if ( col < ccolso2 || col >= cols - ccolso2 )
+        for (col = 0; col < cols; ++col) {
+            if (col < ccolso2 || col >= cols - ccolso2)
                 grayrow[col] = rowptr[crowso2][col];
-            else if ( col == ccolso2 ) {
-                int crow;
-                int const leftcol = col - ccolso2;
+            else if (col == ccolso2) {
+                unsigned int crow;
+                unsigned int const leftcol = col - ccolso2;
                 i = 0;
-                for ( crow = 0; crow < crows; ++crow ) {
-                    int ccol;
+                for (crow = 0; crow < crows; ++crow) {
+                    unsigned int ccol;
                     gray * const temprptr = rowptr[crow] + leftcol;
-                    for ( ccol = 0; ccol < ccols; ++ccol ) {
-                        gray const g = *( temprptr + ccol );
+                    for (ccol = 0; ccol < ccols; ++ccol) {
+                        gray const g = *(temprptr + ccol);
                         ++hist[g];
                         ++i;
                     }
                 }
                 ltmdn = 0;
-                for ( mdn = 0; ltmdn <= median; ++mdn )
+                for (mdn = 0; ltmdn <= median; ++mdn)
                     ltmdn += hist[mdn];
-                mdn--;
-                if ( ltmdn > median ) 
+                --mdn;
+                if (ltmdn > median) 
                     ltmdn -= hist[mdn];
 
                 grayrow[col] = mdn;
             } else {
-                int crow;
-                int const subcol = col - ( ccolso2 + 1 );
-                int const addcol = col + ccolso2;
-                for ( crow = 0; crow < crows; ++crow ) {
-                    left_col[crow] = *( rowptr[crow] + subcol );
-                    right_col[crow] = *( rowptr[crow] + addcol );
+                unsigned int crow;
+                unsigned int const subcol = col - (ccolso2 + 1);
+                unsigned int const addcol = col + ccolso2;
+                for (crow = 0; crow < crows; ++crow) {
+                    leftCol[crow] = *(rowptr[crow] + subcol);
+                    rghtCol[crow] = *(rowptr[crow] + addcol);
                 }
-                for ( crow = 0; crow < crows; ++crow ) {
+                for (crow = 0; crow < crows; ++crow) {
                     {
-                        gray const g = left_col[crow];
-                        hist[(int) g]--;
-                        if ( (int) g < mdn )
-                            ltmdn--;
+                        gray const g = leftCol[crow];
+                        --hist[(unsigned int) g];
+                        if ((unsigned int) g < mdn)
+                            --ltmdn;
                     }
                     {
-                        gray const g = right_col[crow];
-                        hist[(int) g]++;
-                        if ( (int) g < mdn )
-                            ltmdn++;
+                        gray const g = rghtCol[crow];
+                        ++hist[(unsigned int) g];
+                        if ((unsigned int) g < mdn)
+                            ++ltmdn;
                     }
                 }
-                if ( ltmdn > median )
+                if (ltmdn > median)
                     do {
-                        mdn--;
+                        --mdn;
                         ltmdn -= hist[mdn];
-                    } while ( ltmdn > median );
+                    } while (ltmdn > median);
                 else {
                     /* This one change from Pitas algorithm can reduce run
                     ** time by up to 10%.
                     */
-                    while ( ltmdn <= median ) {
+                    while (ltmdn <= median) {
                         ltmdn += hist[mdn];
-                        mdn++;
+                        ++mdn;
                     }
-                    mdn--;
-                    if ( ltmdn > median ) 
+                    --mdn;
+                    if (ltmdn > median) 
                         ltmdn -= hist[mdn];
                 }
                 grayrow[col] = mdn;
             }
         }
-        pgm_writepgmrow( stdout, grayrow, cols, maxval, forceplain );
+        pgm_writepgmrow(stdout, grayrow, cols, maxval, forceplain);
     }
 
     {
         /* Write out remaining unchanged rows. */
-        int irow;
-        for ( irow = crowso2 + 1; irow < crows; ++irow )
-            pgm_writepgmrow( stdout, rowptr[irow], cols, maxval, forceplain );
+        unsigned int irow;
+        for (irow = crowso2 + 1; irow < crows; ++irow)
+            pgm_writepgmrow(stdout, rowptr[irow], cols, maxval, forceplain);
     }
-    pm_freerow( (char *) hist );
-    pgm_freerow( left_col );
-    pgm_freerow( right_col );
+    pgm_freerow(leftCol);
+    pgm_freerow(rghtCol);
+    free(hist);
+    free(rowptr);
 }
 
 
@@ -410,9 +424,6 @@ main(int    argc,
     grays = pgm_allocarray(cols, cmdline.height);
     grayrow = pgm_allocrow(cols);
 
-    /* Allocate pointers to mask row buffer. */
-    rowptr = (gray **) pm_allocrow(cmdline.height, sizeof(gray *));
-
     /* Read in and write out initial rows that won't get changed. */
     for (row = 0; row < cmdline.height - 1; ++row) {
         pgm_readpgmrow(ifP, grays[row], cols, maxval, format);
@@ -434,12 +445,12 @@ main(int    argc,
 
     switch (medianMethod) {
     case SELECT_MEDIAN:
-        select_median(ifP, cmdline.width, cmdline.height, cols, rows, median);
+        selectMedian(ifP, cmdline.width, cmdline.height, cols, rows, median);
         break;
         
     case HISTOGRAM_SORT_MEDIAN:
-        histogram_sort_median(ifP, cmdline.width, cmdline.height,
-                              cols, rows, median);
+        histogramSortMedian(ifP, cmdline.width, cmdline.height,
+                            cols, rows, median);
         break;
     case MEDIAN_UNSPECIFIED:
         pm_error("INTERNAL ERROR: median unspecified");
@@ -450,13 +461,6 @@ main(int    argc,
 
     pgm_freearray(grays, cmdline.height);
     pgm_freerow(grayrow);
-    pm_freerow(rowptr);
 
     return 0;
 }
-
-
-
-
-
-
diff --git a/editor/pnmcat.c b/editor/pnmcat.c
index 20dbf34d..3cf443bb 100644
--- a/editor/pnmcat.c
+++ b/editor/pnmcat.c
@@ -1,4 +1,4 @@
-/* pnmcat.c - concatenate portable anymaps
+/* pnmcat.c - concatenate PNM images
 **
 ** Copyright (C) 1989, 1991 by Jef Poskanzer.
 **
@@ -10,22 +10,41 @@
 ** implied warranty.
 */
 
-#include "pnm.h"
+#include <assert.h>
+
+#include "pm_c_util.h"
 #include "mallocvar.h"
 #include "shhopt.h"
+#include "bitarith.h"
+#include "pnm.h"
 
+#define LEFTBITS pm_byteLeftBits
+#define RIGHTBITS pm_byteRightBits
 
-enum backcolor {BACK_BLACK, BACK_WHITE, BACK_AUTO};
+enum backcolor {BACK_WHITE, BACK_BLACK, BACK_AUTO};
 
 enum orientation {TOPBOTTOM, LEFTRIGHT};
 
 enum justification {JUST_CENTER, JUST_MIN, JUST_MAX};
 
+struct imgInfo {
+    /* This obviously should be a struct pam.  We should convert this
+       to 'pamcat'.
+    */
+    FILE * ifP;
+    int    cols;
+    int    rows;
+    int    format;
+    xelval maxval;
+};
+
+
+
 struct cmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    const char **inputFilespec;  
+    const char ** inputFilespec;
     unsigned int nfiles;
     enum backcolor backcolor;
     enum orientation orientation;
@@ -35,22 +54,24 @@ struct cmdlineInfo {
 
 
 static void
-parseCommandLine(int argc, char ** const argv,
+parseCommandLine(int argc, const char ** const argv,
                  struct cmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec array we return is stored in the storage that
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
-    optEntry *option_def = malloc(100*sizeof(optEntry));
-        /* Instructions to OptParseOptions2 on how to parse our options.
+    optEntry * option_def;
+        /* Instructions to OptParseOptions3() on how to parse our options.
          */
     optStruct3 opt;
 
     unsigned int option_def_index;
-    
+
     unsigned int leftright, topbottom, black, white, jtop, jbottom,
         jleft, jright, jcenter;
 
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
     option_def_index = 0;   /* incremented by OPTENTRY */
     OPTENT3(0, "leftright",  OPT_FLAG,   NULL, &leftright,   0);
     OPTENT3(0, "lr",         OPT_FLAG,   NULL, &leftright,   0);
@@ -68,7 +89,7 @@ parseCommandLine(int argc, char ** const argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (leftright + topbottom > 1)
@@ -134,28 +155,25 @@ parseCommandLine(int argc, char ** const argv,
         unsigned int i;
 
         MALLOCARRAY_NOFAIL(cmdlineP->inputFilespec, argc-1);
-        
+
         for (i = 0; i < argc-1; ++i)
             cmdlineP->inputFilespec[i] = argv[1+i];
         cmdlineP->nfiles = argc-1;
     }
-}        
+}
 
 
 
 static void
 computeOutputParms(unsigned int     const nfiles,
                    enum orientation const orientation,
-                   int                    cols[], 
-                   int                    rows[],
-                   xelval                 maxval[],
-                   int                    format[],
-                   int *            const newcolsP,
-                   int *            const newrowsP,
+                   struct imgInfo   const img[],
+                   unsigned int *   const newcolsP,
+                   unsigned int *   const newrowsP,
                    xelval *         const newmaxvalP,
                    int *            const newformatP) {
 
-    int newcols, newrows;
+    double newcols, newrows;
     int newformat;
     xelval newmaxval;
 
@@ -164,31 +182,43 @@ computeOutputParms(unsigned int     const nfiles,
     newcols = 0;
     newrows = 0;
 
-    for (i = 0; i < nfiles; ++i)	{
+    for (i = 0; i < nfiles; ++i) {
+        const struct imgInfo * const imgP = &img[i];
+
         if (i == 0) {
-            newmaxval = maxval[i];
-            newformat = format[i];
-	    } else {
-            if (PNM_FORMAT_TYPE(format[i]) > PNM_FORMAT_TYPE(newformat))
-                newformat = format[i];
-            if (maxval[i] > newmaxval)
-                newmaxval = maxval[i];
-	    }
+            newmaxval = imgP->maxval;
+            newformat = imgP->format;
+        } else {
+            if (PNM_FORMAT_TYPE(imgP->format) > PNM_FORMAT_TYPE(newformat))
+                newformat = imgP->format;
+            if (imgP->maxval > newmaxval)
+                newmaxval = imgP->maxval;
+        }
         switch (orientation) {
         case LEFTRIGHT:
-            newcols += cols[i];
-            if (rows[i] > newrows)
-                newrows = rows[i];
+            newcols += imgP->cols;
+            if (imgP->rows > newrows)
+                newrows = imgP->rows;
             break;
         case TOPBOTTOM:
-            newrows += rows[i];
-            if (cols[i] > newcols)
-                newcols = cols[i];
+            newrows += imgP->rows;
+            if (imgP->cols > newcols)
+                newcols = imgP->cols;
             break;
-	    }
-	}
-    *newrowsP   = newrows;
-    *newcolsP   = newcols;
+        }
+    }
+
+    /* Note that while 'double' is not in general a precise numerical type,
+       in the case of a sum of integers which is less than INT_MAX, it
+       is exact, because double's precision is greater than int's.
+    */
+    if (newcols > INT_MAX)
+       pm_error("Output width too large: %.0f.", newcols);
+    if (newrows > INT_MAX)
+       pm_error("Output height too large: %.0f.", newrows);
+
+    *newrowsP   = (unsigned int)newrows;
+    *newcolsP   = (unsigned int)newcols;
     *newmaxvalP = newmaxval;
     *newformatP = newformat;
 }
@@ -196,231 +226,631 @@ computeOutputParms(unsigned int     const nfiles,
 
 
 static void
-concatenateLeftRight(FILE *             const ofp,
-                     unsigned int       const nfiles,
-                     int                const newcols,
-                     int                const newrows,
-                     xelval             const newmaxval,
-                     int                const newformat,
-                     enum justification const justification,
-                     FILE *                   ifp[],
-                     int                      cols[],
-                     int                      rows[],
-                     xelval                   maxval[],
-                     int                      format[],
-                     xel *                    xelrow[],
-                     xel                      background[]) {
+copyBitrow(const unsigned char * const source,
+           unsigned char *       const destBitrow,
+           unsigned int          const cols,
+           unsigned int          const offset) {
+/*----------------------------------------------------------------------------
+  Copy from source to destBitrow, without shifting.  Preserve
+  surrounding image data.
+-----------------------------------------------------------------------------*/
+    unsigned char * const dest = & destBitrow[ offset/8 ];
+        /* Copy destination, with leading full bytes ignored. */
+    unsigned int const rs = offset % 8;
+        /* The "little offset", as measured from start of dest.  Source
+           is already shifted by this value.
+        */
+    unsigned int const trs = (cols + rs) % 8;
+        /* The number of partial bits in the final char. */
+    unsigned int const colByteCnt = pbm_packed_bytes(cols + rs);
+        /* # bytes to process, including partial ones on both ends. */
+    unsigned int const last = colByteCnt - 1;
+
+    unsigned char const origHead = dest[0];
+    unsigned char const origEnd  = dest[last];
+
+    unsigned int i;
+
+    assert(colByteCnt >= 1);
+
+    for (i = 0; i < colByteCnt; ++i)
+        dest[i] = source[i];
+
+    if (rs > 0)
+        dest[0] = LEFTBITS(origHead, rs) | RIGHTBITS(dest[0], 8-rs);
+
+    if (trs > 0)
+        dest[last] = LEFTBITS(dest[last], trs) | RIGHTBITS(origEnd, 8-trs);
+}
+
+
+
+static void
+padFillBitrow(unsigned char * const destBitrow,
+              unsigned char   const padColor,
+              unsigned int    const cols,
+              unsigned int    const offset) {
+/*----------------------------------------------------------------------------
+   Fill destBitrow, starting at offset, with padColor.  padColor is a
+   byte -- 0x00 or 0xff -- not a single bit.
+-----------------------------------------------------------------------------*/
+    unsigned char * const dest = &destBitrow[offset/8];
+    unsigned int const rs = offset % 8;
+    unsigned int const trs = (cols + rs) % 8;
+    unsigned int const colByteCnt = pbm_packed_bytes(cols + rs);
+    unsigned int const last = colByteCnt - 1;
+
+    unsigned char const origHead = dest[0];
+    unsigned char const origEnd  = dest[last];
+
+    unsigned int i;
+
+    assert(colByteCnt > 0);
+
+    for (i = 0; i < colByteCnt; ++i)
+        dest[i] = padColor;
+
+    if (rs > 0)
+        dest[0] = LEFTBITS(origHead, rs) | RIGHTBITS(dest[0], 8-rs);
+
+    if (trs > 0)
+        dest[last] = LEFTBITS(dest[last], trs) | RIGHTBITS(origEnd, 8-trs);
+}
+
+
+
+/* concatenateLeftRightPBM() and concatenateLeftRightGen()
+   employ almost identical algorithms.
+   The difference is in the data types and functions.
+
+   Same for concatenateTopBottomPBM() and concatenateTopBottomGen().
+*/
 
+
+struct imgInfoPbm2 {
+    /* Information about one image */
+    unsigned char * proberow;
+        /* Top row of image, when background color is
+           auto-determined.
+        */
+    unsigned int offset;
+        /* start position of image, in bits, counting from left
+           edge
+        */
+    unsigned char background;
+        /* Background color.  0x00 means white; 0xff means black */
+    unsigned int padtop;
+        /* Top padding amount */
+};
+
+
+
+static void
+getPbmImageInfo(struct imgInfo        const img[],
+                unsigned int          const nfiles,
+                unsigned int          const newrows,
+                enum justification    const justification,
+                enum backcolor        const backcolor,
+                struct imgInfoPbm2 ** const img2P) {
+/*----------------------------------------------------------------------------
+   Read the first row of each image in img[] and return that and additional
+   information about images as *img2P.
+-----------------------------------------------------------------------------*/
+    struct imgInfoPbm2 * img2;
+    unsigned int i;
+
+    MALLOCARRAY_NOFAIL(img2, nfiles);
+
+    for (i = 0; i < nfiles; ++i) {
+        switch (justification) {
+        case JUST_MIN:    img2[i].padtop = 0;                           break;
+        case JUST_MAX:    img2[i].padtop = newrows - img[i].rows;       break;
+        case JUST_CENTER: img2[i].padtop = (newrows - img[i].rows) / 2; break;
+        }
+        
+        img2[i].offset = (i == 0) ? 0 : img2[i-1].offset + img[i-1].cols;
+        
+        if (img[i].rows == newrows)  /* no padding */
+            img2[i].proberow = NULL;
+        else {                   /* determine pad color for image i */
+            switch (backcolor) {
+            case BACK_AUTO: {
+                bit bgBit;
+                img2[i].proberow = pbm_allocrow_packed(img[i].cols+7);
+                pbm_readpbmrow_bitoffset(
+                    img[i].ifP, img2[i].proberow,
+                    img[i].cols, img[i].format, img2[i].offset % 8);
+
+                bgBit = pbm_backgroundbitrow(
+                    img2[i].proberow, img[i].cols, img2[i].offset % 8);
+
+                img2[i].background = bgBit == PBM_BLACK ? 0xff : 0x00;
+            } break;
+            case BACK_BLACK:
+                img2[i].proberow   = NULL;
+                img2[i].background = 0xff;
+                break;
+            case BACK_WHITE:
+                img2[i].proberow   = NULL;
+                img2[i].background = 0x00;
+                break;
+            }
+        }
+    }
+    *img2P = img2;
+}
+
+
+
+static void
+destroyPbmImg2(struct imgInfoPbm2 * const img2,
+               unsigned int         const nfiles) {
+
+    unsigned int i;
+
+    for (i = 0; i < nfiles; ++i) {
+        if (img2[i].proberow)
+            free(img2[i].proberow);
+    }
+    free(img2);
+}
+
+
+
+static void
+concatenateLeftRightPbm(FILE *             const ofP,
+                        unsigned int       const nfiles,
+                        unsigned int       const newcols,
+                        unsigned int       const newrows,
+                        enum justification const justification,
+                        struct imgInfo     const img[],   
+                        enum backcolor     const backcolor) {
+
+    unsigned char * const outrow = pbm_allocrow_packed(newcols);
+        /* We use just one outrow.  All padding and image data (with the
+           exeption of following img2.proberow) goes directly into this
+           packed PBM row. 
+        */
+
+    struct imgInfoPbm2 * img2;
+        /* malloc'ed array, one element per image.  Shadows img[] */
     unsigned int row;
-    
-    xel * const newxelrow = pnm_allocrow(newcols);
+
+    getPbmImageInfo(img, nfiles, newrows, justification, backcolor, &img2);
 
     for (row = 0; row < newrows; ++row) {
-        unsigned int new;
         unsigned int i;
 
-        new = 0;
         for (i = 0; i < nfiles; ++i) {
-            int padtop;
 
+            if ((row == 0 && img2[i].padtop > 0) ||
+                row == img2[i].padtop + img[i].rows) {
+
+                /* This row begins a run of padding, either above or below
+                   file 'i', so set 'outrow' to padding.
+                */
+                padFillBitrow(outrow, img2[i].background, img[i].cols,
+                              img2[i].offset);
+            }
+
+            if (row == img2[i].padtop && img2[i].proberow != NULL) {
+                /* Top row has been read to proberow[] to determine
+                   background.  Copy it to outrow[].
+                */
+                copyBitrow(img2[i].proberow, outrow,
+                           img[i].cols, img2[i].offset);
+            } else if (row >= img2[i].padtop &&
+                       row < img2[i].padtop + img[i].rows) {
+                pbm_readpbmrow_bitoffset(
+                    img[i].ifP, outrow, img[i].cols, img[i].format,
+                    img2[i].offset);
+            } else {
+                /* It's a row of padding, so outrow[] is already set
+                   appropriately.
+                */
+            }
+        }
+        pbm_writepbmrow_packed(ofP, outrow, newcols, 0);
+    }
+
+    destroyPbmImg2(img2, nfiles);
+
+    pbm_freerow_packed(outrow);
+}
+
+
+
+static void
+concatenateTopBottomPbm(FILE *             const ofP,
+                        unsigned int       const nfiles,
+                        int                const newcols,
+                        int                const newrows,
+                        enum justification const justification,
+                        struct imgInfo     const img[],
+                        enum backcolor     const backcolor) {
+
+    unsigned char * const outrow = pbm_allocrow_packed(newcols);
+        /* Like the left-right PBM case, all padding and image data
+           goes directly into outrow.  There is no proberow.
+        */
+    unsigned char background, backgroundPrev;
+        /* 0x00 means white; 0xff means black */
+    unsigned int  padleft;
+    bool          backChange;
+        /* Background color is different from that of the previous
+           input image.
+        */
+
+    unsigned int i;
+    unsigned int row, startRow;
+    
+    outrow[pbm_packed_bytes(newcols)-1] = 0x00;
+
+    switch (backcolor){
+    case BACK_AUTO:   /* do nothing */    break;
+    case BACK_BLACK:  background = 0xff;  break;
+    case BACK_WHITE:  background = 0x00;  break;
+    }
+
+    for (i = 0; i < nfiles; ++i) {
+        if (img[i].cols == newcols) {
+            /* No padding */
+            startRow = 0;
+            backChange = FALSE;
+            padleft = 0;
+            outrow[pbm_packed_bytes(newcols)-1] = 0x00;
+        } else {
+            /* Determine amount of padding and color */
             switch (justification) {
+            case JUST_MIN:     padleft = 0;                           break;
+            case JUST_MAX:     padleft = newcols - img[i].cols;       break;
+            case JUST_CENTER:  padleft = (newcols - img[i].cols) / 2; break;
+            }
+
+            switch (backcolor) {
+            case BACK_AUTO: {
+                bit bgBit;
+
+                startRow = 1;
+                
+                pbm_readpbmrow_bitoffset(img[i].ifP,
+                                         outrow, img[i].cols, img[i].format,
+                                         padleft);
+
+                bgBit = pbm_backgroundbitrow(outrow, img[i].cols, padleft);
+                background = bgBit == PBM_BLACK ? 0xff : 0x00;
+
+                backChange = (i == 0 || background != backgroundPrev);
+            } break;
+            case BACK_WHITE:
+            case BACK_BLACK:
+                startRow = 0;
+                backChange = (i==0);
+                break;
+            }
+
+            if (backChange || (i > 0 && img[i-1].cols > img[i].cols)) {
+                unsigned int const padright = newcols - padleft - img[i].cols;
+                
+                if (padleft > 0)
+                    padFillBitrow(outrow, background, padleft, 0);
+                
+                if (padright > 0)            
+                    padFillBitrow(outrow, background, padright,
+                                  padleft + img[i].cols);
+                
+            }
+        }
+            
+        if (startRow == 1)
+            /* Top row already read for auto background color
+               determination.  Write it out.
+            */
+            pbm_writepbmrow_packed(ofP, outrow, newcols, 0);
+        
+        for (row = startRow; row < img[i].rows; ++row) {
+            pbm_readpbmrow_bitoffset(img[i].ifP, outrow, img[i].cols,
+                                     img[i].format, padleft);
+            pbm_writepbmrow_packed(ofP, outrow, newcols, 0);
+        }
+
+        backgroundPrev = background;
+    }
+    free(outrow);
+}
+
+
+
+struct imgGen2 {
+    xel * xelrow;
+    xel * inrow;
+    xel   background;
+    int   padtop;
+};
+
+
+
+static void
+getGenImgInfo(struct imgInfo     const img[],
+              unsigned int       const nfiles,
+              xel *              const newxelrow,
+              unsigned int       const newrows,
+              xelval             const newmaxval,
+              int                const newformat,
+              enum justification const justification,
+              enum backcolor     const backcolor,
+              struct imgGen2 **  const img2P) {
+
+    struct imgGen2 * img2;
+    unsigned int i;
+
+    MALLOCARRAY_NOFAIL(img2, nfiles);
+
+    for (i = 0; i < nfiles; ++i) {
+        switch (justification) {  /* Determine top padding */
             case JUST_MIN:
-                padtop = 0;
+                img2[i].padtop = 0;
                 break;
             case JUST_MAX:
-                padtop = newrows - rows[i];
+                img2[i].padtop = newrows - img[i].rows;
                 break;
             case JUST_CENTER:
-                padtop = ( newrows - rows[i] ) / 2;
+                img2[i].padtop = (newrows - img[i].rows) / 2;
+                break;
+        }
+
+        img2[i].inrow =
+            (i == 0 ? &newxelrow[0] : img2[i-1].inrow + img[i-1].cols);
+
+        if (img[i].rows == newrows)  /* no padding */
+            img2[i].xelrow = NULL;
+        else {
+            /* Determine pad color */
+            switch (backcolor){
+            case BACK_AUTO:
+                img2[i].xelrow = pnm_allocrow(img[i].cols);
+                pnm_readpnmrow(img[i].ifP, img2[i].xelrow,
+                               img[i].cols, img[i].maxval, img[i].format);
+                pnm_promoteformatrow(img2[i].xelrow, img[i].cols,
+                                     img[i].maxval, img[i].format,
+                                     newmaxval, newformat);
+                img2[i].background = pnm_backgroundxelrow(
+                    img2[i].xelrow, img[i].cols, newmaxval, newformat);
+                break;
+            case BACK_BLACK:
+                img2[i].xelrow = NULL;
+                img2[i].background = pnm_blackxel(newmaxval, newformat);
+                break;
+            case BACK_WHITE:
+                img2[i].xelrow = NULL;
+                img2[i].background = pnm_whitexel(newmaxval, newformat);
                 break;
             }
-            if (row < padtop || row >= padtop + rows[i]) {
+        }
+    }
+    *img2P = img2;
+}
+
+
+
+static void
+concatenateLeftRightGen(FILE *             const ofP,
+                        unsigned int       const nfiles,
+                        unsigned int       const newcols,
+                        unsigned int       const newrows,
+                        xelval             const newmaxval,
+                        int                const newformat,
+                        enum justification const justification,
+                        struct imgInfo     const img[],
+                        enum backcolor     const backcolor) {
+
+    xel * const outrow = pnm_allocrow(newcols);
+    struct imgGen2 * img2;
+    unsigned int row;
+
+    getGenImgInfo(img, nfiles, outrow, newrows,
+                  newmaxval, newformat, justification, backcolor, &img2);
+
+    for (row = 0; row < newrows; ++row) {
+        unsigned int i;
+
+        for (i = 0; i < nfiles; ++i) {
+            if ((row == 0 && img2[i].padtop > 0) ||
+                row == img2[i].padtop + img[i].rows) {
+                /* This row begins a run of padding, either above or below
+                   file 'i', so set 'outrow' to padding.
+                */
+                unsigned int col;
+                for (col = 0; col < img[i].cols; ++col)
+                    img2[i].inrow[col] = img2[i].background;
+            }
+            if (row == img2[i].padtop && img2[i].xelrow) {
+                /* We're at the top row of file 'i', and that row
+                   has already been read to xelrow[] to determine
+                   background.  Copy it to 'outrow'.
+                */
                 unsigned int col;
-                for (col = 0; col < cols[i]; ++col)
-                    newxelrow[new+col] = background[i];
+                for (col = 0; col < img[i].cols; ++col)
+                    img2[i].inrow[col] = img2[i].xelrow[col];
+                
+                free(img2[i].xelrow);
+            } else if (row >= img2[i].padtop &&
+                       row < img2[i].padtop + img[i].rows) {
+                pnm_readpnmrow(
+                    img[i].ifP, img2[i].inrow, img[i].cols, img[i].maxval,
+                    img[i].format);
+                pnm_promoteformatrow(
+                    img2[i].inrow, img[i].cols, img[i].maxval,
+                    img[i].format, newmaxval, newformat);
             } else {
-                if (row != padtop) {
-                    /* first row already read */
-                    pnm_readpnmrow(
-                        ifp[i], xelrow[i], cols[i], maxval[i], format[i] );
-                    pnm_promoteformatrow(
-                        xelrow[i], cols[i], maxval[i], format[i],
-                        newmaxval, newformat );
-                }
-                {
-                    unsigned int col;
-                    for (col = 0; col < cols[i]; ++col)
-                        newxelrow[new+col] = xelrow[i][col];
-                }
+                /* It's a row of padding, so outrow[] is already set
+                   appropriately.
+                */
             }
-            new += cols[i];
         }
-        pnm_writepnmrow(ofp, newxelrow, newcols, newmaxval, newformat, 0);
+        pnm_writepnmrow(ofP, outrow, newcols, newmaxval, newformat, 0);
     }
+    pnm_freerow(outrow);
 }
 
 
 
 static void
-concatenateTopBottom(FILE *             const ofp,
-                     unsigned int       const nfiles,
-                     int                const newcols,
-                     int                const newrows,
-                     xelval             const newmaxval,
-                     int                const newformat,
-                     enum justification const justification,
-                     FILE *                   ifp[],
-                     int                      cols[],
-                     int                      rows[],
-                     xelval                   maxval[],
-                     int                      format[],
-                     xel *                    xelrow[],
-                     xel                      background[]) {
-
-    int new;
+concatenateTopBottomGen(FILE *             const ofP,
+                        unsigned int       const nfiles,
+                        int                const newcols,
+                        int                const newrows,
+                        xelval             const newmaxval,
+                        int                const newformat,
+                        enum justification const justification,
+                        struct imgInfo     const img[],
+                        enum backcolor     const backcolor) {
+
     xel * const newxelrow = pnm_allocrow(newcols);
-    int padleft;
+    xel * inrow;
+    unsigned int padleft;
     unsigned int i;
-    unsigned int row;
-    
-    i = 0;
-    switch (justification) {
-    case JUST_MIN:
-        padleft = 0;
-        break;
-    case JUST_MAX:
-        padleft = newcols - cols[i];
-        break;
-    case JUST_CENTER:
-        padleft = (newcols - cols[i]) / 2;
-        break;
+    unsigned int row, startRow;
+    xel background, backgroundPrev;
+    bool backChange;
+        /* The background color is different from that of the previous
+           input image.
+        */
+
+    switch (backcolor) {
+    case BACK_AUTO: /* do nothing now, determine at start of each image */
+                     break;
+    case BACK_BLACK: background = pnm_blackxel(newmaxval, newformat);
+                     break;
+    case BACK_WHITE: background = pnm_whitexel(newmaxval, newformat);
+                     break;
     }
 
-    new = 0;
-
-    for (row = 0; row < newrows; ++row) {
-        if (row - new >= rows[i]) {
-            new += rows[i];
-            ++i;
-            if (i >= nfiles)
-                pm_error("INTERNAL ERROR: i > nfiles");
+    for ( i = 0; i < nfiles; ++i, backgroundPrev = background) {
+        if (img[i].cols == newcols) {
+            /* no padding */
+            startRow = 0;
+            backChange = FALSE;
+            inrow = newxelrow;
+        } else { /* Calculate left padding amount */ 
             switch (justification) {
-            case JUST_MIN:
-                padleft = 0;
-                break;
-            case JUST_MAX:
-                padleft = newcols - cols[i];
-                break;
-            case JUST_CENTER:
-                padleft = (newcols - cols[i]) / 2;
-                break;
+            case JUST_MIN:    padleft = 0;                            break;
+            case JUST_MAX:    padleft = newcols - img[i].cols;        break;
+            case JUST_CENTER: padleft = (newcols - img[i].cols) / 2;  break;
+            }
+
+            if (backcolor == BACK_AUTO) {
+                /* Determine background color */
+
+                startRow = 1;
+                inrow = &newxelrow[padleft];
+
+                pnm_readpnmrow(img[i].ifP, inrow,
+                               img[i].cols, img[i].maxval, img[i].format);
+                pnm_promoteformatrow(inrow, img[i].cols, img[i].maxval,
+                                     img[i].format,
+                                     newmaxval, newformat);
+                background = pnm_backgroundxelrow(
+                    inrow, img[i].cols, newmaxval, newformat);
+
+                backChange = i==0 || !PNM_EQUAL(background, backgroundPrev);
+            } else {
+                /* background color is constant: black or white */
+                startRow = 0;
+                inrow = &newxelrow[padleft];
+                backChange = (i==0);
+            }
+
+            if (backChange || (i > 0 && img[i-1].cols > img[i].cols)) {
+                unsigned int col;
+
+                for (col = 0; col < padleft; ++col)
+                    newxelrow[col] = background;
+                for (col = padleft + img[i].cols; col < newcols; ++col)
+                    newxelrow[col] = background;
             }
         }
-        if (row - new > 0) {
-            pnm_readpnmrow(
-                ifp[i], xelrow[i], cols[i], maxval[i], format[i]);
+
+        if (startRow == 1)
+            /* Top row already read for auto background
+               color determination.  Write it out. */
+            pnm_writepnmrow(ofP, newxelrow, newcols, newmaxval, newformat, 0);
+
+        for (row = startRow; row < img[i].rows; ++row) {
+            pnm_readpnmrow(img[i].ifP,
+                           inrow, img[i].cols, img[i].maxval, img[i].format);
             pnm_promoteformatrow(
-                xelrow[i], cols[i], maxval[i], format[i],
+                inrow, img[i].cols, img[i].maxval, img[i].format,
                 newmaxval, newformat);
+
+            pnm_writepnmrow(ofP, newxelrow, newcols, newmaxval, newformat, 0);
         }
-        {
-            unsigned int col;
-
-            for (col = 0; col < padleft; ++col)
-                newxelrow[col] = background[i];
-            for (col = 0; col < cols[i]; ++col)
-                newxelrow[padleft+col] = xelrow[i][col];
-            for (col = padleft + cols[i]; col < newcols; ++col)
-                newxelrow[col] = background[i];
-        }
-        pnm_writepnmrow(ofp,
-                        newxelrow, newcols, newmaxval, newformat, 0);
-	}
+    }
+    pnm_freerow(newxelrow);
 }
 
 
 
 int
-main(int argc, char ** argv) {
+main(int           argc,
+     const char ** argv) {
 
     struct cmdlineInfo cmdline;
-    FILE** ifp;
-    xel** xelrow;
-    xel* background;
-    xelval* maxval;
+    struct imgInfo * img;  /* malloc'ed array */
     xelval newmaxval;
-    int* rows;
-    int* cols;
-    int* format;
     int newformat;
     unsigned int i;
-    int newrows, newcols;
+    unsigned int newrows, newcols;
 
-    pnm_init( &argc, argv );
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
-    
-    MALLOCARRAY_NOFAIL(ifp,        cmdline.nfiles);
-    MALLOCARRAY_NOFAIL(xelrow,     cmdline.nfiles);
-    MALLOCARRAY_NOFAIL(background, cmdline.nfiles);
-    MALLOCARRAY_NOFAIL(maxval,     cmdline.nfiles);
-    MALLOCARRAY_NOFAIL(rows,       cmdline.nfiles);
-    MALLOCARRAY_NOFAIL(cols,       cmdline.nfiles);
-    MALLOCARRAY_NOFAIL(format,     cmdline.nfiles);
+
+    MALLOCARRAY_NOFAIL(img, cmdline.nfiles);
 
     for (i = 0; i < cmdline.nfiles; ++i) {
-        ifp[i] = pm_openr(cmdline.inputFilespec[i]);
-        pnm_readpnminit(ifp[i], &cols[i], &rows[i], &maxval[i], &format[i]);
-        xelrow[i] = pnm_allocrow(cols[i]);
+        img[i].ifP = pm_openr(cmdline.inputFilespec[i]);
+        pnm_readpnminit(img[i].ifP, &img[i].cols, &img[i].rows,
+                        &img[i].maxval, &img[i].format);
     }
 
-    computeOutputParms(cmdline.nfiles, cmdline.orientation,
-                       cols, rows, maxval, format,
+    computeOutputParms(cmdline.nfiles, cmdline.orientation, img,
                        &newcols, &newrows, &newmaxval, &newformat);
 
-    for (i = 0; i < cmdline.nfiles; ++i) {
-        /* Read first row just to get a good guess at the background. */
-        pnm_readpnmrow(ifp[i], xelrow[i], cols[i], maxval[i], format[i]);
-        pnm_promoteformatrow(
-            xelrow[i], cols[i], maxval[i], format[i], newmaxval, newformat);
-        switch (cmdline.backcolor) {
-        case BACK_AUTO:
-            background[i] =
-                pnm_backgroundxelrow(
-                    xelrow[i], cols[i], newmaxval, newformat);
+    pnm_writepnminit(stdout, newcols, newrows, newmaxval, newformat, 0);
+
+    if (PNM_FORMAT_TYPE(newformat) == PBM_TYPE) {
+        switch (cmdline.orientation) {
+        case LEFTRIGHT:
+            concatenateLeftRightPbm(stdout, cmdline.nfiles,
+                                    newcols, newrows, cmdline.justification,
+                                    img, cmdline.backcolor);
+            break;
+        case TOPBOTTOM:
+            concatenateTopBottomPbm(stdout, cmdline.nfiles,
+                                    newcols, newrows, cmdline.justification,
+                                    img, cmdline.backcolor);
             break;
-        case BACK_BLACK:
-            background[i] = pnm_blackxel(newmaxval, newformat);
+        }
+    } else {
+        switch (cmdline.orientation) {
+        case LEFTRIGHT:
+            concatenateLeftRightGen(stdout, cmdline.nfiles,
+                                    newcols, newrows, newmaxval, newformat,
+                                    cmdline.justification, img,
+                                    cmdline.backcolor);
             break;
-        case BACK_WHITE:
-            background[i] = pnm_whitexel(newmaxval, newformat);
+        case TOPBOTTOM:
+            concatenateTopBottomGen(stdout, cmdline.nfiles,
+                                    newcols, newrows, newmaxval, newformat,
+                                    cmdline.justification, img,
+                                    cmdline.backcolor);
             break;
         }
-	}
-
-    pnm_writepnminit(stdout, newcols, newrows, newmaxval, newformat, 0);
-
-    switch (cmdline.orientation) {
-    case LEFTRIGHT:
-        concatenateLeftRight(stdout, cmdline.nfiles,
-                             newcols, newrows, newmaxval, newformat,
-                             cmdline.justification,
-                             ifp, cols, rows, maxval, format, xelrow,
-                             background);
-        break;
-    case TOPBOTTOM:
-        concatenateTopBottom(stdout, cmdline.nfiles,
-                             newcols, newrows, newmaxval, newformat,
-                             cmdline.justification,
-                             ifp, cols, rows, maxval, format, xelrow,
-                             background);
-        break;
     }
-    free(cmdline.inputFilespec);
-
     for (i = 0; i < cmdline.nfiles; ++i)
-        pm_close(ifp[i]);
-
+        pm_close(img[i].ifP);
+    free(cmdline.inputFilespec);
     pm_close(stdout);
 
     return 0;
diff --git a/editor/pnmcomp.c b/editor/pnmcomp.c
index 5a8b1d55..04bb5365 100644
--- a/editor/pnmcomp.c
+++ b/editor/pnmcomp.c
@@ -19,6 +19,7 @@
 #define _BSD_SOURCE    /* Make sure strcasecmp() is in string.h */
 #include <string.h>
 
+#include "pm_c_util.h"
 #include "pnm.h"
 #include "shhopt.h"
 #include "mallocvar.h"
diff --git a/editor/pnmconvol.c b/editor/pnmconvol.c
index 651b9049..2033a1a3 100644
--- a/editor/pnmconvol.c
+++ b/editor/pnmconvol.c
@@ -19,6 +19,7 @@
 
 #include <assert.h>
 
+#include "pm_c_util.h"
 #include "pnm.h"
 #include "shhopt.h"
 #include "mallocvar.h"
@@ -34,7 +35,7 @@ struct cmdlineInfo {
 
 static void
 parseCommandLine(int argc, char ** argv,
-                 struct cmdlineInfo *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.  
@@ -85,23 +86,23 @@ parseCommandLine(int argc, char ** argv,
 /* Macros to verify that r,g,b values are within proper range */
 
 #define CHECK_GRAY \
-    if ( tempgsum < 0L ) g = 0; \
-    else if ( tempgsum > maxval ) g = maxval; \
+    if (tempgsum < 0L) g = 0; \
+    else if (tempgsum > maxval) g = maxval; \
     else g = tempgsum;
 
 #define CHECK_RED \
-    if ( temprsum < 0L ) r = 0; \
-    else if ( temprsum > maxval ) r = maxval; \
+    if (temprsum < 0L) r = 0; \
+    else if (temprsum > maxval) r = maxval; \
     else r = temprsum;
 
 #define CHECK_GREEN \
-    if ( tempgsum < 0L ) g = 0; \
-    else if ( tempgsum > maxval ) g = maxval; \
+    if (tempgsum < 0L) g = 0; \
+    else if (tempgsum > maxval) g = maxval; \
     else g = tempgsum;
 
 #define CHECK_BLUE \
-    if ( tempbsum < 0L ) b = 0; \
-    else if ( tempbsum > maxval ) b = maxval; \
+    if (tempbsum < 0L) b = 0; \
+    else if (tempbsum > maxval) b = maxval; \
     else b = tempbsum;
 
 struct convolveType {
@@ -111,7 +112,7 @@ struct convolveType {
     void (*pgmConvolver)(const float ** const weights);
 };
 
-static FILE* ifp;
+static FILE * ifp;
 static int crows, ccols, ccolso2, crowso2;
 static int cols, rows;
 static xelval maxval;
diff --git a/editor/pnmcrop.c b/editor/pnmcrop.c
index 9ce388ab..1abfc7d4 100644
--- a/editor/pnmcrop.c
+++ b/editor/pnmcrop.c
@@ -25,19 +25,42 @@
 #include <errno.h>
 #include <assert.h>
 
+#include "pm_c_util.h"
 #include "pnm.h"
 #include "shhopt.h"
 #include "mallocvar.h"
 
 enum bg_choice {BG_BLACK, BG_WHITE, BG_DEFAULT, BG_SIDES};
 
+typedef enum { LEFT = 0, RIGHT = 1, TOP = 2, BOTTOM = 3} edgeLocation;
+
+static const char * const edgeName[] = {
+    "left",
+    "right",
+    "top",
+    "bottom"
+};
+
+typedef struct {
+    unsigned int size[4];
+} borderSet;
+
+typedef enum {
+    /* A position in a PNM image file stream */
+    FILEPOS_BEG,
+        /* Immediately before the raster */
+    FILEPOS_END
+        /* Immediately after the raster */
+} imageFilePos;
+
 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;
-    unsigned int left, right, top, bottom;
+    bool wantCrop[4];
+        /* User wants crop of left, right, top, bottom, resp. */
     unsigned int verbose;
     unsigned int margin;
     const char * borderfile;  /* NULL if none */
@@ -46,7 +69,7 @@ struct cmdlineInfo {
 
 
 static void
-parseCommandLine(int argc, char ** argv,
+parseCommandLine(int argc, const char ** argv,
                  struct cmdlineInfo *cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec array we return is stored in the storage that
@@ -59,6 +82,7 @@ parseCommandLine(int argc, char ** argv,
 
     unsigned int blackOpt, whiteOpt, sidesOpt;
     unsigned int marginSpec, borderfileSpec;
+    unsigned int leftOpt, rightOpt, topOpt, bottomOpt;
     
     unsigned int option_def_index;
 
@@ -68,10 +92,10 @@ parseCommandLine(int argc, char ** argv,
     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, &cmdlineP->left,      0);
-    OPTENT3(0, "right",      OPT_FLAG, NULL, &cmdlineP->right,     0);
-    OPTENT3(0, "top",        OPT_FLAG, NULL, &cmdlineP->top,       0);
-    OPTENT3(0, "bottom",     OPT_FLAG, NULL, &cmdlineP->bottom,    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,    
             &marginSpec,     0);
@@ -82,8 +106,10 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    optParseOptions3(&argc, (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 */
@@ -106,12 +132,16 @@ parseCommandLine(int argc, char ** argv,
     else
         cmdlineP->background = BG_DEFAULT;
 
-    if (!cmdlineP->left && !cmdlineP->right && !cmdlineP->top
-        && !cmdlineP->bottom) {
-        cmdlineP->left = cmdlineP->right = cmdlineP->top 
-            = cmdlineP->bottom = TRUE;
+    if (!leftOpt && !rightOpt && !topOpt && !bottomOpt) {
+        unsigned int i;
+        for (i = 0; i < 4; ++i)
+            cmdlineP->wantCrop[i] = true;
+    } else {
+        cmdlineP->wantCrop[LEFT]   = !!leftOpt;
+        cmdlineP->wantCrop[RIGHT]  = !!rightOpt;
+        cmdlineP->wantCrop[TOP]    = !!topOpt;
+        cmdlineP->wantCrop[BOTTOM] = !!bottomOpt;
     }
-
     if (!marginSpec)
         cmdlineP->margin = 0;
 
@@ -121,6 +151,27 @@ parseCommandLine(int argc, char ** argv,
 
 
 
+typedef struct {
+/*----------------------------------------------------------------------------
+   This describes a cropping operation of a single border (top, bottom,
+   left, or right).
+
+   Our definition of cropping includes padding to make a margin as well as
+   chopping stuff out.
+-----------------------------------------------------------------------------*/
+    unsigned int removeSize;
+        /* Size in pixels of the border to remove */
+    unsigned int padSize;
+        /* Size in pixels of the border to add */
+} cropOp;
+
+
+typedef struct {
+    cropOp op[4];
+} cropSet;
+
+
+
 static xel
 background3Corners(FILE * const ifP,
                    int    const rows,
@@ -188,8 +239,7 @@ computeBackground(FILE *         const ifP,
                   int            const rows,
                   xelval         const maxval,
                   int            const format,
-                  enum bg_choice const backgroundChoice,
-                  int            const verbose) {
+                  enum bg_choice const backgroundChoice) {
 /*----------------------------------------------------------------------------
    Determine what color is the background color of the image in file
    *ifP, which is described by 'cols', 'rows', 'maxval', and 'format'.
@@ -204,10 +254,10 @@ computeBackground(FILE *         const ifP,
     
     switch (backgroundChoice) {
     case BG_WHITE:
-	    background = pnm_whitexel(maxval, format);
+        background = pnm_whitexel(maxval, format);
         break;
     case BG_BLACK:
-	    background = pnm_blackxel(maxval, format);
+        background = pnm_blackxel(maxval, format);
         break;
     case BG_SIDES: 
         background = 
@@ -219,11 +269,6 @@ computeBackground(FILE *         const ifP,
         break;
     }
 
-    if (verbose) {
-        pixel const backgroundPixel = pnm_xeltopixel(background, format);
-        pm_message("Background color is %s", 
-                   ppm_colorname(&backgroundPixel, maxval, TRUE /*hexok*/));
-    }
     return(background);
 }
 
@@ -236,22 +281,18 @@ findBordersInImage(FILE *         const ifP,
                    xelval         const maxval,
                    int            const format,
                    xel            const backgroundColor,
-                   bool           const verbose, 
                    bool *         const hasBordersP,
-                   unsigned int * const leftP,
-                   unsigned int * const rightP, 
-                   unsigned int * const topP,
-                   unsigned int * const bottomP) {
+                   borderSet *    const borderSizeP) {
 /*----------------------------------------------------------------------------
    Find the left, right, top, and bottom borders in the image 'ifP'.
-   Return their sizes in pixels as *leftP, *rightP, *topP, and *bottomP.
+   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.
 -----------------------------------------------------------------------------*/
-    xel* xelrow;        /* A row of the input image */
+    xel * xelrow;        /* A row of the input image */
     int row;
     bool gottop;
     int left, right, bottom, top;
@@ -260,9 +301,9 @@ findBordersInImage(FILE *         const ifP,
     xelrow = pnm_allocrow(cols);
     
     left   = cols;  /* initial value */
-    right  = -1;   /* initial value */
-    top    = rows;   /* initial value */
-    bottom = -1;  /* initial value */
+    right  = -1;    /* initial value */
+    top    = rows;  /* initial value */
+    bottom = -1;    /* initial value */
 
     gottop = FALSE;
     for (row = 0; row < rows; ++row) {
@@ -273,7 +314,7 @@ findBordersInImage(FILE *         const ifP,
         pnm_readpnmrow(ifP, xelrow, cols, maxval, format);
         
         col = 0;
-        while (PNM_EQUAL(xelrow[col], backgroundColor) && col < cols)
+        while (col < cols && PNM_EQUAL(xelrow[col], backgroundColor))
             ++col;
         thisRowLeft = col;
 
@@ -295,92 +336,97 @@ findBordersInImage(FILE *         const ifP,
             bottom = row + 1;   /* New candidate */
         }
     }
+    
+    free(xelrow);
 
     if (right == -1)
         *hasBordersP = FALSE;
     else {
         *hasBordersP = TRUE;
         assert(right <= cols); assert(bottom <= rows);
-        *leftP       = left - 0;
-        *rightP      = cols - right;
-        *topP        = top - 0;
-        *bottomP     = rows - bottom;
+        borderSizeP->size[LEFT]   = left - 0;
+        borderSizeP->size[RIGHT]  = cols - right;
+        borderSizeP->size[TOP]    = top - 0;
+        borderSizeP->size[BOTTOM] = rows - bottom;
     }
 }
 
 
 
 static void
-findBordersInFile(const char *   const borderFileName,
-                  xel            const backgroundColor,
-                  bool           const verbose, 
-                  bool *         const hasBordersP,
-                  unsigned int * const leftP,
-                  unsigned int * const rightP, 
-                  unsigned int * const topP,
-                  unsigned int * const bottomP) {
-
-    FILE * borderFileP;
-    int cols;
-    int rows;
-    xelval maxval;
-    int format;
-    
-    borderFileP = pm_openr(borderFileName);
-    
-    pnm_readpnminit(borderFileP, &cols, &rows, &maxval, &format);
-    
-    findBordersInImage(borderFileP, cols, rows, maxval, format, 
-                       backgroundColor, verbose, hasBordersP,
-                       leftP, rightP, topP, bottomP);
+analyzeImage(FILE *         const ifP,
+             unsigned int   const cols,
+             unsigned int   const rows,
+             xelval         const maxval,
+             int            const format,
+             enum bg_choice const backgroundReq,
+             imageFilePos   const newFilePos,
+             xel *          const backgroundColorP,
+             bool *         const hasBordersP,
+             borderSet *    const borderSizeP) {
+/*----------------------------------------------------------------------------
+   Analyze the PNM image on file stream *ifP to determine its borders
+   and the color of those borders (the assumed background color).
+
+   Return as *backgroundColorP the background color.
 
-    pm_close(borderFileP);
-} 
+   Return as *borderSizeP the set of border sizes (one for each of the
+   four edges).  But iff there are no borders, don't return anything as
+   *borderSizeP and return *hasBordersP == false.
 
+   Expect *ifP to be positioned right after the header and seekable.
+   Return with it positioned either before or after the raster, as
+   requested by 'newFilePos'.
+-----------------------------------------------------------------------------*/
+    pm_filepos rasterpos;
+    xel background;
 
+    pm_tell2(ifP, &rasterpos, sizeof(rasterpos));
+
+    background = computeBackground(ifP, cols, rows, maxval, format,
+                                   backgroundReq);
+
+    pm_seek2(ifP, &rasterpos, sizeof(rasterpos));
+
+    findBordersInImage(ifP, cols, rows, maxval, format, 
+                       background, hasBordersP, borderSizeP);
+
+    if (newFilePos == FILEPOS_BEG)
+        pm_seek2(ifP, &rasterpos, sizeof(rasterpos));
+
+    *backgroundColorP = background;
+}
 
-static void
-reportOneEdge(unsigned int const oldBorderSize,
-              unsigned int const newBorderSize,
-              const char * const place) {
-
-#define ending(n) (((n) > 1) ? "s" : "")
-
-    if (newBorderSize > oldBorderSize)
-        pm_message("Adding %u pixel%s to the %u-pixel %s border",
-                   newBorderSize - oldBorderSize,
-                   ending(newBorderSize - oldBorderSize),
-                   oldBorderSize, place);
-    else if (newBorderSize < oldBorderSize)
-        pm_message("Cropping %u pixel%s from the %u-pixel %s border",
-                   oldBorderSize - newBorderSize,
-                   ending(oldBorderSize - newBorderSize),
-                   oldBorderSize, place);
-    else
-        pm_message("Leaving %s border unchanged at %u pixel%s",
-                   place, oldBorderSize, ending(oldBorderSize));
-}        
+
+
+static const char *
+ending(unsigned int const n) {
+
+    return n > 1 ? "s" : "";
+}
 
 
 
 static void
-reportCroppingParameters(unsigned int const oldLeftBorderSize,
-                         unsigned int const oldRightBorderSize,
-                         unsigned int const oldTopBorderSize,
-                         unsigned int const oldBottomBorderSize,
-                         unsigned int const newLeftBorderSize,
-                         unsigned int const newRightBorderSize,
-                         unsigned int const newTopBorderSize,
-                         unsigned int const newBottomBorderSize) {
-
-    if (oldLeftBorderSize == 0 && oldRightBorderSize == 0 &&
-        oldTopBorderSize == 0 && oldBottomBorderSize == 0)
-        pm_message("No Border found.");
-
-    reportOneEdge(oldLeftBorderSize,   newLeftBorderSize,   "left"   );
-    reportOneEdge(oldRightBorderSize,  newRightBorderSize,  "right"  );
-    reportOneEdge(oldTopBorderSize,    newTopBorderSize,    "top"    );
-    reportOneEdge(oldBottomBorderSize, newBottomBorderSize, "bottom" );
+reportCroppingParameters(cropSet const crop) {
+
+    unsigned int i;
+
+    for (i = 0; i < 4; ++i) {
+        if (crop.op[i].removeSize == 0 && crop.op[i].padSize == 0)
+            pm_message("Not cropping %s edge", edgeName[i]);
+        else {
+            if (crop.op[i].padSize > 0)
+                pm_message("Adding %u pixel%s to the %s border",
+                           crop.op[i].padSize, ending(crop.op[i].padSize),
+                           edgeName[i]);
+            if (crop.op[i].removeSize > 0)
+                pm_message("Cropping %u pixel%s from the %s border",
+                           crop.op[i].removeSize,
+                           ending(crop.op[i].removeSize),
+                           edgeName[i]);
+        }
+    }
 }
 
 
@@ -400,21 +446,60 @@ fillRow(xel *        const xelrow,
 
 
 static void
-writeCropped(FILE *       const ifP,
-             unsigned int const cols,
-             unsigned int const rows,
-             xelval       const maxval,
-             int          const format,
-             unsigned int const oldLeftBorder,
-             unsigned int const oldRightBorder,
-             unsigned int const oldTopBorder,
-             unsigned int const oldBottomBorder,
-             unsigned int const newLeftBorder,
-             unsigned int const newRightBorder,
-             unsigned int const newTopBorder,
-             unsigned int const newBottomBorder,
-             xel          const backgroundColor,
-             FILE *       const ofP) {
+readOffBorderNonPbm(unsigned int const height,
+                    FILE *       const ifP,
+                    unsigned int const cols,
+                    xelval       const maxval,
+                    int          const format) {
+
+    xel * xelrow;
+    unsigned int i;
+
+    xelrow = pnm_allocrow(cols);
+
+    for (i = 0; i < height; ++i)
+        pnm_readpnmrow(ifP, xelrow, cols, maxval, format);
+
+    pnm_freerow(xelrow);
+}
+
+
+
+static void
+outputNewBorderNonPbm(unsigned int const height,
+                      unsigned int const width,
+                      xel          const color,
+                      FILE *       const ofP,
+                      xelval       const maxval,
+                      int          const format) {
+/*----------------------------------------------------------------------------
+   Output to 'ofP' a horizontal border (i.e. top or bottom)
+   of color 'backgroundColor', height 'height', width 'width'.
+-----------------------------------------------------------------------------*/
+    xel * xelrow;
+    unsigned int i;
+
+    xelrow = pnm_allocrow(width);
+
+    fillRow(xelrow, width, color);
+
+    for (i = 0; i < height; ++i)
+        pnm_writepnmrow(ofP, xelrow, width, maxval, format, 0);
+    
+    pnm_freerow(xelrow);
+}
+
+
+
+static void
+writeCroppedNonPbm(FILE *       const ifP,
+                   unsigned int const cols,
+                   unsigned int const rows,
+                   xelval       const maxval,
+                   int          const format,
+                   cropSet      const crop,
+                   xel          const backgroundColor,
+                   FILE *       const ofP) {
 
     /* In order to do cropping, padding or both at the same time, we have
        a rather complicated row buffer:
@@ -423,6 +508,11 @@ writeCropped(FILE *       const ifP,
        the foreground pixels, the original border pixels, and the new
        border pixels.
 
+       We're calling foreground everything that isn't being cropped out
+       or padded in.  So the "foreground" may include some of what is really
+       a background border in the original image -- because the user can
+       choose to retain part of that border as a margin.
+
        The foreground pixels are in the center of the
        buffer, starting at Column 'foregroundLeft' and going to
        'foregroundRight'.
@@ -445,72 +535,68 @@ writeCropped(FILE *       const ifP,
 
        That's for the middle rows.  For the top and bottom, we just use
        the left portion of xelrow[], starting at 0.
+
+       This is the general case.  Enhancement for PBM appears below.
+       (Logic works for PBM).
     */
 
     unsigned int const foregroundCols =
-        cols - oldLeftBorder - oldRightBorder;
+        cols - crop.op[LEFT].removeSize - crop.op[RIGHT].removeSize;
     unsigned int const outputCols     = 
-        foregroundCols + newLeftBorder + newRightBorder;
+        foregroundCols + crop.op[LEFT].padSize + crop.op[RIGHT].padSize;
     unsigned int const foregroundRows =
-        rows - oldTopBorder - oldBottomBorder;
+        rows - crop.op[TOP].removeSize - crop.op[BOTTOM].removeSize;
     unsigned int const outputRows     =
-        foregroundRows + newTopBorder + newBottomBorder;
+        foregroundRows + crop.op[TOP].padSize + crop.op[BOTTOM].padSize;
 
-    unsigned int const foregroundLeft  = MAX(oldLeftBorder, newLeftBorder);
+    unsigned int const foregroundLeft  =
+        MAX(crop.op[LEFT].removeSize, crop.op[LEFT].padSize);
         /* Index into xelrow[] of leftmost pixel of foreground */
     unsigned int const foregroundRight = foregroundLeft + foregroundCols;
         /* Index into xelrow[] just past rightmost pixel of foreground */
 
     unsigned int const allocCols =
-        foregroundRight + MAX(oldRightBorder, newRightBorder);
+        foregroundRight + MAX(crop.op[RIGHT].removeSize,
+                              crop.op[RIGHT].padSize);
 
-    xel *xelrow;
+    xel * xelrow;
     unsigned int i;
 
-    assert(outputCols == newLeftBorder + foregroundCols + newRightBorder);
-    assert(outputRows == newTopBorder + foregroundRows + newBottomBorder);
-    
     pnm_writepnminit(ofP, outputCols, outputRows, maxval, format, 0);
 
     xelrow = pnm_allocrow(allocCols);
 
-    /* Read off existing top border */
-    for (i = 0; i < oldTopBorder; ++i)
-        pnm_readpnmrow(ifP, xelrow, cols, maxval, format);
+    readOffBorderNonPbm(crop.op[TOP].removeSize, ifP, cols, maxval, format);
 
+    outputNewBorderNonPbm(crop.op[TOP].padSize, outputCols, backgroundColor,
+                          ofP, maxval, format);
 
-    /* Output new top border */
-    fillRow(xelrow, outputCols, backgroundColor);
-    for (i = 0; i < newTopBorder; ++i)
-        pnm_writepnmrow(ofP, xelrow, outputCols, maxval, format, 0);
+    /* Set left border pixels */
+    fillRow(&xelrow[foregroundLeft - crop.op[LEFT].padSize],
+            crop.op[LEFT].padSize,
+            backgroundColor);
 
+    /* Set right border pixels */
+    fillRow(&xelrow[foregroundRight], crop.op[RIGHT].padSize, backgroundColor);
 
     /* Read and output foreground rows */
     for (i = 0; i < foregroundRows; ++i) {
-        /* Set left border pixels */
-        fillRow(&xelrow[foregroundLeft - newLeftBorder], newLeftBorder,
-                backgroundColor);
-
+ 
         /* Read foreground pixels */
-        pnm_readpnmrow(ifP, &(xelrow[foregroundLeft - oldLeftBorder]), cols,
-                       maxval, format);
-
-        /* Set right border pixels */
-        fillRow(&xelrow[foregroundRight], newRightBorder, backgroundColor);
+        pnm_readpnmrow(ifP,
+                       &(xelrow[foregroundLeft - crop.op[LEFT].removeSize]),
+                       cols, maxval, format);
         
         pnm_writepnmrow(ofP,
-                        &(xelrow[foregroundLeft - newLeftBorder]), outputCols,
-                        maxval, format, 0);
+                        &(xelrow[foregroundLeft - crop.op[LEFT].padSize]),
+                        outputCols, maxval, format, 0);
     }
 
-    /* Read off existing bottom border */
-    for (i = 0; i < oldBottomBorder; ++i)
-        pnm_readpnmrow(ifP, xelrow, cols, maxval, format);
-
-    /* Output new bottom border */
-    fillRow(xelrow, outputCols, backgroundColor);
-    for (i = 0; i < newBottomBorder; ++i)
-        pnm_writepnmrow(ofP, xelrow, outputCols, maxval, format, 0);
+    readOffBorderNonPbm(crop.op[BOTTOM].removeSize, ifP, cols, maxval, format);
+    
+    outputNewBorderNonPbm(crop.op[BOTTOM].padSize, outputCols,
+                          backgroundColor,
+                          ofP, maxval, format);
 
     pnm_freerow(xelrow);
 }
@@ -518,97 +604,308 @@ writeCropped(FILE *       const ifP,
 
 
 static void
-determineNewBorders(struct cmdlineInfo const cmdline,
-                    unsigned int       const leftBorderSize,
-                    unsigned int       const rightBorderSize,
-                    unsigned int       const topBorderSize,
-                    unsigned int       const bottomBorderSize,
-                    unsigned int *     const newLeftSizeP,
-                    unsigned int *     const newRightSizeP,
-                    unsigned int *     const newTopSizeP,
-                    unsigned int *     const newBottomSizeP) {
-
-    *newLeftSizeP   = cmdline.left   ? cmdline.margin : leftBorderSize   ;
-    *newRightSizeP  = cmdline.right  ? cmdline.margin : rightBorderSize  ;
-    *newTopSizeP    = cmdline.top    ? cmdline.margin : topBorderSize    ;
-    *newBottomSizeP = cmdline.bottom ? cmdline.margin : bottomBorderSize ;
-}
+fillRowPBM(unsigned char * const bitrow,
+           unsigned int    const cols,
+           unsigned int    const blackWhite) {
+/*----------------------------------------------------------------------------
+   Fill the packed PBM row buffer bitrow[] with 'cols' columns of
+   black or white: black if 'blackWhite' is 1; white if it is '0'.
+   'blackWhite' cannot be anything else.
+-----------------------------------------------------------------------------*/
+    unsigned int const colChars = pbm_packed_bytes(cols);
+    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;
+}
 
 
-int
-main(int argc, char *argv[]) {
 
-    struct cmdlineInfo cmdline;
-    FILE * ifP;   
-        /* The program's regular input file.  Could be a seekable copy of it
-           in a temporary file.
+static void
+readOffBorderPbm(unsigned int const height,
+                 FILE *       const ifP,
+                 unsigned int const cols,
+                 int          const format) {
+
+    unsigned char * bitrow;
+    unsigned int i;
+
+    bitrow = pbm_allocrow_packed(cols);
+
+    for (i = 0; i < height; ++i)
+        pbm_readpbmrow_packed(ifP, bitrow, cols, format);
+
+    pbm_freerow_packed(bitrow);
+}
+
+
+
+static void
+outputNewBorderPbm(unsigned int const height,
+                   unsigned int const width,
+                   unsigned int const blackWhite,
+                   FILE *       const ofP) {
+/*----------------------------------------------------------------------------
+   Output to 'ofP' a horizontal border (i.e. top or bottom)
+   of height 'height', width 'width'.  Make it black if 'blackWhite' is
+   1; white if 'blackWhite' is 0.  'blackWhite' can't be anything else.
+-----------------------------------------------------------------------------*/
+    unsigned char * bitrow;
+    unsigned int i;
+
+    bitrow = pbm_allocrow_packed(width);
+
+    fillRowPBM(bitrow, width, blackWhite);
+
+    for (i = 0; i < height; ++i)
+        pbm_writepbmrow_packed(ofP, bitrow, width, 0);
+    
+    pbm_freerow_packed(bitrow);
+}
+
+
+
+static void
+writeCroppedPBM(FILE *       const ifP,
+                unsigned int const cols,
+                unsigned int const rows,
+                int          const format,
+                cropSet      const crop,
+                xel          const backgroundColor,
+                FILE *       const ofP) {
+    
+    /* 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     = 
+        foregroundCols + crop.op[LEFT].padSize + crop.op[RIGHT].padSize;
+    unsigned int const foregroundRows =
+        rows - crop.op[TOP].removeSize - crop.op[BOTTOM].removeSize;
+    unsigned int const outputRows     =
+        foregroundRows + crop.op[TOP].padSize + crop.op[BOTTOM].padSize;
+
+    unsigned int const foregroundLeft  =
+        MAX(crop.op[LEFT].removeSize, crop.op[LEFT].padSize);
+    unsigned int const foregroundRight = foregroundLeft + foregroundCols;
+
+    unsigned int const allocCols =
+        foregroundRight + 
+        MAX(crop.op[RIGHT].removeSize, crop.op[RIGHT].padSize);
+
+    unsigned int const backgroundBlackWhite =
+        PNM_EQUAL(backgroundColor, pnm_whitexel(1, PBM_TYPE)) ? 0: 1;
+
+    unsigned int const readOffset    =
+        foregroundLeft - crop.op[LEFT].removeSize;
+    unsigned int const writeOffset   = foregroundLeft - crop.op[LEFT].padSize;
+    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);
+
+    readOffBorderPbm(crop.op[TOP].removeSize, ifP, cols, format);
+
+    outputNewBorderPbm(crop.op[TOP].padSize, outputCols, backgroundBlackWhite,
+                       ofP);
+
+    /* Prepare padding: left and/or right */
+    fillRowPBM(bitrow, allocCols, backgroundBlackWhite);
+
+    /* Read and output foreground rows */
+    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() 
+           (No need to mend any left-side padding)
         */
+        if (crop.op[RIGHT].padSize > 0)    
+            bitrow[lastWriteChar] = backgroundBlackWhite * 0xff;
+    }
 
-    xelval maxval;
-    int format;
-    int rows, cols;   /* dimensions of input image */
-    bool hasBorders;
-    unsigned int oldLeftBorder, oldRightBorder, oldTopBorder, oldBottomBorder;
-        /* The sizes of the borders in the input image */
-    unsigned int newLeftBorder, newRightBorder, newTopBorder, newBottomBorder;
-        /* The sizes of the borders in the output image */
-    xel background;
-    pm_filepos rasterpos;
+    readOffBorderPbm(crop.op[BOTTOM].removeSize, ifP, cols, format);
 
-    pnm_init(&argc, argv);
+    outputNewBorderPbm(crop.op[BOTTOM].padSize, outputCols,
+                       backgroundBlackWhite,
+                       ofP);
 
-    parseCommandLine(argc, argv, &cmdline);
+    pbm_freerow_packed(bitrow);
+}
 
-    ifP = pm_openr_seekable(cmdline.inputFilespec);
 
-    pnm_readpnminit(ifP, &cols, &rows, &maxval, &format);
 
-    pm_tell2(ifP, &rasterpos, sizeof(rasterpos));
+static void
+determineCrops(struct cmdlineInfo const cmdline,
+               borderSet *        const oldBorderSizeP,
+               cropSet *          const cropP) {
+
+    edgeLocation i;
+
+    for (i = 0; i < 4; ++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;
+            } else {
+                cropP->op[i].removeSize = 0;
+                cropP->op[i].padSize    =
+                    cmdline.margin - oldBorderSizeP->size[i];
+            }
+        } else {
+            cropP->op[i].removeSize = 0;
+            cropP->op[i].padSize    = 0;
+        }
+    }
+}
 
-    background = computeBackground(ifP, cols, rows, maxval, format,
-                                   cmdline.background, cmdline.verbose);
 
-    if (cmdline.borderfile) {
-        findBordersInFile(cmdline.borderfile,
-                          background, cmdline.verbose, &hasBorders,
-                          &oldLeftBorder, &oldRightBorder,
-                          &oldTopBorder,  &oldBottomBorder);
-    } else {
-        pm_seek2(ifP, &rasterpos, sizeof(rasterpos));
 
-        findBordersInImage(ifP, cols, rows, maxval, format, 
-                           background, cmdline.verbose, &hasBorders,
-                           &oldLeftBorder, &oldRightBorder,
-                           &oldTopBorder,  &oldBottomBorder);
+static void
+validateComputableSize(unsigned int const cols,
+                       unsigned int const rows,
+                       cropSet      const crop) {
+
+    double const newcols =
+        (double)cols +
+        (double)crop.op[LEFT].padSize + (double)crop.op[RIGHT].padSize;
+
+    double const newrows =
+        (double)rows +
+        (double)crop.op[TOP].padSize + (double)crop.op[BOTTOM].padSize;
+
+    if (newcols > INT_MAX)
+       pm_error("Output width too large: %.0f.", newcols);
+    if (newrows > INT_MAX)
+       pm_error("Output height too large: %.0f.", newrows);
+}
+
+
+
+static void
+cropOneImage(struct cmdlineInfo const cmdline,
+             FILE *             const ifP,
+             FILE *             const bdfP,
+             FILE *             const ofP) {
+/*----------------------------------------------------------------------------
+   Crop the image to which the stream *ifP is presently positioned
+   and write the results to *ofP.  If bdfP is non-null, use the image
+   to which stream *bdfP is presently positioned as the borderfile
+   (the file that tells us where the existing borders are in the input
+   image).  Leave *ifP and *bdfP positioned after the image.
+
+   Both files are seekable.
+-----------------------------------------------------------------------------*/
+    xelval maxval, bmaxval;
+    int format, bformat;
+    int rows, cols, brows, bcols;
+    bool hasBorders;
+    borderSet oldBorder;
+        /* The sizes of the borders in the input image */
+    cropSet crop;
+        /* The crops we have to do on each side */
+    xel background;
+
+    pnm_readpnminit(ifP, &cols, &rows, &maxval, &format);
+
+    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 (cmdline.verbose) {
+        pixel const backgroundPixel = pnm_xeltopixel(background, format);
+        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.");
 
-    determineNewBorders(cmdline, 
-                        oldLeftBorder, oldRightBorder,
-                        oldTopBorder,  oldBottomBorder,
-                        &newLeftBorder, &newRightBorder,
-                        &newTopBorder,  &newBottomBorder);
+    determineCrops(cmdline, &oldBorder, &crop);
+
+    validateComputableSize(cols, rows, crop);
 
     if (cmdline.verbose) 
-        reportCroppingParameters(oldLeftBorder, oldRightBorder,
-                                 oldTopBorder,  oldBottomBorder,
-                                 newLeftBorder, newRightBorder,
-                                 newTopBorder,  newBottomBorder);
+        reportCroppingParameters(crop);
 
-    pm_seek2(ifP, &rasterpos, sizeof(rasterpos));
+    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);
+}
+
+
+
+int
+main(int argc, const char *argv[]) {
+
+    struct cmdlineInfo cmdline;
+    FILE * ifP;   
+        /* The program's regular input file.  Could be a seekable copy of
+           it in a temporary file.
+        */
+    FILE * bdfP;
+        /* The border file.  NULL if none. */
+    bool eof;    /* no more images in input stream */
+    bool beof;   /* no more images in borderfile stream */
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr_seekable(cmdline.inputFilespec);
 
-    writeCropped(ifP, cols, rows, maxval, format,
-                 oldLeftBorder, oldRightBorder,
-                 oldTopBorder,  oldBottomBorder,
-                 newLeftBorder, newRightBorder,
-                 newTopBorder,  newBottomBorder,
-                 background, stdout);
+    if (cmdline.borderfile)
+        bdfP = pm_openr(cmdline.borderfile);
+    else
+        bdfP = NULL;
+
+    eof = beof = FALSE;
+    while (!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."); 
+                else
+                    pm_error("Border file has more images than image file.");
+            }
+        }
+    }
 
     pm_close(stdout);
     pm_close(ifP);
-    
+    if (bdfP)
+        pm_close(bdfP);
+
     return 0;
 }
diff --git a/editor/pnmcut.c b/editor/pnmcut.c
deleted file mode 100644
index a21fcffb..00000000
--- a/editor/pnmcut.c
+++ /dev/null
@@ -1,427 +0,0 @@
- /* pnmcut.c - cut a rectangle out of a portable anymap
-**
-** 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.
-*/
-
-#include <limits.h>
-#include "pnm.h"
-#include "shhopt.h"
-
-#define UNSPEC INT_MAX
-    /* UNSPEC is the value we use for an argument that is not specified
-       by the user.  Theoretically, the user could specify this value,
-       but we hope not.
-       */
-
-struct cmdline_info {
-    /* 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 */
-
-    /* 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 
-       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;
-    int pad;
-
-    int verbose;
-};
-
-
-
-static xel black_xel;  /* A black xel */
-
-
-static void
-parse_command_line(int argc, char ** argv,
-                   struct cmdline_info *cmdline_p) {
-/*----------------------------------------------------------------------------
-   Note that the file spec array we return is stored in the storage that
-   was passed to us as the argv array.
------------------------------------------------------------------------------*/
-    optStruct *option_def = malloc(100*sizeof(optStruct));
-        /* Instructions to OptParseOptions2 on how to parse our options.
-         */
-    optStruct2 opt;
-
-    unsigned int option_def_index;
-
-    option_def_index = 0;   /* incremented by OPTENTRY */
-    OPTENTRY(0,   "left",       OPT_INT,    &cmdline_p->left,           0);
-    OPTENTRY(0,   "right",      OPT_INT,    &cmdline_p->right,          0);
-    OPTENTRY(0,   "top",        OPT_INT,    &cmdline_p->top,            0);
-    OPTENTRY(0,   "bottom",     OPT_INT,    &cmdline_p->bottom,         0);
-    OPTENTRY(0,   "width",      OPT_INT,    &cmdline_p->width,          0);
-    OPTENTRY(0,   "height",     OPT_INT,    &cmdline_p->height,         0);
-    OPTENTRY(0,   "pad",        OPT_FLAG,   &cmdline_p->pad,            0);
-    OPTENTRY(0,   "verbose",    OPT_FLAG,   &cmdline_p->verbose,        0);
-
-    /* Set the defaults */
-    cmdline_p->left = UNSPEC;
-    cmdline_p->right = UNSPEC;
-    cmdline_p->top = UNSPEC;
-    cmdline_p->bottom = UNSPEC;
-    cmdline_p->width = UNSPEC;
-    cmdline_p->height = UNSPEC;
-    cmdline_p->pad = FALSE;
-    cmdline_p->verbose = FALSE;
-
-    opt.opt_table = option_def;
-    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
-    opt.allowNegNum = TRUE;  /* We may have parms that are negative numbers */
-
-    optParseOptions2(&argc, argv, opt, 0);
-        /* Uses and sets argc, argv, and some of *cmdline_p and others. */
-
-    if (cmdline_p->width < 0)
-        pm_error("-width may not be negative.");
-    if (cmdline_p->height < 0)
-        pm_error("-height may not be negative.");
-
-    if ((argc-1) != 0 && (argc-1) != 1 && (argc-1) != 4 && (argc-1) != 5)
-        pm_error("Wrong number of arguments.  "
-                 "Must be 0, 1, 4, or 5 arguments.");
-
-    switch (argc-1) {
-    case 0:
-        cmdline_p->input_filespec = "-";
-        break;
-    case 1:
-        cmdline_p->input_filespec = argv[1];
-        break;
-    case 4:
-    case 5: {
-        int warg, harg;  /* The "width" and "height" command line arguments */
-
-        if (sscanf(argv[1], "%d", &cmdline_p->left) != 1)
-            pm_error("Invalid number for left column argument");
-        if (sscanf(argv[2], "%d", &cmdline_p->top) != 1)
-            pm_error("Invalid number for top row argument");
-        if (sscanf(argv[3], "%d", &warg) != 1)
-            pm_error("Invalid number for width argument");
-        if (sscanf(argv[4], "%d", &harg) != 1)
-            pm_error("Invalid number for height argument");
-
-        if (warg > 0) {
-            cmdline_p->width = warg;
-            cmdline_p->right = UNSPEC;
-        } else {
-            cmdline_p->width = UNSPEC;
-            cmdline_p->right = warg -1;
-        }
-        if (harg > 0) {
-            cmdline_p->height = harg;
-            cmdline_p->bottom = UNSPEC;
-        } else {
-            cmdline_p->height = UNSPEC;
-            cmdline_p->bottom = harg - 1;
-        }
-
-        if (argc-1 == 4)
-            cmdline_p->input_filespec = "-";
-        else
-            cmdline_p->input_filespec = argv[5];
-        break;
-    }
-    }
-}
-
-
-
-static void
-compute_cut_bounds(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 leftcol_p, int * const rightcol_p,
-                   int * const toprow_p, int * const bottomrow_p) {
-/*----------------------------------------------------------------------------
-   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.
-
-   Return the location of the rectangle as *leftcol_p, *rightcol_p,
-   *toprow_p, and *bottomrow_p.  
------------------------------------------------------------------------------*/
-
-    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.
-
-           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.
-           */
-
-    /* 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 */
-
-    if (leftcol == UNSPEC && rightcol == UNSPEC && widtharg == UNSPEC) {
-        *leftcol_p = 0;
-        *rightcol_p = cols - 1;
-    }
-    if (leftcol == UNSPEC && rightcol == UNSPEC && widtharg != UNSPEC) {
-        *leftcol_p = 0;
-        *rightcol_p = 0 + widtharg - 1;
-    }
-    if (leftcol == UNSPEC && rightcol != UNSPEC && widtharg == UNSPEC) {
-        *leftcol_p = 0;
-        *rightcol_p = rightcol;
-    }
-    if (leftcol == UNSPEC && rightcol != UNSPEC && widtharg != UNSPEC) {
-        *leftcol_p = rightcol - widtharg + 1;
-        *rightcol_p = rightcol;
-    }
-    if (leftcol != UNSPEC && rightcol == UNSPEC && widtharg == UNSPEC) {
-        *leftcol_p = leftcol;
-        *rightcol_p = cols - 1;
-    }
-    if (leftcol != UNSPEC && rightcol == UNSPEC && widtharg != UNSPEC) {
-        *leftcol_p = leftcol;
-        *rightcol_p = leftcol + widtharg - 1;
-    }
-    if (leftcol != UNSPEC && rightcol != UNSPEC && widtharg == UNSPEC) {
-        *leftcol_p = leftcol;
-        *rightcol_p = 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.");
-    }
-
-
-    /* Sort out top, bottom, and height specifications */
-
-    if (toprow == UNSPEC && bottomrow == UNSPEC && heightarg == UNSPEC) {
-        *toprow_p = 0;
-        *bottomrow_p = rows - 1;
-    }
-    if (toprow == UNSPEC && bottomrow == UNSPEC && heightarg != UNSPEC) {
-        *toprow_p = 0;
-        *bottomrow_p = 0 + heightarg - 1;
-    }
-    if (toprow == UNSPEC && bottomrow != UNSPEC && heightarg == UNSPEC) {
-        *toprow_p = 0;
-        *bottomrow_p = bottomrow;
-    }
-    if (toprow == UNSPEC && bottomrow != UNSPEC && heightarg != UNSPEC) {
-        *toprow_p = bottomrow - heightarg + 1;
-        *bottomrow_p = bottomrow;
-    }
-    if (toprow != UNSPEC && bottomrow == UNSPEC && heightarg == UNSPEC) {
-        *toprow_p = toprow;
-        *bottomrow_p = rows - 1;
-    }
-    if (toprow != UNSPEC && bottomrow == UNSPEC && heightarg != UNSPEC) {
-        *toprow_p = toprow;
-        *bottomrow_p = toprow + heightarg - 1;
-    }
-    if (toprow != UNSPEC && bottomrow != UNSPEC && heightarg == UNSPEC) {
-        *toprow_p = toprow;
-        *bottomrow_p = 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.");
-    }
-
-}
-
-
-
-static void
-reject_out_of_bounds(const int cols, const int rows, 
-                     const int leftcol, const int rightcol, 
-                     const int toprow, const int bottomrow) {
-
-    /* Reject coordinates off the edge */
-
-    if (leftcol < 0)
-        pm_error("You have specified a left edge (%d) that is beyond\n"
-                 "the left edge of the image (0)", leftcol);
-    if (rightcol > cols-1)
-        pm_error("You have specified a right edge (%d) that is beyond\n"
-                 "the right edge of the image (%d)", rightcol, cols-1);
-    if (rightcol < 0)
-        pm_error("You have specified a right edge (%d) that is beyond\n"
-                 "the left edge of the image (0)", rightcol);
-    if (rightcol > cols-1)
-        pm_error("You have specified a right edge (%d) that is beyond\n"
-                 "the right edge of the image (%d)", rightcol, cols-1);
-    if (leftcol > rightcol) 
-        pm_error("You have specified a left edge (%d) that is to the right\n"
-                 "of the right edge you specified (%d)", 
-                 leftcol, rightcol);
-    
-    if (toprow < 0)
-        pm_error("You have specified a top edge (%d) that is above the top "
-                 "edge of the image (0)", toprow);
-    if (bottomrow > rows-1)
-        pm_error("You have specified a bottom edge (%d) that is below the\n"
-                 "bottom edge of the image (%d)", bottomrow, rows-1);
-    if (bottomrow < 0)
-        pm_error("You have specified a bottom edge (%d) that is above the\n"
-                 "top edge of the image (0)", bottomrow);
-    if (bottomrow > rows-1)
-        pm_error("You have specified a bottom edge (%d) that is below the\n"
-                 "bottom edge of the image (%d)", bottomrow, rows-1);
-    if (toprow > bottomrow) 
-        pm_error("You have specified a top edge (%d) that is below\n"
-                 "the bottom edge you specified (%d)", 
-                 toprow, bottomrow);
-}
-
-
-
-static void
-write_black_rows(FILE *outfile, const int rows, const int cols, 
-                 xel * const output_row,
-                 const pixval maxval, const int format) {
-/*----------------------------------------------------------------------------
-   Write out to file 'outfile' 'rows' rows of 'cols' black xels each,
-   part of an image of format 'format' with maxval 'maxval'.  
-
-   Use *output_row as a buffer.  It is at least 'cols' xels wide.
------------------------------------------------------------------------------*/
-    int row;
-    for (row = 0; row < rows; row++) {
-        int col;
-        for (col = 0; col < cols; col++) output_row[col] = black_xel;
-        pnm_writepnmrow(outfile, output_row, cols, maxval, format, 0);
-    }
-}
-
-
-
-int
-main(int argc, char *argv[]) {
-
-    FILE* ifp;
-    xel* xelrow;  /* Row from input image */
-    xel* output_row;  /* Row of output image */
-    xelval maxval;
-    int rows, cols, format, row;
-    int leftcol, rightcol, toprow, bottomrow;
-    int output_cols;  /* Width of output image */
-    struct cmdline_info cmdline;
-
-    pnm_init( &argc, argv );
-
-    parse_command_line(argc, argv, &cmdline);
-
-    ifp = pm_openr(cmdline.input_filespec);
-
-    pnm_readpnminit(ifp, &cols, &rows, &maxval, &format);
-    xelrow = pnm_allocrow(cols);
-
-    black_xel = pnm_blackxel(maxval, format);
-
-    compute_cut_bounds(cols, rows, 
-                       cmdline.left, cmdline.right, 
-                       cmdline.top, cmdline.bottom, 
-                       cmdline.width, cmdline.height, 
-                       &leftcol, &rightcol, &toprow, &bottomrow);
-
-    if (!cmdline.pad)
-        reject_out_of_bounds(cols, rows, leftcol, rightcol, toprow, bottomrow);
-
-    if (cmdline.verbose) {
-        pm_message("Image goes from Row 0, Column 0 through Row %d, Column %d",
-                   rows-1, cols-1);
-        pm_message("Cutting from Row %d, Column %d through Row %d Column %d",
-                   toprow, leftcol, bottomrow, rightcol);
-    }
-
-    output_cols = rightcol-leftcol+1;
-    output_row = pnm_allocrow(output_cols);
-    
-    pnm_writepnminit(stdout, output_cols, bottomrow-toprow+1, 
-                     maxval, format, 0 );
-
-    /* Implementation note:  If speed is ever an issue, we can probably
-       speed up significantly the non-padding case by writing a special
-       case loop here for the case cmdline.pad == FALSE.
-       */
-
-    /* Write out top padding */
-    write_black_rows(stdout, 0 - toprow, output_cols, output_row, 
-                     maxval, format);
-    
-    /* Read input and write out rows extracted from it */
-    for (row = 0; row < rows; row++) {
-        pnm_readpnmrow(ifp, xelrow, cols, maxval, format);
-        if (row >= toprow && row <= bottomrow) {
-            int col;
-            /* Put in left padding */
-            for (col = leftcol; col < 0; col++) { 
-                output_row[col-leftcol] = black_xel;
-            }
-            /* Put in extracted columns */
-            for (col = MAX(leftcol, 0); col <= MIN(rightcol, cols-1); col++) {
-                output_row[col-leftcol] = xelrow[col];
-            }
-            /* Put in right padding */
-            for (col = MAX(cols, leftcol); col <= rightcol; col++) {
-                output_row[col-leftcol] = black_xel;
-            }
-            pnm_writepnmrow(stdout, output_row, output_cols, 
-                            maxval, format, 0);
-        }
-    }
-    /* 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.
-       */
-    /* Write out bottom padding */
-    write_black_rows(stdout, bottomrow - (rows-1), output_cols, output_row, 
-                     maxval, format);
-
-    pnm_freerow(output_row);
-    pnm_freerow(xelrow);
-    pm_close(ifp);
-    pm_close(stdout);
-    
-    exit( 0 );
-}
-
diff --git a/editor/pnmflip b/editor/pnmflip
index 6149aaa2..44d95b45 100755
--- a/editor/pnmflip
+++ b/editor/pnmflip
@@ -57,6 +57,7 @@ foreach (@ARGV) {
         if (defined($infile)) {
             print(STDERR
                   "You may specify at most one non-option parameter.\n");
+            exit(10);
         } else {
             $infile = $_;
         }
diff --git a/editor/pnmgamma.c b/editor/pnmgamma.c
index efdfe039..b079adf1 100644
--- a/editor/pnmgamma.c
+++ b/editor/pnmgamma.c
@@ -13,8 +13,9 @@
 #include <math.h>
 #include <ctype.h>
 
-#include "shhopt.h"
+#include "pm_c_util.h"
 #include "mallocvar.h"
+#include "shhopt.h"
 #include "pnm.h"
 
 enum transferFunction {
@@ -32,7 +33,7 @@ struct cmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    const char *filespec;  /* '-' if stdin */
+    const char * filespec;  /* '-' if stdin */
     enum transferFunction transferFunction;
     float rgamma, ggamma, bgamma;
     unsigned int maxval;
@@ -103,13 +104,12 @@ getGammaFromOpts(struct cmdlineInfo * const cmdlineP,
 
     if (gammaSpec)
         if (gammaOpt < 0.0)
-            pm_error("Invalid gamma value.  "
-                         "Must be positive floating point number.");
+            pm_error("Invalid gamma value %f.  Must be positive.", gammaOpt);
     
     if (rgammaSpec) {
         if (cmdlineP->rgamma < 0.0)
-            pm_error("Invalid gamma value.  "
-                     "Must be positive floating point number.");
+            pm_error("Invalid red gamma value %f.  Must be positive.",
+                     cmdlineP->rgamma);
     } else {
         if (gammaSpec)
             cmdlineP->rgamma = gammaOpt;
@@ -118,8 +118,8 @@ getGammaFromOpts(struct cmdlineInfo * const cmdlineP,
     }
     if (ggammaSpec) {
         if (cmdlineP->ggamma < 0.0) 
-            pm_error("Invalid gamma value.  "
-                     "Must be positive floating point number.");
+            pm_error("Invalid green gamma value %f.  Must be positive.",
+                     cmdlineP->ggamma);
     } else {
         if (gammaSpec)
             cmdlineP->ggamma = gammaOpt;
@@ -128,8 +128,8 @@ getGammaFromOpts(struct cmdlineInfo * const cmdlineP,
     }
     if (bgammaSpec) {
         if (cmdlineP->bgamma < 0.0)
-            pm_error("Invalid gamma value.  "
-                     "Must be positive floating point number.");
+            pm_error("Invalid blue gamma value %f.  Must be positive.",
+                     cmdlineP->bgamma);
     } else {
         if (gammaSpec)
             cmdlineP->bgamma = gammaOpt;
diff --git a/editor/pnmhisteq.c b/editor/pnmhisteq.c
index 2c6893bd..1987efc3 100644
--- a/editor/pnmhisteq.c
+++ b/editor/pnmhisteq.c
@@ -12,6 +12,7 @@
 
 #include <string.h>
 
+#include "pm_c_util.h"
 #include "pnm.h"
 #include "shhopt.h"
 #include "mallocvar.h"
diff --git a/editor/pnmindex.sh b/editor/pnmindex.sh
index 15ba1abd..dfc5b82a 100755
--- a/editor/pnmindex.sh
+++ b/editor/pnmindex.sh
@@ -90,9 +90,8 @@ if [ $# -eq 0 ]; then
 fi
 
 tempdir="${TMPDIR-/tmp}/pnmindex.$$"
-mkdir $tempdir || { echo "Could not create temporary file. Exiting."; exit 1;}
-chmod 700 $tempdir
-
+mkdir -m 0700 $tempdir || \
+  { echo "Could not create temporary file. Exiting."; exit 1;}
 trap 'rm -rf $tempdir' 0 1 3 15
 
 tmpfile=$tempdir/pi.tmp
diff --git a/editor/pnminvert.test b/editor/pnminvert.test
index 606e4e5c..5534f20d 100644
--- a/editor/pnminvert.test
+++ b/editor/pnminvert.test
@@ -2,7 +2,7 @@ echo Test 1.  Should print 1240379484 41
 ./pnminvert ../testgrid.pbm | cksum
 echo Test 2.  Should print 1416115901 101484
 ./pnminvert ../testimg.ppm | cksum
-echo Test 3.  Should print 4215652354 33838
+echo Test 3.  Should print 2961441369 33838
 ppmtopgm ../testimg.ppm | ./pnminvert | cksum
 echo Test 4.  Should print 2595564405 14
 pbmmake -w 7 7 | ./pnminvert | cksum
diff --git a/editor/pnmmargin b/editor/pnmmargin
index 31420f99..b31deefd 100755
--- a/editor/pnmmargin
+++ b/editor/pnmmargin
@@ -12,9 +12,8 @@
 # implied warranty.
 
 tempdir="${TMPDIR-/tmp}/pnmmargin.$$"
-mkdir $tempdir || { echo "Could not create temporary file. Exiting."; exit 1;}
-chmod 700 $tempdir
-
+mkdir -m 0700 $tempdir || \
+  { echo "Could not create temporary file. Exiting." 1>&2; exit 1;}
 trap 'rm -rf $tempdir' 0 1 3 15
 
 tmp1=$tempdir/pnmm1
@@ -23,22 +22,27 @@ tmp3=$tempdir/pnmm3
 tmp4=$tempdir/pnmm4
 
 color="-gofigure"
+plainopt=""
 
 # Parse args.
 while true ; do
     case "$1" in
-	-w* )
+        -p|-pl|-pla|-plai|-plain )
+        plainopt="-plain"
+        shift
+        ;;
+	-w|-wh|-whi|-whit|-white )
 	color="-white"
 	shift
 	;;
-	-b* )
+	-b|-bl|-bla|-blac|-black )
 	color="-black"
 	shift
 	;;
-	-c* )
+	-c|-co|-col|-colo|-color )
 	shift
 	if [ ! ${1-""} ] ; then
-	    echo "usage: $0 [-white|-black|-color <colorspec>] <size> [pnmfile]" 1>&2
+       	    echo "usage: $0 [-white|-black|-color <colorspec>] <size> [pnmfile]" 1>&2
 	    exit 1
 	fi
 	color="$1"
@@ -67,22 +71,38 @@ if [ ${2-""} ] ; then
 fi
 
 # Capture input file in a tmp file, in case it's a pipe.
+# TODO: This code does not consider the case when the input file is specified
+# and there is also input coming in from a pipe.
+
 cat $@ > $tmp1
 
-# Construct spacer files.
-case "$color" in
-    -gofigure )
-    pnmcut 0 0 1 1 $tmp1 | pnmtile $size 1 > $tmp2
-    ;;
-    -white | -black )
-    pbmmake $color $size 1 > $tmp2
-    ;;
-    * )
-    ppmmake $color $size 1 > $tmp2
-    ;;
-esac
-pamflip -rotate90 $tmp2 > $tmp3
+if [ $size -eq 0 ] ; then
+    # Zero margin; just copy input to output
+    pamtopnm $plainopt $tmp1;
+else
+    # If -white or -black, write output with pnmpad and exit.
+    # Otherwise construct spacer files.
+    
+    case "$color" in
+        -gofigure )
+        pnmcut 0 0 1 1 $tmp1 | pnmtile $size 1 > $tmp2
+        ;;
+        -white | -black )
+        pnmpad $plainopt $color \
+            -left=$size -right=$size -top=$size -bottom=$size $tmp1
+        exit
+        ;;
+        * )
+        ppmmake $color $size 1 > $tmp2
+        ;;
+    esac
+    pamflip -rotate90 $tmp2 > $tmp3
+    
+    # Cat things together.
+    pnmcat -lr $tmp2 $tmp1 $tmp2 > $tmp4
+    pnmcat -tb $plainopt $tmp3 $tmp4 $tmp3
+fi
+
+
+
 
-# Cat things together.
-pnmcat -lr $tmp2 $tmp1 $tmp2 > $tmp4
-pnmcat -tb $tmp3 $tmp4 $tmp3
diff --git a/editor/pnmmontage.c b/editor/pnmmontage.c
index 8916d251..2e30a43b 100644
--- a/editor/pnmmontage.c
+++ b/editor/pnmmontage.c
@@ -10,19 +10,136 @@
  * implied warranty.
  */
 
+#define _BSD_SOURCE  /* Make sure strdup() is in <string.h> */
+#include <assert.h>
 #include <limits.h>
 #include <string.h>
 
-#include "pam.h"
-#include "shhopt.h"
-#include "nstring.h"
+#include "pm_c_util.h"
 #include "mallocvar.h"
+#include "nstring.h"
+#include "shhopt.h"
+#include "pam.h"
+
+
+
+struct cmdlineInfo {
+    const char * header;
+    const char * data;
+    const char * prefix;
+    unsigned int quality;
+    unsigned int quality2;
+    unsigned int nFiles;
+    const char ** inFileName;
+};
+
+
+
+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 OptParseOptions3 on how to parse our options. */
+    optStruct3 opt;
+    unsigned int dataSpec, headerSpec, prefixSpec, qualitySpec;
+    unsigned int option_def_index;
+    unsigned int i;
+    unsigned int q[10];
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+  
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3( 0,  "data",    OPT_STRING, &cmdlineP->data, &dataSpec, 0);
+    OPTENT3( 0,  "header",  OPT_STRING, &cmdlineP->header, &headerSpec, 0);
+    OPTENT3('q', "quality", OPT_UINT,   &cmdlineP->quality,   &qualitySpec, 0);
+    OPTENT3('p', "prefix",  OPT_STRING, &cmdlineP->prefix,    &prefixSpec, 0);
+    OPTENT3('0', "0",       OPT_FLAG,   NULL, &q[0],      0);
+    OPTENT3('1', "1",       OPT_FLAG,   NULL, &q[1],      0);
+    OPTENT3('2', "2",       OPT_FLAG,   NULL, &q[2],      0);
+    OPTENT3('3', "3",       OPT_FLAG,   NULL, &q[3],      0);
+    OPTENT3('4', "4",       OPT_FLAG,   NULL, &q[4],      0);
+    OPTENT3('5', "5",       OPT_FLAG,   NULL, &q[5],      0);
+    OPTENT3('6', "6",       OPT_FLAG,   NULL, &q[6],      0);
+    OPTENT3('7', "7",       OPT_FLAG,   NULL, &q[7],      0);
+    OPTENT3('8', "8",       OPT_FLAG,   NULL, &q[8],      0);
+    OPTENT3('9', "9",       OPT_FLAG,   NULL, &q[9],      0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;
+    opt.allowNegNum = FALSE;
+
+    optParseOptions3(&argc, (char**)argv, opt, sizeof(opt), 0);
+
+    if (!dataSpec)
+        cmdlineP->data = NULL;
+    if (!headerSpec)
+        cmdlineP->header = NULL;
+    if (!prefixSpec)
+        cmdlineP->prefix = "";
+    if (!qualitySpec)
+        cmdlineP->quality = 200;
+
+    
+    /* cmdlineP->quality2 is the greatest number from the --1, --2, etc.
+       options, or 5 if none of those are specified.
+    */
+    cmdlineP->quality2 = 5;  /* initial value */
+    for (i = 0; i < 10; ++i) {
+        if (q[i])
+            cmdlineP->quality2 = i;
+    }
+
+    cmdlineP->nFiles = argc-1;
 
-typedef struct { int f[sizeof(int) * 8 + 1]; } factorset;
-typedef struct { int x; int y; } coord;
+    MALLOCARRAY_NOFAIL(cmdlineP->inFileName, argc-1);
 
-static int qfactor = 200;
-static int quality = 5;
+    for (i = 0; i < argc-1; ++i) {
+        if (cmdlineP->data && strchr(argv[i+1], ':'))
+            pm_error("Filename '%s' contains a \":\", which is forbidden "
+                     "with -data", argv[i+1]);
+        else
+            cmdlineP->inFileName[i] = strdup(argv[i+1]);
+    }
+}
+
+
+
+typedef struct {
+    int f[sizeof(int) * 8 + 1];
+} factorset;
+
+typedef struct {
+    int x; int y;
+} coord;
+
+typedef struct {
+    coord ul;
+    coord size;
+} rectangle;
+
+static coord
+lr(rectangle const r) {
+/*----------------------------------------------------------------------------
+   Return the coordinates of the lower right corner of 'r'
+   (i.e. the pixel just beyond the lowest rightmost one).
+-----------------------------------------------------------------------------*/
+    coord retval;
+
+    retval.x = r.ul.x + r.size.x;
+    retval.y = r.ul.y + r.size.y;
+
+    return retval;
+}
 
 static factorset 
 factor(int n)
@@ -63,109 +180,151 @@ gcd(int n, int m)
   return (g);
 }
 
-static __inline__ int imax(int n, int m) { return (n > m ? n : m); }
 
-static int 
-checkcollision(coord *locs, coord *szs, coord *cloc, coord *csz, int n)
-{
-  int i;
-  for (i = 0; i < n; ++i)
-  {
-    if ((locs[i].x < cloc->x + csz->x) &&
-        (locs[i].y < cloc->y + csz->y) &&
-        (locs[i].x + szs[i].x > cloc->x) &&
-        (locs[i].y + szs[i].y > cloc->y))
-      return (1);
-  }
-  return (0);
+
+static bool
+overlaps(rectangle const a,
+         rectangle const b) {
+
+    return
+        (a.ul.x < lr(b).x && a.ul.y < lr(b).y) &&
+        (lr(a).x > b.ul.x && lr(a).y > b.ul.y);
+}
+
+
+
+static bool
+collides(rectangle         const test,
+         const rectangle * const fieldList,
+         unsigned int      const n) {
+/*----------------------------------------------------------------------------
+   Return true iff the rectangle 'test' overlaps any of the 'n' rectangles
+   fieldList[].
+-----------------------------------------------------------------------------*/
+    unsigned int i;
+
+    for (i = 0; i < n; ++i)
+        if (overlaps(fieldList[i], test))
+            return true;
+
+    return false;
 }
 
+
+
 static void 
-recursefindpack(coord *current, coord currentsz, coord *set, 
-                coord *best, int minarea, int *maxarea, 
-                int depth, int n, int xinc, int yinc)
-{
-  coord c;
-  if (depth == n)
-  {
-    if (currentsz.x * currentsz.y < *maxarea)
-    {
-      memcpy(best, current, sizeof(coord) * n);
-      *maxarea = currentsz.x * currentsz.y;
-    }
-    return;
-  }
+recursefindpack(rectangle *    const current,
+                coord          const currentsz,
+                coord *        const best,
+                unsigned int   const minarea,
+                unsigned int * const maxareaP, 
+                unsigned int   const depth,
+                unsigned int   const n,
+                unsigned int   const xinc,
+                unsigned int   const yinc,
+                unsigned int   const quality,
+                unsigned int   const qfactor) {
+
+    if (depth == n) {
+        if (currentsz.x * currentsz.y < *maxareaP) {
+            unsigned int i;
+            for (i = 0; i < n; ++i)
+                best[i] = current[i].ul;
+            *maxareaP = currentsz.x * currentsz.y;
+        }
+    } else {
+        unsigned int i;
 
-  for (current[depth].x = 0; 
-       imax(current[depth].x + set[depth].x, currentsz.x) * 
-           imax(currentsz.y, set[depth].y) < *maxarea; 
-       current[depth].x += xinc)
-  {
-    for (current[depth].y = 0; 
-         imax(current[depth].x + set[depth].x, currentsz.x) * 
-             imax(currentsz.y, current[depth].y + set[depth].y) < *maxarea; 
-         current[depth].y += yinc)
-    {
-      c.x = imax(current[depth].x + set[depth].x, currentsz.x);
-      c.y = imax(current[depth].y + set[depth].y, currentsz.y);
-      if (!checkcollision(current, set, &current[depth], &set[depth], depth))
-      {
-        recursefindpack(current, c, set, best, minarea, maxarea, 
-                        depth + 1, n, xinc, yinc);
-        if (*maxarea <= minarea)
-          return;
-      }
+        rectangle * const newP = &current[depth];
+
+        for (i = 0; ; ++i) {
+            for (newP->ul.x = 0, newP->ul.y = i * yinc;
+                 newP->ul.y <= i * yinc;) {
+
+                coord c;
+
+                c.x = MAX(lr(*newP).x, currentsz.x);
+                c.y = MAX(lr(*newP).y, currentsz.y);
+                pm_message("current = (%u.%u, %u.%u) new = (%u.%u, %u.%u)",
+                           current[0].ul.x, current[0].size.x,
+                           current[0].ul.y, current[0].size.y,
+                           newP->ul.x,   newP->size.x,
+                           newP->ul.y,   newP->size.y);
+                if (!collides(*newP, current, depth)) {
+                    pm_message("Depth %u: Doesn't collide at i=%u", depth,i);
+                    recursefindpack(current, c, best, minarea, maxareaP,
+                                    depth + 1, n, xinc, yinc,
+                                    quality, qfactor);
+                    if (*maxareaP <= minarea)
+                        return;
+                }
+                if (newP->ul.x == (i - 1) * xinc)
+                    newP->ul.y = 0;
+                if (newP->ul.x < i * xinc)
+                    newP->ul.x += xinc;
+                else
+                    newP->ul.y += yinc;
+            }
+        }
     }
-  }
 }
 
+
+
 static void 
-findpack(struct pam *imgs, int n, coord *coords)
-{
-  int minarea;
-  int i;
-  int rdiv;
-  int cdiv;
-  int minx = -1;
-  int miny = -1;
-  coord *current;
-  coord *set;
-  int z = INT_MAX;
-  coord c = { 0, 0 };
-
-  if (quality > 1)
-  {
-    for (minarea = i = 0; i < n; ++i)
-      minarea += imgs[i].height * imgs[i].width,
-      minx = imax(minx, imgs[i].width),
-      miny = imax(miny, imgs[i].height);
+findpack(struct pam * const imgs,
+         unsigned int const n,
+         coord *      const coords,
+         unsigned int const quality,
+         unsigned int const qfactor) {
 
-    minarea = minarea * qfactor / 100;
-  }
-  else
-  {
-    minarea = INT_MAX - 1;
-  }
+    int minarea;
+    int i;
+    int rdiv;
+    int cdiv;
+    int minx;
+    int miny;
+    rectangle * current;
+    unsigned int z;
+    coord c;
+
+    minx = -1; miny = -1;  /* initial value */
+    z = UINT_MAX;  /* initial value */
+    c.x = 0; c.y = 0;  /* initial value */
+
+    if (quality > 1) {
+        unsigned int realMinarea;
+        for (realMinarea = i = 0; i < n; ++i)
+            realMinarea += imgs[i].height * imgs[i].width,
+                minx = MAX(minx, imgs[i].width),
+                miny = MAX(miny, imgs[i].height);
+
+        minarea = realMinarea * qfactor / 100;
+    } else {
+        minarea = INT_MAX - 1;
+    }
 
-  /* It's relatively easy to show that, if all the images
-   * are multiples of a particular size, then a best
-   * packing will always align the images on a grid of
-   * that size.
-   *
-   * This speeds computation immensely.
-   */
-  for (rdiv = imgs[0].height, i = 1; i < n; ++i)
-    rdiv = gcd(imgs[i].height, rdiv);
-
-  for (cdiv = imgs[0].width, i = 1; i < n; ++i)
-    cdiv = gcd(imgs[i].width, cdiv);
-
-  MALLOCARRAY(current, n);
-  MALLOCARRAY(set, n);
-  for (i = 0; i < n; ++i)
-    set[i].x = imgs[i].width,
-    set[i].y = imgs[i].height;
-  recursefindpack(current, c, set, coords, minarea, &z, 0, n, cdiv, rdiv);
+    /* It's relatively easy to show that, if all the images
+     * are multiples of a particular size, then a best
+     * packing will always align the images on a grid of
+     * that size.
+     *
+     * This speeds computation immensely.
+     */
+    for (rdiv = imgs[0].height, i = 1; i < n; ++i)
+        rdiv = gcd(imgs[i].height, rdiv);
+
+    for (cdiv = imgs[0].width, i = 1; i < n; ++i)
+        cdiv = gcd(imgs[i].width, cdiv);
+
+    MALLOCARRAY(current, n);
+
+    for (i = 0; i < n; ++i) {
+        current[i].size.x = imgs[i].width;
+        current[i].size.y = imgs[i].height;
+    }
+    recursefindpack(current, c, coords, minarea, &z, 0, n, cdiv, rdiv,
+                    quality, qfactor);
 }
 
 
@@ -255,202 +414,263 @@ writePam(struct pam *       const outpamP,
 
 
 
-int 
-main(int argc, char **argv)
-{
-  struct pam *imgs;
-  struct pam outimg;
-  struct pam p;
-  int nfiles;
-  int i, j;
-  unsigned int q[10];
-  coord *coords;
-  const char *headfname = NULL;
-  const char *datafname = NULL;
-  const char *prefix = "";
-  FILE *header;
-  FILE *data;
-  char **names;
-  char *c;
-
-  optEntry *option_def = malloc(100*sizeof(optEntry));
-      /* Instructions to OptParseOptions3 on how to parse our options.
-       */
-  optStruct3 opt;
-
-  unsigned int option_def_index;
-
-  option_def_index = 0;   /* incremented by OPTENTRY */
-  OPTENT3( 0,  "data",    OPT_STRING, &datafname, NULL, 0);
-  OPTENT3( 0,  "header",  OPT_STRING, &headfname, NULL, 0);
-  OPTENT3('q', "quality", OPT_UINT,   &qfactor,   NULL, 0);
-  OPTENT3('p', "prefix",  OPT_STRING, &prefix,    NULL, 0);
-  OPTENT3('0', "0",       OPT_FLAG,   NULL, &q[0],      0);
-  OPTENT3('1', "1",       OPT_FLAG,   NULL, &q[1],      0);
-  OPTENT3('2', "2",       OPT_FLAG,   NULL, &q[2],      0);
-  OPTENT3('3', "3",       OPT_FLAG,   NULL, &q[3],      0);
-  OPTENT3('4', "4",       OPT_FLAG,   NULL, &q[4],      0);
-  OPTENT3('5', "5",       OPT_FLAG,   NULL, &q[5],      0);
-  OPTENT3('6', "6",       OPT_FLAG,   NULL, &q[6],      0);
-  OPTENT3('7', "7",       OPT_FLAG,   NULL, &q[7],      0);
-  OPTENT3('8', "8",       OPT_FLAG,   NULL, &q[8],      0);
-  OPTENT3('9', "9",       OPT_FLAG,   NULL, &q[9],      0);
-
-  opt.opt_table = option_def;
-  opt.short_allowed = FALSE;
-  opt.allowNegNum = FALSE;
-
-  pnm_init(&argc, argv);
-
-  /* Check for flags. */
-  optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
-
-  if (headfname)
-    header = pm_openw(headfname);
-
-  if (datafname)
-    data = pm_openw(datafname);
-
-  for (i = 0; i < 10; ++i)
-  {
-    if (q[i])
-    {
-      quality = i;
-      switch (quality)
-      {
-        case 0: case 1: break;
-        case 2: case 3: case 4: case 5: case 6: 
-            qfactor = 100 * (8 - quality); 
-            break;
-        case 7: qfactor = 150; break;
-        case 8: qfactor = 125; break;
-        case 9: qfactor = 100; break;
-      }
+static void
+writeData(FILE *             const dataFileP,
+          unsigned int       const width,
+          unsigned int       const height,
+          unsigned int       const nfiles,
+          const char **      const names,
+          const coord *      const coords,
+          const struct pam * const imgs) {
+
+    unsigned int i;
+
+    fprintf(dataFileP, ":0:0:%u:%u\n", width, height);
+
+    for (i = 0; i < nfiles; ++i) {
+        fprintf(dataFileP, "%s:%u:%u:%u:%u\n", names[i], coords[i].x,
+                coords[i].y, imgs[i].width, imgs[i].height);
     }
-  }
+}
 
-  if (1 < argc)
-    nfiles = argc - 1;
-  else
-    nfiles = 1;
 
-  MALLOCARRAY(imgs, nfiles);
-  MALLOCARRAY(coords, nfiles);
-  MALLOCARRAY(names, nfiles);
-  
-  if (!imgs || !coords || !names)
-    pm_error("out of memory");
 
-  if (1 < argc)
-  {
-    for (i = 0; i < nfiles; ++i)
-    {
-      if (strchr(argv[i+1], ':'))
-      {
-        imgs[i].file = pm_openr(strchr(argv[i+1], ':') + 1);
-        *strchr(argv[i+1], ':') = 0;
-        names[i] = argv[i+1];
-      }
-      else
-      {
-        imgs[i].file = pm_openr(argv[i+1]);
-        names[i] = argv[i+1];
-      }
+static void
+writeHeader(FILE * const headerFileP,
+            const char * const prefix,
+            unsigned int const width,
+            unsigned int const height,
+            unsigned int const nfiles,
+            const char ** const names,
+            const coord * const coords,
+            const struct pam * imgs) {
+
+    unsigned int i;
+
+    fprintf(headerFileP, "#define %sOVERALLX %u\n", prefix, width);
+
+    fprintf(headerFileP, "#define %sOVERALLY %u\n", prefix, height);
+
+    fprintf(headerFileP, "\n");
+
+    for (i = 0; i < nfiles; ++i) {
+        char * const buffer = strdup(names[i]);
+        coord const coord = coords[i];
+        struct pam const img = imgs[i];
+
+        unsigned int j;
+        
+        *strchr(buffer, '.') = 0;
+        for (j = 0; buffer[j]; ++j) {
+            if (ISLOWER(buffer[j]))
+                buffer[j] = TOUPPER(buffer[j]);
+        }
+        fprintf(headerFileP, "#define %s%sX %u\n", 
+                prefix, buffer, coord.x);
+
+        fprintf(headerFileP, "#define %s%sY %u\n",
+                prefix, buffer, coord.y);
+
+        fprintf(headerFileP, "#define %s%sSZX %u\n",
+                prefix, buffer, img.width);
+
+        fprintf(headerFileP, "#define %s%sSZY %u\n",
+                prefix, buffer, img.height);
+
+        fprintf(headerFileP, "\n");
     }
-  }
-  else
-  {
-    imgs[0].file = stdin;
-  }
+}
 
-  pnm_readpaminit(imgs[0].file, &imgs[0], PAM_STRUCT_SIZE(tuple_type));
-  outimg.maxval = imgs[0].maxval;
-  outimg.format = imgs[0].format;
-  memcpy(outimg.tuple_type, imgs[0].tuple_type, sizeof(imgs[0].tuple_type));
-  outimg.depth = imgs[0].depth;
 
-  for (i = 1; i < nfiles; ++i)
-  {
-    pnm_readpaminit(imgs[i].file, &imgs[i], PAM_STRUCT_SIZE(tuple_type));
-    if (PAM_FORMAT_TYPE(imgs[i].format) > PAM_FORMAT_TYPE(outimg.format))
-      outimg.format = imgs[i].format,
-      memcpy(outimg.tuple_type, imgs[i].tuple_type, 
-             sizeof(imgs[i].tuple_type));
-    outimg.maxval = imax(imgs[i].maxval, outimg.maxval);
-    outimg.depth = imax(imgs[i].depth, outimg.depth);
-  }
 
-  for (i = 0; i < nfiles - 1; ++i)
-    for (j = i + 1; j < nfiles; ++j)
-      if (imgs[j].width * imgs[j].height > imgs[i].width * imgs[i].height)
-        p = imgs[i], imgs[i] = imgs[j], imgs[j] = p,
-        c = names[i], names[i] = names[j], names[j] = c;
+static void
+sortImagesByArea(unsigned int  const nfiles,
+                 struct pam *  const imgs,
+                 const char ** const names) {
+/*----------------------------------------------------------------------------
+   Sort the images described by 'imgs' and 'names' in place, from largest
+   area to smallest.
+-----------------------------------------------------------------------------*/
+    /* Bubble sort */
+
+    unsigned int i;
+
+    for (i = 0; i < nfiles - 1; ++i) {
+        unsigned int j;
+        for (j = i + 1; j < nfiles; ++j) {
+            if (imgs[j].width * imgs[j].height >
+                imgs[i].width * imgs[i].height) {
+
+                struct pam p;
+                const char * c;
+                
+                p = imgs[i]; imgs[i] = imgs[j]; imgs[j] = p;
+                c = names[i]; names[i] = names[j]; names[j] = c;
+            }
+        }
+    }
+}
 
-  findpack(imgs, nfiles, coords);
 
-  outimg.height = outimg.width = 0;
-  for (i = 0; i < nfiles; ++i)
-  {
-    outimg.width = imax(outimg.width, imgs[i].width + coords[i].x);
-    outimg.height = imax(outimg.height, imgs[i].height + coords[i].y);
-  }
 
-  outimg.size = sizeof(outimg);
-  outimg.len = PAM_STRUCT_SIZE(allocation_depth);
-  pnm_setminallocationdepth(&outimg, outimg.depth);
-  outimg.plainformat = false;
-  outimg.file = stdout;
+static void
+computeOutputType(sample *           const maxvalP,
+                  int *              const formatP,
+                  char *             const tupleTypeP,
+                  unsigned int *     const depthP,
+                  unsigned int       const nfiles,
+                  const struct pam * const imgs) {
+
+    unsigned int i;
+
+    sample maxval;
+    int format;
+    const char * tupleType;
+    unsigned int depth;
+
+    assert(nfiles > 0);
+
+    /* initial guesses */
+    maxval    = imgs[0].maxval;
+    format    = imgs[0].format;
+    depth     = imgs[0].depth;
+    tupleType = imgs[0].tuple_type;
+
+    for (i = 1; i < nfiles; ++i) {
+        if (PAM_FORMAT_TYPE(imgs[i].format) > PAM_FORMAT_TYPE(format)) {
+            format    = imgs[i].format;
+            tupleType = imgs[i].tuple_type;
+        }
+        maxval = MAX(maxval, imgs[i].maxval);
+        depth  = MAX(depth,  imgs[i].depth);
+    }
+
+    *maxvalP = maxval;
+    *formatP = format;
+    *depthP  = depth;
+    memcpy(tupleTypeP, tupleType, sizeof(imgs[0].tuple_type));
+}
 
-  writePam(&outimg, nfiles, coords, imgs);
 
-  if (datafname)
-  {
-    fprintf(data, ":0:0:%u:%u\n", outimg.width, outimg.height);
 
-    for (i = 0; i < nfiles; ++i)
-    {
-      fprintf(data, "%s:%u:%u:%u:%u\n", names[i], coords[i].x,
-          coords[i].y, imgs[i].width, imgs[i].height);
+static void
+computeOutputDimensions(int * const widthP,
+                        int * const heightP,
+                        unsigned int const nfiles,
+                        const struct pam * const imgs,
+                        const coord * const coords) {
+
+    unsigned int widthGuess, heightGuess;
+    unsigned int i;
+
+    widthGuess  = 0;  /* initial value */
+    heightGuess = 0;  /* initial value */
+    
+    for (i = 0; i < nfiles; ++i) {
+        widthGuess  = MAX(widthGuess,  imgs[i].width  + coords[i].x);
+        heightGuess = MAX(heightGuess, imgs[i].height + coords[i].y);
     }
-  }
 
-  if (headfname)
-  {
-    fprintf(header, "#define %sOVERALLX %u\n"
-                    "#define %sOVERALLY %u\n"
-                    "\n",
-                    prefix, outimg.width,
-                    prefix, outimg.height);
+    *widthP  = widthGuess;
+    *heightP = heightGuess;
+}
 
-    for (i = 0; i < nfiles; ++i)
-    {
-      *strchr(names[i], '.') = 0;
-      for (j = 0; names[i][j]; ++j)
-      {
-        if (ISLOWER(names[i][j]))
-          names[i][j] = TOUPPER(names[i][j]);
-      }
-      fprintf(header, "#define %s%sX %u\n"
-                      "#define %s%sY %u\n"
-                      "#define %s%sSZX %u\n"
-                      "#define %s%sSZY %u\n"
-                      "\n",
-                      prefix, names[i], coords[i].x,
-                      prefix, names[i], coords[i].y,
-                      prefix, names[i], imgs[i].width,
-                      prefix, names[i], imgs[i].height);
+
+
+int 
+main(int argc, const char **argv) {
+
+    struct cmdlineInfo cmdline;
+    struct pam * imgs;
+    struct pam outimg;
+    unsigned int nfiles;
+    coord * coords;
+    FILE * header;
+    FILE * data;
+    const char ** names;
+    unsigned int i;
+    unsigned int qfactor;  /* In per cent */
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    header = cmdline.header ? pm_openw(cmdline.header) : NULL;
+    data = cmdline.data ? pm_openw(cmdline.data) : NULL;
+
+    switch (cmdline.quality2) {
+    case 0: case 1:
+        qfactor = cmdline.quality;
+        break;
+    case 2: case 3: case 4: case 5: case 6: 
+        qfactor = 100 * (8 - cmdline.quality2); 
+        break;
+    case 7: qfactor = 150; break;
+    case 8: qfactor = 125; break;
+    case 9: qfactor = 100; break;
+    default: pm_error("Internal error - impossible value of 'quality2': %u",
+                      cmdline.quality2);
     }
-  }
 
-  for (i = 0; i < nfiles; ++i)
-    pm_close(imgs[i].file);
-  pm_close(stdout);
+    nfiles = cmdline.nFiles > 0 ? cmdline.nFiles : 1;
 
-  if (headfname)
-    pm_close(header);
+    MALLOCARRAY(imgs, nfiles);
+    MALLOCARRAY(coords, nfiles);
+    MALLOCARRAY(names, nfiles);
+  
+    if (!imgs || !coords || !names)
+        pm_error("out of memory");
+
+    if (cmdline.nFiles > 0) {
+        unsigned int i;
+
+        for (i = 0; i < cmdline.nFiles; ++i) {
+            imgs[i].file = pm_openr(cmdline.inFileName[i]);
+            names[i] = strdup(cmdline.inFileName[i]);
+        }
+    } else {
+        imgs[0].file = stdin;
+        names[0] = strdup("stdin");
+    }
+
+    for (i = 0; i < nfiles; ++i)
+        pnm_readpaminit(imgs[i].file, &imgs[i], PAM_STRUCT_SIZE(tuple_type));
 
-  if (datafname)
-    pm_close(data);
+    sortImagesByArea(nfiles, imgs, names);
 
-  return 0;
+    findpack(imgs, nfiles, coords, cmdline.quality2, qfactor);
+
+    computeOutputType(&outimg.maxval, &outimg.format, outimg.tuple_type,
+                      &outimg.depth, nfiles, imgs);
+
+    computeOutputDimensions(&outimg.width, &outimg.height, nfiles,
+                            imgs, coords);
+
+    pnm_setminallocationdepth(&outimg, outimg.depth);
+
+    outimg.size = sizeof(outimg);
+    outimg.len = PAM_STRUCT_SIZE(allocation_depth);
+    pnm_setminallocationdepth(&outimg, outimg.depth);
+    outimg.plainformat = false;
+    outimg.file = stdout;
+ 
+    writePam(&outimg, nfiles, coords, imgs);
+
+    if (data)
+        writeData(data, outimg.width, outimg.height,
+                  nfiles, names, coords, imgs);
+
+    if (header)
+        writeHeader(header, cmdline.prefix, outimg.width, outimg.height,
+                    nfiles, names, coords, imgs);
+
+    for (i = 0; i < nfiles; ++i)
+        pm_close(imgs[i].file);
+    pm_close(stdout);
+    if (header)
+        pm_close(header);
+    if (data)
+        pm_close(data);
+
+    return 0;
 }
diff --git a/editor/pnmnlfilt.c b/editor/pnmnlfilt.c
index 20705f82..bde0cd82 100644
--- a/editor/pnmnlfilt.c
+++ b/editor/pnmnlfilt.c
@@ -52,6 +52,63 @@
 #include "pm_c_util.h"
 #include "pnm.h"
 
+struct cmdlineInfo {
+    const char * inputFileName;
+    double alpha;
+    double radius;
+};
+
+
+static void 
+parseCommandLine(int argc, 
+                 char ** argv, 
+                 struct cmdlineInfo  * const cmdlineP) {
+
+    if (argc-1 < 2)
+        pm_error("You must specify at least two arguments: alpha and radius.  "
+                 "You specified %u", argc-1);
+
+    if (sscanf(argv[1], "%lf", &cmdlineP->alpha) != 1)
+        pm_error("Invalid alpha (1st) argument '%s'.  "
+                 "Must be a decimal number",
+                 argv[1]);
+
+    if (sscanf( argv[2], "%lf", &cmdlineP->radius ) != 1)
+        pm_error("Invalid radius (2nd) argument '%s'.  "
+                 "Must be a decimal number", 
+                 argv[2]);
+    
+    if ((cmdlineP->alpha > -0.1 && cmdlineP->alpha < 0.0) ||
+        (cmdlineP->alpha > 0.5 && cmdlineP->alpha < 1.0))
+        pm_error( "Alpha must be in range 0.0 <= alpha <= 0.5 "
+                  "for alpha trimmed mean.  "
+                  "You specified %f", cmdlineP->alpha);
+    if (cmdlineP->alpha > 2.0)
+        pm_error("Alpha must be in range 1.0 <= cmdlineP->alpha <= 2.0 "
+                  "for optimal estimation.  You specified %f",
+                 cmdlineP->alpha);
+
+    if (cmdlineP->alpha < -0.9 ||
+        (cmdlineP->alpha > -0.1 && cmdlineP->alpha < 0.0))
+        pm_error( "Alpha must be in range -0.9 <= alpha <= -0.1 "
+                  "for edge enhancement.  You specified %f",
+                  cmdlineP->alpha);
+
+    if (cmdlineP->radius < 1.0/3 || cmdlineP->radius > 1.0)
+        pm_error("Radius must be in range 1/3 <= radius <= 1. "
+                 "You specified %f", cmdlineP->radius);
+
+    if (argc-1 < 3)
+        cmdlineP->inputFileName = "-";
+    else
+        cmdlineP->inputFileName = argv[3];
+
+    if (argc-1 > 3)
+        pm_error("Too many arguments: %u.  The most allowed are 3: alpha, "
+                 "radius, and file name", argc-1);
+}
+
+
 /* MXIVAL is the maximum input sample value we can handle.
    It is limited by our willingness to allocate storage in various arrays
    that are indexed by sample values.
@@ -76,7 +133,6 @@ int noisevariance;
 */
 static  xel *irows[3];
 static  xel *irow0, *irow1, *irow2, *orow;
-static  double radius=0.0,alpha= -1.0;
 static  int rows, cols, format, oformat, row, col;
 static  int (*atfunc)(int *);
 static  xelval maxval;
@@ -756,7 +812,7 @@ atfilt5(int *p) {
 
 
 static void 
-do_one_frame(FILE *ifp) {
+do_one_frame(FILE * const ifP) {
 
     pnm_writepnminit( stdout, cols, rows, omaxval, oformat, 0 );
     
@@ -770,12 +826,12 @@ do_one_frame(FILE *ifp) {
 
             if (row == 0) {
                 irow0 = irow1;
-                pnm_readpnmrow( ifp, irow1, cols, maxval, format );
+                pnm_readpnmrow( ifP, irow1, cols, maxval, format );
             }
             if (row == (rows-1))
                 irow2 = irow1;
             else
-                pnm_readpnmrow( ifp, irow2, cols, maxval, format );
+                pnm_readpnmrow( ifP, irow2, cols, maxval, format );
 
             for (col = cols-1,po= col>0?1:0,no=0,
                      ip0=irow0,ip1=irow1,ip2=irow2,op=orow;
@@ -846,7 +902,7 @@ do_one_frame(FILE *ifp) {
 
             if (row == 0) {
                 irow0 = irow1;
-                pnm_readpnmrow( ifp, irow1, cols, maxval, format );
+                pnm_readpnmrow( ifP, irow1, cols, maxval, format );
                 if ( promote )
                     pnm_promoteformatrow( irow1, cols, maxval, 
                                           format, maxval, oformat );
@@ -854,7 +910,7 @@ do_one_frame(FILE *ifp) {
             if (row == (rows-1))
                 irow2 = irow1;
             else {
-                pnm_readpnmrow( ifp, irow2, cols, maxval, format );
+                pnm_readpnmrow( ifP, irow2, cols, maxval, format );
                 if ( promote )
                     pnm_promoteformatrow( irow2, cols, maxval, 
                                           format, maxval, oformat );
@@ -932,44 +988,18 @@ int (*atfuncs[6]) (int *) = {atfilt0,atfilt1,atfilt2,atfilt3,atfilt4,atfilt5};
 int
 main(int argc, char *argv[]) {
 
-    FILE * ifp;
+    FILE * ifP;
+    struct cmdlineInfo cmdline;
 	bool eof;  /* We've hit the end of the input stream */
     unsigned int imageSeq;  /* Sequence number of image, starting from 0 */
 
-    const char* const usage = "alpha radius pnmfile\n"
-        "0.0 <= alpha <= 0.5 for alpha trimmed mean -or- \n"
-        "1.0 <= alpha <= 2.0 for optimal estimation -or- \n"
-        "-0.1 >= alpha >= -0.9 for edge enhancement\n"
-        "0.3333 <= radius <= 1.0 specify effective radius\n";
-
-    pnm_init( &argc, argv );
+    pnm_init(&argc, argv);
 
-    if ( argc < 3 || argc > 4 )
-        pm_usage( usage );
+    parseCommandLine(argc, argv, &cmdline);
 
-    if ( sscanf( argv[1], "%lf", &alpha ) != 1 )
-        pm_usage( usage );
-    if ( sscanf( argv[2], "%lf", &radius ) != 1 )
-        pm_usage( usage );
-        
-    if ((alpha > -0.1 && alpha < 0.0) || (alpha > 0.5 && alpha < 1.0))
-        pm_error( "Alpha must be in range 0.0 <= alpha <= 0.5 "
-                  "for alpha trimmed mean" );
-    if (alpha > 2.0)
-        pm_error( "Alpha must be in range 1.0 <= alpha <= 2.0 "
-                  "for optimal estimation" );
-    if (alpha < -0.9 || (alpha > -0.1 && alpha < 0.0))
-        pm_error( "Alpha must be in range -0.9 <= alpha <= -0.1 "
-                  "for edge enhancement" );
-    if (radius < 0.333 || radius > 1.0)
-        pm_error( "Radius must be in range 0.333333333 <= radius <= 1.0" );
+    ifP = pm_openr(cmdline.inputFileName);
 
-    if ( argc == 4 )
-        ifp = pm_openr( argv[3] );
-    else
-        ifp = stdin;
-        
-    pnm_readpnminit( ifp, &cols, &rows, &maxval, &format );
+    pnm_readpnminit(ifP, &cols, &rows, &maxval, &format);
         
     if (maxval > MXIVAL) 
         pm_error("The maxval of the input image (%d) is too large.\n"
@@ -980,12 +1010,12 @@ main(int argc, char *argv[]) {
     /* force output to max precision without forcing new 2-byte format */
     omaxval = MIN(maxval, PPM_MAXMAXVAL);
         
-    atfunc = atfuncs[atfilt_setup(alpha, radius,
+    atfunc = atfuncs[atfilt_setup(cmdline.alpha, cmdline.radius,
                                   (double)omaxval/(double)maxval)];
 
-    if ( oformat < PGM_TYPE ) {
+    if (oformat < PGM_TYPE) {
         oformat = RPGM_FORMAT;
-        pm_message( "promoting file to PGM" );
+        pm_message("promoting file to PGM");
     }
 
     orow = pnm_allocrow(cols);
@@ -999,8 +1029,8 @@ main(int argc, char *argv[]) {
     eof = FALSE;  /* We're already in the middle of the first image */
     imageSeq = 0;
     while (!eof) {
-        do_one_frame(ifp);
-        pm_nextimage(ifp, &eof);
+        do_one_frame(ifP);
+        pm_nextimage(ifP, &eof);
         if (!eof) {
             /* Read and validate header of next image */
             int imageCols, imageRows;
@@ -1008,7 +1038,7 @@ main(int argc, char *argv[]) {
             int imageFormat;
 
             ++imageSeq;
-            pnm_readpnminit(ifp, &imageCols, &imageRows, 
+            pnm_readpnminit(ifP, &imageCols, &imageRows, 
                             &imageMaxval, &imageFormat);
             verifySame(imageSeq,
                        imageCols, imageRows, imageMaxval, imageFormat,
@@ -1020,9 +1050,7 @@ main(int argc, char *argv[]) {
     pnm_freerow(irow1);
     pnm_freerow(irow2);
     pnm_freerow(orow);
-    pm_close(ifp);
+    pm_close(ifP);
 
     return 0;
 }
-
-
diff --git a/editor/pnmnorm.c b/editor/pnmnorm.c
index b36ad462..27d51115 100644
--- a/editor/pnmnorm.c
+++ b/editor/pnmnorm.c
@@ -29,9 +29,10 @@
 
 #include <assert.h>
 
-#include "pnm.h"
-#include "shhopt.h"
+#include "pm_c_util.h"
 #include "mallocvar.h"
+#include "shhopt.h"
+#include "pnm.h"
 
 enum brightMethod {BRIGHT_LUMINOSITY, BRIGHT_COLORVALUE, BRIGHT_SATURATION};
 
@@ -60,8 +61,8 @@ struct cmdlineInfo {
 
 
 static void
-parseCommandLine (int argc, char ** argv,
-                  struct cmdlineInfo *cmdlineP) {
+parseCommandLine(int argc, const char ** argv,
+                 struct cmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    parse program command line described in Unix standard form by argc
    and argv.  Return the information in the options as *cmdlineP.  
@@ -116,7 +117,7 @@ parseCommandLine (int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
+    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdline_p and others. */
 
     if (!cmdlineP->wpercentSpec)
@@ -354,21 +355,85 @@ computeAdjustmentForExpansionLimit(xelval   const maxval,
 
 
 static void
-computeEndValues(FILE *             const ifp,
-                 int                const cols,
-                 int                const rows,
-                 xelval             const maxval,
-                 int                const format,
-                 struct cmdlineInfo const cmdline,
-                 xelval *           const bvalueP,
-                 xelval *           const wvalueP) {
+disOverlap(xelval   const reqBvalue,
+           xelval   const reqWvalue,
+           bool     const bIsFixed,
+           bool     const wIsFixed,
+           xelval   const maxval,
+           xelval * const nonOlapBvalueP,
+           xelval * const nonOlapWvalueP) {
 /*----------------------------------------------------------------------------
-   Figure out what original values will be translated to full white and
-   full black -- thus defining to what all the other values get translated.
+   Compute black and white end values that don't overlap, i.e. the
+   black value is darker than the white, from an initial attempt that
+   might overlap.
 
-   This may involve looking at the image.  The image is in the file
-   'ifp', which is positioned just past the header (at the raster).
-   Leave it positioned arbitrarily.
+   'req{B|W}value' is that initial attempt.  We return the
+   nonoverlapping version as *nonOlap{B|W}valueP.
+
+   '{b|w}IsFixed' means we cannot change that endpoint.
+
+   If both ends are fixed 'reqBvalue' and 'reqWvalue' overlap, we just
+   fail the program -- the user asked for the impossible.
+
+   Where one end is fixed and the other is not, we move the unfixed end
+   to be one unit above or below the fixed end, as appropriate.
+
+   Where both ends are free, we move them to the point halfway between them,
+   the white end being one more than the black end.
+-----------------------------------------------------------------------------*/
+    assert(maxval > 0);
+
+    if (reqBvalue < reqWvalue) {
+        /* No overlap; initial attempt is fine. */
+        *nonOlapBvalueP = reqBvalue;
+        *nonOlapWvalueP = reqWvalue;
+    } else {
+        if (bIsFixed && wIsFixed)
+            pm_error("The colors which become black (value <= %u) "
+                     "would overlap the "
+                     "colors which become white (value >= %u).",
+                     reqBvalue, reqWvalue);
+        else if (bIsFixed) {
+            if (reqBvalue >= maxval)
+                pm_error("The black value must be less than the maxval");
+            else {
+                *nonOlapBvalueP = reqBvalue;
+                *nonOlapWvalueP = reqBvalue + 1;
+            }
+        } else if (wIsFixed) {
+            if (reqWvalue == 0)
+                pm_error("The white value must be greater than 0");
+            else {
+                *nonOlapBvalueP = reqWvalue - 1;
+                *nonOlapWvalueP = reqWvalue;
+            }
+        } else {
+            /* Both ends are free; use the point halfway between them. */
+            xelval const midPoint = (reqWvalue + reqBvalue + maxval/2)/2;
+            *nonOlapBvalueP = MIN(midPoint, maxval-1);
+            *nonOlapWvalueP = *nonOlapBvalueP + 1;
+        }
+    }
+}
+
+
+
+static void
+resolvePercentParams(FILE *             const ifP,
+                     unsigned int       const cols,
+                     unsigned int       const rows,
+                     xelval             const maxval,
+                     int                const format,
+                     struct cmdlineInfo const cmdline,
+                     xelval *           const bvalueP,
+                     xelval *           const wvalueP) {
+/*----------------------------------------------------------------------------
+   Figure out the endpoint of the stretch (the value that is to be stretched
+   to black and the one that is to be stretched to white) as requested
+   by the -bvalue, -bpercent, -wvalue, and -wpercent options.
+
+   These values may be invalid due to overlapping, and they may exceed
+   the maximum allowed stretch; Caller must deal with that.
 -----------------------------------------------------------------------------*/
     unsigned int * hist;  /* malloc'ed */
 
@@ -377,45 +442,71 @@ computeEndValues(FILE *             const ifp,
     if (hist == NULL)
         pm_error("Unable to allocate storage for intensity histogram.");
     else {
-        xelval unlimitedBvalue, unlimitedWvalue;
-        unsigned int bLower, wRaise;
-
-        buildHistogram(ifp, cols, rows, maxval, format, hist,
+        buildHistogram(ifP, cols, rows, maxval, format, hist,
                        cmdline.brightMethod);
 
         if (cmdline.bvalueSpec && !cmdline.bpercentSpec) {
-            unlimitedBvalue = cmdline.bvalue;
+            *bvalueP = cmdline.bvalue;
         } else {
             xelval percentBvalue;
             computeBottomPercentile(hist, maxval, cols*rows, cmdline.bpercent, 
                                     &percentBvalue);
             if (cmdline.bvalueSpec)
-                unlimitedBvalue = MIN(percentBvalue, cmdline.bvalue);
+                *bvalueP = MIN(percentBvalue, cmdline.bvalue);
             else
-                unlimitedBvalue = percentBvalue;
+                *bvalueP = percentBvalue;
         }
 
         if (cmdline.wvalueSpec && !cmdline.wpercentSpec) {
-            unlimitedWvalue = cmdline.wvalue;
+            *wvalueP = cmdline.wvalue;
         } else {
             xelval percentWvalue;
             computeTopPercentile(hist, maxval, cols*rows, cmdline.wpercent, 
                                  &percentWvalue);
             if (cmdline.wvalueSpec)
-                unlimitedWvalue = MAX(percentWvalue, cmdline.wvalue);
+                *wvalueP = MAX(percentWvalue, cmdline.wvalue);
             else
-                unlimitedWvalue = percentWvalue;
+                *wvalueP = percentWvalue;
         }
+        free(hist);
+    }
+}
 
-        computeAdjustmentForExpansionLimit(
-            maxval, unlimitedBvalue, unlimitedWvalue, cmdline.maxExpansion,
-            &bLower, &wRaise);
 
-        *bvalueP = unlimitedBvalue - bLower;
-        *wvalueP = unlimitedWvalue + wRaise;
 
-        free(hist);
-    }
+static void
+computeEndValues(FILE *             const ifP,
+                 int                const cols,
+                 int                const rows,
+                 xelval             const maxval,
+                 int                const format,
+                 struct cmdlineInfo const cmdline,
+                 xelval *           const bvalueP,
+                 xelval *           const wvalueP) {
+/*----------------------------------------------------------------------------
+   Figure out what original values will be translated to full white and
+   full black -- thus defining to what all the other values get translated.
+
+   This may involve looking at the image.  The image is in the file
+   'ifp', which is positioned just past the header (at the raster).
+   Leave it positioned arbitrarily.
+-----------------------------------------------------------------------------*/
+    xelval reqBvalue, reqWvalue, nonOlapBvalue, nonOlapWvalue;
+    unsigned int bLower, wRaise;
+
+    resolvePercentParams(ifP, cols, rows, maxval, format, cmdline,
+                         &reqBvalue, &reqWvalue);
+
+    disOverlap(reqBvalue, reqWvalue,
+               cmdline.bvalueSpec, cmdline.wvalueSpec, maxval,
+               &nonOlapBvalue, &nonOlapWvalue);
+
+    computeAdjustmentForExpansionLimit(
+        maxval, nonOlapBvalue, nonOlapWvalue, cmdline.maxExpansion,
+        &bLower, &wRaise);
+
+    *bvalueP = nonOlapBvalue - bLower;
+    *wvalueP = nonOlapWvalue + wRaise;
 }
 
 
@@ -545,9 +636,9 @@ writeRowNormalized(xel *             const xelrow,
                 float const scaler =
                     brightScaler(p, maxval, newBrightness, brightMethod);
 
-                xelval const r = MIN((int)(PPM_GETR(p)*scaler+0.5), maxval);
-                xelval const g = MIN((int)(PPM_GETG(p)*scaler+0.5), maxval);
-                xelval const b = MIN((int)(PPM_GETB(p)*scaler+0.5), maxval);
+                xelval const r = MIN(ROUNDU(PPM_GETR(p)*scaler), maxval);
+                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], 
@@ -563,7 +654,7 @@ writeRowNormalized(xel *             const xelrow,
 
 
 int
-main(int argc, char *argv[]) {
+main(int argc, const char *argv[]) {
 
     struct cmdlineInfo cmdline;
     FILE *ifP;
@@ -572,7 +663,7 @@ main(int argc, char *argv[]) {
     int rows, cols, format;
     xelval bvalue, wvalue;
     
-    pnm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
@@ -584,19 +675,17 @@ main(int argc, char *argv[]) {
 
     computeEndValues(ifP, cols, rows, maxval, format, cmdline, 
                      &bvalue, &wvalue);
-        
-    if (wvalue <= bvalue)
-        pm_error("The colors which become black would overlap the "
-                 "colors which become white.");
-    else {
+    {
         xelval * newBrightness;
         int row;
         xel * xelrow;
         xel * rowbuf;
         
+        assert(wvalue > bvalue);
+
         xelrow = pnm_allocrow(cols);
 
-        pm_message("remapping %d..%d to %d..%d", bvalue, wvalue, 0, maxval);
+        pm_message("remapping %u..%u to %u..%u", bvalue, wvalue, 0, maxval);
 
         computeTransferFunction(bvalue, wvalue, maxval, &newBrightness);
 
@@ -614,7 +703,7 @@ main(int argc, char *argv[]) {
         free(newBrightness);
         pnm_freerow(rowbuf);
         pnm_freerow(xelrow);
-    }
-    pm_close(ifP);
+    } 
+   pm_close(ifP);
     return 0;
 }
diff --git a/editor/pnmpad.c b/editor/pnmpad.c
index e1fbdaec..34672dc5 100644
--- a/editor/pnmpad.c
+++ b/editor/pnmpad.c
@@ -1,28 +1,22 @@
 /* pnmpad.c - add border to sides of a portable anymap
- ** AJCD 4/9/90
- */
-
-/*
- * Changelog
- *
- * 2002/01/25 - Rewrote options parsing code.
- *      Added pad-to-width and pad-to-height with custom
- *      alignment.  MVB.
+   ** AJCD 4/9/90
  */
 
 #include <string.h>
 #include <stdio.h>
 
-#include "pnm.h"
-#include "shhopt.h"
+#include "pm_c_util.h"
 #include "mallocvar.h"
+#include "shhopt.h"
+#include "pnm.h"
 
+#define MAX_WIDTHHEIGHT INT_MAX-10
 
 struct cmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    const char *input_filespec;  /* Filespecs of input files */
+    const char * input_filespec;  /* Filespecs of input files */
     unsigned int xsize;
     unsigned int xsizeSpec;
     unsigned int ysize;
@@ -44,7 +38,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
@@ -62,7 +56,7 @@ parseCommandLine(int argc, char ** argv,
     MALLOCARRAY_NOFAIL(option_def, 100);
 
     option_def_index = 0;   /* incremented by OPTENT3 */
-    OPTENT3(0,   "xsize",     OPT_UINT,    &cmdlineP->xsize,       
+    OPTENT3(0,   "xsize",     OPT_UINT,    &cmdlineP->xsize,
             &cmdlineP->xsizeSpec, 0);
     OPTENT3(0,   "width",     OPT_UINT,    &cmdlineP->xsize,
             &cmdlineP->xsizeSpec, 0);
@@ -70,13 +64,13 @@ parseCommandLine(int argc, char ** argv,
             &cmdlineP->ysizeSpec, 0);
     OPTENT3(0,   "height",    OPT_UINT,    &cmdlineP->ysize,
             &cmdlineP->ysizeSpec, 0);
-    OPTENT3(0,   "left",      OPT_UINT,    &cmdlineP->left, 
+    OPTENT3(0,   "left",      OPT_UINT,    &cmdlineP->left,
             &cmdlineP->leftSpec, 0);
-    OPTENT3(0,   "right",     OPT_UINT,    &cmdlineP->right, 
+    OPTENT3(0,   "right",     OPT_UINT,    &cmdlineP->right,
             &cmdlineP->rightSpec, 0);
-    OPTENT3(0,   "top",       OPT_UINT,    &cmdlineP->top, 
+    OPTENT3(0,   "top",       OPT_UINT,    &cmdlineP->top,
             &cmdlineP->topSpec, 0);
-    OPTENT3(0,   "bottom",    OPT_UINT,    &cmdlineP->bottom, 
+    OPTENT3(0,   "bottom",    OPT_UINT,    &cmdlineP->bottom,
             &cmdlineP->bottomSpec, 0);
     OPTENT3(0,   "xalign",    OPT_FLOAT,   &cmdlineP->xalign,
             &xalignSpec,           0);
@@ -86,7 +80,7 @@ parseCommandLine(int argc, char ** argv,
             &yalignSpec,           0);
     OPTENT3(0,   "valign",    OPT_FLOAT,   &cmdlineP->yalign,
             &yalignSpec,           0);
-    OPTENT3(0,   "black",     OPT_FLAG,    NULL, 
+    OPTENT3(0,   "black",     OPT_FLAG,    NULL,
             &blackOpt,           0);
     OPTENT3(0,   "white",     OPT_FLAG,    NULL,
             &cmdlineP->white,    0);
@@ -97,12 +91,30 @@ parseCommandLine(int argc, char ** argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof opt, 0);
+    optParseOptions3(&argc, (char **)argv, opt, sizeof opt, 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (blackOpt && cmdlineP->white)
         pm_error("You cannot specify both -black and -white");
 
+    if (cmdlineP->topSpec > 1)
+       pm_error("You can specify -top only once");
+
+    if (cmdlineP->bottomSpec > 1)
+       pm_error("You can specify -bottom only once");
+
+    if (cmdlineP->leftSpec > 1)
+       pm_error("You can specify -left only once");
+
+    if (cmdlineP->rightSpec > 1)
+       pm_error("You can specify -right only once");
+
+    if (cmdlineP->xsizeSpec > 1)
+       pm_error("You can specify -width only once");
+
+    if (cmdlineP->ysizeSpec > 1)
+       pm_error("You can specify -height only once");
+
     if (xalignSpec && (cmdlineP->leftSpec || cmdlineP->rightSpec))
         pm_error("You cannot specify both -xalign and -left or -right");
 
@@ -117,20 +129,20 @@ parseCommandLine(int argc, char ** argv,
 
     if (xalignSpec) {
         if (cmdlineP->xalign < 0)
-            pm_error("You have specified a negative -halign value (%f)", 
+            pm_error("You have specified a negative -halign value (%f)",
                      cmdlineP->xalign);
         if (cmdlineP->xalign > 1)
-            pm_error("You have specified a -halign value (%f) greater than 1", 
+            pm_error("You have specified a -halign value (%f) greater than 1",
                      cmdlineP->xalign);
     } else
         cmdlineP->xalign = 0.5;
 
     if (yalignSpec) {
         if (cmdlineP->yalign < 0)
-            pm_error("You have specified a negative -halign value (%f)", 
+            pm_error("You have specified a negative -halign value (%f)",
                      cmdlineP->yalign);
         if (cmdlineP->yalign > 1)
-            pm_error("You have specified a -valign value (%f) greater than 1", 
+            pm_error("You have specified a -valign value (%f) greater than 1",
                      cmdlineP->yalign);
     } else
         cmdlineP->yalign = 0.5;
@@ -139,16 +151,16 @@ parseCommandLine(int argc, char ** argv,
     if (argc-1 > 1)
         pm_error("This program takes at most 1 parameter.  You specified %d",
                  argc-1);
-    else if (argc-1 == 1) 
+    else if (argc-1 == 1)
         cmdlineP->input_filespec = argv[1];
-    else 
+    else
         cmdlineP->input_filespec = "-";
 }
 
 
 
 static void
-parseCommandLineOld(int argc, char ** argv,
+parseCommandLineOld(int argc, const char ** argv,
                     struct cmdlineInfo * const cmdlineP) {
 
     /* This syntax was abandonned in February 2002. */
@@ -166,24 +178,32 @@ parseCommandLineOld(int argc, char ** argv,
         case 'l':
             if (atoi(argv[1]+2) < 0)
                 pm_error("left border too small");
+            else if (atoi(argv[1]+2) > MAX_WIDTHHEIGHT)
+                pm_error("left border too large");
             else
                 cmdlineP->left = atoi(argv[1]+2);
             break;
         case 'r':
             if (atoi(argv[1]+2) < 0)
                 pm_error("right border too small");
+            else if (atoi(argv[1]+2) > MAX_WIDTHHEIGHT)
+                pm_error("right border too large");
             else
                 cmdlineP->right = atoi(argv[1]+2);
             break;
         case 'b':
             if (atoi(argv[1]+2) < 0)
                 pm_error("bottom border too small");
+            else if (atoi(argv[1]+2) > MAX_WIDTHHEIGHT)
+                pm_error("bottom border too large");
             else
                 cmdlineP->bottom = atoi(argv[1]+2);
             break;
         case 't':
             if (atoi(argv[1]+2) < 0)
                 pm_error("top border too small");
+            else if (atoi(argv[1]+2) > MAX_WIDTHHEIGHT)
+                pm_error("top border too large");
             else
                 cmdlineP->top = atoi(argv[1]+2);
             break;
@@ -208,11 +228,36 @@ parseCommandLineOld(int argc, char ** argv,
 
 
 static void
+validateHorizontalSize(struct cmdlineInfo const cmdline,
+                       unsigned int const cols) {
+
+    unsigned int const xsize = cmdline.xsizeSpec ? cmdline.xsize : 0;
+    unsigned int const lpad  = cmdline.leftSpec  ? cmdline.left  : 0;
+    unsigned int const rpad  = cmdline.rightSpec ? cmdline.right : 0;
+
+    if (xsize > MAX_WIDTHHEIGHT)
+        pm_error("The width value you specified is too large.");
+
+    if (lpad > MAX_WIDTHHEIGHT)
+        pm_error("The left padding value you specified is too large.");
+
+    if (rpad > MAX_WIDTHHEIGHT)
+        pm_error("The right padding value you specified is too large.");
+
+    if ((double) cols + (double) lpad + (double) rpad > MAX_WIDTHHEIGHT)
+        pm_error("Given padding value(s) makes output width too large.");
+}
+
+
+
+static void
 computeHorizontalPadSizes(struct cmdlineInfo const cmdline,
-                          int                const cols,
+                          unsigned int       const cols,
                           unsigned int *     const lpadP,
                           unsigned int *     const rpadP) {
 
+    validateHorizontalSize(cmdline, cols);
+
     if (cmdline.xsizeSpec) {
         if (cmdline.leftSpec && cmdline.rightSpec) {
             if (cmdline.left + cols + cmdline.right < cmdline.xsize) {
@@ -226,7 +271,7 @@ computeHorizontalPadSizes(struct cmdlineInfo const cmdline,
             }
         } else if (cmdline.leftSpec) {
             *lpadP = cmdline.left;
-            *rpadP = MAX(cmdline.xsize, cmdline.left + cols) - 
+            *rpadP = MAX(cmdline.xsize, cmdline.left + cols) -
                 (cmdline.left + cols);
         } else if (cmdline.rightSpec) {
             *rpadP = cmdline.right;
@@ -242,7 +287,7 @@ computeHorizontalPadSizes(struct cmdlineInfo const cmdline,
             }
         }
     } else {
-        *lpadP = cmdline.leftSpec ? cmdline.left : 0;
+        *lpadP = cmdline.leftSpec  ? cmdline.left  : 0;
         *rpadP = cmdline.rightSpec ? cmdline.right : 0;
     }
 }
@@ -250,11 +295,36 @@ computeHorizontalPadSizes(struct cmdlineInfo const cmdline,
 
 
 static void
+validateVerticalSize(struct cmdlineInfo const cmdline,
+                     unsigned int       const rows) {
+
+    unsigned int const ysize = cmdline.ysizeSpec  ? cmdline.ysize  : 0;
+    unsigned int const tpad  = cmdline.topSpec    ? cmdline.top    : 0;
+    unsigned int const bpad  = cmdline.bottomSpec ? cmdline.bottom : 0;
+
+    if (ysize > MAX_WIDTHHEIGHT)
+        pm_error("The height value you specified is too large.");
+
+    if (tpad > MAX_WIDTHHEIGHT)
+        pm_error("The top padding value you specified is too large.");
+
+    if (bpad > MAX_WIDTHHEIGHT)
+        pm_error("The bottom padding value you specified is too large.");
+
+    if ((double) rows + (double) tpad + (double) bpad > MAX_WIDTHHEIGHT)
+        pm_error("Given padding value(s) makes output height too large.");
+}
+
+
+
+static void
 computeVerticalPadSizes(struct cmdlineInfo const cmdline,
                         int                const rows,
                         unsigned int *     const tpadP,
                         unsigned int *     const bpadP) {
 
+    validateVerticalSize(cmdline, rows);
+
     if (cmdline.ysizeSpec) {
         if (cmdline.topSpec && cmdline.bottomSpec) {
             if (cmdline.bottom + rows + cmdline.top < cmdline.ysize) {
@@ -268,11 +338,11 @@ computeVerticalPadSizes(struct cmdlineInfo const cmdline,
             }
         } else if (cmdline.topSpec) {
             *tpadP = cmdline.top;
-            *bpadP = MAX(cmdline.ysize, cmdline.top + rows) - 
+            *bpadP = MAX(cmdline.ysize, cmdline.top + rows) -
                 (cmdline.top + rows);
         } else if (cmdline.bottomSpec) {
             *bpadP = cmdline.bottom;
-            *tpadP = MAX(cmdline.ysize, rows + cmdline.bottom) - 
+            *tpadP = MAX(cmdline.ysize, rows + cmdline.bottom) -
                 (rows + cmdline.bottom);
         } else {
             if (cmdline.ysize > rows) {
@@ -285,7 +355,7 @@ computeVerticalPadSizes(struct cmdlineInfo const cmdline,
         }
     } else {
         *bpadP = cmdline.bottomSpec ? cmdline.bottom : 0;
-        *tpadP = cmdline.topSpec ? cmdline.top : 0;
+        *tpadP = cmdline.topSpec    ? cmdline.top    : 0;
     }
 }
 
@@ -311,18 +381,121 @@ computePadSizes(struct cmdlineInfo const cmdline,
 
 
 
+static void
+padPbm(FILE *       const ifP,
+       unsigned int const cols,
+       unsigned int const rows,
+       int          const format,
+       unsigned int const newcols,
+       unsigned int const lpad,
+       unsigned int const rpad,
+       unsigned int const tpad,
+       unsigned int const bpad,
+       bool         const colorWhite) {
+/*----------------------------------------------------------------------------
+  Fast padding routine for PBM
+-----------------------------------------------------------------------------*/
+    unsigned char * const bgrow  = pbm_allocrow_packed(newcols);
+    unsigned char * const newrow = pbm_allocrow_packed(newcols);
+
+    unsigned char const padChar =
+        0xff * (colorWhite ? PBM_WHITE : PBM_BLACK);
+
+    unsigned int const newColChars = pbm_packed_bytes(newcols);
+
+    unsigned int row;
+    unsigned int charCnt;
+
+    /* Set up margin row, input-output buffer */
+    for (charCnt = 0; charCnt < newColChars; ++charCnt)
+        bgrow[charCnt] = newrow[charCnt] = padChar;
+
+    if (newcols % 8 > 0) {
+        bgrow[newColChars-1]  <<= 8 - newcols % 8;
+        newrow[newColChars-1] <<= 8 - newcols % 8;
+    }
+
+    pbm_writepbminit(stdout, newcols, rows + tpad + bpad, 0);
+
+    /* Write top margin */
+    for (row = 0; row < tpad; ++row)
+        pbm_writepbmrow_packed(stdout, bgrow, newcols, 0);
+    
+    /* Read rows, shift and write with left and right margins added */
+    for (row = 0; row < rows; ++row) {
+        pbm_readpbmrow_bitoffset(ifP, newrow, cols, format, lpad);
+        pbm_writepbmrow_packed(stdout, newrow, newcols, 0);
+    }
+
+    pnm_freerow(newrow);
+
+    /* Write bottom margin */
+    for (row = 0; row < bpad; ++row)
+        pbm_writepbmrow_packed(stdout, bgrow, newcols, 0);
+
+    pnm_freerow(bgrow);
+}
+
+
+static void
+padGeneral(FILE *       const ifP,
+           unsigned int const cols,
+           unsigned int const rows,
+           xelval       const maxval,
+           int          const format,
+           unsigned int const newcols,
+           unsigned int const lpad,
+           unsigned int const rpad,
+           unsigned int const tpad,
+           unsigned int const bpad,
+           bool         const colorWhite) {
+/*----------------------------------------------------------------------------
+  General padding routine (logic works for PBM)
+-----------------------------------------------------------------------------*/
+    xel * const bgrow  = pnm_allocrow(newcols);
+    xel * const xelrow = pnm_allocrow(newcols);
+    xel background;
+    unsigned int row, col;
+
+    if (colorWhite)
+        background = pnm_whitexel(maxval, format);
+    else
+        background = pnm_blackxel(maxval, format);
+
+    for (col = 0; col < newcols; ++col)
+        xelrow[col] = bgrow[col] = background;
+
+    pnm_writepnminit(stdout, newcols, rows + tpad + bpad, maxval, format, 0);
+
+    for (row = 0; row < tpad; ++row)
+        pnm_writepnmrow(stdout, bgrow, newcols, maxval, format, 0);
+
+    for (row = 0; row < rows; ++row) {
+        pnm_readpnmrow(ifP, &xelrow[lpad], cols, maxval, format);
+        pnm_writepnmrow(stdout, xelrow, newcols, maxval, format, 0);
+    }
+
+    for (row = 0; row < bpad; ++row)
+        pnm_writepnmrow(stdout, bgrow, newcols, maxval, format, 0);
+
+    pnm_freerow(xelrow);
+    pnm_freerow(bgrow);
+}
+
+
+
 int
-main(int argc, char ** argv) {
+main(int argc, const char ** argv) {
 
     struct cmdlineInfo cmdline;
-    FILE *ifP;
-    xel *xelrow, *bgrow, background;
+    FILE * ifP;
+
     xelval maxval;
-    int rows, cols, newcols, row, col, format;
+    int rows, cols, newcols, format;
     bool depr_cmd; /* use deprecated commandline interface */
     unsigned int lpad, rpad, tpad, bpad;
 
-    pnm_init( &argc, argv );
+    pm_proginit(&argc, argv);
 
     /* detect deprecated options */
     depr_cmd = FALSE;  /* initial assumption */
@@ -332,7 +505,7 @@ main(int argc, char ** argv) {
             if (argv[1][2] >= '0' && argv[1][2] <= '9')
                 depr_cmd = TRUE;
         }
-    } 
+    }
     if (argc > 2 && argv[2][0] == '-') {
         if (argv[2][1] == 't' || argv[2][1] == 'b'
             || argv[2][1] == 'l' || argv[2][1] == 'r') {
@@ -341,45 +514,27 @@ main(int argc, char ** argv) {
         }
     }
 
-    if (depr_cmd) 
+    if (depr_cmd)
         parseCommandLineOld(argc, argv, &cmdline);
-    else 
+    else
         parseCommandLine(argc, argv, &cmdline);
 
     ifP = pm_openr(cmdline.input_filespec);
 
     pnm_readpnminit(ifP, &cols, &rows, &maxval, &format);
-    if (cmdline.white)
-        background = pnm_whitexel(maxval, format);
-    else
-        background = pnm_blackxel(maxval, format);
 
     if (cmdline.verbose) pm_message("image WxH = %dx%d", cols, rows);
 
     computePadSizes(cmdline, cols, rows, &lpad, &rpad, &tpad, &bpad);
 
     newcols = cols + lpad + rpad;
-    xelrow = pnm_allocrow(newcols);
-    bgrow = pnm_allocrow(newcols);
 
-    for (col = 0; col < newcols; col++)
-        xelrow[col] = bgrow[col] = background;
-
-    pnm_writepnminit(stdout, newcols, rows + tpad + bpad, maxval, format, 0);
-
-    for (row = 0; row < tpad; row++)
-        pnm_writepnmrow(stdout, bgrow, newcols, maxval, format, 0);
-
-    for (row = 0; row < rows; row++) {
-        pnm_readpnmrow(ifP, &xelrow[lpad], cols, maxval, format);
-        pnm_writepnmrow(stdout, xelrow, newcols, maxval, format, 0);
-    }
-
-    for (row = 0; row < bpad; row++)
-        pnm_writepnmrow(stdout, bgrow, newcols, maxval, format, 0);
-
-    pnm_freerow(xelrow);
-    pnm_freerow(bgrow);
+    if (PNM_FORMAT_TYPE(format) == PBM_TYPE)
+        padPbm(ifP, cols, rows, format, newcols, lpad, rpad, tpad, bpad,
+               !!cmdline.white);
+    else
+        padGeneral(ifP, cols, rows, maxval, format, 
+                   newcols, lpad, rpad, tpad, bpad, !!cmdline.white);
 
     pm_close(ifP);
 
diff --git a/editor/pnmpaste.c b/editor/pnmpaste.c
index 38b316c6..33834669 100644
--- a/editor/pnmpaste.c
+++ b/editor/pnmpaste.c
@@ -10,176 +10,419 @@
 ** implied warranty.
 */
 
+#include <assert.h>
+
 #include "pm_c_util.h"
+#include "mallocvar.h"
+#include "nstring.h"
+#include "shhopt.h"
 #include "pnm.h"
 
+
+enum boolOp {REPLACE, AND, OR, XOR /*, NAND, NOR, NXOR */ };
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * baseFilename;
+    const char * insetFilename;
+    int insertCol;  /* Negative means from right edge */
+    int insertRow;  /* Negative means from bottom edge */
+    enum boolOp operation;
+};
+
+
+
+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 option_def_index;
+    unsigned int replaceOpt, andOpt, orOpt, xorOpt;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0,   "replace",     OPT_FLAG,    NULL,
+            &replaceOpt,           0);
+    OPTENT3(0,   "and",         OPT_FLAG,    NULL,
+            &andOpt,               0);
+    OPTENT3(0,   "or",          OPT_FLAG,    NULL,
+            &orOpt,                0);
+    OPTENT3(0,   "xor",         OPT_FLAG,    NULL,
+            &xorOpt,               0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = TRUE;  /* We have parms that are negative numbers */
+
+    optParseOptions3(&argc, (char **)argv, opt, sizeof opt, 0);
+        /* 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");
+
+    cmdlineP->operation =
+        replaceOpt ? REPLACE :
+        andOpt     ? AND     :
+        orOpt      ? OR      :
+        xorOpt     ? XOR     :
+        replaceOpt;
+        
+
+    if (argc-1 >= 3) {
+        cmdlineP->insetFilename = argv[1];
+        cmdlineP->insertCol     = atoi(argv[2]);
+        cmdlineP->insertRow     = atoi(argv[3]);
+
+        if (argc-1 >= 4) {
+            cmdlineP->baseFilename = argv[4];
+
+            if (argc-1 > 4)
+                pm_error("Too many arguments: %u.  This program takes "
+                         "at most 4", argc-1);
+        } else
+            cmdlineP->baseFilename = "-";
+    } else
+        pm_error("You must specify at least 3 arguments: \"from\" file "
+                 "name, insert-at column, and insert-at row.  "
+                 "You specified %u", argc-1);
+
+    if (streq(cmdlineP->baseFilename, "-") &&
+        streq(cmdlineP->insetFilename, "-"))
+        pm_error("You can't use Standard Input for both the input images");
+}
+
+
+
+static unsigned char
+leftBits(unsigned char const x,
+         unsigned int  const n){
+/*----------------------------------------------------------------------------
+  Clear rightmost (8-n) bits, retain leftmost (=high) n bits.
+-----------------------------------------------------------------------------*/
+    assert(n <= 8);
+
+    return (x >> (8-n)) << (8-n);
+}
+
+
+
+static unsigned char
+rightBits(unsigned char const x,
+          unsigned int  const n){
+/*----------------------------------------------------------------------------
+  Return rightmost (=low) n bits of x. (Clear the rest).
+-----------------------------------------------------------------------------*/
+    assert(n <= 8);
+
+    return ((unsigned char)(x << (8-n))) >> (8-n);
+}
+
+
+
+static void
+insertDirect(FILE *          const ifP,
+             unsigned char * const destrow,
+             unsigned int    const cols,
+             int             const format,
+             enum boolOp     const operation,
+             unsigned char * const buffer) {
+/*----------------------------------------------------------------------------
+   Read the next row from PBM file 'ifP' and merge it according to
+   'operation' into 'destrow', flush left in packed PBM format.
+
+   'cols' and 'format' describe the 'ifP' image.
+
+   'buffer' is a scratch buffer for our use, at least wide enough to hold
+   a packed PBM row of 'ifP'.
+-----------------------------------------------------------------------------*/
+    unsigned int  const colBytes  = pbm_packed_bytes(cols);
+    unsigned int  const last      = colBytes - 1;
+    unsigned char const origRight = destrow[last];
+
+    if (operation == REPLACE)
+        pbm_readpbmrow_packed(ifP, destrow, cols, format);
+    else {
+        unsigned int i;
+
+        pbm_readpbmrow_packed(ifP, buffer, cols, format);
+
+        for (i = 0; i < colBytes; ++i) {
+            switch (operation) {
+            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;
+            }
+        }
+    }
+
+    if (cols % 8 > 0)
+        destrow[last] = leftBits(destrow[last], cols % 8)
+            | rightBits(origRight, 8 - cols % 8);
+}
+
+
+
+static void
+insertShift(FILE *          const ifP,
+            unsigned char * const destrow,
+            unsigned int    const cols,
+            unsigned int    const format,
+            unsigned int    const offset,
+            enum boolOp     const operation,
+            unsigned char * const buffer) {
+/*----------------------------------------------------------------------------
+   Same as insertDirect(), but start merging 'offset' bits from the left
+   end of 'destrow'.  'offset' is less than 8.
+
+   buffer[] is wide enough to hold a packed PBM row of *ifP plus one
+   byte of margin.
+-----------------------------------------------------------------------------*/
+    unsigned int const shiftBytes = pbm_packed_bytes(cols + offset);
+    unsigned int const last = shiftBytes - 1;
+    unsigned char const origLeft  = destrow[0];
+    unsigned char const origRight = destrow[last];
+
+    unsigned int const padOffset = (cols + offset) % 8;
+
+    unsigned int i;
+
+    assert(offset < 8);
+
+    pbm_readpbmrow_packed(ifP, &buffer[1], cols, format);
+
+    /* Note that buffer[0] is undefined. */
+
+    for (i = 0; i < shiftBytes; ++i) {
+        unsigned int  const rsh = offset;
+        unsigned int  const lsh = 8-rsh;
+        unsigned char const t = buffer[i] << lsh | buffer[i+1] >> rsh;
+
+        switch (operation) {
+        case REPLACE: destrow[i] = t; break;
+        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;
+        */
+        }
+    }
+
+    /* destrow[] now contains garbage in the 'offset' leftmost bits
+       and 8-offset rightmost bits.  Those are supposed to be unchanged
+       from the input, so we restore them now.
+    */
+
+    destrow[0] = leftBits(origLeft, offset) |
+        rightBits(destrow[0], 8-offset);
+   
+    if (padOffset % 8 > 0)
+        destrow[last] = leftBits(destrow[last], padOffset) |
+            rightBits(origRight , 8-padOffset);
+}
+
+
+
+static void
+pastePbm(FILE *       const fpInset,
+         FILE *       const fpBase,
+         int          const insetFormat,
+         int          const baseFormat,
+         unsigned int const insetRows,
+         unsigned int const baseRows,
+         unsigned int const insetCols,
+         unsigned int const baseCols,
+         unsigned int const insertCol,
+         unsigned int const insertRow,
+         enum boolOp  const operation) {
+/*----------------------------------------------------------------------------
+  Fast paste for PBM
+-----------------------------------------------------------------------------*/
+    unsigned char * const baserow = pbm_allocrow_packed(baseCols);
+    unsigned char * const buffer = pbm_allocrow_packed(insetCols+8);
+    int const shiftBytes = insertCol / 8;
+    unsigned int const shiftOffset = insertCol % 8;
+    int const baseColBytes = pbm_packed_bytes(baseCols);
+
+    unsigned int row;
+
+    pbm_writepbminit(stdout, baseCols, baseRows, 0);
+
+    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[shiftBytes], insetCols,
+                             insetFormat, operation, buffer);
+            else
+                insertShift(fpInset, &baserow[shiftBytes], insetCols,
+                            insetFormat, shiftOffset, operation, buffer);
+        }
+
+        if (baseCols % 8 > 0)
+            baserow[baseColBytes-1]
+                = leftBits(baserow[baseColBytes-1] , baseCols % 8);
+
+        pbm_writepbmrow_packed(stdout, baserow, baseCols, 0);
+    }
+    pbm_freerow_packed(buffer);
+    pbm_freerow_packed(baserow);
+}
+
+
+
+static void
+pasteNonPbm(FILE *       const fpInset,
+            FILE *       const fpBase,
+            int          const formatInset,
+            int          const formatBase,
+            int          const newformat,
+            xelval       const maxvalInset,
+            xelval       const maxvalBase,
+            unsigned int const rowsInset,
+            unsigned int const rowsBase,
+            unsigned int const colsInset,
+            unsigned int const colsBase,
+            unsigned int const insertCol,
+            unsigned int const insertRow) {
+
+    /* Logic works for PBM, but cannot do bitwise operations */             
+
+    xelval const newmaxval = MAX(maxvalInset, maxvalBase);
+
+    xel * const xelrowInset = pnm_allocrow(colsInset);
+    xel * const xelrowBase  = pnm_allocrow(colsBase);
+
+    unsigned int row;
+
+    pnm_writepnminit(stdout, colsBase, rowsBase, newmaxval, newformat, 0);
+
+    for (row = 0; row < rowsBase; ++row) {
+        pnm_readpnmrow(fpBase, xelrowBase, colsBase, maxvalBase, formatBase);
+        pnm_promoteformatrow(xelrowBase, colsBase, maxvalBase, formatBase,
+                             newmaxval, newformat);
+
+        if (row >= insertRow && row < insertRow + rowsInset) {
+            unsigned int colInset;
+
+            pnm_readpnmrow(fpInset, xelrowInset, colsInset, maxvalInset,
+                           formatInset);
+            pnm_promoteformatrow(xelrowInset, colsInset, maxvalInset,
+                                 formatInset, newmaxval, newformat );
+            for (colInset = 0; colInset < colsInset; ++colInset)
+                xelrowBase[insertCol + colInset] = xelrowInset[colInset];
+        }
+        pnm_writepnmrow(stdout, xelrowBase, colsBase, newmaxval, newformat, 0);
+    }
+    
+    pnm_freerow(xelrowBase);
+    pnm_freerow(xelrowInset);
+}
+
+
+
 int
-main( argc, argv )
-    int argc;
-    char* argv[];
-    {
-    FILE* ifp1;
-    FILE* ifp2;
-    register xel* xelrow1;
-    register xel* xelrow2;
-    register xel* x1P;
-    register xel* x2P;
-    xelval maxval1, maxval2, newmaxval;
-    int argn, rows1, cols1, format1, x, y;
-    int rows2, cols2, format2, newformat, row;
-    register int col;
-    char function;
-    const char* const usage = "[-replace|-or|-and|-xor] frompnmfile x y [intopnmfile]";
-
-    pnm_init( &argc, argv );
-
-    argn = 1;
-    function = 'r';
-
-    /* Check for flags. */
-    if ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
-	{
-	if ( pm_keymatch( argv[argn], "-replace", 2 ) )
-	    function = 'r';
-	else if ( pm_keymatch( argv[argn], "-or", 2 ) )
-	    function = 'o';
-	else if ( pm_keymatch( argv[argn], "-and", 2 ) )
-	    function = 'a';
-	else if ( pm_keymatch( argv[argn], "-xor", 2 ) )
-	    function = 'x';
-	else
-	    pm_usage( usage );
-	++argn;
-	}
-
-    if ( argn == argc )
-	pm_usage( usage );
-    ifp1 = pm_openr( argv[argn] );
-    ++argn;
-
-    if ( argn == argc )
-	pm_usage( usage );
-    if ( sscanf( argv[argn], "%d", &x ) != 1 )
-	pm_usage( usage );
-    ++argn;
-    if ( argn == argc )
-	pm_usage( usage );
-    if ( sscanf( argv[argn], "%d", &y ) != 1 )
-	pm_usage( usage );
-    ++argn;
-
-    if ( argn != argc )
-	{
-	ifp2 = pm_openr( argv[argn] );
-	++argn;
-	}
+main(int argc, const char ** argv) {
+
+    struct cmdlineInfo cmdline;
+    FILE * fpInset;
+    FILE * fpBase;
+    xelval maxvalInset, maxvalBase;
+    int rowsInset, colsInset;
+    int formatInset;
+    int rowsBase, colsBase;
+    int formatBase;
+    int newformat;
+    unsigned int insertRow, insertCol;
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    fpInset = pm_openr(cmdline.insetFilename);
+    fpBase  = pm_openr(cmdline.baseFilename);
+
+    pnm_readpnminit(fpInset, &colsInset, &rowsInset,
+                    &maxvalInset, &formatInset);
+    pnm_readpnminit(fpBase, &colsBase, &rowsBase, &maxvalBase, &formatBase);
+
+    if (colsBase < colsInset)
+        pm_error(
+            "Image to paste is wider than base image by %u cols",
+            colsInset - colsBase);
+    else if (cmdline.insertCol <= -colsBase)
+        pm_error(
+            "x is too negative -- the second image has only %u cols",
+            colsBase);
+    else if (cmdline.insertCol >= colsBase)
+        pm_error(
+            "x is too large -- the second image has only %u cols",
+            colsBase);
+
+    if (rowsBase < rowsInset)
+        pm_error(
+            "Image to paste is taller than base image by %u rows",
+            rowsInset - rowsBase);
+    else if (cmdline.insertRow <= -rowsBase)
+        pm_error(
+            "y is too negative -- the second image has only %u rows",
+            rowsBase);
+    else if (cmdline.insertRow >= rowsBase)
+        pm_error(
+            "y is too large -- the second image has only %d rows",
+            rowsBase);
+
+    insertCol = cmdline.insertCol < 0 ?
+        colsBase + cmdline.insertCol : cmdline.insertCol;
+    insertRow = cmdline.insertRow < 0 ?
+        rowsBase + cmdline.insertRow : cmdline.insertRow;
+
+    if (insertCol + colsInset > colsBase)
+        pm_error("Extends over right edge by %u pixels",
+                 (insertCol + colsInset) - colsBase);
+    if (insertRow + rowsInset > rowsBase)
+        pm_error("Extends over bottom edge by %u pixels",
+                 (insertRow + rowsInset) - rowsBase);
+
+    newformat = MAX(PNM_FORMAT_TYPE(formatInset), PNM_FORMAT_TYPE(formatBase));
+
+    if (cmdline.operation != REPLACE && newformat != PBM_TYPE)
+        pm_error("no logical operations allowed for a non-PBM image");
+
+    if (newformat == PBM_TYPE)
+        pastePbm(fpInset, fpBase, formatInset, formatBase,
+                 rowsInset, rowsBase, colsInset, colsBase,
+                 insertCol, insertRow, cmdline.operation);
     else
-	ifp2 = stdin;
-
-    if ( argn != argc )
-	pm_usage( usage );
-
-    pnm_readpnminit( ifp1, &cols1, &rows1, &maxval1, &format1 );
-    xelrow1 = pnm_allocrow(cols1);
-    pnm_readpnminit( ifp2, &cols2, &rows2, &maxval2, &format2 );
-    xelrow2 = pnm_allocrow(cols2);
-
-    if ( x <= -cols2 )
-	pm_error(
-	    "x is too negative -- the second anymap has only %d cols",
-	    cols2 );
-    else if ( x >= cols2 )
-	pm_error(
-	    "x is too large -- the second anymap has only %d cols",
-	    cols2 );
-    if ( y <= -rows2 )
-	pm_error(
-	    "y is too negative -- the second anymap has only %d rows",
-	    rows2 );
-    else if ( y >= rows2 )
-	pm_error(
-	    "y is too large -- the second anymap has only %d rows",
-	    rows2 );
-
-    if ( x < 0 )
-	x += cols2;
-    if ( y < 0 )
-	y += rows2;
-
-    if ( x + cols1 > cols2 )
-	pm_error( "x + width is too large by %d pixels", x + cols1 - cols2 );
-    if ( y + rows1 > rows2 )
-	pm_error( "y + height is too large by %d pixels", y + rows1 - rows2 );
-
-    newformat = MAX( PNM_FORMAT_TYPE(format1), PNM_FORMAT_TYPE(format2) );
-    newmaxval = MAX( maxval1, maxval2 );
-
-    if ( function != 'r' && newformat != PBM_TYPE )
-	pm_error( "no logical operations allowed for non-bitmaps" );
-
-    pnm_writepnminit( stdout, cols2, rows2, newmaxval, newformat, 0 );
-
-    for ( row = 0; row < rows2; ++row )
-	{
-	pnm_readpnmrow( ifp2, xelrow2, cols2, maxval2, format2 );
-	pnm_promoteformatrow( xelrow2, cols2, maxval2, format2,
-	    newmaxval, newformat );
-
-	if ( row >= y && row < y + rows1 )
-	    {
-	    pnm_readpnmrow( ifp1, xelrow1, cols1, maxval1, format1 );
-	    pnm_promoteformatrow( xelrow1, cols1, maxval1, format1,
-		newmaxval, newformat );
-	    for ( col = 0, x1P = xelrow1, x2P = &(xelrow2[x]);
-		  col < cols1; ++col, ++x1P, ++x2P )
-		{
-		register xelval b1, b2;
-
-		switch ( function )
-		    {
-		    case 'r':
-		    *x2P = *x1P;
-		    break;
-
-		    case 'o':
-		    b1 = PNM_GET1( *x1P );
-		    b2 = PNM_GET1( *x2P );
-		    if ( b1 != 0 || b2 != 0 )
-			PNM_ASSIGN1( *x2P, newmaxval );
-		    else
-			PNM_ASSIGN1( *x2P, 0 );
-		    break;
-
-		    case 'a':
-		    b1 = PNM_GET1( *x1P );
-		    b2 = PNM_GET1( *x2P );
-		    if ( b1 != 0 && b2 != 0 )
-			PNM_ASSIGN1( *x2P, newmaxval );
-		    else
-			PNM_ASSIGN1( *x2P, 0 );
-		    break;
-
-		    case 'x':
-		    b1 = PNM_GET1( *x1P );
-		    b2 = PNM_GET1( *x2P );
-		    if ( ( b1 != 0 && b2 == 0 ) || ( b1 == 0 && b2 != 0 ) )
-			PNM_ASSIGN1( *x2P, newmaxval );
-		    else
-			PNM_ASSIGN1( *x2P, 0 );
-		    break;
-
-		    default:
-		    pm_error( "can't happen" );
-		    }
-		}
-	    }
-
-	pnm_writepnmrow( stdout, xelrow2, cols2, newmaxval, newformat, 0 );
-	}
-    
-    pm_close( ifp1 );
-    pm_close( ifp2 );
-    pm_close( stdout );
+        pasteNonPbm(fpInset, fpBase,
+                    formatInset, formatBase, newformat,
+                    maxvalInset, maxvalBase,
+                    rowsInset, rowsBase, colsInset, colsBase,
+                    insertCol, insertRow);
 
-    exit( 0 );
-    }
+    pm_close(fpInset);
+    pm_close(fpBase);
+    pm_close(stdout);
+
+    return 0;
+}
diff --git a/editor/pnmremap.c b/editor/pnmremap.c
index 1ed07fdb..db35e2c0 100644
--- a/editor/pnmremap.c
+++ b/editor/pnmremap.c
@@ -40,15 +40,6 @@ enum missingMethod {
     MISSING_CLOSE
 };
 
-#define FS_SCALE 1024
-
-struct fserr {
-    long** thiserr;
-    long** nexterr;
-    bool fsForward;
-};
-
-
 struct cmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
@@ -56,6 +47,7 @@ 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;
     enum missingMethod missingMethod;
     char * missingcolor;      
         /* -missingcolor value.  Null if not specified */
@@ -98,6 +90,8 @@ parseCommandLine (int argc, char ** argv,
             NULL,                       &nofloyd, 0);
     OPTENT3(0,   "nofs",         OPT_FLAG,   
             NULL,                       &nofloyd, 0);
+    OPTENT3(0,   "norandom",     OPT_FLAG,   
+            NULL,                       &cmdlineP->norandom, 0);
     OPTENT3(0,   "firstisdefault", OPT_FLAG,   
             NULL,                       &firstisdefault, 0);
     OPTENT3(0,   "mapfile",      OPT_STRING, 
@@ -143,53 +137,112 @@ parseCommandLine (int argc, char ** argv,
 }
 
 
+typedef enum {
+    ADJUST_NONE,
+    ADJUST_RGBTO1,
+    ADJUST_GRAYSCALETO3
+} depthAdjustment;
+
+
 
 static void
-rgbToDepth1(const struct pam * const pamP,
-            tuple *            const tupleRow) {
-    
-    unsigned int col;
+rgbToDepth1(tuple const tuple) {
 
-    for (col = 0; col < pamP->width; ++col) {
-        unsigned int plane;
-        double grayvalue;
-        grayvalue = 0.0;  /* initial value */
-        for (plane = 0; plane < pamP->depth; ++plane)
-            grayvalue += pnm_lumin_factor[plane] * tupleRow[col][plane];
-        tupleRow[col][0] = (sample) (grayvalue + 0.5);
-    }
+    unsigned int plane;
+    double grayvalue;
+
+    grayvalue = 0.0;  /* initial value */
+
+    for (plane = 0; plane < 3; ++plane)
+        grayvalue += pnm_lumin_factor[plane] * tuple[plane];
+
+    tuple[0] = (sample) (grayvalue + 0.5);
+}
+
+
+
+static void
+grayscaleToDepth3(tuple const tuple) {
+
+    tuple[1] = tuple[0];
+    tuple[2] = tuple[0];
 }
 
 
 
 static void
-grayscaleToDepth3(const struct pam * const pamP,
-                  tuple *            const tupleRow) {
+adjustDepthTuple(tuple           const tuple,
+                 depthAdjustment const adjustment) {
     
-    unsigned int col;
+    switch (adjustment) {
+    case ADJUST_NONE:
+        break;
+    case ADJUST_RGBTO1:
+        rgbToDepth1(tuple);
+        break;
+    case ADJUST_GRAYSCALETO3:
+        grayscaleToDepth3(tuple);
+        break;
+    }
+}
+
 
-    assert(pamP->allocation_depth >= 3);
 
-    for (col = 0; col < pamP->width; ++col) {
-        tupleRow[col][1] = tupleRow[col][0];
-        tupleRow[col][2] = tupleRow[col][0];
+static void
+inverseAdjustDepthTuple(tuple           const tuple,
+                        depthAdjustment const adjustment) {
+    
+    switch (adjustment) {
+    case ADJUST_NONE:
+        break;
+    case ADJUST_RGBTO1:
+        grayscaleToDepth3(tuple);
+        break;
+    case ADJUST_GRAYSCALETO3:
+        rgbToDepth1(tuple);
+        break;
     }
 }
 
 
 
 static void
-adjustDepth(const struct pam * const pamP,
-            tuple *            const tupleRow,
-            unsigned int       const newDepth) {
+adjustDepthRow(tuple *         const tupleRow,
+               unsigned int    const width,
+               depthAdjustment const adjustment) {
 /*----------------------------------------------------------------------------
-   Change the depth of the raster row tupleRow[] of the image
-   described by 'pamP' to newDepth.
+   Change tupleRow[] depth as indicated by 'adjustment',
+   i.e. turned from RGB to grayscale or grayscale to RGB.
+
+   We assume tupleRow[] is consistent with 'adjustment' -- i.e. if
+   'adjustment' says grayscale to RGB, tupleRow[] has an allocation depth of
+   at least 3 and if 'adjustment' says from RGB to grayscale, tupleRow[] has
+   RGB tuples.
+-----------------------------------------------------------------------------*/
+    if (adjustment == ADJUST_NONE) {
+    } else {
+        unsigned int col;
+
+        for (col = 0; col < width; ++col) {
+            if (adjustment == ADJUST_RGBTO1)
+                rgbToDepth1(tupleRow[col]);
+            else {
+                assert(adjustment == ADJUST_GRAYSCALETO3);
+                grayscaleToDepth3(tupleRow[col]);
+            }
+        }
+    }
+}
+
 
-   We don't change the memory allocation; tupleRow[] must already have
-   space allocated for at least 'newDepth' planes.  When we're done,
-   all but the first 'newDepth' planes are meaningless, but the space is
-   still there.
+
+static void
+selectDepthAdjustment(const struct pam * const pamP,
+                      unsigned int       const newDepth,
+                      depthAdjustment *  const adjustmentP) {
+/*----------------------------------------------------------------------------
+   Determine what kind of depth adjustment the pixels of an image described
+   by 'pamP' need to be comparable to pixels with depth 'newDepth'.
 
    The only depth changes we know how to do are:
 
@@ -197,15 +250,23 @@ adjustDepth(const struct pam * const pamP,
 
        We change it to grayscale or black and white.
 
+       For this, we return *adjustmentP == ADJUST_RGBTO1.
+
      - from tuple type GRAYSCALE or BLACKANDWHITE depth 1 to depth 3.
 
        We change it to RGB.
 
+       For this, we return *adjustmentP == ADJUST_GRAYSCALETO3.
+
    For any other depth change request, we issue an error message and abort
    the program.
------------------------------------------------------------------------------*/
-    if (newDepth != pamP->depth) {
 
+   If 'newDepth' is the same depth as the original (no depth change required),
+   we return *adjustmentP == ADJUST_NONE.
+-----------------------------------------------------------------------------*/
+    if (newDepth == pamP->depth)
+        *adjustmentP = ADJUST_NONE;
+    else {
         if (stripeq(pamP->tuple_type, "RGB")) {
             if (newDepth != 1) {
                 pm_error("Map image depth of %u differs from input image "
@@ -214,7 +275,7 @@ adjustDepth(const struct pam * const pamP,
                          "an RGB tuple is 1.",
                          newDepth, pamP->depth);
             } else
-                rgbToDepth1(pamP, tupleRow);
+                *adjustmentP = ADJUST_RGBTO1;
         } else if (stripeq(pamP->tuple_type, "GRAYSCALE") ||
                    stripeq(pamP->tuple_type, "BLACKANDWHITE")) {
             if (newDepth != 3) {
@@ -225,7 +286,7 @@ adjustDepth(const struct pam * const pamP,
                          "a GRAYSCALE or BLACKANDWHITE tuple is 3.",
                          newDepth, pamP->depth);
             } else
-                grayscaleToDepth3(pamP, tupleRow);
+                *adjustmentP = ADJUST_GRAYSCALETO3;
         } else {
             pm_error("Map image depth of %u differs from input image depth "
                      "of %u, and the input image does not have a tuple type "
@@ -240,7 +301,6 @@ adjustDepth(const struct pam * const pamP,
 
 
 
-
 static void
 computeColorMapFromMap(struct pam *   const mappamP, 
                        tuple **       const maptuples, 
@@ -263,15 +323,103 @@ computeColorMapFromMap(struct pam *   const mappamP,
         pnm_computetuplefreqtable(mappamP, maptuples, MAXCOLORS, &colors);
     if (*colormapP == NULL)
         pm_error("too many colors in colormap!");
-    pm_message("%d colors found in colormap", colors);
+    pm_message("%u colors found in colormap", colors);
     *newcolorsP = colors;
 }
 
 
 
+#define FS_SCALE 1024
+
+struct fserr {
+    unsigned int width;
+        /* Width of the image being dithered */
+    long ** thiserr;
+    long ** nexterr;
+    bool    fsForward;
+        /* We are in a left-to-right row */
+    int     begCol;
+        /* First column in the sweep.  Determined by 'fsForward': either
+           the leftmost or rightmost column in the row
+        */
+    int     endCol;
+        /* Column after the last column in the sweep.  Determined by
+           'fsForward': either one past the left end or one past the right
+           end of the row.
+        */
+    int     step;
+        /* What we add to a column number to get the next one in the sweep.
+           Determined by 'fsForward': +1 or -1.
+        */
+};
+
+
+
+static void
+randomizeError(long **      const err,
+               unsigned int const width,
+               unsigned int const depth) {
+/*----------------------------------------------------------------------------
+   Set a random error in the range [-1 .. 1] (normalized via FS_SCALE)
+   in the error array err[][].
+-----------------------------------------------------------------------------*/
+    unsigned int col;
+
+    srand(pm_randseed());
+
+    for (col = 0; col < width; ++col) {
+        unsigned int plane;
+        for (plane = 0; plane < depth; ++plane) 
+            err[plane][col] = rand() % (FS_SCALE * 2) - FS_SCALE;
+    }
+}
+
+
+
+static void
+zeroError(long **      const err,
+          unsigned int const width,
+          unsigned int const depth) {
+/*----------------------------------------------------------------------------
+   Set all errors to zero in the error array err[][].
+-----------------------------------------------------------------------------*/
+    unsigned int col;
+
+    for (col = 0; col < width; ++col) {
+        unsigned int plane;
+        for (plane = 0; plane < depth; ++plane) 
+            err[plane][col] = 0;
+    }
+}
+
+
+
+static void
+fserrSetForward(struct fserr * const fserrP) {
+
+    fserrP->fsForward = TRUE;
+    fserrP->begCol = 0;
+    fserrP->endCol = fserrP->width;
+    fserrP->step   = +1;
+}
+
+
+
+static void
+fserrSetBackward(struct fserr * const fserrP) {
+
+    fserrP->fsForward = FALSE;
+    fserrP->begCol = fserrP->width - 1;
+    fserrP->endCol = -1;
+    fserrP->step   = -1;
+}
+
+
+
 static void
 initFserr(struct pam *   const pamP,
-          struct fserr * const fserrP) {
+          struct fserr * const fserrP,
+          bool           const initRandom) {
 /*----------------------------------------------------------------------------
    Initialize the Floyd-Steinberg error vectors
 -----------------------------------------------------------------------------*/
@@ -279,6 +427,8 @@ initFserr(struct pam *   const pamP,
 
     unsigned int const fserrSize = pamP->width + 2;
 
+    fserrP->width = pamP->width;
+
     MALLOCARRAY(fserrP->thiserr, pamP->depth);
     if (fserrP->thiserr == NULL)
         pm_error("Out of memory allocating Floyd-Steinberg structures "
@@ -299,20 +449,12 @@ initFserr(struct pam *   const pamP,
                      "for Plane %u, size %u", plane, fserrSize);
     }
 
-    srand((int)(time(0) ^ getpid()));
+    if (initRandom)
+        randomizeError(fserrP->thiserr, fserrSize, pamP->depth);
+    else
+        zeroError(fserrP->thiserr, fserrSize, pamP->depth);
 
-    {
-        int col;
-
-        for (col = 0; col < fserrSize; ++col) {
-            unsigned int plane;
-            for (plane = 0; plane < pamP->depth; ++plane) 
-                fserrP->thiserr[plane][col] = 
-                    rand() % (FS_SCALE * 2) - FS_SCALE;
-                    /* (random errors in [-1 .. 1]) */
-        }
-    }
-    fserrP->fsForward = TRUE;
+    fserrSetForward(fserrP);
 }
 
 
@@ -333,7 +475,8 @@ floydInitRow(struct pam * const pamP, struct fserr * const fserrP) {
 
 static void
 floydAdjustColor(struct pam *   const pamP, 
-                 tuple          const tuple, 
+                 tuple          const intuple, 
+                 tuple          const outtuple, 
                  struct fserr * const fserrP, 
                  int            const col) {
 /*----------------------------------------------------------------------------
@@ -343,8 +486,8 @@ floydAdjustColor(struct pam *   const pamP,
 
     for (plane = 0; plane < pamP->depth; ++plane) {
         long int const s =
-            tuple[plane] + fserrP->thiserr[plane][col+1] / FS_SCALE;
-        tuple[plane] = MIN(pamP->maxval, MAX(0,s));
+            intuple[plane] + fserrP->thiserr[plane][col+1] / FS_SCALE;
+        outtuple[plane] = MIN(pamP->maxval, MAX(0,s));
     }
 }
 
@@ -396,7 +539,11 @@ floydSwitchDir(struct pam * const pamP, struct fserr * const fserrP) {
         fserrP->thiserr[plane] = fserrP->nexterr[plane];
         fserrP->nexterr[plane] = temperr;
     }
-    fserrP->fsForward = ! fserrP->fsForward;
+
+    if (fserrP->fsForward)
+        fserrSetBackward(fserrP);
+    else
+        fserrSetForward(fserrP);
 }
 
 
@@ -616,21 +763,106 @@ lookupThroughHash(struct pam *            const pamP,
 
 
 static void
-convertRow(struct pam *            const pamP, 
-           tuple                         tuplerow[],
-           tupletable              const colormap,
-           struct colormapFinder * const colorFinderP,
-           tuplehash               const colorhash, 
-           bool *                  const usehashP,
-           bool                    const floyd, 
-           tuple                   const defaultColor,
-           struct fserr *          const fserrP,
-           unsigned int *          const missingCountP) {
+mapTuple(struct pam *            const pamP,
+         tuple                   const inTuple,
+         tuple                   const defaultColor,
+         tupletable              const colormap,
+         struct colormapFinder * const colorFinderP,
+         tuplehash               const colorhash, 
+         bool *                  const usehashP,
+         tuple                   const outTuple,
+         bool *                  const missingP) {
+
+    int colormapIndex;
+        /* Index into the colormap of the replacement color, or -1 if
+           there is no usable color in the color map.
+        */
+
+    lookupThroughHash(pamP, inTuple, !!defaultColor, colorFinderP, 
+                      colorhash, &colormapIndex, usehashP);
+
+    if (colormapIndex == -1) {
+        *missingP = TRUE;
+
+        assert(defaultColor);  /* Otherwise, lookup would have succeeded */
+
+        pnm_assigntuple(pamP, outTuple, defaultColor);
+    } else {
+        *missingP = FALSE;
+        pnm_assigntuple(pamP, outTuple, colormap[colormapIndex]->tuple);
+    }
+}
+
+
+
+static void
+convertRowStraight(struct pam *            const inpamP,
+                   struct pam *            const outpamP, 
+                   tuple                         inrow[],
+                   depthAdjustment         const depthAdjustment,
+                   tupletable              const colormap,
+                   struct colormapFinder * const colorFinderP,
+                   tuplehash               const colorhash, 
+                   bool *                  const usehashP,
+                   tuple                   const defaultColor,
+                   tuple                         outrow[],
+                   unsigned int *          const missingCountP) {
 /*----------------------------------------------------------------------------
-  Replace the colors in row tuplerow[] (described by *pamP) with the
-  new colors.
+  Like convertRow(), compute outrow[] from inrow[], replacing each pixel with
+  the new colors.  Do a straight pixel for pixel remap; no dithering.
 
-  Use and update the Floyd-Steinberg state *fserrP.
+  Return the number of pixels that were not matched in the color map as
+  *missingCountP.
+
+  *colorFinderP is a color finder based on 'colormap' -- it tells us what
+  index of 'colormap' corresponds to a certain color.
+-----------------------------------------------------------------------------*/
+    unsigned int col;
+    unsigned int missingCount;
+    
+    /* The following modify tuplerow, to make it consistent with
+     *outpamP instead of *inpamP.
+     */
+    assert(outpamP->allocation_depth >= inpamP->depth);
+
+    pnm_scaletuplerow(inpamP, outrow, inrow, outpamP->maxval);
+
+    adjustDepthRow(outrow, outpamP->width, depthAdjustment);
+
+    missingCount = 0;  /* initial value */
+    
+    for (col = 0; col < outpamP->width; ++col) {
+        bool missing;
+        mapTuple(outpamP, outrow[col], defaultColor,
+                 colormap, colorFinderP,
+                 colorhash, usehashP, outrow[col], &missing);
+
+        if (missing)
+            ++missingCount;
+    }
+
+    *missingCountP = missingCount;
+}
+
+
+
+static void
+convertRowDither(struct pam *            const inpamP,
+                 struct pam *            const outpamP,
+                 tuple                   const inrow[],
+                 depthAdjustment         const depthAdjustment,
+                 tupletable              const colormap,
+                 struct colormapFinder * const colorFinderP,
+                 tuplehash               const colorhash, 
+                 bool *                  const usehashP,
+                 tuple                   const defaultColor,
+                 struct fserr *          const fserrP,
+                 tuple                         outrow[],
+                 unsigned int *          const missingCountP) {
+/*----------------------------------------------------------------------------
+  Like convertRow(), compute outrow[] from inrow[], replacing each pixel with
+  the new colors.  Do a Floyd-Steinberg dither, using and updating the error
+  accumulator *fserrP.
 
   Return the number of pixels that were not matched in the color map as
   *missingCountP.
@@ -638,79 +870,136 @@ convertRow(struct pam *            const pamP,
   *colorFinderP is a color finder based on 'colormap' -- it tells us what
   index of 'colormap' corresponds to a certain color.
 -----------------------------------------------------------------------------*/
-    int col;
-    int limitcol;
-        /* The column at which to stop processing the row.  If we're scanning
-           forwards, this is the rightmost column.  If we're scanning 
-           backward, this is the leftmost column.
+    tuple const ditheredTuple = pnm_allocpamtuple(inpamP);
+        /* The input tuple we're converting, adjusted by the dither */
+    tuple const normTuple = pnm_allocpamtuple(outpamP);
+        /* Same as above, normalized to the maxval of the output file /
+           colormap.
         */
-    
-    if (floyd)
-        floydInitRow(pamP, fserrP);
+    unsigned int missingCount;
+    int col;
 
-    *missingCountP = 0;  /* initial value */
+    floydInitRow(inpamP, fserrP);
+
+    missingCount = 0;  /* initial value */
     
-    if ((!floyd) || fserrP->fsForward) {
-        col = 0;
-        limitcol = pamP->width;
-    } else {
-        col = pamP->width - 1;
-        limitcol = -1;
+    for (col = fserrP->begCol; col != fserrP->endCol; col += fserrP->step) {
+        bool missing;
+
+        floydAdjustColor(inpamP, inrow[col], ditheredTuple, fserrP, col);
+
+        /* Convert tuple to the form of those in the colormap */
+        assert(outpamP->allocation_depth >= inpamP->depth);
+        pnm_scaletuple(inpamP, normTuple, ditheredTuple, outpamP->maxval);
+        adjustDepthTuple(normTuple, depthAdjustment);
+
+        mapTuple(outpamP, normTuple, defaultColor,
+                 colormap, colorFinderP,
+                 colorhash, usehashP, outrow[col], &missing);
+
+        if (missing)
+            ++missingCount;
+
+        /* Convert tuple back to the form of the input, where dithering
+           takes place.
+        */
+        pnm_scaletuple(outpamP, normTuple, outrow[col], inpamP->maxval);
+        inverseAdjustDepthTuple(normTuple, depthAdjustment);
+
+        floydPropagateErr(inpamP, fserrP, col, inrow[col], normTuple);
     }
-    do {
-        int colormapIndex;
-            /* Index into the colormap of the replacement color, or -1 if
-               there is no usable color in the color map.
-            */
 
-        if (floyd) 
-            floydAdjustColor(pamP, tuplerow[col], fserrP, col);
+    floydSwitchDir(inpamP, fserrP);
 
-        lookupThroughHash(pamP, tuplerow[col], 
-                          !!defaultColor, colorFinderP, 
-                          colorhash, &colormapIndex, usehashP);
-        if (floyd)
-            floydPropagateErr(pamP, fserrP, col, tuplerow[col], 
-                              colormap[colormapIndex]->tuple);
+    pnm_freepamtuple(normTuple);
+    pnm_freepamtuple(ditheredTuple);
 
-        if (colormapIndex == -1) {
-            ++*missingCountP;
+    *missingCountP = missingCount;
+}
 
-            assert(defaultColor);  // Otherwise, lookup would have succeeded
 
-            pnm_assigntuple(pamP, tuplerow[col], defaultColor);
-        } else 
-            pnm_assigntuple(pamP, tuplerow[col], 
-                            colormap[colormapIndex]->tuple);
 
-        if (floyd && !fserrP->fsForward) 
-            --col;
-        else
-            ++col;
-    } while (col != limitcol);
+static void
+convertRow(struct pam *            const inpamP,
+           struct pam *            const outpamP,
+           tuple                         inrow[],
+           depthAdjustment               depthAdjustment,
+           tupletable              const colormap,
+           struct colormapFinder * const colorFinderP,
+           tuplehash               const colorhash, 
+           bool *                  const usehashP,
+           bool                    const floyd, 
+           tuple                   const defaultColor,
+           struct fserr *          const fserrP,
+           tuple                         outrow[],
+           unsigned int *          const missingCountP) {
+/*----------------------------------------------------------------------------
+  Replace the colors in row tuplerow[] (described by *inpamP) with the
+  new colors and convert so it is described by *outpamP.
+
+  Use and update the Floyd-Steinberg state *fserrP.
+
+  Return the number of pixels that were not matched in the color map as
+  *missingCountP.
 
+  *colorFinderP is a color finder based on 'colormap' -- it tells us what
+  index of 'colormap' corresponds to a certain color.
+
+  outrow[] doubles as a work space, so we require it to have an allocation
+  depth at least as great as that of inrow[].
+-----------------------------------------------------------------------------*/
+    /* The following both consults and adds to 'colorhash' and
+       its associated '*usehashP'.  It modifies 'tuplerow' too.
+    */
     if (floyd)
-        floydSwitchDir(pamP, fserrP);
+        convertRowDither(inpamP, outpamP, inrow,
+                         depthAdjustment, colormap, colorFinderP, colorhash,
+                         usehashP, defaultColor,
+                         fserrP, outrow, missingCountP);
+    else 
+        convertRowStraight(inpamP, outpamP, inrow,
+                           depthAdjustment, colormap, colorFinderP, colorhash,
+                           usehashP, defaultColor,
+                           outrow, missingCountP);
 }
 
 
 
 static void
-copyRaster(struct pam *   const inpamP, 
-           struct pam *   const outpamP,
-           tupletable     const colormap, 
-           unsigned int   const colormapSize,
-           bool           const floyd, 
-           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, 
+           bool               const randomize,
+           tuple              const defaultColor, 
+           unsigned int *     const missingCountP) {
 
     tuplehash const colorhash = pnm_createtuplehash();
+
+    tuple * inrow;
+    tuple * outrow;
+    struct pam workpam;
+        /* This is for work space we use for building up the output
+           pixels.  To save time and memory, we modify them in place in a
+           buffer, which ultimately holds the output pixels.  This pam
+           structure is thus the same as the *outpamP, but with a tuple
+           allocation depth large enough to handle intermediate results.
+        */
+    depthAdjustment depthAdjustment;
     struct colormapFinder * colorFinderP;
     bool usehash;
     struct fserr fserr;
-    tuple * tuplerow = pnm_allocpamrow(inpamP);
     int row;
 
+    workpam = *outpamP;
+    workpam.allocation_depth = MAX(workpam.depth, inpamP->depth);
+    workpam.size             = sizeof(workpam);
+    workpam.len              = PAM_STRUCT_SIZE(allocation_depth);
+
+    inrow  = pnm_allocpamrow(inpamP);
+    outrow = pnm_allocpamrow(&workpam);
+
     if (outpamP->maxval != inpamP->maxval && defaultColor)
         pm_error("The maxval of the colormap (%u) is not equal to the "
                  "maxval of the input image (%u).  This is allowable only "
@@ -718,40 +1007,34 @@ copyRaster(struct pam *   const inpamP,
                  "specify -firstisdefault or -missingcolor)",
                  (unsigned int)outpamP->maxval, (unsigned int)inpamP->maxval);
 
+    selectDepthAdjustment(inpamP, outpamP->depth, &depthAdjustment);
+
     usehash = TRUE;
 
     createColormapFinder(outpamP, colormap, colormapSize, &colorFinderP);
 
     if (floyd)
-        initFserr(inpamP, &fserr);
+        initFserr(inpamP, &fserr, randomize);
 
     *missingCountP = 0;  /* initial value */
 
     for (row = 0; row < inpamP->height; ++row) {
         unsigned int missingCount;
 
-        pnm_readpamrow(inpamP, tuplerow);
+        pnm_readpamrow(inpamP, inrow);
 
-        /* The following modify tuplerow, to make it consistent with
-           *outpamP instead of *inpamP.
-        */
-        assert(inpamP->allocation_depth >= outpamP->depth);
-        pnm_scaletuplerow(inpamP, tuplerow, tuplerow, outpamP->maxval);
-        adjustDepth(inpamP, tuplerow, outpamP->depth); 
-
-        /* The following both consults and adds to 'colorhash' and
-           its associated 'usehash'.  It modifies 'tuplerow' too.
-        */
-        convertRow(outpamP, tuplerow, colormap, colorFinderP, colorhash, 
-                   &usehash,
-                   floyd, defaultColor, &fserr, &missingCount);
+        convertRow(inpamP, &workpam, inrow,
+                   depthAdjustment, colormap, colorFinderP, colorhash,
+                   &usehash, floyd, defaultColor,
+                   &fserr,  outrow, &missingCount);
         
         *missingCountP += missingCount;
         
-        pnm_writepamrow(outpamP, tuplerow);
+        pnm_writepamrow(outpamP, outrow);
     }
     destroyColormapFinder(colorFinderP);
-    pnm_freepamrow(tuplerow);
+    pnm_freepamrow(inrow);
+    pnm_freepamrow(outrow);
     pnm_destroytuplehash(colorhash);
 }
 
@@ -763,6 +1046,7 @@ remap(FILE *             const ifP,
       tupletable         const colormap, 
       unsigned int       const colormapSize,
       bool               const floyd,
+      bool               const randomize,
       tuple              const defaultColor,
       bool               const verbose) {
 /*----------------------------------------------------------------------------
@@ -801,7 +1085,7 @@ remap(FILE *             const ifP,
         pnm_setminallocationdepth(&inpam, outpam.depth);
     
         copyRaster(&inpam, &outpam, colormap, colormapSize, floyd,
-                   defaultColor, &missingCount);
+                   randomize, defaultColor, &missingCount);
         
         if (verbose)
             pm_message("%u pixels not matched in color map", missingCount);
@@ -920,7 +1204,7 @@ main(int argc, char * argv[] ) {
     }
 
     remap(ifP, &outpamCommon, colormap, colormapSize, 
-          cmdline.floyd, defaultColor,
+          cmdline.floyd, !cmdline.norandom, defaultColor,
           cmdline.verbose);
 
     pnm_freepamtuple(firstColor);
diff --git a/editor/pnmrotate.c b/editor/pnmrotate.c
index ba817c17..ba37f4e0 100644
--- a/editor/pnmrotate.c
+++ b/editor/pnmrotate.c
@@ -15,9 +15,11 @@
 #include <math.h>
 #include <assert.h>
 
-#include "pnm.h"
-#include "shhopt.h"
+#include "pm_c_util.h"
 #include "mallocvar.h"
+#include "shhopt.h"
+#include "ppm.h"
+#include "pnm.h"
 
 #define SCALE 4096
 #define HALFSCALE 2048
@@ -159,27 +161,6 @@ computeNewFormat(bool     const antialias,
 
 
 
-static bool
-isWhite(xel    const color,
-        xelval const maxval) {
-
-    return (PPM_GETR(color) == maxval &&
-            PPM_GETG(color) == maxval &&
-            PPM_GETB(color) == maxval);
-}
-
-
-
-static bool
-isBlack(xel const color) {
-
-    return (PPM_GETR(color) == 0 &&
-            PPM_GETG(color) == 0 &&
-            PPM_GETB(color) == 0);
-}
-
-
-
 static xel
 backgroundColor(const char * const backgroundColorName,
                 xel *        const topRow,
@@ -190,24 +171,7 @@ backgroundColor(const char * const backgroundColorName,
     xel retval;
 
     if (backgroundColorName) {
-        retval = ppm_parsecolor(backgroundColorName, maxval);
-
-        switch(PNM_FORMAT_TYPE(format)) {
-        case PGM_TYPE:
-            if (!PPM_ISGRAY(retval))
-                pm_error("Image is PGM (grayscale), "
-                         "but you specified a non-gray "
-                         "background color '%s'", backgroundColorName);
-
-            break;
-        case PBM_TYPE:
-            if (!isWhite(retval, maxval) && !isBlack(retval))
-                pm_error("Image is PBM (black and white), "
-                         "but you specified '%s', which is neither black "
-                         "nor white, as background color", 
-                         backgroundColorName);
-            break;
-        }
+        retval = pnm_parsecolorxel(backgroundColorName, maxval, format);
     } else 
         retval = pnm_backgroundxelrow(topRow, cols, maxval, format);
 
@@ -725,7 +689,7 @@ shearXToOutputFile(FILE *                 const ofP,
     unsigned int row;
     xel * xelrow;
     
-    pnm_writepnminit(stdout, newcols, rows, maxval, format, 0);
+    pnm_writepnminit(ofP, newcols, rows, maxval, format, 0);
 
     xelrow = pnm_allocrow(newcols);
 
@@ -742,7 +706,7 @@ shearXToOutputFile(FILE *                 const ofP,
         shearFinal(xels[row], xelrow, cols, newcols, format, 
                    bgColor, antialias, shiftAmount, x2shearjunk);
 
-        pnm_writepnmrow(stdout, xelrow, newcols, maxval, format, 0);
+        pnm_writepnmrow(ofP, xelrow, newcols, maxval, format, 0);
     }
     pnm_freerow(xelrow);
 }
diff --git a/editor/pnmscale.c b/editor/pnmscale.c
deleted file mode 100644
index f75f440c..00000000
--- a/editor/pnmscale.c
+++ /dev/null
@@ -1,748 +0,0 @@
-/* pnmscale.c - read a portable anymap and scale it
-**
-** 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.
-**
-*/
-
-/* 
-
-      DON'T ADD NEW FUNCTION TO THIS PROGRAM.  ADD IT TO pamscale.c
-      INSTEAD.
-
-*/
-
- 
-#include <math.h>
-#include <string.h>
-
-#include "pnm.h"
-#include "shhopt.h"
-
-/* The pnm library allows us to code this program without branching cases
-   for PGM and PPM, but we do the branch anyway to speed up processing of 
-   PGM images.
-*/
-
-
-struct cmdline_info {
-    /* 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 */
-    unsigned int xsize;
-    unsigned int ysize;
-    float xscale;
-    float yscale;
-    unsigned int xbox;
-    unsigned int ybox;
-    unsigned int pixels;
-    unsigned int verbose;
-    unsigned int nomix;
-};
-
-
-static void
-parse_command_line(int argc, char ** argv,
-                   struct cmdline_info *cmdline_p) {
-/*----------------------------------------------------------------------------
-   Note that the file spec array we return is stored in the storage that
-   was passed to us as the argv array.
------------------------------------------------------------------------------*/
-    optEntry *option_def = malloc( 100*sizeof( optEntry ) );
-        /* Instructions to optParseOptions3 on how to parse our options.
-         */
-    optStruct3 opt;
-
-    unsigned int option_def_index;
-    unsigned int xysize;
-    int xsize, ysize, pixels;
-    int reduce;
-    float xscale, yscale, scale_parm;
-
-    option_def_index = 0;   /* incremented by OPTENTRY */
-    OPTENT3(0, "xsize",     OPT_UINT,    &xsize,               NULL, 0);
-    OPTENT3(0, "width",     OPT_UINT,    &xsize,               NULL, 0);
-    OPTENT3(0, "ysize",     OPT_UINT,    &ysize,               NULL, 0);
-    OPTENT3(0, "height",    OPT_UINT,    &ysize,               NULL, 0);
-    OPTENT3(0, "xscale",    OPT_FLOAT,   &xscale,              NULL, 0);
-    OPTENT3(0, "yscale",    OPT_FLOAT,   &yscale,              NULL, 0);
-    OPTENT3(0, "pixels",    OPT_UINT,    &pixels,              NULL, 0);
-    OPTENT3(0, "reduce",    OPT_UINT,    &reduce,              NULL, 0);
-    OPTENT3(0, "xysize",    OPT_FLAG,    NULL, &xysize,              0);
-    OPTENT3(0, "verbose",   OPT_FLAG,    NULL, &cmdline_p->verbose,  0);
-    OPTENT3(0, "nomix",     OPT_FLAG,    NULL, &cmdline_p->nomix,    0);
-
-    /* Set the defaults. -1 = unspecified */
-    /* (Now that we're using ParseOptions3, we don't have to do this -1
-       nonsense, but we don't want to risk screwing these complex 
-       option compatibilities up, so we'll convert that later.
-    */
-    xsize = -1;
-    ysize = -1;
-    xscale = -1.0;
-    yscale = -1.0;
-    pixels = -1;
-    reduce = -1;
-
-    opt.opt_table = option_def;
-    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
-    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
-
-    optParseOptions3( &argc, argv, opt, sizeof(opt), 0 );
-        /* Uses and sets argc, argv, and some of *cmdline_p and others. */
-
-    if (xsize == 0)
-        pm_error("-xsize/width must be greater than zero.");
-    if (ysize == 0)
-        pm_error("-ysize/height must be greater than zero.");
-    if (xscale != -1.0 && xscale <= 0.0)
-        pm_error("-xscale must be greater than zero.");
-    if (yscale != -1.0 && yscale <= 0.0)
-        pm_error("-yscale must be greater than zero.");
-    if (reduce <= 0 && reduce != -1)
-        pm_error("-reduce must be greater than zero.");
-
-    if (xsize != -1 && xscale != -1)
-        pm_error("Cannot specify both -xsize/width and -xscale.");
-    if (ysize != -1 && yscale != -1)
-        pm_error("Cannot specify both -ysize/height and -yscale.");
-    
-    if (xysize && 
-        (xsize != -1 || xscale != -1 || ysize != -1 || yscale != -1 || 
-         reduce != -1 || pixels != -1) )
-        pm_error("Cannot specify -xysize with other dimension options.");
-    if (pixels != -1 && 
-        (xsize != -1 || xscale != -1 || ysize != -1 || yscale != -1 ||
-         reduce != -1) )
-        pm_error("Cannot specify -pixels with other dimension options.");
-    if (reduce != -1 && 
-        (xsize != -1 || xscale != -1 || ysize != -1 || yscale != -1) )
-        pm_error("Cannot specify -reduce with other dimension options.");
-
-    if (pixels == 0)
-        pm_error("-pixels must be greater than zero");
-
-    /* Get the program parameters */
-
-    if (xysize) {
-        /* parameters are xbox, ybox, and optional filespec */
-        scale_parm = 0.0;
-        if (argc-1 < 2)
-            pm_error("You must supply at least two parameters with -xysize:\n "
-                     "x and y dimensions of the bounding box.");
-        else if (argc-1 > 3)
-            pm_error("Too many arguments.  With -xysize, you need 2 or 3 "
-                     "arguments.");
-        else {
-            char * endptr;
-            cmdline_p->xbox = strtol(argv[1], &endptr, 10);
-            if (strlen(argv[1]) > 0 && *endptr != '\0')
-                pm_error("horizontal xysize not an integer: '%s'", argv[1]);
-            if (cmdline_p->xbox <= 0)
-                pm_error("horizontal size is not positive: %d", 
-                         cmdline_p->xbox);
-
-            cmdline_p->ybox = strtol(argv[2], &endptr, 10);
-            if (strlen(argv[2]) > 0 && *endptr != '\0')
-                pm_error("vertical xysize not an integer: '%s'", argv[2]);
-            if (cmdline_p->ybox <= 0)
-                pm_error("vertical size is not positive: %d", 
-                         cmdline_p->ybox);
-            
-            if (argc-1 < 3)
-                cmdline_p->input_filespec = "-";
-            else
-                cmdline_p->input_filespec = argv[3];
-        }
-    } else {
-        cmdline_p->xbox = 0;
-        cmdline_p->ybox = 0;
-        
-        if (xsize == -1 && xscale == -1 && ysize == -1 && yscale == -1
-            && pixels == -1 && reduce == -1) {
-            /* parameters are scale factor and optional filespec */
-            if (argc-1 < 1)
-                pm_error("With no dimension options, you must supply at least "
-                         "one parameter: \nthe scale factor.");
-            else {
-                scale_parm = atof(argv[1]);
-
-                if (scale_parm == 0.0)
-                    pm_error("The scale parameter %s is not "
-                             "a positive number.",
-                             argv[1]);
-                else {
-                    if (argc-1 < 2)
-                        cmdline_p->input_filespec = "-";
-                    else
-                        cmdline_p->input_filespec = argv[2];
-                }
-            }
-        } else {
-            /* Only parameter allowed is optional filespec */
-            if (argc-1 < 1)
-                cmdline_p->input_filespec = "-";
-            else
-                cmdline_p->input_filespec = argv[1];
-
-            if (reduce != -1) {
-                scale_parm = ((double) 1.0) / ((double) reduce);
-                pm_message("reducing by %d gives scale factor of %f.", 
-                           reduce, scale_parm);
-            } else
-                scale_parm = 0.0;
-        }
-    }
-
-    cmdline_p->xsize = xsize == -1 ? 0 : xsize;
-    cmdline_p->ysize = ysize == -1 ? 0 : ysize;
-    cmdline_p->pixels = pixels == -1 ? 0 : pixels;
-
-    if (scale_parm) {
-        cmdline_p->xscale = scale_parm;
-        cmdline_p->yscale = scale_parm;
-    } else {
-        cmdline_p->xscale = xscale == -1.0 ? 0.0 : xscale;
-        cmdline_p->yscale = yscale == -1.0 ? 0.0 : yscale;
-    }
-}
-
-
-
-static void
-compute_output_dimensions(const struct cmdline_info cmdline, 
-                          const int rows, const int cols,
-                          int * newrowsP, int * newcolsP) {
-
-    if (cmdline.pixels) {
-        if (rows * cols <= cmdline.pixels) {
-            *newrowsP = rows;
-            *newcolsP = cols;
-        } else {
-            const double scale =
-                sqrt( (float) cmdline.pixels / ((float) cols * (float) rows));
-            *newrowsP = rows * scale;
-            *newcolsP = cols * scale;
-        }
-    } else if (cmdline.xbox) {
-        const double aspect_ratio = (float) cols / (float) rows;
-        const double box_aspect_ratio = 
-            (float) cmdline.xbox / (float) cmdline.ybox;
-        
-        if (box_aspect_ratio > aspect_ratio) {
-            *newrowsP = cmdline.ybox;
-            *newcolsP = *newrowsP * aspect_ratio + 0.5;
-        } else {
-            *newcolsP = cmdline.xbox;
-            *newrowsP = *newcolsP / aspect_ratio + 0.5;
-        }
-    } else {
-        if (cmdline.xsize)
-            *newcolsP = cmdline.xsize;
-        else if (cmdline.xscale)
-            *newcolsP = cmdline.xscale * cols + .5;
-        else if (cmdline.ysize)
-            *newcolsP = cols * ((float) cmdline.ysize/rows) +.5;
-        else
-            *newcolsP = cols;
-
-        if (cmdline.ysize)
-            *newrowsP = cmdline.ysize;
-        else if (cmdline.yscale)
-            *newrowsP = cmdline.yscale * rows +.5;
-        else if (cmdline.xsize)
-            *newrowsP = rows * ((float) cmdline.xsize/cols) +.5;
-        else
-            *newrowsP = rows;
-    }    
-
-    /* If the calculations above yielded (due to rounding) a zero 
-       dimension, we fudge it up to 1.  We do this rather than considering
-       it a specification error (and dying) because it's friendlier to 
-       automated processes that work on arbitrary input.  It saves them
-       having to check their numbers to avoid catastrophe.
-    */
-
-    if (*newcolsP < 1) *newcolsP = 1;
-    if (*newrowsP < 1) *newrowsP = 1;
-}        
-
-
-
-static void
-horizontal_scale(const xel inputxelrow[], xel newxelrow[], 
-                 const int cols, const int newcols, const float xscale, 
-                 const int format, const xelval maxval,
-                 float * const stretchP) {
-/*----------------------------------------------------------------------------
-   Take the input row inputxelrow[], which is 'cols' columns wide, and
-   scale it by a factor of 'xscale', to create
-   the output row newxelrow[], which is 'newcols' columns wide.
-
-   'format' and 'maxval' describe the Netpbm format of the both input and
-   output rows.
------------------------------------------------------------------------------*/
-    float r, g, b;
-    float fraccoltofill, fraccolleft;
-    unsigned int col;
-    unsigned int newcol;
-    
-    newcol = 0;
-    fraccoltofill = 1.0;  /* Output column is "empty" now */
-    r = g = b = 0;          /* initial value */
-    for (col = 0; col < cols; ++col) {
-        /* Process one pixel from input ('inputxelrow') */
-        fraccolleft = xscale;
-        /* Output all columns, if any, that can be filled using information
-           from this input column, in addition to what's already in the output
-           column.
-        */
-        while (fraccolleft >= fraccoltofill) {
-            /* Generate one output pixel in 'newxelrow'.  It will consist
-               of anything accumulated from prior input pixels in 'r','g', 
-               and 'b', plus a fraction of the current input pixel.
-            */
-            switch (PNM_FORMAT_TYPE(format)) {
-            case PPM_TYPE:
-                r += fraccoltofill * PPM_GETR(inputxelrow[col]);
-                g += fraccoltofill * PPM_GETG(inputxelrow[col]);
-                b += fraccoltofill * PPM_GETB(inputxelrow[col]);
-                PPM_ASSIGN( newxelrow[newcol], 
-                            MIN(maxval, (int) (r + 0.5)), 
-                            MIN(maxval, (int) (g + 0.5)), 
-                            MIN(maxval, (int) (b + 0.5))
-                    );
-                break;
-
-            default:
-                g += fraccoltofill * PNM_GET1(inputxelrow[col]);
-                PNM_ASSIGN1( newxelrow[newcol], MIN(maxval, (int) (g + 0.5)));
-                break;
-            }
-            fraccolleft -= fraccoltofill;
-            /* Set up to start filling next output column */
-            newcol++;
-            fraccoltofill = 1.0;
-            r = g = b = 0.0;
-        }
-        /* 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.
-        */
-        if (fraccolleft > 0.0) {
-            switch (PNM_FORMAT_TYPE(format)) {
-            case PPM_TYPE:
-                r += fraccolleft * PPM_GETR(inputxelrow[col]);
-                g += fraccolleft * PPM_GETG(inputxelrow[col]);
-                b += fraccolleft * PPM_GETB(inputxelrow[col]);
-                break;
-                    
-            default:
-                g += fraccolleft * PNM_GET1(inputxelrow[col]);
-                break;
-            }
-            fraccoltofill -= fraccolleft;
-        }
-    }
-
-    if (newcol < newcols-1 || newcol > newcols)
-        pm_error("Internal error: last column filled is %d, but %d "
-                 "is the rightmost output column.",
-                 newcol, newcols-1);
-
-    if (newcol < newcols ) {
-        /* 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.
-        */
-
-        *stretchP = fraccoltofill;
-
-        switch (PNM_FORMAT_TYPE(format)) {
-        case PPM_TYPE:
-            r += fraccoltofill * PPM_GETR(inputxelrow[cols-1]);
-            g += fraccoltofill * PPM_GETG(inputxelrow[cols-1]);
-            b += fraccoltofill * PPM_GETB(inputxelrow[cols-1]);
-
-            PPM_ASSIGN(newxelrow[newcol], 
-                       MIN(maxval, (int) (r + 0.5)), 
-                       MIN(maxval, (int) (g + 0.5)), 
-                       MIN(maxval, (int) (b + 0.5))
-                );
-            break;
-                
-        default:
-            g += fraccoltofill * PNM_GET1(inputxelrow[cols-1]);
-            PNM_ASSIGN1( newxelrow[newcol], MIN(maxval, (int) (g + 0.5)));
-            break;
-        }
-    } else 
-        *stretchP = 0;
-}
-
-
-
-static void
-zeroAccum(int const cols, int const format, 
-          float rs[], float gs[], float bs[]) {
-
-    int col;
-
-    for ( col = 0; col < cols; ++col )
-        rs[col] = gs[col] = bs[col] = 0.0;
-}
-
-
-
-static void
-accumOutputRow(xel * const xelrow, float const fraction, 
-               float rs[], float gs[], float bs[], 
-               int const cols, int const format) {
-/*----------------------------------------------------------------------------
-   Take 'fraction' times the color in row xelrow and add it to 
-   rs/gs/bs.  'fraction' is less than 1.0.
------------------------------------------------------------------------------*/
-    int col;
-
-    switch ( PNM_FORMAT_TYPE(format) ) {
-    case PPM_TYPE:
-        for ( col = 0; col < cols; ++col ) {
-            rs[col] += fraction * PPM_GETR(xelrow[col]);
-            gs[col] += fraction * PPM_GETG(xelrow[col]);
-            bs[col] += fraction * PPM_GETB(xelrow[col]);
-        }
-        break;
-
-    default:
-        for ( col = 0; col < cols; ++col)
-            gs[col] += fraction * PNM_GET1(xelrow[col]);
-        break;
-    }
-}
-
-
-
-static void
-makeRow(xel * const xelrow, float rs[], float gs[], float bs[],
-        int const cols, xelval const maxval, int const format) {
-/*----------------------------------------------------------------------------
-   Make an xel row at 'xelrow' with format 'format' and
-   maxval 'maxval' out of the color values in 
-   rs[], gs[], and bs[].
------------------------------------------------------------------------------*/
-    int col;
-
-    switch ( PNM_FORMAT_TYPE(format) ) {
-    case PPM_TYPE:
-        for ( col = 0; col < cols; ++col) {
-            PPM_ASSIGN(xelrow[col], 
-                       MIN(maxval, (int) (rs[col] + 0.5)), 
-                       MIN(maxval, (int) (gs[col] + 0.5)), 
-                       MIN(maxval, (int) (bs[col] + 0.5))
-                );
-        }
-        break;
-
-    default:
-        for ( col = 0; col < cols; ++col ) {
-            PNM_ASSIGN1(xelrow[col], 
-                        MIN(maxval, (int) (gs[col] + 0.5)));
-        }
-        break;
-    }
-}
-
-
-
-static void
-scaleWithMixing(FILE * const ifP,
-                int const cols, int const rows,
-                xelval const maxval, int const format,
-                int const newcols, int const newrows,
-                xelval const newmaxval, int const newformat,
-                float const xscale, float const yscale,
-                bool const verbose) {
-/*----------------------------------------------------------------------------
-   Scale the image on input file 'ifP' (which is described by 
-   'cols', 'rows', 'format', and 'maxval') by xscale horizontally and
-   yscale vertically and write the result to standard output as format
-   'newformat' and with maxval 'newmaxval'.
-
-   The input file is positioned past the header, to the beginning of the
-   raster.  The output file is too.
-
-   Mix colors from input rows together in the output rows.
------------------------------------------------------------------------------*/
-    /* 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
-       them -- only a sequence.  Instead, think of the raster row as a
-       bunch of pixels 1 unit wide adjacent to each other.  For
-       example, we are going to scale a 100 pixel row to a 150 pixel
-       row.  Imagine placing the input row right above the output row
-       and stretching it so it is the same size as the output row.  It
-       still contains 100 pixels, but they are 1.5 units wide each.
-       Our goal is to make the output row look as much as possible
-       like the input row, while observing that a pixel can be only
-       one color.
-
-       Output Pixel 0 is completely covered by Input Pixel 0, so we
-       make Output Pixel 0 the same color as Input Pixel 0.  Output
-       Pixel 1 is covered half by Input Pixel 0 and half by Input
-       Pixel 1.  So we make Output Pixel 1 a 50/50 mix of Input Pixels
-       0 and 1.  If you stand back far enough, input and output will
-       look the same.
-
-       This works for all scale factors, both scaling up and scaling down.
-       
-       This program always stretches or squeezes the input row to be the
-       same length as the output row; The output row's pixels are always
-       1 unit wide.
-
-       The same thing works in the vertical direction.  We think of
-       rows as stacked strips of 1 unit height.  We conceptually
-       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.  
-    */
-
-    xel* xelrow;  /* An input row */
-    xel* vertScaledRow;
-        /* An output row after vertical scaling, but before horizontal
-           scaling
-        */
-    xel* newxelrow;
-    float rowsleft;
-        /* The number of rows of output that need to be formed from the
-           current input row (the one in xelrow[]), less the number that 
-           have already been formed (either in the rs/gs/bs accumulators
-           or output to the file).  This can be fractional because of the
-           way we define rows as having height.
-        */
-    float fracrowtofill;
-        /* The fraction of the current output row (the one in vertScaledRow[])
-           that hasn't yet been filled in from an input row.
-        */
-    float *rs, *gs, *bs;
-        /* The red, green, and blue color intensities so far accumulated
-           from input rows for the current output row.
-        */
-    int rowsread;
-        /* Number of rows of the input file that have been read */
-    int row;
-    
-    xelrow = pnm_allocrow(cols); 
-    vertScaledRow = pnm_allocrow(cols);
-    rs = (float*) pm_allocrow( cols, sizeof(float) );
-    gs = (float*) pm_allocrow( cols, sizeof(float) );
-    bs = (float*) pm_allocrow( cols, sizeof(float) );
-    rowsread = 0;
-    rowsleft = 0.0;
-    zeroAccum(cols, format, rs, gs, bs);
-    fracrowtofill = 1.0;
-
-    newxelrow = pnm_allocrow( newcols );
-
-    for ( row = 0; row < newrows; ++row ) {
-        /* First scale Y from xelrow[] into vertScaledRow[]. */
-
-        if ( newrows == rows ) { /* shortcut Y scaling if possible */
-            pnm_readpnmrow( ifP, vertScaledRow, cols, newmaxval, format );
-	    } else {
-            while (fracrowtofill > 0) {
-                if (rowsleft <= 0.0) {
-                    if (rowsread < rows) {
-                        pnm_readpnmrow(ifP, xelrow, cols, newmaxval, format);
-                        ++rowsread;
-                    } else {
-                        /* We need another input row to fill up this
-                           output row, but there aren't any more.
-                           That's because of rounding down on our
-                           scaling arithmetic.  So we go ahead with
-                           the data from the last row we read, which
-                           amounts to stretching out the last output
-                           row.  
-                        */
-                        if (verbose)
-                            pm_message("%f of bottom row stretched due to "
-                                       "arithmetic imprecision", 
-                                       fracrowtofill);
-                    }
-                    rowsleft = yscale;
-                }
-                if (rowsleft < fracrowtofill) {
-                    accumOutputRow(xelrow, rowsleft, rs, gs, bs, 
-                                   cols, format);
-                    fracrowtofill -= rowsleft;
-                    rowsleft = 0.0;
-                } else {
-                    accumOutputRow(xelrow, fracrowtofill, rs, gs, bs,
-                                   cols, format);
-                    rowsleft = rowsleft - fracrowtofill;
-                    fracrowtofill = 0.0;
-                }
-            }
-            makeRow(vertScaledRow, rs, gs, bs, cols, newmaxval, format);
-            zeroAccum(cols, format, rs, gs, bs);
-            fracrowtofill = 1.0;
-	    }
-
-        /* Now scale vertScaledRow horizontally into newxelrow and write
-           it out. 
-        */
-
-        if (newcols == cols)	/* shortcut X scaling if possible */
-            pnm_writepnmrow(stdout, vertScaledRow, newcols, 
-                            newmaxval, newformat, 0);
-        else {
-            float stretch;
-
-            horizontal_scale(vertScaledRow, newxelrow, cols, newcols, xscale, 
-                             format, newmaxval, &stretch);
-            
-            if (verbose && row == 0)
-                pm_message("%f of right column stretched due to "
-                           "arithmetic imprecision", 
-                           stretch);
-            
-            pnm_writepnmrow(stdout, newxelrow, newcols, 
-                            newmaxval, newformat, 0 );
-        }
-	}
-    pnm_freerow(newxelrow);
-    pnm_freerow(xelrow);
-    pnm_freerow(vertScaledRow);
-}
-
-
-
-static void
-scaleWithoutMixing(FILE * const ifP,
-                   int const cols, int const rows,
-                   xelval const maxval, int const format,
-                   int const newcols, int const newrows,
-                   xelval const newmaxval, int const newformat,
-                   float const xscale, float const yscale) {
-/*----------------------------------------------------------------------------
-   Scale the image on input file 'ifP' (which is described by 
-   'cols', 'rows', 'format', and 'maxval') by xscale horizontally and
-   yscale vertically and write the result to standard output as format
-   'newformat' and with maxval 'newmaxval'.
-
-   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 
-   input pixel.
------------------------------------------------------------------------------*/
-    xel* xelrow;  /* An input row */
-    xel* newxelrow;
-    int row;
-    int rowInXelrow;
-
-    xelrow = pnm_allocrow(cols); 
-    rowInXelrow = -1;
-
-    newxelrow = pnm_allocrow(newcols);
-
-    for (row = 0; row < newrows; ++row) {
-        int col;
-        
-        int const inputRow = (int) (row / yscale);
-
-        for (; rowInXelrow < inputRow; ++rowInXelrow) 
-            pnm_readpnmrow(ifP, xelrow, cols, newmaxval, format);
-        
-
-        for (col = 0; col < newcols; ++col) {
-            int const inputCol = (int) (col / xscale);
-            
-            newxelrow[col] = xelrow[inputCol];
-        }
-
-        pnm_writepnmrow(stdout, newxelrow, newcols, 
-                        newmaxval, newformat, 0 );
-	}
-    pnm_freerow(xelrow);
-    pnm_freerow(newxelrow);
-}
-
-
-
-int
-main(int argc, char **argv ) {
-
-    struct cmdline_info cmdline;
-    FILE* ifP;
-    int rows, cols, format, newformat, newrows, newcols;
-    xelval maxval, newmaxval;
-    float xscale, yscale;
-
-    pnm_init( &argc, argv );
-
-    parse_command_line(argc, argv, &cmdline);
-
-    ifP = pm_openr(cmdline.input_filespec);
-
-    pnm_readpnminit( ifP, &cols, &rows, &maxval, &format );
-
-    /* Promote PBM files to PGM. */
-    if ( PNM_FORMAT_TYPE(format) == PBM_TYPE ) {
-        newformat = PGM_TYPE;
-        newmaxval = PGM_MAXMAXVAL;
-        pm_message( "promoting from PBM to PGM" );
-	} else {
-        newformat = format;
-        newmaxval = maxval;
-    }
-    compute_output_dimensions(cmdline, rows, cols, &newrows, &newcols);
-
-    /* We round the scale factor down so that we never fill up the 
-       output while (a fractional pixel of) input remains unused.  Instead,
-       we will run out of input while (a fractional pixel of) output is 
-       unfilled -- which is easier for our algorithm to handle.
-       */
-    xscale = (float) newcols / cols;
-    yscale = (float) newrows / rows;
-
-    if (cmdline.verbose) {
-        pm_message("Scaling by %f horizontally to %d columns.", 
-                   xscale, newcols );
-        pm_message("Scaling by %f vertically to %d rows.", 
-                   yscale, newrows);
-    }
-
-    if (xscale * cols < newcols - 1 ||
-        yscale * rows < newrows - 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_writepnminit(stdout, newcols, newrows, newmaxval, newformat, 0);
-
-    if (cmdline.nomix) 
-        scaleWithoutMixing(ifP, cols, rows, maxval, format,
-                           newcols, newrows, newmaxval, newformat, 
-                           xscale, yscale);
-    else
-        scaleWithMixing(ifP, cols, rows, maxval, format,
-                        newcols, newrows, newmaxval, newformat, 
-                        xscale, yscale, cmdline.verbose);
-
-    pm_close(ifP);
-    pm_close(stdout);
-    
-    exit(0);
-}
diff --git a/editor/pnmscalefixed.c b/editor/pnmscalefixed.c
index d562c670..8deb65d7 100644
--- a/editor/pnmscalefixed.c
+++ b/editor/pnmscalefixed.c
@@ -20,6 +20,8 @@
 */
  
 #include <math.h>
+
+#include "pm_c_util.h"
 #include "pnm.h"
 #include "shhopt.h"
 
diff --git a/editor/pnmshear.c b/editor/pnmshear.c
index ae104db9..359df299 100644
--- a/editor/pnmshear.c
+++ b/editor/pnmshear.c
@@ -12,9 +12,12 @@
 
 #define _XOPEN_SOURCE   /* get M_PI in math.h */
 
+#include <assert.h>
 #include <math.h>
 #include <string.h>
 
+#include "pm_c_util.h"
+#include "ppm.h"
 #include "pnm.h"
 #include "shhopt.h"
 
@@ -28,26 +31,34 @@ struct cmdline_info {
     const char *       input_filespec;  /* Filespec of input file */
     double       angle;           /* requested shear angle, in radians */
     unsigned int noantialias;     /* -noantialias option */
+    const char * background;      /* NULL if none */
 };
 
 
 
 static void
-parse_command_line(int argc, char ** argv,
-                   struct cmdline_info *cmdlineP) {
+parseCommandLine(int argc, char ** argv,
+                 struct cmdline_info *cmdlineP) {
 
     optStruct3 opt;
     unsigned int option_def_index = 0;
     optEntry *option_def = malloc(100*sizeof(optEntry));
 
+    unsigned int backgroundSpec;
+
     OPTENT3(0, "noantialias",      OPT_FLAG,  NULL, &cmdlineP->noantialias, 0);
+    OPTENT3(0, "background",       OPT_STRING, &cmdlineP->background,
+            &backgroundSpec, 0);
     
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;
     opt.allowNegNum = TRUE;
 
     optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
-    
+
+    if (!backgroundSpec)
+        cmdlineP->background = NULL;
+
     if (argc-1 < 1)
         pm_error("Need an argument:  the shear angle.\n");
     else {
@@ -69,9 +80,14 @@ parse_command_line(int argc, char ** argv,
 }
 
 
+
 static void
-makeNewXel(xel * const outputXelP, xel const curXel, xel const prevXel,
-           double const fracnew0, double const omfracnew0, int const format) {
+makeNewXel(xel *  const outputXelP,
+           xel    const curXel,
+           xel    const prevXel,
+           double const fracnew0,
+           double const omfracnew0,
+           int    const format) {
 /*----------------------------------------------------------------------------
    Create an output xel as *outputXel, which is part curXel and part
    prevXel, the part given by the fractions omfracnew0 and fracnew0,
@@ -81,35 +97,40 @@ makeNewXel(xel * const outputXelP, xel const curXel, xel const prevXel,
    The format of the pixel is 'format'.
 -----------------------------------------------------------------------------*/
 
-    switch ( PNM_FORMAT_TYPE(format) ) {
+    switch (PNM_FORMAT_TYPE(format)) {
     case PPM_TYPE:
-        PPM_ASSIGN( *outputXelP,
-                    ( fracnew0 * PPM_GETR(prevXel) 
-                      + omfracnew0 * PPM_GETR(curXel) 
-                      + HALFSCALE ) / SCALE,
-                    ( fracnew0 * PPM_GETG(prevXel) 
-                      + omfracnew0 * PPM_GETG(curXel) 
-                      + HALFSCALE ) / SCALE,
-                    ( fracnew0 * PPM_GETB(prevXel) 
-                      + omfracnew0 * PPM_GETB(curXel) 
-                      + HALFSCALE ) / SCALE );
+        PPM_ASSIGN(*outputXelP,
+                   (fracnew0 * PPM_GETR(prevXel) 
+                    + omfracnew0 * PPM_GETR(curXel) 
+                    + HALFSCALE) / SCALE,
+                   (fracnew0 * PPM_GETG(prevXel) 
+                    + omfracnew0 * PPM_GETG(curXel) 
+                    + HALFSCALE) / SCALE,
+                   (fracnew0 * PPM_GETB(prevXel) 
+                    + omfracnew0 * PPM_GETB(curXel) 
+                    + HALFSCALE) / SCALE );
         break;
         
     default:
-        PNM_ASSIGN1( *outputXelP,
-                     ( fracnew0 * PNM_GET1(prevXel) 
-                       + omfracnew0 * PNM_GET1(curXel) 
-                       + HALFSCALE ) / SCALE );
+        PNM_ASSIGN1(*outputXelP,
+                    (fracnew0 * PNM_GET1(prevXel) 
+                     + omfracnew0 * PNM_GET1(curXel) 
+                     + HALFSCALE) / SCALE );
         break;
     }
 }
 
 
+
 static void
-shear_row(xel * const xelrow, int const cols, 
-          xel * const newxelrow, int const newcols, 
-          double const shearCols,
-          int const format, xel const bgxel, bool const antialias) {
+shearRow(xel *        const xelrow,
+         unsigned int const cols, 
+         xel *        const newxelrow,
+         unsigned int const newcols, 
+         double       const shearCols,
+         int          const format,
+         xel          const bgxel,
+         bool         const antialias) {
 /*----------------------------------------------------------------------------
    Shear the row 'xelrow' by 'shearCols' columns, and return the result as
    'newxelrow'.  They are 'cols' and 'newcols' columns wide, respectively.
@@ -122,47 +143,68 @@ shear_row(xel * const xelrow, int const cols,
    The format of the input xels (which implies something about the
    output xels too) is 'format'.
 -----------------------------------------------------------------------------*/
-    int const intShearCols = (int) shearCols;
+    unsigned int const intShearCols = (unsigned int) shearCols;
+
+    assert(shearCols >= 0.0);
         
-    if ( antialias ) {
-        const long fracnew0 = ( shearCols - intShearCols ) * SCALE;
+    if (antialias) {
+        const long fracnew0 = (shearCols - intShearCols) * SCALE;
         const long omfracnew0 = SCALE - fracnew0;
 
-        int col;
+        unsigned int col;
         xel prevXel;
             
-        for ( col = 0; col < newcols; ++col )
+        for (col = 0; col < newcols; ++col)
             newxelrow[col] = bgxel;
 
         prevXel = bgxel;
-        for ( col = 0; col < cols; ++col){
+        for (col = 0; col < cols; ++col) {
             makeNewXel(&newxelrow[intShearCols + col],
                        xelrow[col], prevXel, fracnew0, omfracnew0,
                        format);
             prevXel = xelrow[col];
         }
-        if ( fracnew0 > 0 ) 
+        if (fracnew0 > 0) 
             /* Need to add a column for what's left over */
             makeNewXel(&newxelrow[intShearCols + cols],
                        bgxel, prevXel, fracnew0, omfracnew0, format);
     } else {
-        int col;
-        for ( col = 0; col < intShearCols; ++col )
+        unsigned int col;
+        for (col = 0; col < intShearCols; ++col)
             newxelrow[col] = bgxel;
-        for ( col = 0; col < cols; ++col )
+        for (col = 0; col < cols; ++col)
             newxelrow[intShearCols+col] = xelrow[col];
-        for ( col = intShearCols + cols; col < newcols; ++col )
+        for (col = intShearCols + cols; col < newcols; ++col)
             newxelrow[col] = bgxel;
     }
 }
 
 
+static xel
+backgroundColor(const char * const backgroundColorName,
+                xel *        const topRow,
+                int          const cols,
+                xelval       const maxval,
+                int          const format) {
+
+    xel retval;
+
+    if (backgroundColorName) {
+        retval = pnm_parsecolorxel(backgroundColorName, maxval, format);
+    } else 
+        retval = pnm_backgroundxelrow(topRow, cols, maxval, format);
+
+    return retval;
+}
+
+
 
 int
 main(int argc, char * argv[]) {
-    FILE* ifp;
-    xel* xelrow;
-    xel* newxelrow;
+
+    FILE * ifP;
+    xel * xelrow;
+    xel * newxelrow;
     xel bgxel;
     int rows, cols, format; 
     int newformat, newcols; 
@@ -172,57 +214,55 @@ main(int argc, char * argv[]) {
 
     struct cmdline_info cmdline;
 
-    pnm_init( &argc, argv );
+    pnm_init(&argc, argv);
 
-    parse_command_line( argc, argv, &cmdline );
+    parseCommandLine(argc, argv, &cmdline);
 
-    ifp = pm_openr( cmdline.input_filespec );
+    ifP = pm_openr(cmdline.input_filespec);
 
-    pnm_readpnminit( ifp, &cols, &rows, &maxval, &format );
-    xelrow = pnm_allocrow( cols );
+    pnm_readpnminit(ifP, &cols, &rows, &maxval, &format);
+    xelrow = pnm_allocrow(cols);
 
     /* Promote PBM files to PGM. */
-    if ( !cmdline.noantialias && PNM_FORMAT_TYPE(format) == PBM_TYPE ) {
+    if (!cmdline.noantialias && PNM_FORMAT_TYPE(format) == PBM_TYPE) {
         newformat = PGM_TYPE;
         newmaxval = PGM_MAXMAXVAL;
-        pm_message( "promoting from PBM to PGM - "
-                    "use -noantialias to avoid this" );
+        pm_message("promoting from PBM to PGM - "
+                   "use -noantialias to avoid this");
     } else {
         newformat = format;
         newmaxval = maxval;
     }
 
-    shearfac = tan( cmdline.angle );
-    if ( shearfac < 0.0 )
-        shearfac = -shearfac;
+    shearfac = fabs(tan(cmdline.angle));
 
     newcols = rows * shearfac + cols + 0.999999;
 
-    pnm_writepnminit( stdout, newcols, rows, newmaxval, newformat, 0 );
-    newxelrow = pnm_allocrow( newcols );
-
-    for ( row = 0; row < rows; ++row ) {
+    pnm_writepnminit(stdout, newcols, rows, newmaxval, newformat, 0);
+    newxelrow = pnm_allocrow(newcols);
+    
+    for (row = 0; row < rows; ++row) {
         double shearCols;
 
-        pnm_readpnmrow( ifp, xelrow, cols, newmaxval, format );
+        pnm_readpnmrow(ifP, xelrow, cols, newmaxval, format);
 
-        if ( row == 0 )
-            bgxel = pnm_backgroundxelrow( xelrow, cols, newmaxval, format );
+        if (row == 0)
+            bgxel = backgroundColor(cmdline.background,
+                                    xelrow, cols, newmaxval, format);
 
-        if ( cmdline.angle > 0.0 )
+        if (cmdline.angle > 0.0)
             shearCols = row * shearfac;
         else
-            shearCols = ( rows - row ) * shearfac;
+            shearCols = (rows - row) * shearfac;
 
-        shear_row(xelrow, cols, newxelrow, newcols, 
+        shearRow(xelrow, cols, newxelrow, newcols, 
                   shearCols, format, bgxel, !cmdline.noantialias);
 
-        pnm_writepnmrow( stdout, newxelrow, newcols, newmaxval, newformat, 0 );
+        pnm_writepnmrow(stdout, newxelrow, newcols, newmaxval, newformat, 0);
     }
+    
+    pm_close(ifP);
+    pm_close(stdout);
 
-    pm_close( ifp );
-    pm_close( stdout );
-
-    exit( 0 );
+    return 0;
 }
-
diff --git a/editor/pnmsmooth.c b/editor/pnmsmooth.c
index a18511c7..16d8ec33 100644
--- a/editor/pnmsmooth.c
+++ b/editor/pnmsmooth.c
@@ -23,12 +23,12 @@
 #include <unistd.h>
 #include <string.h>
 #include <errno.h>
-#include <sys/wait.h>
 
 #include "pm_c_util.h"
 #include "mallocvar.h"
 #include "shhopt.h"
 #include "nstring.h"
+#include "pm_system.h"
 #include "pnm.h"
 
 
@@ -36,7 +36,7 @@ struct cmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    const char *inputFilespec;  /* Filespec of input file */
+    const char * inputFilespec;  /* Filespec of input file */
     unsigned int width;
     unsigned int height;
     const char * dump;
@@ -176,38 +176,6 @@ writeConvolutionImage(FILE *       const cofp,
 
 
 
-static void
-runPnmconvol(const char * const inputFilespec,
-             const char * const convolutionImageFilespec) {
-
-    /* fork a Pnmconvol process */
-    pid_t rc;
-
-    rc = fork();
-    if (rc < 0)
-        pm_error("fork() failed.  errno=%d (%s)", errno, strerror(errno));
-    else if (rc == 0) {
-        /* child process executes following code */
-
-        execlp("pnmconvol",
-               "pnmconvol", convolutionImageFilespec, inputFilespec,
-               NULL);
-
-        pm_error("error executing pnmconvol command.  errno=%d (%s)",
-                 errno, strerror(errno));
-    } else {
-        /* This is the parent */
-        pid_t const childPid = rc;
-
-        int status;
-
-        /* wait for child to finish */
-        while (wait(&status) != childPid);
-    }
-}
-
-
-
 int
 main(int argc, char ** argv) {
 
@@ -232,7 +200,8 @@ main(int argc, char ** argv) {
     if (cmdline.dump) {
         /* We're done.  Convolution image is in user's file */
     } else {
-        runPnmconvol(cmdline.inputFilespec, tempfileName);
+        pm_system_lp("pnmconvol", NULL, NULL, NULL, NULL,
+                     "pnmconvol", tempfileName, cmdline.inputFilespec, NULL);
 
         unlink(tempfileName);
         strfree(tempfileName);
diff --git a/editor/pnmstitch.c b/editor/pnmstitch.c
index 61f02a04..45aee2f4 100644
--- a/editor/pnmstitch.c
+++ b/editor/pnmstitch.c
@@ -216,7 +216,7 @@ struct cmdlineInfo {
     unsigned int verbose;
 };
 
-static char minus[] = "-";
+
 
 static void
 parseCommandLine ( int argc, char ** argv,
@@ -293,10 +293,10 @@ parseCommandLine ( int argc, char ** argv,
         /* NOTREACHED */
     } else {
         if (argc-1 == 0) {
-            cmdlineP->leftFilespec = minus;
-            cmdlineP->rightFilespec = minus;
+            cmdlineP->leftFilespec = "-";
+            cmdlineP->rightFilespec = "-";
         } else if (argc-1 == 1) {
-            cmdlineP->leftFilespec = minus;
+            cmdlineP->leftFilespec = "-";
             cmdlineP->rightFilespec = argv[1];
         } else {
             cmdlineP->leftFilespec = argv[1];
@@ -311,7 +311,7 @@ parseCommandLine ( int argc, char ** argv,
         } else if (outputSpec) {
             cmdlineP->outputFilespec = outputOpt;
         } else {
-            cmdlineP->outputFilespec = minus;
+            cmdlineP->outputFilespec = "-";
         }
     }
 } /* parseCommandLine() - end */
@@ -439,7 +439,7 @@ readinit(const char * const name)
     else {
         FILE * ifP;
 
-        if (strcmp(name,minus) == 0) {
+        if (streq(name, "-")) {
             ifP = stdin;
             image->name = strdup("<stdin>");
         } else {
@@ -468,7 +468,7 @@ readinit(const char * const name)
 static bool
 writeinit(Image * image)
 {
-    if (strcmp(image->name,minus) == 0) {
+    if (streq(image->name, "-")) {
         image->pam.file = stdout;
         strfree(image->name);
         image->name = strdup("<stdout>");
diff --git a/editor/pnmtile.c b/editor/pnmtile.c
index 96bf658d..21512b36 100644
--- a/editor/pnmtile.c
+++ b/editor/pnmtile.c
@@ -10,54 +10,105 @@
 ** implied warranty.
 */
 
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "shhopt.h"
 #include "pnm.h"
 
-int
-main( argc, argv )
-    int argc;
-    char* argv[];
-    {
-    FILE* ifp;
-    xel** xels;
-    register xel* xelrow;
-    xelval maxval;
-    int rows, cols, format, width, height, row, col;
-    const char* const usage = "width height [pnmfile]";
 
-    pnm_init( &argc, argv );
 
-    if ( argc < 3 || argc > 4 )
-	pm_usage( usage );
+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 width;
+    unsigned int height;
+};
+
+
+
+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 option_def_index;
 
-    if ( sscanf( argv[1], "%d", &width ) != 1 )
-	pm_usage( usage );
-    if ( sscanf( argv[2], "%d", &height ) != 1 )
-	pm_usage( usage );
+    MALLOCARRAY_NOFAIL(option_def, 100);
 
-    if ( width < 1 )
-	pm_error( "width is less than 1" );
-    if ( height < 1 )
-	pm_error( "height is less than 1" );
+    option_def_index = 0;   /* incremented by OPTENT3 */
 
-    if ( argc == 4 )
-	ifp = pm_openr( argv[3] );
-    else
-	ifp = stdin;
+    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 */
 
-    xels = pnm_readpnm( ifp, &cols, &rows, &maxval, &format );
-    pm_close( ifp );
+    OPTENTINIT;
 
-    xelrow = pnm_allocrow( width );
+    optParseOptions3(&argc, (char**)argv, opt, sizeof opt, 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
-    pnm_writepnminit( stdout, width, height, maxval, format, 0 );
-    for ( row = 0; row < height; ++row )
-	{
-	for ( col = 0; col < width; ++col )
-	    xelrow[col] = xels[row % rows][col % cols];
-	pnm_writepnmrow( stdout, xelrow, width, maxval, format, 0 );
-	}
+    if (argc-1 < 2)
+        pm_error("You must specify at least two parameters: "
+                 "width and height.  You specified %u",
+                 argc-1);
+    else {
+        cmdlineP->width  = pm_parse_width(argv[1]);
+        cmdlineP->height = pm_parse_height(argv[2]);
 
-    pm_close( stdout );
+        if (argc-1 > 2) {
+            cmdlineP->inputFileName = argv[3];
 
-    exit( 0 );
+            if (argc-1 > 3)
+                pm_error("There are at most three arguments: "
+                         "width, height, file name.  You specified %u",
+                         argc-1);
+        } else 
+            cmdlineP->inputFileName = "-";
     }
+}
+
+
+
+int
+main(int argc, const char ** argv) {
+
+    struct cmdlineInfo cmdline;
+    FILE * ifP;
+    xel ** xels;
+    xel * xelrow;
+    xelval maxval;
+    int rows, cols;
+    int format;
+    unsigned int row;
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    xels = pnm_readpnm(ifP, &cols, &rows, &maxval, &format);
+    pm_close(ifP);
+
+    xelrow = pnm_allocrow(cmdline.width);
+
+    pnm_writepnminit(stdout, cmdline.width, cmdline.height, maxval, format, 0);
+    for (row = 0; row < cmdline.height; ++row) {
+        unsigned int col;
+        for (col = 0; col < cmdline.width; ++col)
+            xelrow[col] = xels[row % rows][col % cols];
+        pnm_writepnmrow(stdout, xelrow, cmdline.width, maxval, format, 0);
+    }
+
+    pm_close(stdout);
+
+    return 0;
+}
diff --git a/editor/ppm3d.c b/editor/ppm3d.c
deleted file mode 100644
index c37ceeb1..00000000
--- a/editor/ppm3d.c
+++ /dev/null
@@ -1,138 +0,0 @@
-/* ppmto3d.c - convert a portable pixmap to a portable graymap
-**
-** 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.
-*/
-
-#include "ppm.h"
-#include "lum.h"
-
-static void
-computeGrayscaleRow(const pixel * const inputRow,
-                    gray *        const outputRow,
-                    pixval        const maxval,
-                    unsigned int  const cols) {
-
-    if (maxval <= 255) {
-        unsigned int col;
-        /* Use fast approximation to 0.299 r + 0.587 g + 0.114 b. */
-        for (col = 0; col < cols; ++col)
-            outputRow[col] = ppm_fastlumin(inputRow[col]);
-    } else {
-        unsigned int col;
-        /* Can't use fast approximation, so fall back on floats. */
-        for (col = 0; col < cols; ++col)
-            outputRow[col] = PPM_LUMIN(inputRow[col]) + 0.5;
-    }
-}
-
-
-
-int
-main (int argc, char *argv[]) {
-
-    int offset; 
-    int cols, rows, row;
-    pixel* pixelrow;
-    pixval maxval;
-
-    FILE* Lifp;
-    pixel* Lpixelrow;
-    gray* Lgrayrow;
-    int Lrows, Lcols, Lformat;
-    pixval Lmaxval;
-   
-    FILE* Rifp;
-    pixel* Rpixelrow;
-    gray* Rgrayrow;
-    int Rrows, Rcols, Rformat;
-    pixval Rmaxval;
-   
-    ppm_init (&argc, argv);
-
-    if (argc-1 > 3 || argc-1 < 2) 
-        pm_error("Wrong number of arguments (%d).  Arguments are "
-                 "leftppmfile rightppmfile [horizontal_offset]", argc-1);
-
-    Lifp = pm_openr (argv[1]);
-    Rifp = pm_openr (argv[2]);
-
-    if (argc-1 >= 3) 
-        offset = atoi (argv[3]);
-    else
-        offset = 30;
-
-    ppm_readppminit (Lifp, &Lcols, &Lrows, &Lmaxval, &Lformat);
-    ppm_readppminit (Rifp, &Rcols, &Rrows, &Rmaxval, &Rformat);
-    
-    if ((Lcols != Rcols) || (Lrows != Rrows) || 
-        (Lmaxval != Rmaxval) || 
-        (PPM_FORMAT_TYPE(Lformat) != PPM_FORMAT_TYPE(Rformat)))
-        pm_error ("Pictures are not of same size and format");
-    
-    cols = Lcols;
-    rows = Lrows;
-    maxval = Lmaxval;
-   
-    ppm_writeppminit (stdout, cols, rows, maxval, 0);
-    Lpixelrow = ppm_allocrow (cols);
-    Lgrayrow = pgm_allocrow (cols);
-    Rpixelrow = ppm_allocrow (cols);
-    Rgrayrow = pgm_allocrow (cols);
-    pixelrow = ppm_allocrow (cols);
-
-    for (row = 0; row < rows; ++row) {
-        ppm_readppmrow(Lifp, Lpixelrow, cols, maxval, Lformat);
-        ppm_readppmrow(Rifp, Rpixelrow, cols, maxval, Rformat);
-
-        computeGrayscaleRow(Lpixelrow, Lgrayrow, maxval, cols);
-        computeGrayscaleRow(Rpixelrow, Rgrayrow, maxval, cols);
-        {
-            int col;
-            gray* LgP;
-            gray* RgP;
-            pixel* pP;
-            for (col = 0, pP = pixelrow, LgP = Lgrayrow, RgP = Rgrayrow;
-                 col < cols + offset;
-                 ++col) {
-            
-                if (col < offset/2)
-                    ++LgP;
-                else if (col >= offset/2 && col < offset) {
-                    const pixval Blue = (pixval) (float) *LgP;
-                    const pixval Red = (pixval) 0;
-                    PPM_ASSIGN (*pP, Red, Blue, Blue);
-                    ++LgP;
-                    ++pP;
-                } else if (col >= offset && col < cols) {
-                    const pixval Red = (pixval) (float) *RgP;
-                    const pixval Blue = (pixval) (float) *LgP;
-                    PPM_ASSIGN (*pP, Red, Blue, Blue);
-                    ++LgP;
-                    ++RgP;
-                    ++pP;
-                } else if (col >= cols && col < cols + offset/2) {
-                    const pixval Blue = (pixval) 0;
-                    const pixval Red = (pixval) (float) *RgP;
-                    PPM_ASSIGN (*pP, Red, Blue, Blue);
-                    ++RgP;
-                    ++pP;
-                } else
-                    ++RgP;
-            }
-        }    
-        ppm_writeppmrow(stdout, pixelrow, cols, maxval, 0);
-    }
-
-    pm_close(Lifp);
-    pm_close(Rifp);
-    pm_close(stdout);
-
-    return 0;
-}
diff --git a/editor/ppmbrighten.c b/editor/ppmbrighten.c
index 93649082..9bcf7bb5 100644
--- a/editor/ppmbrighten.c
+++ b/editor/ppmbrighten.c
@@ -11,6 +11,7 @@
 ** implied warranty.
 */
 
+#include "pm_c_util.h"
 #include "ppm.h"
 #include "shhopt.h"
 #include "mallocvar.h"
@@ -21,7 +22,7 @@ struct cmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    const char *inputFilespec;  /* '-' if stdin */
+    const char * inputFilespec;  /* '-' if stdin */
     float saturation;
     float value;
     unsigned int normalize;
@@ -29,10 +30,9 @@ struct cmdlineInfo {
 
 
 
-
 static void
-parseCommandLine (int argc, char ** argv,
-                  struct cmdlineInfo *cmdlineP) {
+parseCommandLine(int argc, char ** argv,
+                 struct cmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    parse program command line described in Unix standard form by argc
    and argv.  Return the information in the options as *cmdlineP.  
@@ -270,9 +270,9 @@ int
 main(int argc, char * argv[]) {
 
     struct cmdlineInfo cmdline;
-    FILE *ifP;
+    FILE * ifP;
     pixval minValue, maxValue;
-    pixel *pixelrow;
+    pixel * pixelrow;
     pixval maxval;
     int rows, cols, format, row;
 
diff --git a/editor/ppmchange.c b/editor/ppmchange.c
index 6d3839bd..f0d2cc38 100644
--- a/editor/ppmchange.c
+++ b/editor/ppmchange.c
@@ -13,6 +13,7 @@
 **     28 Jan 94 -  Added multiple color substitution function.
 */
 
+#include "pm_c_util.h"
 #include "ppm.h"
 #include "shhopt.h"
 #include "mallocvar.h"
@@ -144,22 +145,22 @@ changeRow(const pixel * const inrow,
    The input row is 'inrow'.  The output is returned as 'outrow', in
    storage which must be already allocated.  Both are 'cols' columns wide.
 -----------------------------------------------------------------------------*/
-    int col;
+    unsigned int col;
 
     for (col = 0; col < cols; ++col) {
-        int i;
-        int have_match; /* logical: It's a color user said to change */
+        unsigned int i;
+        bool haveMatch; /* logical: It's a color user said to change */
         pixel newcolor;  
         /* Color to which we must change current pixel.  Undefined unless
-           'have_match' is true.
+           'haveMatch' is true.
         */
 
-        have_match = FALSE;  /* haven't found a match yet */
-        for (i = 0; i < ncolors && !have_match; ++i) {
-            have_match = colormatch(inrow[col], colorfrom[i], closeness);
+        haveMatch = FALSE;  /* haven't found a match yet */
+        for (i = 0; i < ncolors && !haveMatch; ++i) {
+            haveMatch = colormatch(inrow[col], colorfrom[i], closeness);
             newcolor = colorto[i];
         }
-        if (have_match)
+        if (haveMatch)
             outrow[col] = newcolor;
         else if (remainder_specified)
             outrow[col] = remainder_color;
diff --git a/editor/ppmcolormask.c b/editor/ppmcolormask.c
index 2829e634..0d7a214c 100644
--- a/editor/ppmcolormask.c
+++ b/editor/ppmcolormask.c
@@ -61,7 +61,7 @@ parseColorOpt(const char *         const colorOpt,
         const char * token;
         token = strsepN(&cursor, ",");
         if (token) {
-            if (STRNEQ(token, "bk:", 3)) {
+            if (strneq(token, "bk:", 3)) {
                 cmdlineP->maskColor[colorCount].matchType = MATCH_BK;
                 cmdlineP->maskColor[colorCount].u.bkColor =
                     ppm_bk_color_from_name(&token[3]);
diff --git a/editor/ppmdraw.c b/editor/ppmdraw.c
index 0dd03bc9..3bd271a6 100644
--- a/editor/ppmdraw.c
+++ b/editor/ppmdraw.c
@@ -515,7 +515,7 @@ parseDrawCommand(struct tokenSet             const commandTokens,
         if (drawCommandP == NULL)
             pm_error("Out of memory to parse '%s' command", verb);
 
-        if (STREQ(verb, "setpos")) {
+        if (streq(verb, "setpos")) {
             drawCommandP->verb = VERB_SETPOS;
             if (commandTokens.count < 3)
                 pm_error("Not enough tokens for a 'setpos' command.  "
@@ -524,22 +524,22 @@ parseDrawCommand(struct tokenSet             const commandTokens,
                 drawCommandP->u.setposArg.x = atoi(commandTokens.token[1]);
                 drawCommandP->u.setposArg.y = atoi(commandTokens.token[2]);
             }
-        } else if (STREQ(verb, "setlinetype")) {
+        } else if (streq(verb, "setlinetype")) {
             drawCommandP->verb = VERB_SETLINETYPE;
             if (commandTokens.count < 2)
                 pm_error("Not enough tokens for a 'setlinetype' command.  "
                          "Need %u.  Got %u", 2, commandTokens.count);
             else {
                 const char * const typeArg = commandTokens.token[1];
-                if (STREQ(typeArg, "normal"))
+                if (streq(typeArg, "normal"))
                     drawCommandP->u.setlinetypeArg.type = PPMD_LINETYPE_NORMAL;
-                else if (STREQ(typeArg, "normal"))
+                else if (streq(typeArg, "normal"))
                     drawCommandP->u.setlinetypeArg.type = 
                         PPMD_LINETYPE_NODIAGS;
                 else
                     pm_error("Invalid type");
             }
-        } else if (STREQ(verb, "setlineclip")) {
+        } else if (streq(verb, "setlineclip")) {
             drawCommandP->verb = VERB_SETLINECLIP;
             if (commandTokens.count < 2)
                 pm_error("Not enough tokens for a 'setlineclip' command.  "
@@ -547,7 +547,7 @@ parseDrawCommand(struct tokenSet             const commandTokens,
             else
                 drawCommandP->u.setlineclipArg.clip =
                     atoi(commandTokens.token[1]);
-        } else if (STREQ(verb, "setcolor")) {
+        } else if (streq(verb, "setcolor")) {
             drawCommandP->verb = VERB_SETCOLOR;
             if (commandTokens.count < 2)
                 pm_error("Not enough tokens for a 'setcolor' command.  "
@@ -555,7 +555,7 @@ parseDrawCommand(struct tokenSet             const commandTokens,
             else
                 drawCommandP->u.setcolorArg.colorName =
                     strdup(commandTokens.token[1]);
-        } else if (STREQ(verb, "setfont")) {
+        } else if (streq(verb, "setfont")) {
             drawCommandP->verb = VERB_SETFONT;
             if (commandTokens.count < 2)
                 pm_error("Not enough tokens for a 'setfont' command.  "
@@ -563,7 +563,7 @@ parseDrawCommand(struct tokenSet             const commandTokens,
             else
                 drawCommandP->u.setfontArg.fontFileName =
                     strdup(commandTokens.token[1]);
-        } else if (STREQ(verb, "line")) {
+        } else if (streq(verb, "line")) {
             drawCommandP->verb = VERB_LINE;
             if (commandTokens.count < 5)
                 pm_error("Not enough tokens for a 'line' command.  "
@@ -574,7 +574,7 @@ parseDrawCommand(struct tokenSet             const commandTokens,
                 drawCommandP->u.lineArg.x1 = atoi(commandTokens.token[3]);
                 drawCommandP->u.lineArg.y1 = atoi(commandTokens.token[4]);
             } 
-        } else if (STREQ(verb, "line_here")) {
+        } else if (streq(verb, "line_here")) {
             drawCommandP->verb = VERB_LINE_HERE;
             if (commandTokens.count < 3)
                 pm_error("Not enough tokens for a 'line_here' command.  "
@@ -585,7 +585,7 @@ parseDrawCommand(struct tokenSet             const commandTokens,
                 argP->right = atoi(commandTokens.token[1]);
                 argP->down = atoi(commandTokens.token[2]);
             } 
-       } else if (STREQ(verb, "spline3")) {
+       } else if (streq(verb, "spline3")) {
             drawCommandP->verb = VERB_SPLINE3;
             if (commandTokens.count < 7)
                 pm_error("Not enough tokens for a 'spline3' command.  "
@@ -600,7 +600,7 @@ parseDrawCommand(struct tokenSet             const commandTokens,
                 argP->x2 = atoi(commandTokens.token[5]);
                 argP->y2 = atoi(commandTokens.token[6]);
             } 
-        } else if (STREQ(verb, "circle")) {
+        } else if (streq(verb, "circle")) {
             drawCommandP->verb = VERB_CIRCLE;
             if (commandTokens.count < 4)
                 pm_error("Not enough tokens for a 'circle' command.  "
@@ -611,7 +611,7 @@ parseDrawCommand(struct tokenSet             const commandTokens,
                 argP->cy     = atoi(commandTokens.token[2]);
                 argP->radius = atoi(commandTokens.token[3]);
             } 
-        } else if (STREQ(verb, "filledrectangle")) {
+        } else if (streq(verb, "filledrectangle")) {
             drawCommandP->verb = VERB_FILLEDRECTANGLE;
             if (commandTokens.count < 5)
                 pm_error("Not enough tokens for a 'filledrectangle' command.  "
@@ -624,7 +624,7 @@ parseDrawCommand(struct tokenSet             const commandTokens,
                 argP->width  = atoi(commandTokens.token[3]);
                 argP->height = atoi(commandTokens.token[4]);
             } 
-        } else if (STREQ(verb, "text")) {
+        } else if (streq(verb, "text")) {
             drawCommandP->verb = VERB_TEXT;
             if (commandTokens.count < 6)
                 pm_error("Not enough tokens for a 'text' command.  "
@@ -638,7 +638,7 @@ parseDrawCommand(struct tokenSet             const commandTokens,
                 if (drawCommandP->u.textArg.text == NULL)
                     pm_error("Out of storage parsing 'text' command");
             }
-        } else if (STREQ(verb, "text_here")) {
+        } else if (streq(verb, "text_here")) {
             drawCommandP->verb = VERB_TEXT_HERE;
             if (commandTokens.count < 4)
                 pm_error("Not enough tokens for a 'text_here' command.  "
@@ -705,7 +705,7 @@ processToken(const char *      const scriptText,
     memcpy(token, &scriptText[tokenStart], tokenLength);
     token[tokenLength] = '\0';
     
-    if (STREQ(token, ";")) {
+    if (streq(token, ";")) {
         disposeOfCommandTokens(tokenSetP, scriptP);
         free(token);
     } else {
diff --git a/editor/ppmfade b/editor/ppmfade
index 9bd122e9..fbc62968 100755
--- a/editor/ppmfade
+++ b/editor/ppmfade
@@ -32,14 +32,7 @@ my $last_file = "undefined";
 my $base_name = "fade";		# default base name of output files
 my $image = "ppm";		# default output storage format
 my $mode = $SPREAD;		# default fading mode
-#
-#  Check those command line args.
-#
-sub usage();
 
-if (@ARGV == 0) {
-    usage();
-}
 
 my $n;  # argument number
 
@@ -49,7 +42,7 @@ for ($n = 0; $n < @ARGV; $n++) {
         $first_file = $ARGV[$n];
         if (-e $first_file) {
         } else {
-            print "I can't find $first_file\n";
+            print "I can't find first file '$first_file'\n";
             exit 20;
         }
     } elsif ($ARGV[$n] eq "-l") {
@@ -57,7 +50,7 @@ for ($n = 0; $n < @ARGV; $n++) {
         $last_file = $ARGV[$n];
         if (-e $last_file) {
         } else {
-            print "I can't find $last_file\n";
+            print "I can't find last file '$last_file'\n";
             exit 20;
         }
     } elsif ($ARGV[$n] eq "-base") {
@@ -298,14 +291,3 @@ for ($i = 1; $i <= $nframes; $i++) {
 system("rm junk*$$.ppm");
 
 exit(0);
-
-
-
-sub usage() {
-   print "Usage: ppmfade [-f first_file] [-l last_file]\n";
-   print "               [-spread|-relief|-oil|-edge|-bentley|-block]\n";
-   print "               [-base basename]\n";
-   print "Notes: Default base: fade\n";
-   print "       The resulting image files will be named fade.NNNN.ppm.\n";
-   exit(100);
-}
diff --git a/editor/ppmquantall b/editor/ppmquantall
index bc314d4d..4807de8c 100755
--- a/editor/ppmquantall
+++ b/editor/ppmquantall
@@ -71,9 +71,8 @@ for i in ${files[@]}; do
 done
 
 tempdir="${TMPDIR-/tmp}/ppmquantall.$$"
-mkdir $tempdir || { echo "Could not create temporary file. Exiting."; exit 1;}
-chmod 700 $tempdir
-
+mkdir -m 0700 $tempdir || \
+  { echo "Could not create temporary file. Exiting."; exit 1;}
 trap 'rm -rf $tempdir' 0 1 3 15
 
 all=$tempdir/pqa.all.$$
diff --git a/editor/ppmshift.c b/editor/ppmshift.c
deleted file mode 100644
index 1f8a599b..00000000
--- a/editor/ppmshift.c
+++ /dev/null
@@ -1,137 +0,0 @@
-
-/*********************************************************************/
-/* ppmshift -  shift lines of a picture left or right by x pixels    */
-/* Frank Neumann, October 1993                                       */
-/* V1.1 16.11.1993                                                   */
-/*                                                                   */
-/* version history:                                                  */
-/* V1.0    11.10.1993  first version                                 */
-/* V1.1    16.11.1993  Rewritten to be NetPBM.programming conforming */
-/*********************************************************************/
-
-#include "ppm.h"
-
-/* global variables */
-#ifdef AMIGA
-static char *version = "$VER: ppmshift 1.1 (16.11.93)"; /* Amiga version identification */
-#endif
-
-/**************************/
-/* start of main function */
-/**************************/
-int main(argc, argv)
-int argc;
-char *argv[];
-{
-	FILE* ifp;
-	time_t timenow;
-	int argn, rows, cols, format, i = 0, j = 0;
-	pixel *srcrow, *destrow;
-	pixel *pP = NULL, *pP2 = NULL;
-	pixval maxval;
-	int shift, nowshift;
-	const char * const usage = "shift [ppmfile]\n        shift: maximum number of pixels to shift a line by\n";
-
-	/* parse in 'default' parameters */
-	ppm_init(&argc, argv);
-
-	argn = 1;
-
-	/* parse in shift number */
-	if (argn == argc)
-		pm_usage(usage);
-	if (sscanf(argv[argn], "%d", &shift) != 1)
-		pm_usage(usage);
-	if (shift < 0)
-		pm_error("shift factor must be 0 or more");
-	++argn;
-
-	/* parse in filename (if present, stdin otherwise) */
-	if (argn != argc)
-	{
-		ifp = pm_openr(argv[argn]);
-		++argn;
-	}
-	else
-		ifp = stdin;
-
-	if (argn != argc)
-		pm_usage(usage);
-
-	/* read first data from file */
-	ppm_readppminit(ifp, &cols, &rows, &maxval, &format);
-
-	if (shift > cols)
-    {
-		shift = cols;
-        pm_message("shift amount is larger than picture width - reset to %d", shift);
-    }
-
-	/* no error checking required here, ppmlib does it all for us */
-	srcrow = ppm_allocrow(cols);
-
-	/* allocate a row of pixel data for the new pixels */
-	destrow = ppm_allocrow(cols);
-
-	ppm_writeppminit(stdout, cols, rows, maxval, 0);
-
-	/* get time of day to feed the random number generator */
-	timenow = time(NULL);
-	srand(timenow);
-
-	/** now do the shifting **/
-	/* the range by which a line is shifted lays in the range from */
-	/* -shift/2 .. +shift/2 pixels; however, within this range it is */
-    /* randomly chosen */
-	for (i = 0; i < rows; i++)
-	{
-		if (shift != 0)
-			nowshift = (rand() % (shift+1)) - ((shift+1) / 2);
-		else
-			nowshift = 0;
-
-		ppm_readppmrow(ifp, srcrow, cols, maxval, format);
-
-		pP = srcrow;
-		pP2 = destrow;
-
-		/* if the shift value is less than zero, we take the original pixel line and */
-		/* copy it into the destination line translated to the left by x pixels. The */
-        /* empty pixels on the right end of the destination line are filled up with  */
-		/* the pixel that is the right-most in the original pixel line.              */
-		if (nowshift < 0)
-		{
-			pP+= abs(nowshift);
-			for (j = 0; j < cols; j++)
-			{
-				PPM_ASSIGN(*pP2, PPM_GETR(*pP), PPM_GETG(*pP), PPM_GETB(*pP));
-				pP2++;
-                if (j < (cols+nowshift)-1)
-					pP++;
-			}
-		}
-		/* if the shift value is 0 or positive, the first <nowshift> pixels of the */
-		/* destination line are filled with the first pixel from the source line,  */
-		/* and the rest of the source line is copied to the dest line              */
-		else
-		{
-			for (j = 0; j < cols; j++)
-			{
-				PPM_ASSIGN(*pP2, PPM_GETR(*pP), PPM_GETG(*pP), PPM_GETB(*pP));
-				pP2++;
-                if (j >= nowshift)
-					pP++;
-			}
-		}
-
-		/* write out one line of graphic data */
-		ppm_writeppmrow(stdout, destrow, cols, maxval, 0);
-	}
-
-	pm_close(ifp);
-	ppm_freerow(srcrow);
-	ppm_freerow(destrow);
-
-	exit(0);
-}
-
diff --git a/editor/ppmspread.c b/editor/ppmspread.c
deleted file mode 100644
index 569d1266..00000000
--- a/editor/ppmspread.c
+++ /dev/null
@@ -1,127 +0,0 @@
-/*********************************************************************/
-/* ppmspread -  randomly displace a PPM's pixels by a certain amount */
-/* Frank Neumann, October 1993                                       */
-/* V1.1 16.11.1993                                                   */
-/*                                                                   */
-/* version history:                                                  */
-/* V1.0 12.10.1993    first version                                  */
-/* V1.1 16.11.1993    Rewritten to be NetPBM.programming conforming  */
-/*********************************************************************/
-
-#include <string.h>
-#include "ppm.h"
-
-/* global variables */
-#ifdef AMIGA
-static char *version = "$VER: ppmspread 1.1 (16.11.93)"; /* Amiga version identification */
-#endif
-
-/**************************/
-/* start of main function */
-/**************************/
-int main(argc, argv)
-int argc;
-char *argv[];
-{
-	FILE* ifp;
-	int argn, rows, cols, i, j;
-	int xdis, ydis, xnew, ynew;
-	pixel **destarray, **srcarray;
-	pixel *pP, *pP2;
-	pixval maxval;
-	pixval r1, g1, b1;
-	int amount;
-	time_t timenow;
-	const char * const usage = "amount [ppmfile]\n        amount: # of pixels to displace a pixel by at most\n";
-
-	/* parse in 'default' parameters */
-	ppm_init(&argc, argv);
-
-	argn = 1;
-
-	/* parse in amount & seed */
-	if (argn == argc)
-		pm_usage(usage);
-	if (sscanf(argv[argn], "%d", &amount) != 1)
-		pm_usage(usage);
-	if (amount < 0)
-		pm_error("amount should be a positive number");
-	++argn;
-
-	/* parse in filename (if present, stdin otherwise) */
-	if (argn != argc)
-	{
-		ifp = pm_openr(argv[argn]);
-		++argn;
-	}
-	else
-		ifp = stdin;
-
-	if (argn != argc)
-		pm_usage(usage);
-
-	/* read entire picture into buffer */
-	srcarray = ppm_readppm(ifp, &cols, &rows, &maxval);
-
-	/* allocate an entire picture buffer for dest picture */
-	destarray = ppm_allocarray(cols, rows);
-
-	/* clear out the buffer */
-	for (i=0; i < rows; i++)
-		memset(destarray[i], 0, cols * sizeof(pixel));
-
-	/* set seed for random number generator */
-	/* get time of day to feed the random number generator */
-	timenow = time(NULL);
-	srand(timenow);
-
-	/* start displacing pixels */
-	for (i = 0; i < rows; i++)
-	{
-		pP = srcarray[i];
-
-		for (j = 0; j < cols; j++)
-		{
-			xdis = (rand() % (amount+1)) - ((amount+1) / 2);
-			ydis = (rand() % (amount+1)) - ((amount+1) / 2);
-
-			xnew = j + xdis;
-			ynew = i + ydis;
-
-			/* only set the displaced pixel if it's within the bounds of the image */
-			if (xnew >= 0 && xnew < cols && ynew >= 0 && ynew < rows)
-			{
-				/* displacing a pixel is accomplished by swapping it with another */
-				/* pixel in its vicinity - so, first store other pixel's RGB      */
-                pP2 = srcarray[ynew] + xnew;
-				r1 = PPM_GETR(*pP2);
-				g1 = PPM_GETG(*pP2);
-				b1 = PPM_GETB(*pP2);
-				/* set second pixel to new value */
-				pP2 = destarray[ynew] + xnew;
-				PPM_ASSIGN(*pP2, PPM_GETR(*pP), PPM_GETG(*pP), PPM_GETB(*pP));
-
-				/* now, set first pixel to (old) value of second */
-				pP2 = destarray[i] + j;
-				PPM_ASSIGN(*pP2, r1, g1, b1);
-			}
-			else
-			{
-                /* displaced pixel is out of bounds; leave the old pixel there */
-                pP2 = destarray[i] + j;
-				PPM_ASSIGN(*pP2, PPM_GETR(*pP), PPM_GETG(*pP), PPM_GETB(*pP));
-			}
-			pP++;
-		}
-	}
-
-	/* write out entire dest picture in one go */
-	ppm_writeppm(stdout, destarray, cols, rows, maxval, 0);
-
-	pm_close(ifp);
-	ppm_freearray(srcarray, rows);
-	ppm_freearray(destarray, rows);
-
-	exit(0);
-}
-
diff --git a/editor/specialty/Makefile b/editor/specialty/Makefile
new file mode 100644
index 00000000..76befbb4
--- /dev/null
+++ b/editor/specialty/Makefile
@@ -0,0 +1,55 @@
+ifeq ($(SRCDIR)x,x)
+  SRCDIR = $(CURDIR)/../..
+  BUILDDIR = $(SRCDIR)
+endif
+SUBDIR = editor/specialty
+VPATH=.:$(SRCDIR)/$(SUBDIR)
+
+include $(BUILDDIR)/config.mk
+
+PORTBINARIES = pamdeinterlace \
+	       pammixinterlace \
+	       pamoil \
+	       pampop9 \
+	       pbmlife \
+	       pgmabel \
+	       pgmbentley \
+	       pgmmorphconv \
+	       pnmindex \
+	       ppm3d \
+	       ppmglobe \
+	       ppmntsc \
+	       ppmrelief \
+	       ppmshift \
+	       ppmspread \
+	       ppmtv \
+
+# 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.
+
+NOMERGEBINARIES = 
+MERGEBINARIES = $(PORTBINARIES)
+
+
+BINARIES = $(MERGEBINARIES) $(NOMERGEBINARIES)
+SCRIPTS =
+
+OBJECTS = $(BINARIES:%=%.o)
+
+MERGE_OBJECTS = $(MERGEBINARIES:%=%.o2)
+
+.PHONY: all
+all: $(BINARIES)
+
+include $(SRCDIR)/common.mk
+
+install.bin: install.bin.local
+
+.PHONY: install.bin.local
+install.bin.local: $(PKGDIR)/bin
+# Remember that $(SYMLINK) might just be a copy command.
+# pamoil replaced pgmoil in June 2001.
+	cd $(PKGDIR)/bin ; \
+	rm -f pgmoil$(EXE) ; \
+	$(SYMLINK) pamoil$(EXE) pgmoil$(EXE)
diff --git a/editor/pamdeinterlace.c b/editor/specialty/pamdeinterlace.c
index db893708..7c6b123c 100644
--- a/editor/pamdeinterlace.c
+++ b/editor/specialty/pamdeinterlace.c
@@ -8,6 +8,7 @@
   Contributed to the public domain.
 ******************************************************************************/
 
+#include "pm_c_util.h"
 #include "pam.h"
 #include "shhopt.h"
 #include "mallocvar.h"
diff --git a/editor/specialty/pammixinterlace.c b/editor/specialty/pammixinterlace.c
new file mode 100644
index 00000000..9f98b406
--- /dev/null
+++ b/editor/specialty/pammixinterlace.c
@@ -0,0 +1,345 @@
+/******************************************************************************
+                             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
+  documentation for any purpose and without fee is hereby granted,
+  provided that the above copyright notice appear in all copies and that
+  both that copyright notice and this permission notice appear in
+  supporting documentation.  This software is provided "as is" without
+  express or implied warranty.
+
+******************************************************************************/
+
+#define _BSD_SOURCE    /* Make sure strcaseeq() is in nstring.h */
+
+#include <string.h>
+
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "nstring.h"
+#include "shhopt.h"
+#include "pam.h"
+
+
+
+static sample
+clamp(sample const val,
+      sample const maxval) {
+
+    return val < 0 ? 0 : val > maxval ? maxval : val;
+}
+
+
+
+static bool
+distant(long const above,
+        long const mid,
+        long const below) {
+
+    return abs(mid - (above + below) / 2) > abs(above - below);
+}
+
+
+
+static void
+filterLinearBlend(tuple *      const outputrow,
+                  tuple **     const tuplerowWindow,
+                  unsigned int const width,
+                  unsigned int const depth,
+                  bool         const adaptive,
+                  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[0][col][plane];
+            long const mid   = tuplerowWindow[1][col][plane];
+            long const below = tuplerowWindow[2][col][plane];
+
+            sample out;
+
+            if (!adaptive || distant(above, mid, below))
+                out = (above + mid * 2 + below) / 4;
+            else
+                out = mid;
+            
+            outputrow[col][plane] = out;
+        }
+    }
+}
+
+
+
+static void
+filterFfmpeg(tuple *      const outputrow,
+             tuple **     const tuplerowWindow,
+             unsigned int const width,
+             unsigned int const depth,
+             bool         const adaptive,
+             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];
+
+            sample out;
+            
+            if (!adaptive || distant(above, mid, below)) {
+                long const a = (- (long)tuplerowWindow[0][col][plane]
+                                + above * 4
+                                + mid * 2
+                                + below * 4
+                                - (long)tuplerowWindow[4][col][plane]) / 8;
+                out = clamp(a, maxval);
+            } else
+                out = mid;
+
+            outputrow[col][plane] = out;
+        }
+    }
+}
+
+
+
+static void
+filterFIR(tuple *      const outputrow,
+          tuple **     const tuplerowWindow,
+          unsigned int const width,
+          unsigned int const depth,
+          bool         const adaptive,
+          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];
+
+            sample out;
+
+            if (!adaptive || distant(above, mid, below)) {
+                long const a = (- (long)tuplerowWindow[0][col][plane]
+                                + above * 2
+                                + mid * 6
+                                + below * 2
+                                - (long)tuplerowWindow[4][col][plane]) / 8;
+                out = clamp(a, maxval);
+            } else
+                out = mid;
+            
+            outputrow[col][plane] = out;
+        }
+    }
+}
+
+
+
+struct filter {
+    const char * name;          /* The command-line name of the filter */
+    unsigned int rows;   /* The number of rows the filter operates on */
+    void (*filter)(tuple *, tuple **,
+                   unsigned int width, unsigned int depth,
+                   bool adaptive, sample maxval);
+};
+
+static struct filter filters[] = {
+    { "fir", 5, filterFIR }, /* FIR is cleanest and default filter */
+    { "ffmpeg", 5, filterFfmpeg },
+    { "linear", 3, filterLinearBlend },
+};
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * inputFileName;  /* Names of input files */
+    struct filter * filterP;
+    unsigned int adaptive;
+};
+
+
+
+static void
+parseCommandLine(int argc, 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;
+    const char * filterName;
+    unsigned int filterSpec;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "filter",   OPT_STRING, &filterName, &filterSpec, 0);
+    OPTENT3(0, "adaptive", OPT_FLAG, NULL, &cmdlineP->adaptive, 0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (!filterSpec)
+        cmdlineP->filterP = &filters[0];
+    else {
+        unsigned int i;
+        cmdlineP->filterP = NULL;
+        for (i = 0; i < sizeof filters / sizeof(struct filter); ++i) {
+            if (strcaseeq(filterName, filters[i].name)) {
+                cmdlineP->filterP = &filters[i];
+                break;
+            }
+        }
+        if (!cmdlineP->filterP)
+            pm_error("The filter name '%s' is not known.", filterName);
+    }
+    
+    if (argc-1 < 1)
+        cmdlineP->inputFileName = "-";
+    else if (argc-1 == 1)
+        cmdlineP->inputFileName = argv[1];
+    else
+        pm_error("You specified too many arguments (%d).  The only "
+                 "argument is the optional input file specification.",
+                 argc-1);
+}
+
+
+
+static void
+allocateRowWindowBuffer(struct pam * const pamP,
+                        tuple **     const tuplerowWindow,
+                        unsigned int const rows) {
+
+    unsigned int row;
+
+    for (row = 0; row < rows; ++row)
+        tuplerowWindow[row] = pnm_allocpamrow(pamP);
+}
+
+
+
+static void
+freeRowWindowBuffer(tuple **     const tuplerowWindow,
+                    unsigned int const rows) {
+
+    unsigned int row;
+
+    for (row = 0; row < rows; ++row)
+        pnm_freepamrow(tuplerowWindow[row]);
+}
+
+
+
+static void
+slideWindowDown(tuple **     const tuplerowWindow,
+                unsigned int const rows) {
+/*----------------------------------------------------------------------------
+  Slide the rows-line tuple row window tuplerowWindow[] down one by moving
+  pointers.
+-----------------------------------------------------------------------------*/
+    tuple * const oldrow0 = tuplerowWindow[0];
+
+    unsigned int i;
+
+    for (i = 0; i < rows-1; ++i)
+        tuplerowWindow[i] = tuplerowWindow[i+1];
+
+    tuplerowWindow[i] = oldrow0;
+}
+
+
+
+int
+main(int argc, char *argv[]) {
+
+    FILE * ifP;
+    struct cmdlineInfo cmdline;
+    struct pam inpam;  
+    struct pam outpam;
+    tuple * tuplerowWindow[5];
+    tuple * outputrow;
+    unsigned int rows;
+    
+    pnm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    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 */
+    outpam.file = stdout;
+
+    pnm_writepaminit(&outpam);
+
+    allocateRowWindowBuffer(&inpam, tuplerowWindow, rows);
+    outputrow = pnm_allocpamrow(&outpam);
+
+    if (inpam.height < rows) {
+        unsigned int row;
+        pm_message("WARNING: Image height less than %d.  No mixing done.",
+                   rows);
+        for (row = 0; row < inpam.height; ++row) {
+            pnm_readpamrow(&inpam, tuplerowWindow[0]);
+            pnm_writepamrow(&outpam, tuplerowWindow[0]);
+        }
+    } else {
+        unsigned int row;
+
+    for (row = 0; row < rows-1; ++row)
+        pnm_readpamrow(&inpam, tuplerowWindow[row]);
+
+    /* Pass through first unfilterable rows */
+    for (row = 0; row < rows/2; ++row)
+        pnm_writepamrow(&outpam, tuplerowWindow[row]);
+
+    for (row = rows / 2 + 1; row < inpam.height - rows / 2 + 1; ++row) {
+        pnm_readpamrow(&inpam, tuplerowWindow[rows-1]);
+        cmdline.filterP->filter(outputrow, tuplerowWindow,
+                                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]);
+    }
+
+    freeRowWindowBuffer(tuplerowWindow, rows);
+    pnm_freepamrow(outputrow);
+    pm_close(inpam.file);
+    pm_close(outpam.file);
+    
+    return 0;
+}
diff --git a/editor/pamoil.c b/editor/specialty/pamoil.c
index 6cb8d3ac..6cb8d3ac 100644
--- a/editor/pamoil.c
+++ b/editor/specialty/pamoil.c
diff --git a/editor/pampop9.c b/editor/specialty/pampop9.c
index d6c61e4f..d6c61e4f 100644
--- a/editor/pampop9.c
+++ b/editor/specialty/pampop9.c
diff --git a/editor/pbmlife.c b/editor/specialty/pbmlife.c
index be34cc69..be34cc69 100644
--- a/editor/pbmlife.c
+++ b/editor/specialty/pbmlife.c
diff --git a/editor/pgmabel.c b/editor/specialty/pgmabel.c
index 4914c4be..4914c4be 100644
--- a/editor/pgmabel.c
+++ b/editor/specialty/pgmabel.c
diff --git a/editor/specialty/pgmbentley.c b/editor/specialty/pgmbentley.c
new file mode 100644
index 00000000..aed92074
--- /dev/null
+++ b/editor/specialty/pgmbentley.c
@@ -0,0 +1,74 @@
+/* pgmbentley.c - read a portable graymap and smear it according to brightness
+**
+** Copyright (C) 1990 by Wilson Bent (whb@hoh-2.att.com)
+**
+** Permission to use, copy, modify, and distribute this software and its
+** documentation for any purpose and without fee is hereby granted, provided
+** that the above copyright notice appear in all copies and that both that
+** copyright notice and this permission notice appear in supporting
+** documentation.  This software is provided "as is" without express or
+** implied warranty.
+*/
+
+#include <stdio.h>
+
+#include "pm_c_util.h"
+#include "pgm.h"
+
+
+static unsigned int const N = 4;
+
+
+int
+main(int argc, const char * argv[]) {
+
+    FILE * ifP;
+    int rows, cols;
+    gray maxval;
+    gray ** gin;
+    gray ** gout;
+    unsigned int row;
+    const char * inputFileName;
+
+    pm_proginit(&argc, argv);
+
+    if (argc-1 < 1)
+        inputFileName = "-";
+    else {
+        inputFileName = argv[1];
+
+        if (argc-1 > 1)
+            pm_error("There are no options and only one argument.  "
+                     "You specified %u", argc-1);
+    }
+	ifP = pm_openr(inputFileName);
+
+    gin = pgm_readpgm(ifP, &cols, &rows, &maxval);
+
+    pm_close(ifP);
+
+    gout = pgm_allocarray(cols, rows);
+
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+        for (col = 0; col < cols; ++col)
+            gout[row][col] = 0;
+    }
+
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+
+        for (col = 0; col < cols; ++col) {
+            unsigned int const brow = MIN(rows-1, row + gin[row][col] / N);
+
+            gout[brow][col] = gin[row][col];
+	    }
+    }
+     
+    pgm_writepgm(stdout, gout, cols, rows, maxval, 0);
+
+    pm_close(stdout);
+    pgm_freearray(gout, rows);
+
+    return 0;
+}
diff --git a/editor/pgmmorphconv.c b/editor/specialty/pgmmorphconv.c
index abc4e718..abc4e718 100644
--- a/editor/pgmmorphconv.c
+++ b/editor/specialty/pgmmorphconv.c
diff --git a/editor/pnmindex.c b/editor/specialty/pnmindex.c
index cb7d3702..ca1da18c 100644
--- a/editor/pnmindex.c
+++ b/editor/specialty/pnmindex.c
@@ -23,10 +23,12 @@
 #include <sys/stat.h>
 
 
-#include "pnm.h"
+#include "pm_config.h"
+#include "pm_c_util.h"
 #include "shhopt.h"
 #include "mallocvar.h"
 #include "nstring.h"
+#include "pnm.h"
 
 struct cmdlineInfo {
     /* All the information the user supplied in the command line,
@@ -200,7 +202,7 @@ makeTempDir(const char ** const tempDirP) {
 
     asprintfN(&mytmpdir, "%s/pnmindex_%d", tmpdir, getpid());
 
-    rc = mkdir(mytmpdir, 0700);
+    rc = pm_mkdir(mytmpdir, 0700);
     if (rc != 0)
         pm_error("Unable to create temporary file directory '%s'.  mkdir() "
                  "fails with errno %d (%s)",
diff --git a/editor/specialty/ppm3d.c b/editor/specialty/ppm3d.c
new file mode 100644
index 00000000..d9ada365
--- /dev/null
+++ b/editor/specialty/ppm3d.c
@@ -0,0 +1,308 @@
+/*=============================================================================
+                                   ppmto3d
+===============================================================================
+  This program converts two PPM images into an anaglyph stereogram image PPM.
+  (for viewing with red/blue 3D glasses).
+
+=============================================================================*/
+
+#include <assert.h>
+
+#include "pm_c_util.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+#include "ppm.h"
+#include "lum.h"
+
+
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * leftInputFileName;  /* '-' if stdin */
+    const char * rghtInputFileName;  /* '-' if stdin */
+    int offset;
+    unsigned int color;
+};
+
+
+
+static void
+parseCommandLine(int argc, char ** argv,
+                 struct cmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   parse program command line described in Unix standard form by argc
+   and argv.  Return the information in the options as *cmdlineP.  
+
+   If command line is internally inconsistent (invalid options, etc.),
+   issue error message to stderr and abort program.
+
+   Note that the strings we return are stored in the storage that
+   was passed to us as the argv array.  We also trash *argv.
+-----------------------------------------------------------------------------*/
+    optEntry * option_def;
+        /* Instructions to optParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+    unsigned int offsetSpec;
+    const char * offsetArg;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "color",   OPT_FLAG,   NULL,
+            &cmdlineP->color,   0);
+    OPTENT3(0, "offset",  OPT_INT,    &cmdlineP->offset,
+            &offsetSpec,  0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    optParseOptions3( &argc, argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+    
+    if (argc-1 < 2)
+        pm_error("You must specify at least two arguments: left and right "
+                 "input file names.  You specified %u", argc-1);
+    else {
+        cmdlineP->leftInputFileName = argv[1];
+        cmdlineP->rghtInputFileName = argv[2];
+
+        if (argc-1 > 2) {
+            offsetArg = argv[3];
+
+            if (argc-1 > 3)
+                pm_error("Program takes at most 3 arguments:  left and "
+                         "right input file names and offset.  "
+                         "You specified %u", argc-1);
+        } else
+            offsetArg = NULL;
+    }
+
+    if (offsetArg && offsetSpec)
+        pm_error("You cannot specify both -offset and the offset "
+                 "argument (i.e. with -offset, there is at most "
+                 "two arguments: left and right input file names");
+    else if (!offsetArg && !offsetSpec)
+        cmdlineP->offset = 0;
+    else if (offsetArg)
+        cmdlineP->offset = atoi(offsetArg);
+}
+
+
+
+static void
+computeGrayscaleRow(const pixel * const inputRow,
+                    gray *        const outputRow,
+                    pixval        const maxval,
+                    unsigned int  const cols) {
+
+    if (maxval <= 255) {
+        unsigned int col;
+        /* Use fast approximation to 0.299 r + 0.587 g + 0.114 b. */
+        for (col = 0; col < cols; ++col)
+            outputRow[col] = ppm_fastlumin(inputRow[col]);
+    } else {
+        unsigned int col;
+        /* Can't use fast approximation, so fall back on floats. */
+        for (col = 0; col < cols; ++col)
+            outputRow[col] = PPM_LUMIN(inputRow[col]) + 0.5;
+    }
+}
+
+
+
+static void
+compute3dRowMono(gray *       const lGrayrow,
+                 gray *       const rGrayrow,
+                 pixel *      const pixelrow,
+                 unsigned int const cols,
+                 int          const offset) {
+    
+    unsigned int col;
+    gray *  lgP;
+    gray *  rgP;
+    pixel * pP;
+
+    assert(abs(offset) <= cols);
+
+    for (col = 0, pP = pixelrow, lgP = lGrayrow, rgP = rGrayrow;
+         col < cols + offset;
+         ++col) {
+            
+        if ((int)col < offset/2)
+            ++lgP;
+        else if ((int)col < offset) {
+            PPM_ASSIGN(*pP, 0, *lgP, *lgP);
+            ++lgP;
+            ++pP;
+        } else if (col < cols) {
+            PPM_ASSIGN(*pP, *rgP, *lgP, *lgP);
+            ++lgP;
+            ++rgP;
+            ++pP;
+        } else if (col < cols + offset/2) {
+            PPM_ASSIGN(*pP, *rgP, 0, 0);
+            ++rgP;
+            ++pP;
+        } else {
+            assert(col < cols + offset);
+            ++rgP;
+        }
+    }
+}    
+
+
+
+static void
+compute3dRowColor(pixel *      const lPixelrow,
+                  pixel *      const rPixelrow,
+                  pixel *      const pixelrow,
+                  unsigned int const cols,
+                  unsigned int const offset) {
+    
+    unsigned int col;
+    pixel * lP;
+    pixel * rP;
+    pixel * pP;
+
+    assert(abs(offset) <= cols);
+
+    for (col = 0, pP = pixelrow, lP = lPixelrow, rP = rPixelrow;
+         col < cols + offset;
+         ++col) {
+            
+        if ((int)col < offset/2)
+            ++lP;
+        else if ((int)col < offset) {
+            PPM_ASSIGN(*pP, 0, PPM_GETG(*lP), PPM_GETB(*lP));
+            ++lP;
+            ++pP;
+        } else if (col < cols) {
+            PPM_ASSIGN(*pP, PPM_GETR(*rP), PPM_GETG(*lP), PPM_GETB(*lP));
+            ++lP;
+            ++rP;
+            ++pP;
+        } else if (col < cols + offset/2) {
+            PPM_ASSIGN(*pP, PPM_GETR(*rP), 0, 0);
+            ++rP;
+            ++pP;
+        } else {
+            assert(col < cols + offset);
+            ++rP;
+        }
+    }
+}    
+
+
+
+static void
+write3dRaster(FILE *       const ofP,
+              FILE *       const lIfP,
+              FILE *       const rIfP,
+              unsigned int const cols,
+              unsigned int const rows,
+              pixval       const maxval,
+              int          const lFormat,
+              int          const rFormat,
+              int          const offset,
+              bool         const color) {
+
+    pixel * lPixelrow;
+    gray *  lGrayrow;
+    pixel * rPixelrow;
+    gray *  rGrayrow;
+    pixel * pixelrow;
+
+    unsigned int row;
+
+    assert(abs(offset) < cols);
+
+    lPixelrow = ppm_allocrow (cols);
+    lGrayrow  = pgm_allocrow (cols);
+    rPixelrow = ppm_allocrow (cols);
+    rGrayrow  = pgm_allocrow (cols);
+    pixelrow  = ppm_allocrow (cols);
+
+    for (row = 0; row < rows; ++row) {
+        ppm_readppmrow(lIfP, lPixelrow, cols, maxval, lFormat);
+        ppm_readppmrow(rIfP, rPixelrow, cols, maxval, rFormat);
+
+        computeGrayscaleRow(lPixelrow, lGrayrow, maxval, cols);
+        computeGrayscaleRow(rPixelrow, rGrayrow, maxval, cols);
+
+        if (color)
+            compute3dRowColor(lPixelrow, rPixelrow, pixelrow, cols, offset);
+        else
+            compute3dRowMono(lGrayrow, rGrayrow, pixelrow, cols, offset);
+
+        ppm_writeppmrow(ofP, pixelrow, cols, maxval, 0);
+    }
+
+    ppm_freerow(pixelrow);
+    pgm_freerow(rGrayrow);
+    ppm_freerow(rPixelrow);
+    pgm_freerow(lGrayrow);
+    ppm_freerow(lPixelrow);
+}
+
+
+
+int
+main(int argc, char *argv[]) {
+
+    struct cmdlineInfo cmdline;
+    FILE * lIfP;
+    FILE * rIfP;
+
+    int cols, rows;
+    pixval maxval;
+
+    int lRows, lCols;
+    int lFormat;
+    pixval lMaxval;
+   
+    int rRows, rCols;
+    int rFormat;
+    pixval rMaxval;
+   
+    ppm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    lIfP = pm_openr(cmdline.leftInputFileName);
+    rIfP = pm_openr(cmdline.rghtInputFileName);
+
+    ppm_readppminit(lIfP, &lCols, &lRows, &lMaxval, &lFormat);
+    ppm_readppminit(rIfP, &rCols, &rRows, &rMaxval, &rFormat);
+    
+    if ((lCols != rCols) || (lRows != rRows) || 
+        (lMaxval != rMaxval) || 
+        (PPM_FORMAT_TYPE(lFormat) != PPM_FORMAT_TYPE(rFormat)))
+        pm_error ("Pictures are not of same size and format");
+    
+    cols   = lCols;
+    rows   = lRows;
+    maxval = lMaxval;
+
+    if (abs(cmdline.offset) >= cols)
+        pm_error("Magnitude of -offset (%u columns) is not less than "
+                 "width of images "
+                 "(%u columns)", abs(cmdline.offset), cols);
+   
+    ppm_writeppminit(stdout, cols, rows, maxval, 0);
+
+    write3dRaster(stdout, lIfP, rIfP, cols, rows, maxval,
+                  lFormat, rFormat, cmdline.offset, cmdline.color);
+
+    pm_close(lIfP);
+    pm_close(rIfP);
+    pm_close(stdout);
+
+    return 0;
+}
+
diff --git a/editor/ppmglobe.c b/editor/specialty/ppmglobe.c
index ee1a57c3..82fae5fb 100644
--- a/editor/ppmglobe.c
+++ b/editor/specialty/ppmglobe.c
@@ -15,6 +15,7 @@
 #include <unistd.h>
 #include <math.h>
 
+#include "pm_c_util.h"
 #include "ppm.h"
 #include "colorname.h"
 #include "shhopt.h"
diff --git a/editor/ppmntsc.c b/editor/specialty/ppmntsc.c
index b9f2ac2f..ae3bcfe9 100644
--- a/editor/ppmntsc.c
+++ b/editor/specialty/ppmntsc.c
@@ -45,6 +45,8 @@
 #include <stdio.h>
 #include <math.h>
 #include <string.h>
+
+#include "pm_c_util.h"
 #include "ppm.h"
 #include "mallocvar.h"
 #include "shhopt.h"
diff --git a/editor/ppmrelief.c b/editor/specialty/ppmrelief.c
index 1c408aec..1c408aec 100644
--- a/editor/ppmrelief.c
+++ b/editor/specialty/ppmrelief.c
diff --git a/editor/specialty/ppmshift.c b/editor/specialty/ppmshift.c
new file mode 100644
index 00000000..a765daa5
--- /dev/null
+++ b/editor/specialty/ppmshift.c
@@ -0,0 +1,132 @@
+
+/*********************************************************************/
+/* ppmshift -  shift lines of a picture left or right by x pixels    */
+/* Frank Neumann, October 1993                                       */
+/* V1.1 16.11.1993                                                   */
+/*                                                                   */
+/* version history:                                                  */
+/* V1.0    11.10.1993  first version                                 */
+/* V1.1    16.11.1993  Rewritten to be NetPBM.programming conforming */
+/*********************************************************************/
+
+#include "ppm.h"
+
+/**************************/
+/* start of main function */
+/**************************/
+int
+main(int    argc,
+     char * argv[]) {
+
+    FILE * ifP;
+    unsigned int row;
+    int argn, rows, cols, format;
+    pixel * srcrow;
+    pixel * destrow;
+    pixval maxval;
+    int shift, nowshift;
+    int shiftArg;
+
+    const char * const usage = "shift [ppmfile]\n        shift: maximum number of pixels to shift a line by\n";
+
+    /* parse in 'default' parameters */
+    ppm_init(&argc, argv);
+
+    argn = 1;
+
+    /* parse in shift number */
+    if (argn == argc)
+        pm_usage(usage);
+    if (sscanf(argv[argn], "%d", &shiftArg) != 1)
+        pm_usage(usage);
+    if (shiftArg < 0)
+        pm_error("shift factor must be 0 or more");
+    ++argn;
+
+    /* parse in filename (if present, stdin otherwise) */
+    if (argn != argc)
+    {
+        ifP = pm_openr(argv[argn]);
+        ++argn;
+    }
+    else
+        ifP = stdin;
+
+    if (argn != argc)
+        pm_usage(usage);
+
+    /* read first data from file */
+    ppm_readppminit(ifP, &cols, &rows, &maxval, &format);
+
+    if (shiftArg > cols) {
+        shift = cols;
+        pm_message("shift amount is larger than picture width - reset to %d",
+                   shift);
+    } else
+        shift = shiftArg;
+
+    srcrow = ppm_allocrow(cols);
+
+    destrow = ppm_allocrow(cols);
+
+    ppm_writeppminit(stdout, cols, rows, maxval, 0);
+
+    srand(pm_randseed());
+
+    /** now do the shifting **/
+    /* the range by which a line is shifted lays in the range from */
+    /* -shift/2 .. +shift/2 pixels; however, within this range it is */
+    /* randomly chosen */
+    for (row = 0; row < rows; ++row) {
+        pixel * pP;
+        pixel * pP2;
+
+        if (shift != 0)
+            nowshift = (rand() % (shift+1)) - ((shift+1) / 2);
+        else
+            nowshift = 0;
+
+        ppm_readppmrow(ifP, srcrow, cols, maxval, format);
+
+        pP  = &srcrow[0];
+        pP2 = &destrow[0];
+
+        /* if the shift value is less than zero, we take the original
+           pixel line and copy it into the destination line translated
+           to the left by x pixels. The empty pixels on the right end
+           of the destination line are filled up with the pixel that
+           is the right-most in the original pixel line.
+        */
+        if (nowshift < 0) {
+            unsigned int col;
+            pP += abs(nowshift);
+            for (col = 0; col < cols; ++col) {
+                PPM_ASSIGN(*pP2, PPM_GETR(*pP), PPM_GETG(*pP), PPM_GETB(*pP));
+                ++pP2;
+                if (col < (cols + nowshift) - 1)
+                    ++pP;
+            }
+        } else {
+            unsigned int col;
+            /* The shift value is 0 or positive, so fill the first
+               <nowshift> pixels of the destination line with the
+               first pixel from the source line, and copy the rest of
+               the source line to the dest line
+            */
+            for (col = 0; col < cols; ++col) {
+                PPM_ASSIGN(*pP2, PPM_GETR(*pP), PPM_GETG(*pP), PPM_GETB(*pP));
+                ++pP2;
+                if (col >= nowshift)
+                    ++pP;
+            }
+        }
+
+        ppm_writeppmrow(stdout, destrow, cols, maxval, 0);
+    }
+
+    pm_close(ifP);
+    ppm_freerow(srcrow);
+    ppm_freerow(destrow);
+
+    return 0;
+}
diff --git a/editor/specialty/ppmspread.c b/editor/specialty/ppmspread.c
new file mode 100644
index 00000000..6753f4fe
--- /dev/null
+++ b/editor/specialty/ppmspread.c
@@ -0,0 +1,117 @@
+/*********************************************************************/
+/* ppmspread -  randomly displace a PPM's pixels by a certain amount */
+/* Frank Neumann, October 1993                                       */
+/* V1.1 16.11.1993                                                   */
+/*                                                                   */
+/* version history:                                                  */
+/* V1.0 12.10.1993    first version                                  */
+/* V1.1 16.11.1993    Rewritten to be NetPBM.programming conforming  */
+/*********************************************************************/
+
+#include <string.h>
+
+#include "ppm.h"
+
+
+
+int
+main(int    argc,
+     char * argv[]) {
+
+    FILE * ifP;
+    int argn, rows, cols;
+    unsigned int row;
+    pixel ** destarray, ** srcarray;
+    pixel * pP;
+    pixel * pP2;
+    pixval maxval;
+    pixval r1, g1, b1;
+    int amount;
+    const char * const usage = "amount [ppmfile]\n        amount: # of pixels to displace a pixel by at most\n";
+
+    /* parse in 'default' parameters */
+    ppm_init(&argc, argv);
+
+    argn = 1;
+
+    /* parse in amount & seed */
+    if (argn == argc)
+        pm_usage(usage);
+    if (sscanf(argv[argn], "%d", &amount) != 1)
+        pm_usage(usage);
+    if (amount < 0)
+        pm_error("amount should be a positive number");
+    ++argn;
+
+    /* parse in filename (if present, stdin otherwise) */
+    if (argn != argc)
+    {
+        ifP = pm_openr(argv[argn]);
+        ++argn;
+    }
+    else
+        ifP = stdin;
+
+    if (argn != argc)
+        pm_usage(usage);
+
+    srcarray = ppm_readppm(ifP, &cols, &rows, &maxval);
+
+    destarray = ppm_allocarray(cols, rows);
+
+    /* clear out the buffer */
+    for (row = 0; row < rows; ++row)
+        memset(destarray[row], 0, cols * sizeof(pixel));
+
+    srand(pm_randseed());
+
+    /* start displacing pixels */
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+        pP = &srcarray[row][0];
+
+        for (col = 0; col < cols; ++col) {
+            int const xdis = (rand() % (amount+1)) - ((amount+1) / 2);
+            int const ydis = (rand() % (amount+1)) - ((amount+1) / 2);
+
+            int const xnew = col + xdis;
+            int const ynew = row + ydis;
+
+            /* only set the displaced pixel if it's within the bounds
+               of the image
+            */
+            if (xnew >= 0 && xnew < cols && ynew >= 0 && ynew < rows) {
+                /* displacing a pixel is accomplished by swapping it
+                   with another pixel in its vicinity - so, first
+                   store other pixel's RGB
+                */
+                pP2 = &srcarray[ynew][xnew];
+                r1 = PPM_GETR(*pP2);
+                g1 = PPM_GETG(*pP2);
+                b1 = PPM_GETB(*pP2);
+                /* set second pixel to new value */
+                pP2 = &destarray[ynew][xnew];
+                PPM_ASSIGN(*pP2, PPM_GETR(*pP), PPM_GETG(*pP), PPM_GETB(*pP));
+                
+                /* now, set first pixel to (old) value of second */
+                pP2 = &destarray[row][col];
+                PPM_ASSIGN(*pP2, r1, g1, b1);
+            } else {
+                /* displaced pixel is out of bounds; leave the old
+                   pixel there
+                */
+                pP2 = &destarray[row][col];
+                PPM_ASSIGN(*pP2, PPM_GETR(*pP), PPM_GETG(*pP), PPM_GETB(*pP));
+            }
+            ++pP;
+        }
+    }
+
+    ppm_writeppm(stdout, destarray, cols, rows, maxval, 0);
+
+    pm_close(ifP);
+    ppm_freearray(srcarray, rows);
+    ppm_freearray(destarray, rows);
+
+    return 0;
+}
diff --git a/editor/ppmtv.c b/editor/specialty/ppmtv.c
index da25102a..da25102a 100644
--- a/editor/ppmtv.c
+++ b/editor/specialty/ppmtv.c
diff --git a/generator/Makefile b/generator/Makefile
index 52de9b10..3c30cdd0 100644
--- a/generator/Makefile
+++ b/generator/Makefile
@@ -5,7 +5,7 @@ endif
 SUBDIR = generator
 VPATH=.:$(SRCDIR)/$(SUBDIR)
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
 # 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.
@@ -37,4 +37,4 @@ MERGE_OBJECTS = $(MERGEBINARIES:%=%.o2)
 .PHONY: all
 all: $(BINARIES)
 
-include $(SRCDIR)/Makefile.common
+include $(SRCDIR)/common.mk
diff --git a/generator/pamgradient.c b/generator/pamgradient.c
index 08db86e3..aa559d27 100644
--- a/generator/pamgradient.c
+++ b/generator/pamgradient.c
@@ -1,5 +1,6 @@
 #include <string.h>
 
+#include "pm_c_util.h"
 #include "pam.h"
 #include "shhopt.h"
 #include "mallocvar.h"
@@ -11,7 +12,6 @@ struct cmdlineInfo {
     tuple colorTopRight;
     tuple colorBottomLeft;
     tuple colorBottomRight;
-    unsigned depth;
     unsigned int cols;
     unsigned int rows;
     unsigned int maxval;
@@ -121,8 +121,7 @@ interpolate(struct pam * const pamP,
 
 
 static int
-isgray(struct pam * const pamP,
-       tuple        const color) {
+isgray(tuple const color) {
 
     return (color[PAM_RED_PLANE] == color[PAM_GRN_PLANE])
             && (color[PAM_RED_PLANE] == color[PAM_BLU_PLANE]);
@@ -176,10 +175,10 @@ main(int argc, char *argv[]) {
     pam.maxval           = cmdline.maxval;
     pam.bytes_per_sample = pnm_bytespersample(pam.maxval);
     pam.format           = PAM_FORMAT;
-    if (isgray(&pam, cmdline.colorTopLeft)
-            && isgray(&pam, cmdline.colorTopRight)
-            && isgray(&pam, cmdline.colorBottomLeft)
-            && isgray(&pam, cmdline.colorBottomRight)) {
+    if (isgray(cmdline.colorTopLeft)
+            && isgray(cmdline.colorTopRight)
+            && isgray(cmdline.colorBottomLeft)
+            && isgray(cmdline.colorBottomRight)) {
         pam.depth = 1;
         strcpy(pam.tuple_type, PAM_PGM_TUPLETYPE);
     } else {
diff --git a/generator/pamseq.c b/generator/pamseq.c
index 58419d12..98eac1cc 100644
--- a/generator/pamseq.c
+++ b/generator/pamseq.c
@@ -3,6 +3,7 @@
 #include <stdlib.h>
 #include <stdio.h>
 
+#include "pm_c_util.h"
 #include "pam.h"
 #include "shhopt.h"
 
diff --git a/generator/pamstereogram.c b/generator/pamstereogram.c
index 0e6e6cc9..0ce63853 100644
--- a/generator/pamstereogram.c
+++ b/generator/pamstereogram.c
@@ -50,6 +50,7 @@
 #include <assert.h>
 
 #include "pm_config.h"
+#include "pm_c_util.h"
 #include "pam.h"
 #include "shhopt.h"
 #include "mallocvar.h"
@@ -455,12 +456,17 @@ termPatternPixel(outGenerator * const outGenP) {
 static void
 initPatternPixel(outGenerator *     const outGenP,
                  struct cmdlineInfo const cmdline) {
-
+/*----------------------------------------------------------------------------
+   Initialize parts of output generator *outGenP that are based on the
+   supplied pattern file, assuming there is one.
+-----------------------------------------------------------------------------*/
     struct patternPixelState * stateP;
     FILE * patternFileP;
 
     MALLOCVAR_NOFAIL(stateP);
-        
+
+    assert(cmdline.patFilespec);
+    
     patternFileP = pm_openr(cmdline.patFilespec);
 
     stateP->patTuples =
diff --git a/generator/pamstereogram.test b/generator/pamstereogram.test
index 7eb01fff..80f70ee0 100644
--- a/generator/pamstereogram.test
+++ b/generator/pamstereogram.test
@@ -20,7 +20,7 @@ echo Test 10.  Should print 1266273778 293:
 ./pamstereogram -randomseed=1 -makemask ../testgrid.pbm | cksum 
 
 echo Test 11.  Should print 3034751595 1070:
-./pamgauss 100 10 -maxval=10000 -sigma 20 | pamfunc -multiplier=500 | \
+pamgauss 100 10 -maxval=10000 -sigma 20 | pamfunc -multiplier=500 | \
   ./pamstereogram -randomseed=1 -dpi=10 -makemask | cksum
 
 # Grayscale
@@ -28,11 +28,11 @@ echo Test 11.  Should print 3034751595 1070:
 echo Test 20.  Should print 2468969328 289:
 ./pamstereogram -randomseed=1 -grayscale ../testgrid.pbm | cksum 
 echo Test 21.  Should print 1946982115 4068:
-./pamseq 1 100 | pnmtile 200 20 | \
+pamseq 1 100 | pnmtile 200 20 | \
   ./pamstereogram -randomseed=1 -dpi=10 -grayscale | \
   cksum
 echo Test 22.  Should print 2078013430 4068:
-./pamseq 1 100 | pnmtile 200 20 | \
+pamseq 1 100 | pnmtile 200 20 | \
   ./pamstereogram -randomseed=1 -dpi=10 -grayscale -maxval 255 | \
   cksum
 
@@ -41,7 +41,7 @@ echo Test 22.  Should print 2078013430 4068:
 echo Test 30.  Should print 1319392622 731:
 ./pamstereogram -randomseed=1 -color ../testgrid.pbm | cksum 
 echo Test 31.  Should print 389886159 12062:
-./pamseq 1 100 | pnmtile 200 20 | \
+pamseq 1 100 | pnmtile 200 20 | \
   ./pamstereogram -randomseed=1 -dpi=10 -color | \
   cksum
 
diff --git a/generator/pbmmake.c b/generator/pbmmake.c
index afe1dac3..41d80274 100644
--- a/generator/pbmmake.c
+++ b/generator/pbmmake.c
@@ -11,6 +11,7 @@
 **
 */
 
+#include "pm_c_util.h"
 #include "shhopt.h"
 #include "mallocvar.h"
 #include "pbm.h"
@@ -75,16 +76,8 @@ parseCommandLine(int argc, char ** argv,
                  "non-option arguments: width and height in pixels",
                  argc-1);
     else {
-        cmdlineP->width  = atoi(argv[1]);
-        cmdlineP->height = atoi(argv[2]);
-
-        if (cmdlineP->width < 1) 
-            pm_error("Width must be positive.  You specified %d.", 
-                     cmdlineP->width);
-
-        if (cmdlineP->height < 1) 
-            pm_error("Height must be positive.  You specified %d.",
-                     cmdlineP->height);
+        cmdlineP->width  = pm_parse_width(argv[1]);
+        cmdlineP->height = pm_parse_height(argv[2]);
     }
 }
 
diff --git a/generator/pbmtext.c b/generator/pbmtext.c
index d0a6291b..693c3f59 100644
--- a/generator/pbmtext.c
+++ b/generator/pbmtext.c
@@ -14,69 +14,76 @@
 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
 
 #include <string.h>
+#include <limits.h>
+#include <assert.h>
 
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "shhopt.h"
 #include "pbm.h"
 #include "pbmfont.h"
-#include "shhopt.h"
-#include "mallocvar.h"
 
-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 *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 */
+    const char * 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;
 };
 
 
 
 
 static void
-parse_command_line(int argc, char ** argv,
-                   struct cmdline_info *cmdline_p) {
+parseCommandLine(int argc, const char ** argv,
+                 struct cmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that the file spec array we return is stored in the storage that
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
-    optEntry *option_def = malloc(100*sizeof(optEntry));
+    optEntry * option_def;
         /* Instructions to OptParseOptions3 on how to parse our options.
          */
     optStruct3 opt;
 
     unsigned int option_def_index;
 
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
     option_def_index = 0;   /* incremented by OPTENTRY */
-    OPTENT3(0, "font",      OPT_STRING, &cmdline_p->font, NULL,        0);
-    OPTENT3(0, "builtin",   OPT_STRING, &cmdline_p->builtin, NULL,     0);
-    OPTENT3(0, "dump",      OPT_FLAG,   NULL, &cmdline_p->dump,        0);
-    OPTENT3(0, "space",     OPT_FLOAT,  &cmdline_p->space, NULL,       0);
-    OPTENT3(0, "width",     OPT_UINT,   &cmdline_p->width, NULL,       0);
-    OPTENT3(0, "lspace",    OPT_INT,    &cmdline_p->lspace, NULL,      0);
-    OPTENT3(0, "nomargins", OPT_FLAG,   NULL, &cmdline_p->nomargins,   0);
+    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);
 
     /* Set the defaults */
-    cmdline_p->font = NULL;
-    cmdline_p->builtin = NULL;
-    cmdline_p->space = 0.0;
-    cmdline_p->width = 0;
-    cmdline_p->lspace = 0;
+    cmdlineP->font = NULL;
+    cmdlineP->builtin = NULL;
+    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 */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
-        /* Uses and sets argc, argv, and some of *cmdline_p and others. */
+    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (argc-1 == 0)
-        cmdline_p->text = NULL;
+        cmdlineP->text = NULL;
     else {
         char *text;
         int i;
@@ -87,7 +94,7 @@ parse_command_line(int argc, char ** argv,
         text = malloc(totaltextsize);  /* initial allocation */
         text[0] = '\0';
         
-        for (i = 1; i < argc; i++) {
+        for (i = 1; i < argc; ++i) {
             if (i > 1) {
                 totaltextsize += 1;
                 text = realloc(text, totaltextsize);
@@ -101,11 +108,59 @@ parse_command_line(int argc, char ** argv,
                 pm_error("out of memory allocating space for input text");
             strcat(text, argv[i]);
         }
-        cmdline_p->text = text;
+        cmdlineP->text = text;
+    }
+}
+
+
+
+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(struct cmdlineInfo const cmdline,
+            struct font **     const fontPP) {
+
+    struct font * fontP;
+
+    if (cmdline.font)
+        fontP = pbm_loadfont(cmdline.font);
+    else {
+        if (cmdline.builtin)
+            fontP = pbm_defaultfont(cmdline.builtin);
+        else
+            fontP = pbm_defaultfont("bdf");
     }
+
+    if (cmdline.verbose)
+        reportFont(fontP);
+
+    if (cmdline.dump) {
+        pbm_dumpfont(fontP);
+        exit(0);
+    }
+    *fontPP = fontP;
 }
 
 
+
 struct text {
     char **      textArray;  /* malloc'ed */
     unsigned int allocatedLineCount;
@@ -113,6 +168,7 @@ struct text {
 };
 
 
+
 static void
 allocTextArray(struct text * const textP,
                unsigned int  const maxLineCount,
@@ -131,6 +187,7 @@ allocTextArray(struct text * const textP,
 }
 
 
+
 static void
 freeTextArray(struct text const text) {
 
@@ -145,30 +202,72 @@ freeTextArray(struct text const text) {
 
 
 static void
-fix_control_chars(char * const buf, struct font * const fn) {
+fixControlChars(const char *  const input,
+                struct font * const fontP,
+                const char ** const outputP) {
+/*----------------------------------------------------------------------------
+   Return a translation of input[] that can be rendered as glyphs in
+   the font 'fontP'.  Return it as newly malloced *outputP.
 
-    int i;
+   Expand tabs to spaces.
 
-    /* chop off terminating newline */
-    if (strlen(buf) >= 1 && buf[strlen(buf)-1] == '\n')
-        buf[strlen(buf)-1] = '\0';
-    
-    for ( i = 0; buf[i] != '\0'; ++i ) {
-        if ( buf[i] == '\t' ) { 
-            /* Turn tabs into the right number of spaces. */
-            int j, n, l;
-            n = ( i + 8 ) / 8 * 8;
-            l = strlen( buf );
-            for ( j = l; j > i; --j )
-                buf[j + n - i - 1] = buf[j];
-            for ( ; i < n; ++i )
-                buf[i] = ' ';
-            --i;
+   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).
+-----------------------------------------------------------------------------*/
+    /* 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
+       character that there is space in the output buffer for a worst
+       case tab expansion, plus a terminating NUL, reallocating as
+       necessary.  And we originally allocate enough for the entire line
+       assuming no tabs.
+    */
+
+    unsigned int const tabSize = 8;
+
+    unsigned int inCursor, outCursor;
+    char * output;      /* Output buffer.  Malloced */
+    size_t outputSize;  /* Currently allocated size of 'output' */
+
+    outputSize = strlen(input) + 1 + tabSize;
+        /* Leave room for one worst case tab expansion and NUL terminator */
+    MALLOCARRAY(output, outputSize);
+
+    if (output == NULL)
+        pm_error("Couldn't allocate %u bytes for a line of text.", outputSize);
+
+    for (inCursor = 0, outCursor = 0; input[inCursor] != '\0'; ++inCursor) {
+        if (outCursor + 1 + tabSize > outputSize) {
+            outputSize = outCursor + 1 + 4 * tabSize;
+            REALLOCARRAY(output, outputSize);
+            if (output == NULL)
+                pm_error("Couldn't allocate %u bytes for a line of text.",
+                         outputSize);
         }
-        else if ( !fn->glyph[(unsigned char)buf[i]] )
-            /* Turn unknown chars into a single space. */
-            buf[i] = ' ';
+        if (input[inCursor] == '\n' && input[inCursor+1] == '\0') {
+            /* This is a terminating newline.  We don't do those. */
+        } else if (input[inCursor] == '\t') { 
+            /* Expand this tab into the right number of spaces. */
+            unsigned int const nextTabStop =
+                (outCursor + tabSize) / tabSize * tabSize;
+
+            while (outCursor < nextTabStop)
+                output[outCursor++] = ' ';
+        } else if (!fontP->glyph[(unsigned char)input[inCursor]]) {
+            /* Turn this unknown char into a single space. */
+            output[outCursor++] = ' ';
+        } else
+            output[outCursor++] = input[inCursor];
+
+        assert(outCursor <= outputSize);
     }
+    output[outCursor++] = '\0';
+
+    assert(outCursor <= outputSize);
+
+    *outputP = output;
 }
 
 
@@ -195,35 +294,34 @@ fill_rect(bit** const bits,
 static void
 get_line_dimensions(const char line[], const struct font * const font_p, 
                     const float intercharacter_space,
-                    int * const bwid_p, int * const backup_space_needed_p) {
+                    double * const bwidP, int * const backup_space_needed_p) {
 /*----------------------------------------------------------------------------
    Determine the width in pixels of the line of text line[] in the font
-   *font_p, and return it as *bwid_p.  Also determine how much of this
+   *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.
 -----------------------------------------------------------------------------*/
     int cursor;  /* cursor into the line of text */
-    float accumulated_ics;
+    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.
-           */
-
-    int no_chars_yet; 
-        /* logical: we haven't seen any renderable characters yet in 
-           the line.
+           we have more than one pixel.  Note that this can be negative
+           (which means were crowding, rather than spreading, text).
         */
+    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'.
         */
 
     no_chars_yet = TRUE;   /* initial value */
-    *bwid_p = 0;  /* initial value */
-    accumulated_ics = 0.0;  /* 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]];
@@ -235,18 +333,23 @@ get_line_dimensions(const char line[], const struct font * const font_p,
                     *backup_space_needed_p = -glyphP->x;
                 else {
                     *backup_space_needed_p = 0;
-                    *bwid_p += glyphP->x;
+                    bwid += glyphP->x;
                 }
             } else {
                 /* handle extra intercharacter space (-space option) */
-                int full_pixels;  /* integer part of accumulated_ics */
-                accumulated_ics += intercharacter_space;
-                full_pixels = (int) accumulated_ics;
-                *bwid_p += full_pixels;
-                accumulated_ics -= full_pixels;
+                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_p += glyphP->xadd;
+            bwid += glyphP->xadd;
         }
     }
     if (no_chars_yet)
@@ -258,9 +361,13 @@ get_line_dimensions(const char line[], const struct font * const font_p,
            right at the right edge of the glyph (no extra space to
            anticipate another character).
         */
-        *bwid_p -= lastGlyphP->xadd;
-        *bwid_p += lastGlyphP->width + lastGlyphP->x;
+        bwid -= lastGlyphP->xadd;
+        bwid += lastGlyphP->width + lastGlyphP->x;
     }
+    if (bwid > INT_MAX)
+        pm_error("Image width too large.");
+    else
+        *bwidP = bwid; 
 }
 
 
@@ -377,7 +484,8 @@ struct outputTextCursor {
            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.  
+           pixel.  Note that this is negative if we're crowding, rather
+           than spreading, characters.
         */
 };
 
@@ -442,11 +550,12 @@ placeCharacterInOutput(char                      const lastch,
                     cursorP->widthSoFar += fontP->glyph[glyphIndex]->x;
             } else {
                 /* handle extra intercharacter space (-space option) */
-                int fullPixels;  /* integer part of accumulatedIcs */
                 cursorP->accumulatedIcs += cursorP->intercharacterSpace;
-                fullPixels = (int) cursorP->accumulatedIcs;
-                cursorP->widthSoFar     += fullPixels;
-                cursorP->accumulatedIcs -= fullPixels;
+                {
+                    int const fullPixels = (int)cursorP->accumulatedIcs;
+                    cursorP->widthSoFar     += fullPixels;
+                    cursorP->accumulatedIcs -= fullPixels;
+                }
             }
             cursorP->widthSoFar += fontP->glyph[glyphIndex]->xadd;
         }
@@ -522,8 +631,9 @@ truncateText(struct text   const inputText,
         /* 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.
-           */
+           we have more than one pixel.  Note that this is negative if we're
+           crowding, not spreading, characters.
+        */
 
         int noCharsYet; 
         /* logical: we haven't seen any renderable characters yet in 
@@ -546,11 +656,12 @@ truncateText(struct text   const inputText,
                         widthSoFar += fontP->glyph[lastch]->x;
                 } else {
                     /* handle extra intercharacter space (-space option) */
-                    int fullPixels;  /* integer part of accumulatedIcs */
                     accumulatedIcs += intercharacterSpace;
-                    fullPixels = (int) intercharacterSpace;
-                    widthSoFar     += fullPixels;
-                    accumulatedIcs -= fullPixels;
+                    {
+                        int const fullPixels = (int) intercharacterSpace;
+                        widthSoFar     += fullPixels;
+                        accumulatedIcs -= fullPixels;
+                    }
                 }
                 widthSoFar += fontP->glyph[lastch]->xadd;
             }
@@ -569,16 +680,17 @@ truncateText(struct text   const inputText,
 
 static void
 getText(const char          cmdline_text[], 
-        struct font * const fn, 
+        struct font * const fontP,
         struct text * const input_textP) {
 
     struct text input_text;
 
     if (cmdline_text) {
-        allocTextArray(&input_text, 1, strlen(cmdline_text)*8);
-        strcpy(input_text.textArray[0], cmdline_text);
-        fix_control_chars(input_text.textArray[0], fn);
+        MALLOCARRAY_NOFAIL(input_text.textArray, 1);
+        input_text.allocatedLineCount = 1;
         input_text.lineCount = 1;
+        fixControlChars(cmdline_text, fontP,
+                        (const char**)&input_text.textArray[0]);
     } else {
         /* Read text from stdin. */
 
@@ -595,18 +707,16 @@ getText(const char          cmdline_text[],
         
         lineCount = 0;  /* initial value */
         while (fgets(buf, sizeof(buf), stdin) != NULL) {
-            if (strlen(buf)*8 + 1 >= sizeof(buf))
+            if (strlen(buf) + 1 >= sizeof(buf))
                 pm_error("A line of input text is longer than %u characters."
-                         "Cannot process.", (sizeof(buf)-1)/8);
-            fix_control_chars(buf, fn);
+                         "Cannot process.", sizeof(buf)-1);
             if (lineCount >= maxlines) {
                 maxlines *= 2;
-                text_array = (char**) realloc((char*) text_array, 
-                                              maxlines * sizeof(char*));
+                REALLOCARRAY(text_array, maxlines);
                 if (text_array == NULL)
                     pm_error("out of memory");
             }
-            text_array[lineCount] = strdup(buf);
+            fixControlChars(buf, fontP, (const char **)&text_array[lineCount]);
             if (text_array[lineCount] == NULL)
                 pm_error("out of memory");
             ++lineCount;
@@ -621,62 +731,97 @@ getText(const char          cmdline_text[],
 
 
 static void
-compute_image_width(struct text         const lp, 
-                    const struct font * const fn,
-                    float               const intercharacter_space,
-                    int *               const maxwidthP, 
-                    int *               const maxleftbP) {
-    int line;
-    
-    *maxwidthP = 0;  /* initial value */
-    *maxleftbP = 0;  /* initial value */
-    for (line = 0; line < lp.lineCount; ++line) {
-        int bwid, backup_space_needed;
-        
-        get_line_dimensions(lp.textArray[line], fn, intercharacter_space,
-                            &bwid, &backup_space_needed);
+computeImageHeight(struct text         const formattedText, 
+                   const struct font * 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);     
+    else {
+        double const rowsD = 2 * (double) vmargin + 
+            (double) formattedText.lineCount * fontP->maxheight + 
+            (double) (formattedText.lineCount-1) * interlineSpace;
         
-        *maxwidthP = MAX(*maxwidthP, bwid);
-        *maxleftbP = MAX(*maxleftbP, backup_space_needed);
+        if (rowsD > INT_MAX-10)
+            pm_error("Image height too large.");
+        else
+            *rowsP = (unsigned int) rowsD;
+    }
+}
+
+
+
+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) {
+
+    if (intercharacterSpace < 0 && fontP->maxwidth < -intercharacterSpace)
+        pm_error("-space value (%f) negative; 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;
+        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);
+        }
+        colsD = 2 * (double) hmargin + (double) maxwidth;
+    
+        if (colsD > INT_MAX-10)
+            pm_error("Image width too large.");
+        else
+            *colsP = (unsigned int) colsD;
+    
+        *maxleftbP = maxleftb;
     }
 }
 
 
 
 int
-main(int argc, char *argv[]) {
+main(int argc, const char *argv[]) {
 
-    struct cmdline_info cmdline;
-    bit** bits;
-    int rows, cols;
-    struct font* fontP;
-    int vmargin, hmargin;
+    struct cmdlineInfo cmdline;
+    bit ** bits;
+    unsigned int rows, cols;
+    struct font * fontP;
+    unsigned int vmargin, hmargin;
     struct text inputText;
     struct text formattedText;
-    int maxwidth;
-        /* width in pixels of the longest line of text in the image */
     int maxleftb;
 
-    pbm_init( &argc, argv );
+    pm_proginit(&argc, argv);
 
-    parse_command_line(argc, argv, &cmdline);
+    parseCommandLine(argc, argv, &cmdline);
     
-    if (cmdline.font)
-        fontP = pbm_loadfont(cmdline.font);
-    else {
-        if (cmdline.builtin)
-            fontP = pbm_defaultfont(cmdline.builtin);
-        else
-            fontP = pbm_defaultfont("bdf");
-    }
-
-    if (cmdline.dump) {
-        pbm_dumpfont(fontP);
-        exit(0);
-    }
+    computeFont(cmdline, &fontP);
 
     getText(cmdline.text, fontP, &inputText);
-    
+       
     if (cmdline.nomargins) {
         vmargin = 0;
         hmargin = 0;
@@ -689,8 +834,11 @@ main(int argc, char *argv[]) {
             hmargin = 2 * fontP->maxwidth;
         }
     }
-
+    
     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,
@@ -701,19 +849,18 @@ main(int argc, char *argv[]) {
         freeTextArray(inputText);
     } else
         formattedText = inputText;
+        
+    if (formattedText.lineCount == 0)
+        pm_error("No input text.");
     
-    rows = 2 * vmargin + 
-        formattedText.lineCount * fontP->maxheight + 
-        (formattedText.lineCount-1) * cmdline.lspace;
-
-    compute_image_width(formattedText, fontP, cmdline.space,
-                        &maxwidth, &maxleftb);
+    computeImageHeight(formattedText, fontP, cmdline.lspace, vmargin,
+                       &rows);
 
-    cols = 2 * hmargin + maxwidth;
+    computeImageWidth(formattedText, fontP, cmdline.space, hmargin,
+                      &cols, &maxleftb);
 
     if (cols == 0 || rows == 0)
-        pm_error("Input is all whitespace and/or non-renderable characters.  "
-                 "No output.");
+        pm_error("Input is all whitespace and/or non-renderable characters.");
 
     bits = pbm_allocarray(cols, rows);
 
@@ -724,9 +871,10 @@ main(int argc, char *argv[]) {
     insert_characters(bits, formattedText, fontP, vmargin, hmargin + maxleftb, 
                       cmdline.space, cmdline.lspace);
 
-    /* All done. */
     pbm_writepbm(stdout, bits, cols, rows, 0);
 
+    pbm_freearray(bits, rows);
+
     freeTextArray(formattedText);
     pm_close(stdout);
 
diff --git a/generator/pbmtextps.c b/generator/pbmtextps.c
index 08c77564..f879fa88 100644
--- a/generator/pbmtextps.c
+++ b/generator/pbmtextps.c
@@ -17,37 +17,27 @@
  */
 #define _XOPEN_SOURCE   /* Make sure popen() is in stdio.h */
 #define _BSD_SOURCE     /* Make sure stdrup() is in string.h */
+#include <unistd.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
-#include "pbm.h"
+
+#include "pm_c_util.h"
+#include "mallocvar.h"
 #include "nstring.h"
 #include "shhopt.h"
+#include "pbm.h"
 
 
 #define BUFFER_SIZE 2048
 
-static const char *gs_exe_path = 
-#ifdef GHOSTSCRIPT_EXECUTABLE_PATH
-GHOSTSCRIPT_EXECUTABLE_PATH;
-#else
-0;
-#endif
-
-static const char *pnmcrop_exe_path = 
-#ifdef PNMCROP_EXECUTABLE_PATH
-PNMCROP_EXECUTABLE_PATH;
-#else
-0;
-#endif
-
 struct cmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    int          res;         /* resolution, DPI */
-    int          fontsize;    /* Size of font in points */
+    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) */
@@ -58,26 +48,85 @@ struct cmdlineInfo {
 
 
 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
+buildTextFromArgs(int           const argc,
+                  char **       const argv,
+                  const char ** const textP) {
+
+    char * text;
+    unsigned int totalTextSize;
+    unsigned int i;
+
+    text = strdup("");
+    totalTextSize = 1;
+
+    for (i = 1; i < argc; ++i) {
+        if (i > 1) {
+            totalTextSize += 1;
+            text = realloc(text, totalTextSize);
+            if (text == NULL)
+                pm_error("out of memory");
+            strcat(text, " ");
+        } 
+        totalTextSize += strlen(argv[i]);
+        text = realloc(text, totalTextSize);
+        if (text == NULL)
+            pm_error("out of memory");
+        strcat(text, argv[i]);
+    }
+    *textP = text;
+}
+
+
+
+static void
 parseCommandLine(int argc, char ** argv,
                  struct cmdlineInfo *cmdlineP) {
 /*---------------------------------------------------------------------------
   Note that the file spec array we return is stored in the storage that
   was passed to us as the argv array.
 ---------------------------------------------------------------------------*/
-    optEntry *option_def = malloc(100*sizeof(optEntry));
+    optEntry * option_def;
     /* Instructions to OptParseOptions2 on how to parse our options.
    */
     optStruct3 opt;
 
     unsigned int option_def_index;
-    int i;
-    char * text;
-    int totaltextsize = 0;
+
+    MALLOCARRAY(option_def, 100);
 
     option_def_index = 0;   /* incremented by OPTENTRY */
-    OPTENT3(0, "resolution", OPT_INT,    &cmdlineP->res,            NULL,  0);
+    OPTENT3(0, "resolution", OPT_UINT,   &cmdlineP->res,            NULL,  0);
     OPTENT3(0, "font",       OPT_STRING, &cmdlineP->font,           NULL,  0);
-    OPTENT3(0, "fontsize",   OPT_INT,    &cmdlineP->fontsize,       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);
 
@@ -93,48 +142,43 @@ parseCommandLine(int argc, char ** argv,
 
     optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
 
-    text = strdup("");
-    totaltextsize = 1;
-
-    for (i = 1; i < argc; i++) {
-        if (i > 1) {
-            totaltextsize += 1;
-            text = realloc(text, totaltextsize);
-            if (text == NULL)
-                pm_error("out of memory");
-            strcat(text, " ");
-        } 
-        totaltextsize += strlen(argv[i]);
-        text = realloc(text, totaltextsize);
-        if (text == NULL)
-            pm_error("out of memory");
-        strcat(text, argv[i]);
-    }
-    cmdlineP->text = text;
+    buildTextFromArgs(argc, argv, &cmdlineP->text);
 }
 
 
 
 static const char *
-construct_postscript(struct cmdlineInfo const cmdl) {
+construct_postscript(struct cmdlineInfo const cmdline) {
 
     const char * retval;
     const char * template;
 
-    if (cmdl.stroke <= 0) 
-        template = "/%s findfont\n%d scalefont\nsetfont\n12 36 moveto\n"
-            "(%s) show\nshowpage\n";
+    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\nsetfont\n12 36 moveto\n"
-            "%f setlinewidth\n0 setgray\n"
-            "(%s) true charpath\nstroke\nshowpage\n";
-
-    if (cmdl.stroke < 0)
-        asprintfN(&retval, template, cmdl.font, cmdl.fontsize, 
-                  cmdl.text);
+        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)
+        asprintfN(&retval, template, cmdline.font, cmdline.fontsize, 
+                  cmdline.text);
     else
-        asprintfN(&retval, template, cmdl.font, cmdl.fontsize, 
-                  cmdl.stroke, cmdl.text);
+        asprintfN(&retval, template, cmdline.font, cmdline.fontsize, 
+                  cmdline.stroke, cmdline.text);
 
     return retval;
 }
@@ -142,24 +186,27 @@ construct_postscript(struct cmdlineInfo const cmdl) {
 
 
 static const char *
-gs_executable_name()
-{
+gsExecutableName() {
+
+    const char * const which = "which gs";
+
     static char buffer[BUFFER_SIZE];
-    if(! gs_exe_path) {
-        const char * const which = "which gs";
-        FILE *f;
-        memset(buffer, 0, BUFFER_SIZE);
-        if(!(f = popen(which, "r")))
-            pm_error("Can't find ghostscript");
-        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");
-    }
-    else
-        strcpy(buffer, gs_exe_path);
+
+    FILE * f;
+
+    memset(buffer, 0, BUFFER_SIZE);
+
+    f = popen(which, "r");
+    if (!f)
+        pm_error("Can't find ghostscript");
+
+    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");
 
     return buffer;
 }
@@ -167,30 +214,33 @@ gs_executable_name()
 
 
 static const char *
-crop_executable_name()
-{
+cropExecutableName(void) {
+
+    const char * const which = "which pnmcrop";
+
     static char buffer[BUFFER_SIZE];
-    if(! pnmcrop_exe_path) {
-        const char * const which = "which pnmcrop";
-        FILE *f;
-        memset(buffer, 0, BUFFER_SIZE);
-        if(!(f = popen(which, "r"))) {
-            return 0;
-        }
-    
+    const char * retval;
+
+    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')
+        if (buffer[strlen(buffer) - 1] == '\n')
             buffer[strlen(buffer) - 1] = 0;
         pclose(f);
-        if(buffer[0] != '/' && buffer[0] != '.') {
-            buffer[0] = 0;
+            
+        if (buffer[0] != '/' && buffer[0] != '.') {
+            retval = NULL;
             pm_message("Can't find pnmcrop");
-        }
+        } else
+            retval = buffer;
     }
-    else
-        strcpy(buffer, pnmcrop_exe_path);
-
-    return buffer;
+    return retval;
 }
 
 
@@ -201,12 +251,33 @@ gsCommand(const char *       const psFname,
           struct cmdlineInfo const cmdline) {
 
     const char * retval;
-    int const x = cmdline.res * 11;
-    int const y = cmdline.res * (cmdline.fontsize * 2 + 72)  / 72.;
+    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);
+         
     asprintfN(&retval, "%s -g%dx%d -r%d -sDEVICE=pbm "
               "-sOutputFile=%s -q -dBATCH -dNOPAUSE %s </dev/null >/dev/null", 
-              gs_executable_name(), x, y, cmdline.res, 
+              gsExecutableName(), (int) x, (int) y, cmdline.res, 
               outputFilename, psFname);
+
     return retval;
 }
 
@@ -216,11 +287,12 @@ static const char *
 cropCommand(const char * const inputFileName) {
 
     const char * retval;
+    const char * plainOpt = pm_plain_output ? "-plain" : "" ;
     
-    if (crop_executable_name()) {
-        asprintfN(&retval, "%s -top -right %s", 
-                  crop_executable_name(), inputFileName);
-        if (retval == NULL)
+    if (cropExecutableName()) {
+        asprintfN(&retval, "%s -top -right %s %s", 
+                  cropExecutableName(), plainOpt, inputFileName);
+        if (retval == strsol)
             pm_error("Unable to allocate memory");
     } else
         retval = NULL;
@@ -234,7 +306,7 @@ static void
 writeProgram(const char *       const psFname,
              struct cmdlineInfo const cmdline) {
 
-    const char *ps;
+    const char * ps;
     FILE * psfile;
 
     psfile = fopen(psFname, "w");
@@ -288,14 +360,13 @@ cropToStdout(const char * const inputFileName,
     const char * com;
 
     com = cropCommand(inputFileName);
+
     if (com == NULL) {
         /* No pnmcrop.  So don't crop. */
         pm_message("Can't find pnmcrop command, image will be large");
-        asprintfN(&com, "cat %s", inputFileName);
-        if (com == NULL) 
-            pm_error("Unable to allocate memory.");
+        writeFileToStdout(inputFileName);
     } else {
-        FILE *pnmcrop;
+        FILE * pnmcrop;
 
         if (verbose)
             pm_message("Running crop command '%s'", com);
@@ -326,8 +397,8 @@ cropToStdout(const char * const inputFileName,
             }
             fclose(pnmcrop);
         }
+        strfree(com);
     }
-    strfree(com);
 }
 
 
diff --git a/generator/pbmupc.c b/generator/pbmupc.c
index 5f694a00..6ef75654 100644
--- a/generator/pbmupc.c
+++ b/generator/pbmupc.c
@@ -433,17 +433,8 @@ putdigit( d, bits, row0, col0 )
 	    bits[row0 + row][col0 + col] = digits[d][row][col];
     }
 
-#if __STDC__
 static int
 addlines( int d, bit** bits, int row0, int col0, int height, bit color )
-#else /*__STDC__*/
-static int
-addlines( d, bits, row0, col0, height, color )
-    int d;
-    bit** bits;
-    int row0, col0, height;
-    bit color;
-#endif /*__STDC__*/
     {
     switch ( d )
 	{
@@ -524,16 +515,8 @@ addlines( d, bits, row0, col0, height, color )
     return col0;
     }
 
-#if __STDC__
 static int
 rect( bit** bits, int row0, int col0, int height, int width, bit color )
-#else /*__STDC__*/
-static int
-rect( bits, row0, col0, height, width, color )
-    bit** bits;
-    int row0, col0, height, width;
-    bit color;
-#endif /*__STDC__*/
     {
     int row, col;
 
diff --git a/generator/pgmcrater.c b/generator/pgmcrater.c
index a48f3de1..ec592381 100644
--- a/generator/pgmcrater.c
+++ b/generator/pgmcrater.c
@@ -53,7 +53,6 @@
 
 #include <assert.h>
 #include <math.h>
-#include <unistd.h>
 
 #include "pm_c_util.h"
 #include "pgm.h"
@@ -107,11 +106,10 @@ static int modulo(int t, int n)
 
 static void initseed()
 {
-    int i;
+    unsigned int i;
 
-    i = time(NULL) ^ getpid();
-    srand(i);
-    for (i = 0; i < 7; i++) 
+    srand(pm_randseed());
+    for (i = 0; i < 7; ++i) 
         V rand();
 }
 
diff --git a/generator/pgmmake.c b/generator/pgmmake.c
index 42d96581..bc7f025c 100644
--- a/generator/pgmmake.c
+++ b/generator/pgmmake.c
@@ -70,14 +70,8 @@ parseCommandLine(int argc, char ** argv,
             pm_error("Gray level must be in the range [0.0, 1.0].  "
                      "You specified %f", grayLevel);
         cmdlineP->grayLevel = ROUNDU(grayLevel * cmdlineP->maxval);
-        cmdlineP->cols = atoi(argv[2]);
-        cmdlineP->rows = atoi(argv[3]);
-        if (cmdlineP->cols <= 0)
-            pm_error("width argument must be a positive number.  You "
-                     "specified '%s'", argv[2]);
-        if (cmdlineP->rows <= 0)
-            pm_error("height argument must be a positive number.  You "
-                     "specified '%s'", argv[3]);
+        cmdlineP->cols = pm_parse_width(argv[2]);
+        cmdlineP->rows = pm_parse_height(argv[3]);
     }
 }
 
diff --git a/generator/pgmnoise.c b/generator/pgmnoise.c
index 708d0cd9..215cbfeb 100644
--- a/generator/pgmnoise.c
+++ b/generator/pgmnoise.c
@@ -1,79 +1,115 @@
+/*********************************************************************
+   pgmnoise -  create a PGM with white noise
+   Frank Neumann, October 1993
+*********************************************************************/
+
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "shhopt.h"
+#include "pgm.h"
 
-/*********************************************************************/
-/* pgmnoise -  create a portable graymap with white noise            */
-/* Frank Neumann, October 1993                                       */
-/* V1.1 16.11.1993                                                   */
-/*                                                                   */
-/* version history:                                                  */
-/* V1.0 12.10.1993  first version                                    */
-/* V1.1 16.11.1993  Rewritten to be NetPBM.programming conforming    */
-/*********************************************************************/
 
-#include <unistd.h>
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    unsigned int width;
+    unsigned int height;
+    unsigned int randomseed;
+    unsigned int randomseedSpec;
+};
+
+
+
+
+static void
+parseCommandLine(int argc, const char ** const argv,
+                 struct cmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   Note that the file spec array we return is stored in the storage that
+   was passed to us as the argv array.
+-----------------------------------------------------------------------------*/
+    optEntry * option_def;
+        /* Instructions to OptParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+    unsigned int option_def_index;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0,   "randomseed",   OPT_INT,    &cmdlineP->randomseed,
+            &cmdlineP->randomseedSpec,      0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
+
+    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (argc-1 != 2)
+        pm_error("Wrong number of arguments: %u.  "
+                 "Arguments are width and height of image, in pixels",
+                 argc-1);
+    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
+            cmdlineP->width = width;
+
+        if (height <= 0)
+            pm_error("Height must be positive, not %d", width);
+        else
+            cmdlineP->height = height;
+    }
+}
 
-#include "pgm.h"
 
-/* global variables */
-#ifdef AMIGA
-static char *version = "$VER: pgmnoise 1.1 (16.11.93)"; /* Amiga version identification */
-#endif
-
-/**************************/
-/* start of main function */
-/**************************/
-int main(argc, argv)
-int argc;
-char *argv[];
-{
-	int argn, rows, cols, i, j;
-	gray *destrow;
-	const char * const usage = "width height\n        width and height are picture dimensions in pixels\n";
-	time_t timenow;
-
-	/* parse in 'default' parameters */
-	pgm_init(&argc, argv);
-
-	argn = 1;
-
-	/* parse in dim factor */
-	if (argn == argc)
-		pm_usage(usage);
-	if (sscanf(argv[argn], "%d", &cols) != 1)
-		pm_usage(usage);
-	argn++;
-	if (argn == argc)
-		pm_usage(usage);
-	if (sscanf(argv[argn], "%d", &rows) != 1)
-		pm_usage(usage);
-
-	if (cols <= 0 || rows <= 0)
-		pm_error("picture dimensions should be positive numbers");
-	++argn;
-
-	if (argn != argc)
-		pm_usage(usage);
-
-	/* no error checking required here, ppmlib does it all for us */
-	destrow = pgm_allocrow(cols);
-
-	pgm_writepgminit(stdout, cols, rows, PGM_MAXMAXVAL, 0);
-
-	/* get time of day to feed the random number generator */
-	timenow = time(NULL);
-	srand(timenow ^ getpid());
-
-	/* create the (gray) noise */
-	for (i = 0; i < rows; i++)
-	{
-		for (j = 0; j < cols; j++)
-			destrow[j] = rand() % (PGM_MAXMAXVAL+1);
-
-		/* write out one line of graphic data */
-		pgm_writepgmrow(stdout, destrow, cols, PGM_MAXMAXVAL, 0);
-	}
-
-	pgm_freerow(destrow);
-
-	exit(0);
+
+
+static void
+pgmnoise(FILE * const ofP,
+         unsigned int const cols,
+         unsigned int const rows,
+         gray         const maxval) {
+
+    unsigned int row;
+    gray * destrow;
+
+    destrow = pgm_allocrow(cols);
+
+    pgm_writepgminit(ofP, cols, rows, maxval, 0);
+
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+        for (col = 0; col < cols; ++col)
+            destrow[col] = rand() % (maxval + 1);
+
+        pgm_writepgmrow(ofP, destrow, cols, maxval, 0);
+    }
+
+    pgm_freerow(destrow);
+}
+
+
+
+int main(int          argc,
+         const char * argv[]) {
+    
+    struct cmdlineInfo cmdline;
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    srand(cmdline.randomseedSpec ? cmdline.randomseed : pm_randseed());
+
+    pgmnoise(stdout, cmdline.width, cmdline.height, PGM_MAXMAXVAL);
+
+    return 0;
 }
 
diff --git a/generator/pgmramp.c b/generator/pgmramp.c
index b84ed345..c0fb42b1 100644
--- a/generator/pgmramp.c
+++ b/generator/pgmramp.c
@@ -11,6 +11,8 @@
 */
 
 #include <math.h>
+
+#include "pm_c_util.h"
 #include "pgm.h"
 #include "shhopt.h"
 
@@ -96,14 +98,8 @@ parseCommandLine(int argc, char ** argv,
         pm_error("Only two arguments allowed: width and height.  "
                  "You specified %d", argc-1);
     else {
-        cmdlineP->cols = atoi(argv[1]);
-        cmdlineP->rows = atoi(argv[2]);
-        if (cmdlineP->cols <= 0)
-            pm_error("width argument must be a positive number.  You "
-                     "specified '%s'", argv[1]);
-        if (cmdlineP->rows <= 0)
-            pm_error("height argument must be a positive number.  You "
-                     "specified '%s'", argv[2]);
+        cmdlineP->cols = pm_parse_width(argv[1]);
+        cmdlineP->rows = pm_parse_height(argv[2]);
     }
 }
 
diff --git a/generator/ppmcolors.c b/generator/ppmcolors.c
index 9112e1b8..ecae2285 100644
--- a/generator/ppmcolors.c
+++ b/generator/ppmcolors.c
@@ -9,6 +9,7 @@
 
 ******************************************************************************/
 
+#include "pm_c_util.h"
 #include "ppm.h"
 #include "shhopt.h"
 #include "nstring.h"
diff --git a/generator/ppmforge.c b/generator/ppmforge.c
index cac56e38..c77076e3 100644
--- a/generator/ppmforge.c
+++ b/generator/ppmforge.c
@@ -35,7 +35,6 @@
 
 #include <math.h>
 #include <assert.h>
-#include <unistd.h>
 
 #include "pm_c_util.h"
 #include "ppm.h"
@@ -283,10 +282,9 @@ static unsigned int
 initseed(void) {
     /*  Generate initial random seed.  */
 
-    int i;
+    unsigned int i;
 
-    i = time(NULL) ^ getpid();
-    srand(i);
+    srand(pm_randseed());
     for (i = 0; i < 7; ++i)
         rand();
     return rand();
diff --git a/generator/ppmmake.c b/generator/ppmmake.c
index eee32485..e59b47d5 100644
--- a/generator/ppmmake.c
+++ b/generator/ppmmake.c
@@ -76,14 +76,8 @@ parseCommandLine(int argc, char ** argv,
                  "You specified %d", argc-1);
     else {
         cmdlineP->color = ppm_parsecolor(argv[1], cmdlineP->maxval);
-        cmdlineP->cols = atoi(argv[2]);
-        cmdlineP->rows = atoi(argv[3]);
-        if (cmdlineP->cols <= 0)
-            pm_error("width argument must be a positive number.  You "
-                     "specified '%s'", argv[2]);
-        if (cmdlineP->rows <= 0)
-            pm_error("height argument must be a positive number.  You "
-                     "specified '%s'", argv[3]);
+        cmdlineP->cols = pm_parse_width(argv[2]);
+        cmdlineP->rows = pm_parse_height(argv[3]);
     }
 }
 
diff --git a/generator/ppmpat.c b/generator/ppmpat.c
index 89585c3e..772fa51d 100644
--- a/generator/ppmpat.c
+++ b/generator/ppmpat.c
@@ -12,17 +12,244 @@
 
 #define _XOPEN_SOURCE  /* get M_PI in math.h */
 
+#include <assert.h>
 #include <math.h>
+#include <limits.h>
 
 #include "pm_c_util.h"
+#include "mallocvar.h"
+#include "shhopt.h"
 #include "ppm.h"
 #include "ppmdraw.h"
 
 
+typedef enum {
+    PAT_GINGHAM2,
+    PAT_GINGHAM3,
+    PAT_MADRAS,
+    PAT_TARTAN,
+    PAT_POLES,
+    PAT_SQUIG,
+    PAT_CAMO,
+    PAT_ANTICAMO
+} pattern;
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    pattern basePattern;
+    unsigned int width;
+    unsigned int height;
+};
+
+
+
+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 option_def_index;
+    unsigned int basePatternCount;
+    unsigned int gingham2;
+    unsigned int gingham3;
+    unsigned int madras;
+    unsigned int tartan;
+    unsigned int poles;
+    unsigned int squig;
+    unsigned int camo;
+    unsigned int anticamo;
+
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0, "gingham2",  OPT_FLAG,   NULL, &gingham2,   0);
+    OPTENT3(0, "g2",        OPT_FLAG,   NULL, &gingham2,   0);
+    OPTENT3(0, "gingham3",  OPT_FLAG,   NULL, &gingham3,   0);
+    OPTENT3(0, "g3",        OPT_FLAG,   NULL, &gingham3,   0);
+    OPTENT3(0, "madras",    OPT_FLAG,   NULL, &madras,     0);
+    OPTENT3(0, "tartan",    OPT_FLAG,   NULL, &tartan,     0);
+    OPTENT3(0, "poles",     OPT_FLAG,   NULL, &poles,      0);
+    OPTENT3(0, "squig",     OPT_FLAG,   NULL, &squig,      0);
+    OPTENT3(0, "camo",      OPT_FLAG,   NULL, &camo,       0);
+    OPTENT3(0, "anticamo",  OPT_FLAG,   NULL, &anticamo,   0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    basePatternCount =
+        gingham2 +
+        gingham3 +
+        madras +
+        tartan +
+        poles +
+        squig +
+        camo +
+        anticamo;
+
+    if (basePatternCount < 1)
+        pm_error("You must specify a base pattern option such as -gingham2");
+    else if (basePatternCount > 1)
+        pm_error("You may not specify more than one base pattern option.  "
+                 "You specified %u", basePatternCount);
+    else {
+        if (gingham2)
+            cmdlineP->basePattern = PAT_GINGHAM2;
+        else if (gingham3)
+            cmdlineP->basePattern = PAT_GINGHAM3;
+        else if (madras)
+            cmdlineP->basePattern = PAT_MADRAS;
+        else if (tartan)
+            cmdlineP->basePattern = PAT_TARTAN;
+        else if (poles)
+            cmdlineP->basePattern = PAT_POLES;
+        else if (squig)
+            cmdlineP->basePattern = PAT_SQUIG;
+        else if (camo)
+            cmdlineP->basePattern = PAT_CAMO;
+        else if (anticamo)
+            cmdlineP->basePattern = PAT_ANTICAMO;
+        else
+            assert(false);  /* Every possibility is accounted for */
+    }
+    if (argc-1 != 2)
+        pm_error("You must specify 2 non-option arguments: width and height "
+                 "in pixels.  You specified %u", argc-1);
+    else {
+        cmdlineP->width  = atoi(argv[1]);
+        cmdlineP->height = atoi(argv[2]);
+
+        if (cmdlineP->width < 1)
+            pm_error("Width must be at least 1 pixel");
+        if (cmdlineP->height < 1)
+            pm_error("Height must be at least 1 pixel");
+    }
+}
+
+
+
+static void
+validateComputableDimensions(unsigned int const cols,
+                             unsigned int const rows) {
+
+    /*
+      Notes on width and height limits:
+
+      cols * 3, rows * 3 appear in madras, tartan
+      cols*rows appears in poles
+      cols+rows appears in squig
+
+      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);
+}
+
 
 
 static pixel
-random_anticamo_color(pixval const maxval) {
+randomColor(pixval const maxval) {
+
+    pixel p;
+
+    PPM_ASSIGN(p,
+               rand() % (maxval + 1),
+               rand() % (maxval + 1),
+               rand() % (maxval + 1)
+        );
+    
+    return p;
+}
+
+
+
+#define DARK_THRESH 0.25
+
+static pixel
+randomBrightColor(pixval const maxval) {
+
+    pixel p;
+
+    do {
+        p = randomColor(maxval);
+    } while (PPM_LUMIN(p) <= maxval * DARK_THRESH);
+
+    return p;
+}
+
+
+
+static pixel
+randomDarkColor(pixval const maxval) {
+
+    pixel p;
+
+    do {
+        p = randomColor(maxval);
+    } while (PPM_LUMIN(p) > maxval * DARK_THRESH);
+
+    return p;
+}
+
+
+
+static pixel
+averageTwoColors(pixel const p1,
+                 pixel const p2) {
+
+    pixel p;
+
+    PPM_ASSIGN(p,
+               (PPM_GETR(p1) + PPM_GETR(p2)) / 2,
+               (PPM_GETG(p1) + PPM_GETG(p2)) / 2,
+               (PPM_GETB(p1) + PPM_GETB(p2)) / 2);
+
+    return p;
+}
+
+
+
+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, 
+                 const void * const clientdata) {
+
+    if (col >= 0 && col < cols && row >= 0 && row < rows)
+        pixels[row][col] =
+            averageTwoColors(pixels[row][col], *((const pixel*) clientdata));
+}
+
+
+
+/*----------------------------------------------------------------------------
+   Camouflage stuff
+-----------------------------------------------------------------------------*/
+
+
+
+static pixel
+randomAnticamoColor(pixval const maxval) {
 
     int v1, v2, v3;
     pixel p;
@@ -70,17 +297,14 @@ random_anticamo_color(pixval const maxval) {
 
 
 
-/* Camouflage stuff. */
-
 static pixel
-random_camo_color(pixval const maxval) {
+randomCamoColor(pixval const maxval) {
 
-    int v1, v2, v3;
-    pixel p;
+    int const v1 = (maxval + 1 ) / 8;
+    int const v2 = (maxval + 1 ) / 4;
+    int const v3 = (maxval + 1 ) / 2;
 
-    v1 = (maxval + 1 ) / 8;
-    v2 = (maxval + 1 ) / 4;
-    v3 = (maxval + 1 ) / 2;
+    pixel p;
 
     switch (rand() % 10) {
     case 0:
@@ -122,6 +346,46 @@ rnduni(void) {
 
 
 
+static void
+clearBackground(pixel **     const pixels,
+                unsigned int const cols,
+                unsigned int const rows,
+                pixval       const maxval,
+                bool         const antiflag) {
+
+    pixel color;
+
+    if (antiflag)
+        color = randomAnticamoColor(maxval);
+    else
+        color = randomCamoColor(maxval);
+
+    ppmd_filledrectangle(
+        pixels, cols, rows, maxval, 0, 0, cols, rows, PPMD_NULLDRAWPROC,
+        &color);
+}
+
+
+static void
+camoFill(pixel **         const pixels,
+         unsigned int     const cols,
+         unsigned int     const rows,
+         pixval           const maxval,
+         struct fillobj * const fh,
+         bool             const antiflag) {
+         
+    pixel color;
+
+    if (antiflag)
+        color = randomAnticamoColor(maxval);
+    else
+        color = randomCamoColor(maxval);
+
+    ppmd_fill(pixels, cols, rows, maxval, fh, PPMD_NULLDRAWPROC, &color);
+}        
+
+
+
 #define BLOBRAD 50
 
 #define MIN_POINTS 7
@@ -136,63 +400,68 @@ rnduni(void) {
 
 
 static void
+computeXsYs(int *        const xs,
+            int *        const ys,
+            unsigned int const cols,
+            unsigned int const rows,
+            unsigned int const pointCt) {
+
+    unsigned int const cx = rand() % cols;
+    unsigned int const cy = rand() % rows;
+    double const a = rnduni() * (MAX_ELLIPSE_FACTOR - MIN_ELLIPSE_FACTOR) +
+        MIN_ELLIPSE_FACTOR;
+    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;
+        double const tx   = a * sin(p * 2.0 * M_PI / pointCt);
+        double const ty   = b * cos(p * 2.0 * M_PI / pointCt);
+        double const tang = atan2(ty, tx) + theta;
+        xs[p] = MAX(0, MIN(cols-1, cx + BLOBRAD * c * sin(tang)));
+        ys[p] = MAX(0, MIN(rows-1, cy + BLOBRAD * c * cos(tang)));
+    }
+}
+
+
+
+static void
 camo(pixel **     const pixels,
      unsigned int const cols,
      unsigned int const rows,
      pixval       const maxval,
      bool         const antiflag) {
 
-    pixel color;
-    int n, i, cx, cy;
-    struct fillobj * fh;
+    unsigned int const n = (rows * cols) / (BLOBRAD * BLOBRAD) * 5;
 
-    /* Clear background. */
-    if (antiflag)
-        color = random_anticamo_color( maxval );
-    else
-        color = random_camo_color( maxval );
+    unsigned int i;
 
-    ppmd_filledrectangle(
-        pixels, cols, rows, maxval, 0, 0, cols, rows, PPMD_NULLDRAWPROC,
-        &color);
+    clearBackground(pixels, cols, rows, maxval, antiflag);
 
-    n = (rows * cols) / (BLOBRAD * BLOBRAD) * 5;
     for (i = 0; i < n; ++i) {
-        int points, p, xs[MAX_POINTS], ys[MAX_POINTS], x0, y0;
-        float a, b, c, theta, tang, tx, ty;
-        
-        cx = rand() % cols;
-        cy = rand() % rows;
-        
-        points = rand() % ( MAX_POINTS - MIN_POINTS + 1 ) + MIN_POINTS;
-        a = rnduni() * (MAX_ELLIPSE_FACTOR - MIN_ELLIPSE_FACTOR) +
-            MIN_ELLIPSE_FACTOR;
-        b = rnduni() * (MAX_ELLIPSE_FACTOR - MIN_ELLIPSE_FACTOR) +
-            MIN_ELLIPSE_FACTOR;
-        theta = rnduni() * 2.0 * M_PI;
-        for (p = 0; p < points; ++p) {
-            tx = a * sin(p * 2.0 * M_PI / points);
-            ty = b * cos(p * 2.0 * M_PI / points);
-            tang = atan2(ty, tx) + theta;
-            c = rnduni() * (MAX_POINT_FACTOR - MIN_POINT_FACTOR) +
-                MIN_POINT_FACTOR;
-            xs[p] = cx + BLOBRAD * c * sin(tang);
-            ys[p] = cy + BLOBRAD * c * cos(tang);
-        }
-        x0 = (xs[0] + xs[points - 1]) / 2;
-        y0 = (ys[0] + ys[points - 1]) / 2;
+        unsigned int const pointCt =
+            rand() % (MAX_POINTS - MIN_POINTS + 1) + MIN_POINTS;
+
+        int xs[MAX_POINTS], ys[MAX_POINTS];
+        int x0, y0;
+        struct fillobj * fh;
+
+        computeXsYs(xs, ys, cols, rows, pointCt);
+
+        x0 = (xs[0] + xs[pointCt - 1]) / 2;
+        y0 = (ys[0] + ys[pointCt - 1]) / 2;
 
         fh = ppmd_fill_create();
 
         ppmd_polyspline(
-            pixels, cols, rows, maxval, x0, y0, points, xs, ys, x0, y0,
-            ppmd_fill_drawproc, fh );
+            pixels, cols, rows, maxval, x0, y0, pointCt, xs, ys, x0, y0,
+            ppmd_fill_drawproc, fh);
         
-        if (antiflag)
-            color = random_anticamo_color(maxval);
-        else
-            color = random_camo_color(maxval);
-        ppmd_fill(pixels, cols, rows, maxval, fh, PPMD_NULLDRAWPROC, &color);
+        camoFill(pixels, cols, rows, maxval, fh, antiflag);
         
         ppmd_fill_destroy(fh);
     }
@@ -200,454 +469,398 @@ camo(pixel **     const pixels,
 
 
 
-static pixel
-random_color(pixval const maxval) {
-
-    pixel p;
-
-    PPM_ASSIGN(p,
-               rand() % (maxval + 1),
-               rand() % (maxval + 1),
-               rand() % (maxval + 1)
-        );
-    
-    return p;
-}
-
-
-
-#define DARK_THRESH 0.25
-
-#if __STDC__
-static pixel
-random_bright_color( pixval maxval )
-#else /*__STDC__*/
-static pixel
-random_bright_color( maxval )
-    pixval maxval;
-#endif /*__STDC__*/
-    {
-    pixel p;
-
-    do
-    {
-    p = random_color( maxval );
-    }
-    while ( PPM_LUMIN( p ) <= maxval * DARK_THRESH );
-
-    return p;
-    }
-
-#if __STDC__
-static pixel
-random_dark_color( pixval maxval )
-#else /*__STDC__*/
-static pixel
-random_dark_color( maxval )
-    pixval maxval;
-#endif /*__STDC__*/
-    {
-    pixel p;
-
-    do
-    {
-    p = random_color( maxval );
-    }
-    while ( PPM_LUMIN( p ) > maxval * DARK_THRESH );
-
-    return p;
-    }
-
-static pixel
-average_two_colors( p1, p2 )
-pixel p1, p2;
-    {
-    pixel p;
-
-    PPM_ASSIGN(
-    p, ( (int) PPM_GETR(p1) + (int) PPM_GETR(p2) ) / 2,
-    ( (int) PPM_GETG(p1) + (int) PPM_GETG(p2) ) / 2,
-    ( (int) PPM_GETB(p1) + (int) PPM_GETB(p2) ) / 2 );
+/*----------------------------------------------------------------------------
+   Gingham stuff
+-----------------------------------------------------------------------------*/
 
-    return p;
-    }
 
-/* Gingham stuff. */
 
 static void
-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 )
-        pixels[row][col] =
-            average_two_colors( pixels[row][col], *( (pixel*) clientdata ) );
-}
+gingham2(pixel **     const pixels,
+         unsigned int const cols,
+         unsigned int const rows,
+         pixval       const maxval) {
 
-static void
-gingham2( pixel** pixels, int cols, int rows, pixval maxval )
-{
-    pixel const backcolor = random_dark_color( maxval );
-    pixel const forecolor = random_bright_color( maxval );
-    int const colso2 = cols / 2;
-    int const rowso2 = rows / 2;
+    pixel const backcolor = randomDarkColor(maxval);
+    pixel const forecolor = randomBrightColor(maxval);
+    unsigned int const colso2 = cols / 2;
+    unsigned int const rowso2 = rows / 2;
 
     /* Warp. */
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 0, colso2, rows, PPMD_NULLDRAWPROC,
-        &backcolor );
+        &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, colso2, 0, cols - colso2, rows,
-        PPMD_NULLDRAWPROC, &forecolor );
+        PPMD_NULLDRAWPROC, &forecolor);
 
     /* Woof. */
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 0, cols, rowso2, average_drawproc,
-        &backcolor );
+        &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, rowso2, cols, rows - rowso2,
-        average_drawproc, &forecolor );
-    }
+        average_drawproc, &forecolor);
+}
+
+
 
-#if __STDC__
-static void
-gingham3( pixel** pixels, int cols, int rows, pixval maxval )
-#else /*__STDC__*/
 static void
-gingham3( pixels, cols, rows, maxval )
-    pixel** pixels;
-    int cols, rows;
-    pixval maxval;
-#endif /*__STDC__*/
-    {
-    int colso4, rowso4;
-    pixel backcolor, fore1color, fore2color;
-
-    colso4 = cols / 4;
-    rowso4 = rows / 4;
-    backcolor = random_dark_color( maxval );
-    fore1color = random_bright_color( maxval );
-    fore2color = random_bright_color( maxval );
+gingham3(pixel **     const pixels,
+         unsigned int const cols,
+         unsigned int const rows,
+         pixval       const 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,
-        &backcolor );
+        &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, colso4, 0, colso4, rows, PPMD_NULLDRAWPROC,
-        &fore1color );
+        &fore1color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 2 * colso4, 0, colso4, rows,
-        PPMD_NULLDRAWPROC, &fore2color );
+        PPMD_NULLDRAWPROC, &fore2color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 3 * colso4, 0, cols - colso4, rows,
-        PPMD_NULLDRAWPROC, &fore1color );
+        PPMD_NULLDRAWPROC, &fore1color);
 
     /* Woof. */
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 0, cols, rowso4, average_drawproc,
-        &backcolor );
+        &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, rowso4, cols, rowso4, average_drawproc,
-        &fore1color );
+        &fore1color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 2 * rowso4, cols, rowso4,
-        average_drawproc, &fore2color );
+        average_drawproc, &fore2color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 3 * rowso4, cols, rows - rowso4,
-        average_drawproc, &fore1color );
-    }
+        average_drawproc, &fore1color);
+}
+
+
 
-#if __STDC__
-static void
-madras( pixel** pixels, int cols, int rows, pixval maxval )
-#else /*__STDC__*/
 static void
-madras( pixels, cols, rows, maxval )
-    pixel** pixels;
-    int cols, rows;
-    pixval maxval;
-#endif /*__STDC__*/
-    {
-    int cols2, rows2, cols3, rows3, cols12, rows12, cols6a, rows6a, cols6b,
-    rows6b;
-    pixel backcolor, fore1color, fore2color;
-
-    cols2 = cols * 2 / 44;
-    rows2 = rows * 2 / 44;
-    cols3 = cols * 3 / 44;
-    rows3 = rows * 3 / 44;
-    cols12 = cols - 10 * cols2 - 4 * cols3;
-    rows12 = rows - 10 * rows2 - 4 * rows3;
-    cols6a = cols12 / 2;
-    rows6a = rows12 / 2;
-    cols6b = cols12 - cols6a;
-    rows6b = rows12 - rows6a;
-    backcolor = random_dark_color( maxval );
-    fore1color = random_bright_color( maxval );
-    fore2color = random_bright_color( maxval );
+madras(pixel **     const pixels,
+       unsigned int const cols,
+       unsigned int const rows,
+       pixval       const maxval) {
+
+    unsigned int const cols2  = cols * 2 / 44;
+    unsigned int const rows2  = rows * 2 / 44;
+    unsigned int const cols3  = cols * 3 / 44;
+    unsigned int const rows3  = rows * 3 / 44;
+    unsigned int const cols12 = cols - 10 * cols2 - 4 * cols3;
+    unsigned int const rows12 = rows - 10 * rows2 - 4 * rows3;
+    unsigned int const cols6a = cols12 / 2;
+    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(
         pixels, cols, rows, maxval, 0, 0, cols2, rows, PPMD_NULLDRAWPROC,
-        &backcolor );
+        &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, cols2, 0, cols3, rows, PPMD_NULLDRAWPROC,
-        &fore1color );
+        &fore1color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, cols2 + cols3, 0, cols2, rows,
-        PPMD_NULLDRAWPROC, &backcolor );
+        PPMD_NULLDRAWPROC, &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 2 * cols2 + cols3, 0, cols2, rows,
-        PPMD_NULLDRAWPROC, &fore2color );
+        PPMD_NULLDRAWPROC, &fore2color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 3 * cols2 + cols3, 0, cols2, rows,
-        PPMD_NULLDRAWPROC, &backcolor );
+        PPMD_NULLDRAWPROC, &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 4 * cols2 + cols3, 0, cols6a, rows,
-        PPMD_NULLDRAWPROC, &fore1color );
+        PPMD_NULLDRAWPROC, &fore1color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 4 * cols2 + cols3 + cols6a, 0, cols2, rows,
-        PPMD_NULLDRAWPROC, &backcolor );
+        PPMD_NULLDRAWPROC, &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 5 * cols2 + cols3 + cols6a, 0, cols3, rows,
-        PPMD_NULLDRAWPROC, &fore2color );
+        PPMD_NULLDRAWPROC, &fore2color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 5 * cols2 + 2 * cols3 + cols6a, 0, cols2,
-        rows, PPMD_NULLDRAWPROC, &backcolor );
+        rows, PPMD_NULLDRAWPROC, &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 6 * cols2 + 2 * cols3 + cols6a, 0, cols3,
-        rows, PPMD_NULLDRAWPROC, &fore2color );
+        rows, PPMD_NULLDRAWPROC, &fore2color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 6 * cols2 + 3 * cols3 + cols6a, 0, cols2,
-        rows, PPMD_NULLDRAWPROC, &backcolor );
+        rows, PPMD_NULLDRAWPROC, &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 7 * cols2 + 3 * cols3 + cols6a, 0, cols6b,
-        rows, PPMD_NULLDRAWPROC, &fore1color );
+        rows, PPMD_NULLDRAWPROC, &fore1color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 7 * cols2 + 3 * cols3 + cols6a + cols6b, 0,
-        cols2, rows, PPMD_NULLDRAWPROC, &backcolor );
+        cols2, rows, PPMD_NULLDRAWPROC, &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 8 * cols2 + 3 * cols3 + cols6a + cols6b, 0,
-        cols2, rows, PPMD_NULLDRAWPROC, &fore2color );
+        cols2, rows, PPMD_NULLDRAWPROC, &fore2color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 9 * cols2 + 3 * cols3 + cols6a + cols6b, 0,
-        cols2, rows, PPMD_NULLDRAWPROC, &backcolor );
+        cols2, rows, PPMD_NULLDRAWPROC, &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 10 * cols2 + 3 * cols3 + cols6a + cols6b, 
-        0, cols3, rows, PPMD_NULLDRAWPROC, &fore1color );
+        0, cols3, rows, PPMD_NULLDRAWPROC, &fore1color);
 
     /* Woof. */
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 0, cols, rows2, average_drawproc,
-        &backcolor );
+        &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, rows2, cols, rows3, average_drawproc,
-        &fore2color );
+        &fore2color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, rows2 + rows3, cols, rows2,
-        average_drawproc, &backcolor );
+        average_drawproc, &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 2 * rows2 + rows3, cols, rows2,
-        average_drawproc, &fore1color );
+        average_drawproc, &fore1color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 3 * rows2 + rows3, cols, rows2,
-        average_drawproc, &backcolor );
+        average_drawproc, &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 4 * rows2 + rows3, cols, rows6a,
-        average_drawproc, &fore2color );
+        average_drawproc, &fore2color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 4 * rows2 + rows3 + rows6a, cols, rows2,
-        average_drawproc, &backcolor );
+        average_drawproc, &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 5 * rows2 + rows3 + rows6a, cols, rows3,
-        average_drawproc, &fore1color );
+        average_drawproc, &fore1color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 5 * rows2 + 2 * rows3 + rows6a, cols,
-        rows2, average_drawproc, &backcolor );
+        rows2, average_drawproc, &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 6 * rows2 + 2 * rows3 + rows6a, cols,
-        rows3, average_drawproc, &fore1color );
+        rows3, average_drawproc, &fore1color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 6 * rows2 + 3 * rows3 + rows6a, cols,
-        rows2, average_drawproc, &backcolor );
+        rows2, average_drawproc, &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 7 * rows2 + 3 * rows3 + rows6a, cols,
-        rows6b, average_drawproc, &fore2color );
+        rows6b, average_drawproc, &fore2color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 7 * rows2 + 3 * rows3 + rows6a + rows6b,
-        cols, rows2, average_drawproc, &backcolor );
+        cols, rows2, average_drawproc, &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 8 * rows2 + 3 * rows3 + rows6a + rows6b,
-        cols, rows2, average_drawproc, &fore1color );
+        cols, rows2, average_drawproc, &fore1color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 9 * rows2 + 3 * rows3 + rows6a + rows6b,
-        cols, rows2, average_drawproc, &backcolor );
+        cols, rows2, average_drawproc, &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 
         10 * rows2 + 3 * rows3 + rows6a + rows6b,
-        cols, rows3, average_drawproc, &fore2color );
-    }
+        cols, rows3, average_drawproc, &fore2color);
+}
+
+
 
-#if __STDC__
-static void
-tartan( pixel** pixels, int cols, int rows, pixval maxval )
-#else /*__STDC__*/
 static void
-tartan( pixels, cols, rows, maxval )
-    pixel** pixels;
-    int cols, rows;
-    pixval maxval;
-#endif /*__STDC__*/
-    {
-    int cols1, rows1, cols3, rows3, cols10, rows10, cols5a, rows5a, cols5b,
-    rows5b;
-    pixel backcolor, fore1color, fore2color;
-
-    cols1 = cols / 22;
-    rows1 = rows / 22;
-    cols3 = cols * 3 / 22;
-    rows3 = rows * 3 / 22;
-    cols10 = cols - 3 * cols1 - 3 * cols3;
-    rows10 = rows - 3 * rows1 - 3 * rows3;
-    cols5a = cols10 / 2;
-    rows5a = rows10 / 2;
-    cols5b = cols10 - cols5a;
-    rows5b = rows10 - rows5a;
-    backcolor = random_dark_color( maxval );
-    fore1color = random_bright_color( maxval );
-    fore2color = random_bright_color( maxval );
+tartan(pixel **     const pixels,
+       unsigned int const cols,
+       unsigned int const rows,
+       pixval       const maxval) {
+
+    unsigned int const cols1  = cols / 22;
+    unsigned int const rows1  = rows / 22;
+    unsigned int const cols3  = cols * 3 / 22;
+    unsigned int const rows3  = rows * 3 / 22;
+    unsigned int const cols10 = cols - 3 * cols1 - 3 * cols3;
+    unsigned int const rows10 = rows - 3 * rows1 - 3 * rows3;
+    unsigned int const cols5a = cols10 / 2;
+    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(
         pixels, cols, rows, maxval, 0, 0, cols5a, rows, PPMD_NULLDRAWPROC,
-        &backcolor );
+        &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, cols5a, 0, cols1, rows, PPMD_NULLDRAWPROC,
-        &fore1color );
+        &fore1color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, cols5a + cols1, 0, cols5b, rows,
         PPMD_NULLDRAWPROC, &backcolor );
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, cols10 + cols1, 0, cols3, rows,
-        PPMD_NULLDRAWPROC, &fore2color );
+        PPMD_NULLDRAWPROC, &fore2color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, cols10 + cols1 + cols3, 0, cols1, rows,
-        PPMD_NULLDRAWPROC, &backcolor );
+        PPMD_NULLDRAWPROC, &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, cols10 + 2 * cols1 + cols3, 0, cols3, rows,
-        PPMD_NULLDRAWPROC, &fore2color );
+        PPMD_NULLDRAWPROC, &fore2color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, cols10 + 2 * cols1 + 2 * cols3, 0, cols1,
-        rows, PPMD_NULLDRAWPROC, (char*) &backcolor );
+        rows, PPMD_NULLDRAWPROC, &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, cols10 + 3 * cols1 + 2 * cols3, 0, cols3,
-        rows, PPMD_NULLDRAWPROC, &fore2color );
+        rows, PPMD_NULLDRAWPROC, &fore2color);
 
     /* Woof. */
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 0, cols, rows5a, average_drawproc,
-        &backcolor );
+        &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, rows5a, cols, rows1, average_drawproc,
-        &fore1color );
+        &fore1color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, rows5a + rows1, cols, rows5b,
-        average_drawproc, &backcolor );
+        average_drawproc, &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, rows10 + rows1, cols, rows3,
-        average_drawproc, &fore2color );
+        average_drawproc, &fore2color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, rows10 + rows1 + rows3, cols, rows1,
-        average_drawproc, &backcolor );
+        average_drawproc, &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, rows10 + 2 * rows1 + rows3, cols, rows3,
-        average_drawproc, &fore2color );
+        average_drawproc, &fore2color);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, rows10 + 2 * rows1 + 2 * rows3, cols,
-        rows1, average_drawproc, &backcolor );
+        rows1, average_drawproc, &backcolor);
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, rows10 + 3 * rows1 + 2 * rows3, cols,
-        rows3, average_drawproc, &fore2color );
-    }
+        rows3, average_drawproc, &fore2color);
+}
+
+
+
+/*----------------------------------------------------------------------------
+   Poles stuff
+-----------------------------------------------------------------------------*/
+
 
-/* Poles stuff. */
 
 #define MAXPOLES 500
 
-#if __STDC__
-static void
-poles( pixel** pixels, int cols, int rows, pixval maxval )
-#else /*__STDC__*/
+
+
 static void
-poles( pixels, cols, rows, maxval )
-    pixel** pixels;
-    int cols, rows;
-    pixval maxval;
-#endif /*__STDC__*/
-    {
-    int poles, i, xs[MAXPOLES], ys[MAXPOLES], col, row;
-    pixel colors[MAXPOLES];
+placeAndColorPolesRandomly(int *        const xs,
+                           int *        const ys,
+                           pixel *      const colors,
+                           unsigned int const cols,
+                           unsigned int const rows,
+                           pixval       const maxval,
+                           unsigned int const poleCt) {
 
-    poles = cols * rows / 30000;
+    unsigned int i;
 
-    /* Place and color poles randomly. */
-    for ( i = 0; i < poles; ++i )
-    {
-    xs[i] = rand() % cols;
-    ys[i] = rand() % rows;
-    colors[i] = random_bright_color( maxval );
+    for (i = 0; i < poleCt; ++i) {
+        xs[i] = rand() % cols;
+        ys[i] = rand() % rows;
+        colors[i] = randomBrightColor(maxval);
     }
+}
+
+
+
+static void
+assignInterpolatedColor(pixel * const resultP,
+                        pixel   const color1,
+                        double  const dist1,
+                        pixel   const color2,
+                        double  const dist2) {
+    
+    if (dist1 == 0) 
+        /* pixel is a pole */
+        *resultP = color1;
+    else {
+        double const sum = dist1 + dist2;
+
+        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);
+    }
+}
 
-    /* Now interpolate points. */
-    for ( row = 0; row < rows; ++row )
-    for ( col = 0; col < cols; ++col )
-        {
-        register long dist1, dist2, newdist, r, g, b;
-        pixel color1, color2;
 
-        /* Find two closest poles. */
-        dist1 = dist2 = 2000000000;
-        for ( i = 0; i < poles; ++i )
-        {
-        newdist = ( col - xs[i] ) * ( col - xs[i] ) +
-              ( row - ys[i] ) * ( row - ys[i] );
-        if ( newdist < dist1 )
-            {
-            dist2 = dist1;
-            color2 = color1;
-            dist1 = newdist;
-            color1 = colors[i];
-            }
-        else if ( newdist < dist2 )
-            {
-            dist2 = newdist;
-            color2 = colors[i];
-            }
-        }
 
-        /* And assign interpolated color. */
-        newdist = dist1 + dist2;
-        r = PPM_GETR(color1)*dist2/newdist + PPM_GETR(color2)*dist1/newdist;
-        g = PPM_GETG(color1)*dist2/newdist + PPM_GETG(color2)*dist1/newdist;
-        b = PPM_GETB(color1)*dist2/newdist + PPM_GETB(color2)*dist1/newdist;
-        PPM_ASSIGN( pixels[row][col], r, g, b );
+static void
+poles(pixel **     const pixels,
+      unsigned int const cols,
+      unsigned int const rows,
+      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);
+
+    /* Interpolate points */
+
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+        for (col = 0; col < cols; ++col) {
+            double dist1, dist2;
+            pixel color1, color2;
+            unsigned int i;
+
+            /* Find two closest poles. */
+            dist1 = dist2 = (SQR((double)cols) + SQR((double)rows));
+            for (i = 0; i < poleCt; ++i) {
+                double const newdist =
+                    (double)((int)col - xs[i]) * ((int)col - xs[i]) +
+                    (double)((int)row - ys[i]) * ((int)row - ys[i]);
+                if (newdist < dist1) {
+                    dist2  = dist1;
+                    color2 = color1;
+                    dist1  = newdist;
+                    color1 = colors[i];
+                } else if (newdist < dist2) {
+                    dist2  = newdist;
+                    color2 = colors[i];
+                }
+            }
+            assignInterpolatedColor(&pixels[row][col],
+                                    color1, dist1, color2, dist2);
         }
     }
+}
+
+
 
-/* Squig stuff. */
+/*----------------------------------------------------------------------------
+   Squig stuff
+-----------------------------------------------------------------------------*/
 
 #define SQUIGS 5
 #define SQ_POINTS 7
 #define SQ_MAXCIRCLE_POINTS 5000
 
-static int sq_radius, sq_circlecount;
+static int sq_circlecount;
 static pixel sq_colors[SQ_MAXCIRCLE_POINTS];
-static int sq_xoffs[SQ_MAXCIRCLE_POINTS], sq_yoffs[SQ_MAXCIRCLE_POINTS];
+static ppmd_point sq_offs[SQ_MAXCIRCLE_POINTS];
+
+
 
 static void
 validateSquigAspect(unsigned int const cols,
@@ -662,52 +875,61 @@ validateSquigAspect(unsigned int const cols,
 
 
 
-static void
-sq_measurecircle_drawproc(pixel** const pixels, 
-                          int const cols, 
-                          int const rows, 
-                          pixval const maxval, 
-                          int const col, 
-                          int const row, 
-                          const void* const clientdata)
-{
-    sq_xoffs[sq_circlecount] = col;
-    sq_yoffs[sq_circlecount] = row;
-    ++sq_circlecount;
+static ppmd_point
+vectorSum(ppmd_point const a,
+          ppmd_point const b) {
+
+    return ppmd_makePoint(a.x + b.x, a.y + b.y);
 }
 
+
+
+static ppmd_drawprocp sqMeasureCircleDrawproc;
+
 static void
-sq_rainbowcircle_drawproc(pixel** const pixels, 
-                          int const cols, 
-                          int const rows, 
-                          pixval const maxval, 
-                          int const col, 
-                          int const row, 
-                          const void* const clientdata )
-{
-    int i;
+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;
+}
 
-    for ( i = 0; i < sq_circlecount; ++i )
-    ppmd_point_drawproc(
-        pixels, cols, rows, maxval, col + sq_xoffs[i], row + sq_yoffs[i],
-        &(sq_colors[i]) );
-    }
 
 
+static ppmd_drawprocp sqRainbowCircleDrawproc;
 
 static void
-sq_assign_colors(int     const circlecount,
-                 pixval  const maxval,
-                 pixel * const colors) {
+sqRainbowCircleDrawproc(pixel **     const pixels, 
+                        unsigned int const cols, 
+                        unsigned int const rows, 
+                        pixval       const maxval, 
+                        ppmd_point   const p,
+                        const void * const clientdata) {
 
-    pixel rc1, rc2, rc3;
-    float cco3;
     unsigned int i;
 
-    rc1 = random_bright_color(maxval);
-    rc2 = random_bright_color(maxval);
-    rc3 = random_bright_color(maxval);
-    cco3 = (circlecount - 1) / 3.0;
+    for (i = 0; i < sq_circlecount; ++i)
+        ppmd_point_drawprocp(
+            pixels, cols, rows, maxval, vectorSum(p, sq_offs[i]),
+            &sq_colors[i]);
+}
+
+
+
+static void
+sqAssignColors(unsigned int const circlecount,
+               pixval       const maxval,
+               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;
+
+    unsigned int i;
 
     for (i = 0; i < circlecount ; ++i) {
         if (i < cco3) {
@@ -745,338 +967,224 @@ sq_assign_colors(int     const circlecount,
 }
 
 
-#if __STDC__
-static void
-squig( pixel** pixels, int cols, int rows, pixval maxval )
-#else /*__STDC__*/
+
 static void
-squig( pixels, cols, rows, maxval )
-    pixel** pixels;
-    int cols, rows;
-    pixval maxval;
-#endif /*__STDC__*/
-    {
+clearImageToBlack(pixel **     const pixels,
+                  unsigned int const cols,
+                  unsigned int const rows,
+                  pixval       const maxval) {
+
     pixel color;
-    int i, j, xc[SQ_POINTS], yc[SQ_POINTS], x0, y0, x1, y1, x2, y2, x3, y3;
 
-    validateSquigAspect(cols, rows);
-    
-    /* Clear image to black. */
-    PPM_ASSIGN( color, 0, 0, 0 );
+    PPM_ASSIGN(color, 0, 0, 0);
+
     ppmd_filledrectangle(
         pixels, cols, rows, maxval, 0, 0, cols, rows, PPMD_NULLDRAWPROC,
-        &color );
+        &color);
+}
 
-    /* Draw the squigs. */
-    (void) ppmd_setlinetype( PPMD_LINETYPE_NODIAGS );
-    (void) ppmd_setlineclip( 0 );
-    for ( i = SQUIGS; i > 0; --i )
-    {
-    /* Measure circle. */
-    sq_radius = ( cols + rows ) / 2 / ( 25 + i * 2 );
-    sq_circlecount = 0;
-    ppmd_circle(
-        pixels, cols, rows, maxval, 0, 0, sq_radius,
-        sq_measurecircle_drawproc, NULL );
-    sq_assign_colors( sq_circlecount, maxval, sq_colors );
-
-    /* Choose wrap-around point. */
-    switch ( rand() % 4 )
-        {
-        case 0:
-        x1 = rand() % cols;
-        y1 = 0;
-        if ( x1 < cols / 2 )
-        xc[0] = rand() % ( x1 * 2 + 1);
+
+
+static void
+chooseWrapAroundPoint(unsigned int const cols,
+                      unsigned int const rows,
+                      ppmd_point * const pFirstP,
+                      ppmd_point * const pLastP,
+                      ppmd_point * const p0P,
+                      ppmd_point * const p1P,
+                      ppmd_point * const p2P,
+                      ppmd_point * const p3P) {
+                      
+    switch (rand() % 4) {
+    case 0:
+        p1P->x = rand() % cols;
+        p1P->y = 0;
+        if (p1P->x < cols / 2)
+            pFirstP->x = rand() % (p1P->x * 2 + 1);
         else
-        xc[0] = cols - 1 - rand() % ( ( cols - x1 ) * 2 );
-        yc[0] = rand() % rows;
-        x2 = x1;
-        y2 = rows - 1;
-        xc[SQ_POINTS - 1] = 2 * x2 - xc[0];
-        yc[SQ_POINTS - 1] = y2 - yc[0];
-        x0 = xc[SQ_POINTS - 1];
-        y0 = yc[SQ_POINTS - 1] - rows;
-        x3 = xc[0];
-        y3 = yc[0] + rows;
+            pFirstP->x = cols - 1 - rand() % ((cols - p1P->x) * 2);
+        pFirstP->y = rand() % rows;
+        p2P->x = p1P->x;
+        p2P->y = rows - 1;
+        pLastP->x = 2 * p2P->x - pFirstP->x;
+        pLastP->y = p2P->y - pFirstP->y;
+        p0P->x = pLastP->x;
+        p0P->y = pLastP->y - rows;
+        p3P->x = pFirstP->x;
+        p3P->y = pFirstP->y + rows;
         break;
 
-        case 1:
-        x2 = rand() % cols;
-        y2 = 0;
-        if ( x2 < cols / 2 )
-        xc[SQ_POINTS - 1] = rand() % ( x2 * 2 + 1);
+    case 1:
+        p2P->x = rand() % cols;
+        p2P->y = 0;
+        if (p2P->x < cols / 2)
+            pLastP->x = rand() % (p2P->x * 2 + 1);
         else
-        xc[SQ_POINTS - 1] = cols - 1 - rand() % ( ( cols - x2 ) * 2 );
-        yc[SQ_POINTS - 1] = rand() % rows;
-        x1 = x2;
-        y1 = rows - 1;
-        xc[0] = 2 * x1 - xc[SQ_POINTS - 1];
-        yc[0] = y1 - yc[SQ_POINTS - 1];
-        x0 = xc[SQ_POINTS - 1];
-        y0 = yc[SQ_POINTS - 1] + rows;
-        x3 = xc[0];
-        y3 = yc[0] - rows;
+            pLastP->x = cols - 1 - rand() % ((cols - p2P->x) * 2);
+        pLastP->y = rand() % rows;
+        p1P->x = p2P->x;
+        p1P->y = rows - 1;
+        pFirstP->x = 2 * p1P->x - pLastP->x;
+        pFirstP->y = p1P->y - pLastP->y;
+        p0P->x = pLastP->x;
+        p0P->y = pLastP->y + rows;
+        p3P->x = pFirstP->x;
+        p3P->y = pFirstP->y - rows;
         break;
 
-        case 2:
-        x1 = 0;
-        y1 = rand() % rows;
-        xc[0] = rand() % cols;
-        if ( y1 < rows / 2 )
-        yc[0] = rand() % ( y1 * 2 + 1);
+    case 2:
+        p1P->x = 0;
+        p1P->y = rand() % rows;
+        pFirstP->x = rand() % cols;
+        if (p1P->y < rows / 2)
+            pFirstP->y = rand() % (p1P->y * 2 + 1);
         else
-        yc[0] = rows - 1 - rand() % ( ( rows - y1 ) * 2 );
-        x2 = cols - 1;
-        y2 = y1;
-        xc[SQ_POINTS - 1] = x2 - xc[0];
-        yc[SQ_POINTS - 1] = 2 * y2 - yc[0];
-        x0 = xc[SQ_POINTS - 1] - cols;
-        y0 = yc[SQ_POINTS - 1];
-        x3 = xc[0] + cols;
-        y3 = yc[0];
+            pFirstP->y = rows - 1 - rand() % ((rows - p1P->y) * 2);
+        p2P->x = cols - 1;
+        p2P->y = p1P->y;
+        pLastP->x = p2P->x - pFirstP->x;
+        pLastP->y = 2 * p2P->y - pFirstP->y;
+        p0P->x = pLastP->x - cols;
+        p0P->y = pLastP->y;
+        p3P->x = pFirstP->x + cols;
+        p3P->y = pFirstP->y;
         break;
 
-        case 3:
-        x2 = 0;
-        y2 = rand() % rows;
-        xc[SQ_POINTS - 1] = rand() % cols;
-        if ( y2 < rows / 2 )
-        yc[SQ_POINTS - 1] = rand() % ( y2 * 2 + 1);
+    case 3:
+        p2P->x = 0;
+        p2P->y = rand() % rows;
+        pLastP->x = rand() % cols;
+        if (p2P->y < rows / 2)
+            pLastP->y = rand() % (p2P->y * 2 + 1);
         else
-        yc[SQ_POINTS - 1] = rows - 1 - rand() % ( ( rows - y2 ) * 2 );
-        x1 = cols - 1;
-        y1 = y2;
-        xc[0] = x1 - xc[SQ_POINTS - 1];
-        yc[0] = 2 * y1 - yc[SQ_POINTS - 1];
-        x0 = xc[SQ_POINTS - 1] + cols;
-        y0 = yc[SQ_POINTS - 1];
-        x3 = xc[0] - cols;
-        y3 = yc[0];
+            pLastP->y = rows - 1 - rand() % ((rows - p2P->y) * 2);
+        p1P->x = cols - 1;
+        p1P->y = p2P->y;
+        pFirstP->x = p1P->x - pLastP->x;
+        pFirstP->y = 2 * p1P->y - pLastP->y;
+        p0P->x = pLastP->x + cols;
+        p0P->y = pLastP->y;
+        p3P->x = pFirstP->x - cols;
+        p3P->y = pFirstP->y;
         break;
-        }
-
-    for ( j = 1; j < SQ_POINTS - 1; ++j )
-        {
-        xc[j] = ( rand() % ( cols - 2 * sq_radius ) ) + sq_radius;
-        yc[j] = ( rand() % ( rows - 2 * sq_radius ) ) + sq_radius;
-        }
-
-    ppmd_line(
-        pixels, cols, rows, maxval, x0, y0, x1, y1,
-        sq_rainbowcircle_drawproc, NULL );
-    ppmd_polyspline(
-        pixels, cols, rows, maxval, x1, y1, SQ_POINTS, xc, yc, x2, y2,
-        sq_rainbowcircle_drawproc, NULL );
-    ppmd_line(
-        pixels, cols, rows, maxval, x2, y2, x3, y3,
-        sq_rainbowcircle_drawproc, NULL );
     }
-    }
-
+}
 
 
-/* Test pattern.  Just a place to put ppmdraw exercises. */
 
 static void
-test(pixel **     const pixels,
-     unsigned int const cols,
-     unsigned int const rows,
-     pixval       const maxval) {
+squig(pixel **     const pixels,
+      unsigned int const cols,
+      unsigned int const rows,
+      pixval       const maxval) {
 
-    pixel color;
-    struct fillobj * fh;
+    int i;
 
-    /* Clear image to black. */
-    PPM_ASSIGN( color, 0, 0, 0 );
-    ppmd_filledrectangle(
-        pixels, cols, rows, maxval, 0, 0, cols, rows, PPMD_NULLDRAWPROC,
-        &color);
+    validateSquigAspect(cols, rows);
+    
+    clearImageToBlack(pixels, cols, rows, maxval);
 
-    fh = ppmd_fill_create();
-
-    ppmd_line(pixels, cols, rows, maxval, 
-              cols/8, rows/8, cols/2, rows/4, ppmd_fill_drawproc, fh);
-    ppmd_line(pixels, cols, rows, maxval, 
-              cols/2, rows/4, cols-cols/8, rows/8, ppmd_fill_drawproc, fh);
-    ppmd_line(pixels, cols, rows, maxval, 
-              cols-cols/8, rows/8, cols/2, rows/2, ppmd_fill_drawproc, fh);
-    ppmd_spline3(pixels, cols, rows, maxval, 
-                 cols/2, rows/2, cols/2-cols/16, rows/2-rows/10, 
-                 cols/2-cols/8, rows/2, ppmd_fill_drawproc, fh);
-    ppmd_spline3(pixels, cols, rows, maxval, 
-                 cols/2-cols/8, rows/2, cols/4+cols/16, rows/2+rows/10, 
-                 cols/4, rows/2, ppmd_fill_drawproc, fh);
-    ppmd_line(pixels, cols, rows, maxval, 
-              cols/4, rows/2, cols/8, rows/2, ppmd_fill_drawproc, fh);
-    ppmd_line(pixels, cols, rows, maxval, 
-              cols/8, rows/2, cols/8, rows/8, ppmd_fill_drawproc, fh);
-
-    PPM_ASSIGN(color, maxval, maxval, maxval);
-    ppmd_fill(pixels, cols, rows, maxval, fh, PPMD_NULLDRAWPROC, &color);
+    /* 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);
+
+        ppmd_point c[SQ_POINTS];
+        ppmd_point p0, p1, p2, p3;
+        sq_circlecount = 0;
+        ppmd_circlep(pixels, cols, rows, maxval,
+                     ppmd_makePoint(0, 0), radius,
+                     sqMeasureCircleDrawproc, NULL);
+        sqAssignColors(sq_circlecount, maxval, sq_colors);
+
+        chooseWrapAroundPoint(cols, rows, &c[0], &c[SQ_POINTS-1],
+                              &p0, &p1, &p2, &p3);
 
-    ppmd_fill_destroy(fh);
+        {
+            /* Do the middle points */
+            unsigned int j;
+
+            for (j = 1; j < SQ_POINTS - 1; ++j) {
+              /* validateSquigAspect() assures that
+                 cols - 2 * radius, rows -2 * radius are positive
+              */
+                c[j].x = (rand() % (cols - 2 * radius)) + radius;
+                c[j].y = (rand() % (rows - 2 * radius)) + radius;
+            }
+        }
 
+        ppmd_linep(
+            pixels, cols, rows, maxval, p0, p1,
+            sqRainbowCircleDrawproc, NULL);
+        ppmd_polysplinep(
+            pixels, cols, rows, maxval, p1, SQ_POINTS, c, p2,
+            sqRainbowCircleDrawproc, NULL);
+        ppmd_linep(
+            pixels, cols, rows, maxval, p2, p3,
+            sqRainbowCircleDrawproc, NULL);
+    }
 }
 
 
 
 int
-main(int argc, char ** argv) {
-
-    pixel** pixels;
-    int argn, pattern, cols, rows;
-#define PAT_NONE 0
-#define PAT_GINGHAM2 1
-#define PAT_GINGHAM3 2
-#define PAT_MADRAS 3
-#define PAT_TARTAN 4
-#define PAT_POLES 5
-#define PAT_SQUIG 6
-#define PAT_CAMO 7
-#define PAT_ANTICAMO 8
-#define PAT_TEST 9
-    const char* const usage = "-gingham2|-g2|-gingham3|-g3|-madras|-tartan|-poles|-squig|-camo|-anticamo <width> <height>";
-
-
-    ppm_init(&argc, argv);
-
-    argn = 1;
-    pattern = PAT_NONE;
-
-    while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
-    {
-        if ( pm_keymatch( argv[argn], "-gingham2", 9 ) ||
-             pm_keymatch( argv[argn], "-g2", 3 ) )
-        {
-            if ( pattern != PAT_NONE )
-                pm_error( "only one base pattern may be specified" );
-            pattern = PAT_GINGHAM2;
-        }
-        else if ( pm_keymatch( argv[argn], "-gingham3", 9 ) ||
-                  pm_keymatch( argv[argn], "-g3", 3 ) )
-        {
-            if ( pattern != PAT_NONE )
-                pm_error( "only one base pattern may be specified" );
-            pattern = PAT_GINGHAM3;
-        }
-        else if ( pm_keymatch( argv[argn], "-madras", 2 ) )
-        {
-            if ( pattern != PAT_NONE )
-                pm_error( "only one base pattern may be specified" );
-            pattern = PAT_MADRAS;
-        }
-        else if ( pm_keymatch( argv[argn], "-tartan", 2 ) )
-        {
-            if ( pattern != PAT_NONE )
-                pm_error( "only one base pattern may be specified" );
-            pattern = PAT_TARTAN;
-        }
-        else if ( pm_keymatch( argv[argn], "-poles", 2 ) )
-        {
-            if ( pattern != PAT_NONE )
-                pm_error( "only one base pattern may be specified" );
-            pattern = PAT_POLES;
-        }
-        else if ( pm_keymatch( argv[argn], "-squig", 2 ) )
-        {
-            if ( pattern != PAT_NONE )
-                pm_error( "only one base pattern may be specified" );
-            pattern = PAT_SQUIG;
-        }
-        else if ( pm_keymatch( argv[argn], "-camo", 2 ) )
-        {
-            if ( pattern != PAT_NONE )
-                pm_error( "only one base pattern may be specified" );
-            pattern = PAT_CAMO;
-        }
-        else if ( pm_keymatch( argv[argn], "-anticamo", 2 ) )
-        {
-            if ( pattern != PAT_NONE )
-                pm_error( "only one base pattern may be specified" );
-            pattern = PAT_ANTICAMO;
-        }
-        else if ( pm_keymatch( argv[argn], "-test", 3 ) )
-        {
-            if ( pattern != PAT_NONE )
-                pm_error( "only one base pattern may be specified" );
-            pattern = PAT_TEST;
-        }
-        else
-            pm_usage( usage );
-        ++argn;
-    }
-    if ( pattern == PAT_NONE )
-        pm_error( "a base pattern must be specified" );
-
-    if ( argn == argc )
-        pm_usage( usage);
-    if ( sscanf( argv[argn], "%d", &cols ) != 1 )
-        pm_usage( usage );
-    ++argn;
-    if ( argn == argc )
-        pm_usage( usage);
-    if ( sscanf( argv[argn], "%d", &rows ) != 1 )
-        pm_usage( usage );
-    ++argn;
-
-    if ( argn != argc )
-        pm_usage( usage);
-
-    if (cols < 1)
-        pm_error("width must be at least 1");
-    if (rows < 1)
-        pm_error("height must be at least 1");
-
-    srand( (int) ( time( 0 ) ^ getpid( ) ) );
-    pixels = ppm_allocarray( cols, rows );
-
-    switch ( pattern )
-    {
+main(int argc, const char ** argv) {
+
+    struct cmdlineInfo cmdline;
+    pixel ** pixels;
+
+    pm_proginit(&argc, argv);
+    
+    parseCommandLine(argc, argv, &cmdline);
+
+    validateComputableDimensions(cmdline.width, cmdline.height);
+    
+    srand(pm_randseed());
+    pixels = ppm_allocarray(cmdline.width, cmdline.height);
+
+    switch (cmdline.basePattern) {
     case PAT_GINGHAM2:
-        gingham2( pixels, cols, rows, PPM_MAXMAXVAL );
+        gingham2(pixels, cmdline.width, cmdline.height, PPM_MAXMAXVAL);
         break;
 
     case PAT_GINGHAM3:
-        gingham3( pixels, cols, rows, PPM_MAXMAXVAL );
+        gingham3(pixels, cmdline.width, cmdline.height, PPM_MAXMAXVAL);
         break;
 
     case PAT_MADRAS:
-        madras( pixels, cols, rows, PPM_MAXMAXVAL );
+        madras(pixels, cmdline.width, cmdline.height, PPM_MAXMAXVAL);
         break;
 
     case PAT_TARTAN:
-        tartan( pixels, cols, rows, PPM_MAXMAXVAL );
+        tartan(pixels, cmdline.width, cmdline.height, PPM_MAXMAXVAL);
         break;
 
     case PAT_POLES:
-        poles( pixels, cols, rows, PPM_MAXMAXVAL );
+        poles(pixels, cmdline.width, cmdline.height, PPM_MAXMAXVAL);
         break;
 
     case PAT_SQUIG:
-        squig( pixels, cols, rows, PPM_MAXMAXVAL );
+        squig(pixels, cmdline.width, cmdline.height, PPM_MAXMAXVAL);
         break;
 
     case PAT_CAMO:
-        camo( pixels, cols, rows, PPM_MAXMAXVAL, 0 );
+        camo(pixels, cmdline.width, cmdline.height, PPM_MAXMAXVAL, 0);
         break;
 
     case PAT_ANTICAMO:
-        camo( pixels, cols, rows, PPM_MAXMAXVAL, 1 );
-        break;
-
-    case PAT_TEST:
-        test( pixels, cols, rows, PPM_MAXMAXVAL );
+        camo(pixels, cmdline.width, cmdline.height, PPM_MAXMAXVAL, 1);
         break;
 
     default:
-        pm_error( "can't happen!" );
+        pm_error("can't happen!");
     }
 
-    /* All done, write it out. */
-    ppm_writeppm( stdout, pixels, cols, rows, PPM_MAXMAXVAL, 0 );
-    pm_close( stdout );
+    ppm_writeppm(stdout, pixels, cmdline.width, cmdline.height,
+                 PPM_MAXMAXVAL, 0);
+
+    ppm_freearray(pixels, cmdline.height);
 
-    exit( 0 );
+    return 0;
 }
 
diff --git a/generator/ppmrainbow b/generator/ppmrainbow
index f98536cd..96e304ac 100755
--- a/generator/ppmrainbow
+++ b/generator/ppmrainbow
@@ -47,7 +47,10 @@ if (!$norepeat) {
     push @colorlist, $ARGV[0];
 }
 
-my $tmpprefix = $tmpdir . "/$myname.$$.";
+my $ourtmp = "$tmpdir/ppmrainbow$$";
+mkdir($ourtmp, 0777) or
+    die("Unable to create directory for temporary files '$ourtmp");
+
 
 my $widthRemaining;
 my $n;
@@ -58,7 +61,7 @@ $widthRemaining = $Twid;
 @outlist = ();
 
 while (@colorlist >= 2) {
-    my $outfile = sprintf("%s%03u.ppm", $tmpprefix, $n);
+    my $outfile = sprintf("%s/file.%03u.ppm", $ourtmp, $n);
     push(@outlist, $outfile);
 
     my $w = int(($widthRemaining-1)/(@colorlist-1))+1;
@@ -78,5 +81,10 @@ while (@colorlist >= 2) {
 exit 0;
 
 END {
-    unlink @outlist if @outlist;
+    if (@outlist) {
+        unlink(@outlist);
+    }
+    if (defined($ourtmp)) {
+        rmdir($ourtmp);
+    }
 }
diff --git a/generator/ppmrough.c b/generator/ppmrough.c
index f8823173..b21adedf 100644
--- a/generator/ppmrough.c
+++ b/generator/ppmrough.c
@@ -10,10 +10,13 @@
 ** documentation.  This software is provided "as is" without express or
 ** implied warranty.  */
 
+#include <stdlib.h>
 #include <math.h>
 #include <sys/time.h>
-#include "ppm.h"
+
+#include "pm_c_util.h"
 #include "shhopt.h"
+#include "ppm.h"
 
 static pixel** PIX;
 static pixval BG_RED, BG_GREEN, BG_BLUE;
diff --git a/lib/Makefile b/lib/Makefile
index 2d49376d..67fac5b2 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -5,7 +5,7 @@ endif
 SUBDIR = lib
 VPATH=.:$(SRCDIR)/$(SUBDIR)
 DLLTOOL=dlltool
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
 ifeq ($(NETPBMLIBTYPE),unixstatic)
 LIBNETPBM = libnetpbm.$(STATICLIBSUFFIX)
@@ -25,7 +25,7 @@ else
   LIBSYSTEM = libsystem.o
 endif
 
-LIBOBJECTS = libpm.o fileio.o bitio.o colorname.o \
+LIBOBJECTS = libpm.o pmfileio.o fileio.o bitio.o colorname.o \
 	libpbm1.o libpbm2.o libpbm3.o libpbmfont.o \
 	libpgm1.o libpgm2.o \
 	libppm1.o libppm2.o libppmcmap.o libppmcolor.o libppmfuzzy.o \
@@ -39,32 +39,37 @@ LIBOBJECTS = libpm.o fileio.o bitio.o colorname.o \
 ifneq (${VMS}x,x)
 LIBOBJECTS += libpbmvms.o
 endif
-# Library objects to be linked but not built by Makefile.common:
-LIBOBJECTS_X = util/shhopt.o util/nstring.o util/filename.o
+# Library objects to be linked but not built by common.mk:
+LIBOBJECTS_X = \
+  util/shhopt.o \
+  util/nstring.o \
+  util/vasprintf.o \
+  util/filename.o \
+  util/nsleep.o \
 
 MANUALS3 = libnetpbm
 MANUALS5 = pbm pgm ppm pnm pam
 
 INTERFACE_HEADERS =  pm.h pbm.h bitio.h pbmfont.h \
-	pgm.h ppm.h ppm.h ppmcmap.h ppmfloyd.h colorname.h \
-	pnm.h pam.h pammap.h util/shhopt.h util/nstring.h util/mallocvar.h \
-	pm_system.h pm_gamma.h
+	pgm.h ppm.h ppmcmap.h ppmfloyd.h colorname.h \
+	pnm.h pam.h pammap.h util/shhopt.h util/mallocvar.h \
+	pm_system.h pm_gamma.h ppmdraw.h ppmdfont.h \
 
 DATAFILES = rgb.txt
 
 .PHONY: all
 all: libnetpbm extra_staticlib
 
-INCLUDES = -I$(SRCDIR)/$(SUBDIR) -I. -Iimportinc
-
 SUBDIRS = util
 SCRIPTS = 
 BINARIES = 
 
 OMIT_LIBRARY_RULE = 1
-include $(SRCDIR)/Makefile.common
+include $(SRCDIR)/common.mk
 
-# The following must go after Makefile.common because $(LIBNETPBM) may 
+INCLUDES = -I$(SRCDIR)/$(SUBDIR) -I. -Iimportinc
+
+# The following must go after common.mk because $(LIBNETPBM) may 
 # contain a reference to $(NETPBM_MAJOR_RELEASE).
 .PHONY: libnetpbm
 libnetpbm: $(LIBNETPBM)
@@ -79,8 +84,8 @@ extra_staticlib: $(EXTRA_STATICLIB)
 #----------------------------------------------------------------------------
 
 $(LIBOBJECTS): %.o: %.c importinc
-# Note that the user may have configured -I options into CFLAGS.
-	$(CC) -c $(INCLUDES) -DNDEBUG $(CFLAGS) $(CFLAGS_SHLIB) \
+# Note that the user may have configured -I options into CPPFLAGS/CFLAGS.
+	$(CC) -c $(INCLUDES) -DNDEBUG $(CPPFLAGS) $(CFLAGS) $(CFLAGS_SHLIB) \
 	  $(CFLAGS_PERSONAL) $(CADD) -o $@ $<
 
 MAJ = $(NETPBM_MAJOR_RELEASE)
@@ -183,7 +188,7 @@ endif
 # ship a pre-made standardppmfont.c, so this rule will not normally be
 # used.
 standardppmdfont.c:standard.ppmdfont
-	ppmdcfont <$< >$@ || (rm $@ && false)
+	ppmdcfont <$< >$@
 
 # Note that we create a new compile.h only for the first make after a
 # make clean.  This is good enough.  We used to do stamp-date for
@@ -240,9 +245,11 @@ install.hdr: $(INTERFACE_HEADERS:%=%_installhdr)
 # prefer not to "install" them, but just to access the Netpbm source
 # directory when you compile your programs.
 
-%_installhdr: $(PKGDIR)/include
+.PHONY: $(INTERFACE_HEADERS:%=%_installhdr)
+
+$(INTERFACE_HEADERS:%=%_installhdr): $(PKGDIR)/include/netpbm
 	$(INSTALL) -c -m $(INSTALL_PERM_HDR) \
-	  $(SRCDIR)/lib/$(@:%_installhdr=%) $(PKGDIR)/include/;
+	  $(SRCDIR)/lib/$(@:%_installhdr=%) $(PKGDIR)/include/netpbm/
 
 .PHONY: install.staticlib
 install.staticlib: $(PKGDIR)/link
@@ -269,7 +276,7 @@ ifeq ($(NETPBMLIBTYPE),dylib)
 	$(SYMLINK) ../lib/libnetpbm.$(MAJ).$(MIN).dylib libnetpbm.dylib
 endif
 
-clean: localclean
+distclean clean: localclean
 
 .PHONY: localclean
 localclean:
diff --git a/lib/bitio.h b/lib/bitio.h
index 15fe0e8a..dfc5a153 100644
--- a/lib/bitio.h
+++ b/lib/bitio.h
@@ -32,7 +32,7 @@
 #ifndef _BITIO_H_
 #define _BITIO_H_
 
-#include "pm.h"
+#include <netpbm/pm.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -86,4 +86,4 @@ pm_bitwrite(BITSTREAM     b,
 #ifdef __cplusplus
 }
 #endif
-#endif /* _BITIO_H_ */
+#endif
diff --git a/lib/colorname.c b/lib/colorname.c
index dcda3ab8..cfaf026a 100644
--- a/lib/colorname.c
+++ b/lib/colorname.c
@@ -43,6 +43,41 @@ pm_canonstr(char * const str) {
 
 
 
+static void
+openColornameFileSearch(const char * const searchPath,
+                        FILE **      const filePP) {
+/*----------------------------------------------------------------------------
+   Open the color name file, finding it via the search path 'searchPath'.
+
+   Return as *filePP the stream handle for it, but if we don't find it
+   (or just can open it) anywhere, return *filePP == NULL.
+-----------------------------------------------------------------------------*/
+    char * buffer;
+
+    buffer = strdup(searchPath);
+
+    if (buffer) {
+        char * cursor;
+        bool eol;
+
+        cursor = &buffer[0];
+        eol = FALSE;    /* initial value */
+        *filePP = NULL;  /* initial value */
+        while (!eol && !*filePP) {
+            const char * token;
+            token = strsepN(&cursor, ":");
+            if (token) {
+                *filePP = fopen(token, "r");
+            } else
+                eol = TRUE;
+        }
+        free(buffer);
+    } else
+        *filePP = NULL;
+}
+
+
+
 FILE *
 pm_openColornameFile(const char * const fileName, const int must_open) {
 /*----------------------------------------------------------------------------
@@ -57,31 +92,32 @@ pm_openColornameFile(const char * const fileName, const int must_open) {
    exist), exit the program with an error message.  If 'must_open' is
    false and we can't open the file, just return a null pointer.
 -----------------------------------------------------------------------------*/
-    const char *rgbdef;
     FILE *f;
 
     if (fileName == NULL) {
-        if ((rgbdef = getenv(RGBENV))==NULL) {
+        const char * rgbdef = getenv(RGBENV);
+        if (rgbdef) {
+            /* The environment variable is set */
+            f = fopen(rgbdef, "r");
+            if (f == NULL && must_open)
+                pm_error("Can't open the color names dictionary file "
+                         "named %s, per the %s environment variable.  "
+                         "errno = %d (%s)",
+                         rgbdef, RGBENV, errno, strerror(errno));
+        } else {            
             /* The environment variable isn't set, so try the hardcoded
                default color name dictionary locations.
             */
-            if ((f = fopen(RGB_DB1, "r")) == NULL &&
-                (f = fopen(RGB_DB2, "r")) == NULL &&
-                (f = fopen(RGB_DB3, "r")) == NULL && must_open) {
-                pm_error("can't open color names dictionary file named "
-                         "%s, %s, or %s "
+            openColornameFileSearch(RGB_DB_PATH, &f);
+
+            if (f == NULL && must_open) {
+                pm_error("can't open color names dictionary file from the "
+                         "path '%s' "
                          "and Environment variable %s not set.  Set %s to "
                          "the pathname of your rgb.txt file or don't use "
                          "color names.", 
-                         RGB_DB1, RGB_DB2, RGB_DB3, RGBENV, RGBENV);
+                         RGB_DB_PATH, RGBENV, RGBENV);
             }
-        } else {            
-            /* The environment variable is set */
-            if ((f = fopen(rgbdef, "r")) == NULL && must_open)
-                pm_error("Can't open the color names dictionary file "
-                         "named %s, per the %s environment variable.  "
-                         "errno = %d (%s)",
-                         rgbdef, RGBENV, errno, strerror(errno));
         }
     } else {
         f = fopen(fileName, "r");
diff --git a/lib/colorname.h b/lib/colorname.h
index d33980e4..74583144 100644
--- a/lib/colorname.h
+++ b/lib/colorname.h
@@ -3,7 +3,7 @@
 
 #include <string.h>
 #include <stdio.h>
-#include "ppm.h"
+#include <netpbm/ppm.h>
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/editor/dithers.h b/lib/dithers.h
index 24a9fb39..05531a32 100644
--- a/editor/dithers.h
+++ b/lib/dithers.h
@@ -1,3 +1,5 @@
+#ifndef DITHERS_H_INCLUDED
+#define DITHERS_H_INCLUDED
 /*
 ** dithers.h
 **
@@ -85,3 +87,4 @@ static int const cluster8[16][16] = {
    { 62, 55, 47, 37, 36, 46, 54, 61, 65, 72, 80, 90, 91, 81, 73, 66}
 };
 
+#endif
diff --git a/lib/fileio.c b/lib/fileio.c
index 01243f9d..300ae303 100644
--- a/lib/fileio.c
+++ b/lib/fileio.c
@@ -17,23 +17,23 @@
 #include "fileio.h"
 
 char
-pm_getc(FILE * const file) {
+pm_getc(FILE * const fileP) {
     int ich;
     char ch;
 
-    ich = getc(file);
+    ich = getc(fileP);
     if (ich == EOF)
         pm_error("EOF / read error reading a byte");
     ch = (char) ich;
     
     if (ch == '#') {
         do {
-	    ich = getc(file);
-	    if (ich == EOF)
-            pm_error("EOF / read error reading a byte");
-	    ch = (char) ich;
-	    } while (ch != '\n' && ch != '\r');
-	}
+            ich = getc(fileP);
+            if (ich == EOF)
+                pm_error("EOF / read error reading a byte");
+            ch = (char) ich;
+        } while (ch != '\n' && ch != '\r');
+    }
     return ch;
 }
 
@@ -76,7 +76,7 @@ pm_getuint(FILE * const ifP) {
 
     do {
         ch = pm_getc(ifP);
-	} while (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r');
+    } while (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r');
 
     if (ch < '0' || ch > '9')
         pm_error("junk in file where an unsigned integer should be");
@@ -85,10 +85,18 @@ pm_getuint(FILE * const ifP) {
     do {
         unsigned int const digitVal = ch - '0';
 
-        if (i > INT_MAX/10 - digitVal)
+        if (i > INT_MAX/10)
             pm_error("ASCII decimal integer in file is "
                      "too large to be processed.  ");
-        i = i * 10 + digitVal;
+        
+        i *= 10;
+
+        if (i > INT_MAX - digitVal)
+            pm_error("ASCII decimal integer in file is "
+                     "too large to be processed.  ");
+
+        i += digitVal;
+
         ch = pm_getc(ifP);
     } while (ch >= '0' && ch <= '9');
 
@@ -165,6 +173,3 @@ pm_putraw(FILE *       const file,
             pm_error("Error writing %d byte sample to file.", bytes);
     }
 }
-
-
-
diff --git a/lib/fileio.h b/lib/fileio.h
index 158da10a..586c8265 100644
--- a/lib/fileio.h
+++ b/lib/fileio.h
@@ -1,5 +1,7 @@
-#ifndef _NETPBM_FILEIO_H_
-#define _NETPBM_FILEIO_H_
+#ifndef FILEIO_H_INCLUDED
+#define FILEIO_H_INCLUDED
+
+#include <stdio.h>
 
 char
 pm_getc(FILE * const file);
diff --git a/lib/libpam.c b/lib/libpam.c
index 656f5361..ab75fab6 100644
--- a/lib/libpam.c
+++ b/lib/libpam.c
@@ -5,7 +5,7 @@
    that deal with the PAM (Portable Arbitrary Format) image format.
 -----------------------------------------------------------------------------*/
 
-/* See libpm.c for the complicated explanation of this 32/64 bit file
+/* See pmfileio.c for the complicated explanation of this 32/64 bit file
    offset stuff.
 */
 #define _FILE_OFFSET_BITS 64
@@ -86,24 +86,32 @@ validateComputableSize(struct pam * const pamP) {
    Another common operation is adding 1 or 2 to the highest row, column,
    or plane number in the image, so we make sure that's possible.
 -----------------------------------------------------------------------------*/
-    unsigned int const depth = allocationDepth(pamP);
-
-    if (depth > INT_MAX/sizeof(sample))
-        pm_error("image depth (%u) too large to be processed", depth);
-    else if (pamP->width > 0 && depth * sizeof(sample) > INT_MAX/pamP->width)
-        pm_error("image width and depth (%u, %u) too large "
-                 "to be processed.", pamP->width, depth);
-    else if (pamP->width * (depth * sizeof(sample)) >
-             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)
-        pm_error("image width (%u) too large to be processed", pamP->width);
-    if (pamP->height > INT_MAX - 2)
-        pm_error("image height (%u) too large to be processed", pamP->height);
+    if (pamP->width == 0)
+        pm_error("Width is zero.  Image must be at least one pixel wide");
+    else if (pamP->height == 0)
+        pm_error("Height is zero.  Image must be at least one pixel high");
+    else {
+        unsigned int const depth = allocationDepth(pamP);
+
+        if (depth > INT_MAX/sizeof(sample))
+            pm_error("image depth (%u) too large to be processed", depth);
+        else if (depth * sizeof(sample) > INT_MAX/pamP->width)
+            pm_error("image width and depth (%u, %u) too large "
+                     "to be processed.", pamP->width, depth);
+        else if (pamP->width * (depth * sizeof(sample)) >
+                 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)
+            pm_error("image width (%u) too large to be processed",
+                     pamP->width);
+        if (pamP->height > INT_MAX - 2)
+            pm_error("image height (%u) too large to be processed",
+                     pamP->height);
+    }
 }
 
 
@@ -532,16 +540,20 @@ process_header_line(char                const buffer[],
             pamP->maxval = atoi(value);
             headerSeenP->maxval = TRUE;
         } else if (strcmp(label, "TUPLTYPE") == 0) {
-            int len = strlen(pamP->tuple_type);
-            if (len + strlen(value) + 1 > sizeof(pamP->tuple_type)-1)
-                pm_error("TUPLTYPE value too long in PAM header");
-            if (len == 0)
-                strcpy(pamP->tuple_type, value);
+            if (strlen(value) == 0)
+                pm_error("TUPLTYPE header does not have any tuple type text");
             else {
-                strcat(pamP->tuple_type, "\n");
-                strcat(pamP->tuple_type, value);
+                size_t const oldLen = strlen(pamP->tuple_type);
+                if (oldLen + strlen(value) + 1 > sizeof(pamP->tuple_type)-1)
+                    pm_error("TUPLTYPE value too long in PAM header");
+                if (oldLen == 0)
+                    strcpy(pamP->tuple_type, value);
+                else {
+                    strcat(pamP->tuple_type, " ");
+                    strcat(pamP->tuple_type, value);
+                }
+                pamP->tuple_type[sizeof(pamP->tuple_type)-1] = '\0';
             }
-            pamP->tuple_type[sizeof(pamP->tuple_type)-1] = '\0';
         } else 
             pm_error("Unrecognized header line: '%s'.  "
                      "Possible missing ENDHDR line?", label);
@@ -670,10 +682,10 @@ pnm_readpaminitrestaspnm(FILE * const fileP,
 -----------------------------------------------------------------------------*/
     struct pam pam;
 
-    pam.size        = sizeof(struct pam);
-    pam.file        = fileP;
-    pam.len         = PAM_STRUCT_SIZE(tuple_type);
-    pam.format      = PAM_FORMAT;
+    pam.size   = sizeof(struct pam);
+    pam.file   = fileP;
+    pam.len    = PAM_STRUCT_SIZE(tuple_type);
+    pam.format = PAM_FORMAT;
 
     readpaminitrest(&pam);
 
@@ -691,12 +703,11 @@ pnm_readpaminitrestaspnm(FILE * const fileP,
     case 1:
         *formatP = RPGM_FORMAT;
         break;
-    default: {
+    default:
         pm_error("Cannot treat PAM image as PPM or PGM, "
                  "because its depth (%u) "
                  "is not 1 or 3.", pam.depth);
     }
-    }
 
     *colsP   = pam.width;
     *rowsP   = pam.height;
@@ -775,7 +786,8 @@ pnm_readpaminit(FILE *       const file,
         break;
         
     default:
-        pm_error("bad magic number - not a PAM, PPM, PGM, or PBM file");
+        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);
@@ -848,18 +860,22 @@ pnm_writepaminit(struct pam * const pamP) {
 
     if (pamP->size < pamP->len)
         pm_error("pam object passed to pnm_writepaminit() is smaller "
-                 "(%d bytes, according to its 'size' element) "
+                 "(%u bytes, according to its 'size' element) "
                  "than the amount of data in it "
-                 "(%d bytes, according to its 'len' element).",
+                 "(%u bytes, according to its 'len' element).",
                  pamP->size, pamP->len);
 
-    if (pamP->len < PAM_STRUCT_SIZE(bytes_per_sample))
+    if (pamP->size < PAM_STRUCT_SIZE(bytes_per_sample))
         pm_error("pam object passed to pnm_writepaminit() is too small.  "
                  "It must be large\n"
                  "enough to hold at least up through the "
                  "'bytes_per_sample' member, but according\n"
-                 "to its 'len' member, it is only %d bytes long.", 
-                 pamP->len);
+                 "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', "
+                 "but according to the 'len' member, it is only %u bytes "
+                 "long.", pamP->len);
 
     if (pamP->maxval > PAM_OVERALL_MAXVAL)
         pm_error("maxval (%lu) passed to pnm_writepaminit() "
@@ -870,6 +886,8 @@ pnm_writepaminit(struct pam * const pamP) {
     else
         tupleType = pamP->tuple_type;
 
+    if (pamP->len < PAM_STRUCT_SIZE(bytes_per_sample))
+        pamP->len = PAM_STRUCT_SIZE(bytes_per_sample);
     pamP->bytes_per_sample = pnm_bytespersample(pamP->maxval);
     
     switch (PAM_FORMAT_TYPE(pamP->format)) {
diff --git a/lib/libpam.h b/lib/libpam.h
index 4b8e6693..9f8a34d0 100644
--- a/lib/libpam.h
+++ b/lib/libpam.h
@@ -4,7 +4,7 @@
 #ifndef LIBPAM_H_INCLUDED
 #define LIBPAM_H_INCLUDED
 
-#include "pam.h"
+#include "pgm.h"
 
 void
 pnm_readpaminitrestaspnm(FILE * const fileP, 
diff --git a/lib/libpamcolor.c b/lib/libpamcolor.c
index e11415ca..b64f8963 100644
--- a/lib/libpamcolor.c
+++ b/lib/libpamcolor.c
@@ -5,12 +5,16 @@
    that deal with colors in the PAM image format.
 -----------------------------------------------------------------------------*/
 
-/* See libpbm.c for the complicated explanation of this 32/64 bit file
+/* See pmfileio.c for the complicated explanation of this 32/64 bit file
    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 */
+
+#include <string.h>
 #include <limits.h>
 
 #include "pm_c_util.h"
@@ -44,6 +48,34 @@ pnm_parsecolor(const char * const colorname,
 
 
 
+const char *
+pnm_colorname(struct pam * const pamP,
+              tuple        const color,
+              int          const hexok) {
+
+    const char * retval;
+    pixel colorp;
+    char * colorname;
+
+    if (pamP->depth < 3)
+        PPM_ASSIGN(colorp, color[0], color[0], color[0]);
+    else 
+        PPM_ASSIGN(colorp,
+                   color[PAM_RED_PLANE],
+                   color[PAM_GRN_PLANE],
+                   color[PAM_BLU_PLANE]);
+
+    colorname = ppm_colorname(&colorp, pamP->maxval, hexok);
+
+    retval = strdup(colorname);
+    if (retval == NULL)
+        pm_error("Couldn't get memory for color name string");
+
+    return retval;
+}
+
+
+
 double pnm_lumin_factor[3] = {PPM_LUMINR, PPM_LUMING, PPM_LUMINB};
 
 void
diff --git a/lib/libpammap.c b/lib/libpammap.c
index 023a65a0..6fea0eb9 100644
--- a/lib/libpammap.c
+++ b/lib/libpammap.c
@@ -25,12 +25,13 @@
 #define HASH_SIZE 20023
 
 unsigned int
-pnm_hashtuple(struct pam * const pamP, tuple const tuple) {
+pnm_hashtuple(struct pam * const pamP,
+              tuple        const tuple) {
 /*----------------------------------------------------------------------------
    Return the hash value of the tuple 'tuple' -- i.e. an index into a hash
    table.
 -----------------------------------------------------------------------------*/
-    int i;
+    unsigned int i;
     unsigned int hash;
     const unsigned int hash_factor[] = {33023, 30013, 27011};
 
@@ -252,6 +253,7 @@ static void
 computehashrecoverable(struct pam *   const pamP,
                        tuple **       const tupleArray, 
                        unsigned int   const maxsize, 
+                       unsigned int   const newDepth,
                        sample         const newMaxval,
                        unsigned int * const sizeP,
                        tuplehash *    const tuplefreqhashP,
@@ -268,13 +270,16 @@ computehashrecoverable(struct pam *   const pamP,
 
     freqPam = *pamP;
     freqPam.maxval = newMaxval;
+    freqPam.depth = newDepth;
+
+    assert(freqPam.depth <= pamP->depth);
 
     *tuplefreqhashP = pnm_createtuplehash();
     *sizeP = 0;   /* initial value */
     
     *rowbufferP = pnm_allocpamrow(pamP);
     
-    *colorP = pnm_allocpamtuple(&freqPam);
+    *colorP = pnm_allocpamtuple(pamP);
     
     full = FALSE;  /* initial value */
     
@@ -282,7 +287,7 @@ computehashrecoverable(struct pam *   const pamP,
        tuple values. 
     */
     for (row = 0; row < pamP->height && !full; ++row) {
-        int col;
+        unsigned int col;
         const tuple * tuplerow;  /* The row of tuples we are processing */
         
         if (tupleArray)
@@ -313,6 +318,7 @@ static tuplehash
 computetuplefreqhash(struct pam *   const pamP,
                      tuple **       const tupleArray, 
                      unsigned int   const maxsize, 
+                     unsigned int   const newDepth,
                      sample         const newMaxval,
                      unsigned int * const sizeP) {
 /*----------------------------------------------------------------------------
@@ -335,6 +341,10 @@ computetuplefreqhash(struct pam *   const pamP,
   However, if the number of unique tuple values is greater than 'maxsize', 
   return a null return value and *sizeP undefined.
 
+  The tuple values that index the hash have depth 'newDepth'.  We look at
+  only the first 'newDepth' planes of the input.  Caler must ensure that
+  the input has at least that many planes.
+
   The tuple values that index the hash are scaled to a new maxval of
   'newMaxval'.  E.g.  if the input has maxval 100 and 'newMaxval' is
   50, and a particular tuple has sample value 50, it would be counted
@@ -355,19 +365,20 @@ computetuplefreqhash(struct pam *   const pamP,
     rowbuffer = NULL;
     color = NULL;
 
-    if (setjmp(jmpbuf) == 0) {
-        pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
-        computehashrecoverable(pamP, tupleArray, maxsize, newMaxval, sizeP,
-                               &tuplefreqhash, &rowbuffer, &color);
-        pm_setjmpbuf(origJmpbufP);
-    } else {
+    if (setjmp(jmpbuf) != 0) {
         if (color) 
             pnm_freepamtuple(color);
         if (rowbuffer)
             pnm_freepamrow(rowbuffer);
         if (tuplefreqhash)
             pnm_destroytuplehash(tuplefreqhash);
+        pm_setjmpbuf(origJmpbufP);
         pm_longjmp();
+    } else {
+        pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
+        computehashrecoverable(pamP, tupleArray, maxsize, newDepth, newMaxval,
+                               sizeP, &tuplefreqhash, &rowbuffer, &color);
+        pm_setjmpbuf(origJmpbufP);
     }
     return tuplefreqhash;
 }
@@ -382,7 +393,8 @@ pnm_computetuplefreqhash(struct pam *   const pamP,
 /*----------------------------------------------------------------------------
    Compute the tuple frequency hash for the tuple array tupleArray[][].
 -----------------------------------------------------------------------------*/
-    return computetuplefreqhash(pamP, tupleArray, maxsize, pamP->maxval, 
+    return computetuplefreqhash(pamP, tupleArray, maxsize,
+                                pamP->depth, pamP->maxval, 
                                 sizeP);
 }
 
@@ -446,8 +458,9 @@ pnm_alloctupletable(const struct pam * const pamP,
     alloctupletable(pamP, size, &retval, &error);
 
     if (error) {
+        pm_errormsg("%s", error);
         strfree(error);
-        pm_error("Failed to allocation tuple table of size %u", size);
+        pm_longjmp();
     }
     return retval;
 }
@@ -455,8 +468,8 @@ pnm_alloctupletable(const struct pam * const pamP,
 
 
 void
-pnm_freetupletable(struct pam * const pamP,
-                   tupletable   const tupletable) {
+pnm_freetupletable(const struct pam * const pamP,
+                   tupletable         const tupletable) {
 
     /* Note that the address 'tupletable' is, to the operating system, 
        the address of a larger block of memory that contains not only 
@@ -470,8 +483,8 @@ pnm_freetupletable(struct pam * const pamP,
 
 
 void
-pnm_freetupletable2(struct pam * const pamP,
-                    tupletable2  const tupletable) {
+pnm_freetupletable2(const struct pam * const pamP,
+                    tupletable2        const tupletable) {
 
     pnm_freetupletable(pamP, tupletable.table);
 }
@@ -500,8 +513,9 @@ tuplehashtotable(const struct pam * const pamP,
     alloctupletable(pamP, allocsize, &tupletable, &error);
 
     if (error) {
+        pm_errormsg("%s", error);
         strfree(error);
-        pm_error("Failed to allocate table table of size %u", allocsize);
+        pm_longjmp();
     } else {
         unsigned int i, j;
         /* Loop through the hash table. */
@@ -575,9 +589,10 @@ pnm_computetupletablehash(struct pam * const pamP,
 
 
 tupletable
-pnm_computetuplefreqtable2(struct pam *   const pamP,
+pnm_computetuplefreqtable3(struct pam *   const pamP,
                            tuple **       const tupleArray,
                            unsigned int   const maxsize,
+                           unsigned int   const newDepth,
                            sample         const newMaxval,
                            unsigned int * const countP) {
 /*----------------------------------------------------------------------------
@@ -604,6 +619,9 @@ pnm_computetuplefreqtable2(struct pam *   const pamP,
    Return the number of unique tuple values in tupleArray[][] as
    *countP.
 
+   The tuples in the table have depth 'newDepth'.  We look at
+   only the first 'newDepth' planes of the input.  If the input doesn't
+   have that many planes, we throw an error.
 
    Scale the tuple values to a new maxval of 'newMaxval' before
    processing them.  E.g. if the input has maxval 100 and 'newMaxval'
@@ -615,8 +633,13 @@ pnm_computetuplefreqtable2(struct pam *   const pamP,
     tupletable tuplefreqtable;
     unsigned int uniqueCount;
 
+    if (newDepth > pamP->depth)
+        pm_error("pnm_computetuplefreqtable3 called with 'newDepth' "
+                 "argument (%u) greater than input depth (%u)",
+                 newDepth, pamP->depth);
+
     tuplefreqhash = computetuplefreqhash(pamP, tupleArray, maxsize, 
-                                         newMaxval, &uniqueCount);
+                                         newDepth, newMaxval, &uniqueCount);
     if (tuplefreqhash == NULL)
         tuplefreqtable = NULL;
     else {
@@ -635,6 +658,20 @@ pnm_computetuplefreqtable2(struct pam *   const pamP,
 
 
 tupletable
+pnm_computetuplefreqtable2(struct pam *   const pamP,
+                           tuple **       const tupleArray,
+                           unsigned int   const maxsize,
+                           sample         const newMaxval,
+                           unsigned int * const countP) {
+
+    return
+        pnm_computetuplefreqtable3(pamP, tupleArray, maxsize,
+                                   pamP->depth, newMaxval, countP);
+}
+
+
+
+tupletable
 pnm_computetuplefreqtable(struct pam *   const pamP,
                           tuple **       const tupleArray,
                           unsigned int   const maxsize,
diff --git a/lib/libpamn.c b/lib/libpamn.c
index c7610100..fe004e91 100644
--- a/lib/libpamn.c
+++ b/lib/libpamn.c
@@ -10,6 +10,7 @@
 
 #include "pm_c_util.h"
 #include "mallocvar.h"
+#include "nstring.h"
 #include "pam.h"
 #include "fileio.h"
 #include "pm_gamma.h"
@@ -18,15 +19,19 @@
 
 
 
-tuplen *
-pnm_allocpamrown(const struct pam * const pamP) {
+static void
+allocpamrown(const struct pam * const pamP,
+             tuplen **          const tuplerownP,
+             const char **      const errorP) {
 /*----------------------------------------------------------------------------
    We assume that the dimensions of the image are such that arithmetic
    overflow will not occur in our calculations.  NOTE: pnm_readpaminit()
    ensures this assumption is valid.
 -----------------------------------------------------------------------------*/
-    const int bytes_per_tuple = pamP->depth * sizeof(samplen);
+    int const bytes_per_tuple = pamP->depth * sizeof(samplen);
+
     tuplen * tuplerown;
+    const char * error;
 
     /* The tuple row data structure starts with 'width' pointers to
        the tuples, immediately followed by the 'width' tuples
@@ -35,64 +40,106 @@ pnm_allocpamrown(const struct pam * const pamP) {
 
     tuplerown = malloc(pamP->width * (sizeof(tuplen *) + bytes_per_tuple));
     if (tuplerown == NULL)
-        pm_error("Out of memory allocating space for a tuple row of\n"
-                 "%d tuples by %d samples per tuple by %d bytes per sample.",
-                 pamP->width, pamP->depth, sizeof(samplen));
-
-    {
+        asprintfN(&error, "Out of memory allocating space for a tuple row of"
+                  "%u tuples by %u samples per tuple by %u bytes per sample.",
+                  pamP->width, pamP->depth, sizeof(samplen));
+    else {
         /* Now we initialize the pointers to the individual tuples to make this
            a regulation C two dimensional array.
         */
         
-        char *p;
-        int i;
+        unsigned char * p;
+        unsigned int i;
         
-        p = (char*) (tuplerown + pamP->width);  /* location of Tuple 0 */
-        for (i = 0; i < pamP->width; i++) {
+        p = (unsigned char*) (tuplerown + pamP->width);
+            /* location of Tuple 0 */
+        for (i = 0; i < pamP->width; ++i) {
             tuplerown[i] = (tuplen) p;
             p += bytes_per_tuple;
         }
+        *errorP = NULL;
+        *tuplerownP = tuplerown;
     }
-    return(tuplerown);
 }
 
 
 
-void 
-pnm_readpamrown(const struct pam * const pamP, 
-                tuplen *           const tuplenrow) {
+tuplen *
+pnm_allocpamrown(const struct pam * const pamP) {
+/*----------------------------------------------------------------------------
+   We assume that the dimensions of the image are such that arithmetic
+   overflow will not occur in our calculations.  NOTE: pnm_readpaminit()
+   ensures this assumption is valid.
+-----------------------------------------------------------------------------*/
+    const char * error;
+    tuplen * tuplerown;
 
-    /* For speed, we don't check any of the inputs for consistency 
-       here (unless it's necessary to avoid crashing).  Any consistency
-       checking should have been done by a prior call to 
-       pnm_writepaminit().
-    */
-    assert(pamP->maxval != 0);
+    allocpamrown(pamP, &tuplerown, &error);
 
-    /* Need a special case for raw PBM because it has multiple tuples (8)
-       packed into one byte.
-    */
-    if (PAM_FORMAT_TYPE(pamP->format) == PBM_TYPE) {
-        int col;
-        bit *bitrow;
-        if (pamP->depth != 1)
-            pm_error("Invalid pam structure passed to pnm_readpamrow().  "
-                     "It says PBM format, but 'depth' member is not 1.");
-        bitrow = pbm_allocrow(pamP->width);
-        pbm_readpbmrow(pamP->file, bitrow, pamP->width, pamP->format);
-        for (col = 0; col < pamP->width; col++)
-            tuplenrow[col][0] = 
-                bitrow[col] == PBM_BLACK ? 0.0 : 1.0;
+    if (error) {
+        pm_errormsg("pnm_allocpamrown() failed.  %s", error);
+        strfree(error);
+        pm_longjmp();
+    }
+
+    return tuplerown;
+}
+
+
+
+static void
+readpbmrow(const struct pam * const pamP, 
+           tuplen *           const tuplenrow) {
+
+    bit * bitrow;
+    jmp_buf jmpbuf;
+    jmp_buf * origJmpbufP;
+
+    bitrow = pbm_allocrow(pamP->width);
+    
+    if (setjmp(jmpbuf) != 0) {
         pbm_freerow(bitrow);
+        pm_setjmpbuf(origJmpbufP);
+        pm_longjmp();
+    } else {
+        unsigned int col;
+        pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
+
+        pbm_readpbmrow(pamP->file, bitrow, pamP->width, pamP->format);
+
+        for (col = 0; col < pamP->width; ++col)
+            tuplenrow[col][0] = bitrow[col] == PBM_BLACK ? 0.0 : 1.0;
+
+        pm_setjmpbuf(origJmpbufP);
+    }
+    pbm_freerow(bitrow);
+}
+
+
+
+static void
+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);
+        pm_longjmp();
     } else {
         float const scaler = 1.0 / pamP->maxval;
             /* Note: multiplication is faster than division, so we divide
                once here so we can multiply many times later.
             */
-        int col;
-        tuple * tuplerow;
 
-        tuplerow = pnm_allocpamrow(pamP);
+        unsigned int col;
+
+        pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
 
         pnm_readpamrow(pamP, tuplerow);
         for (col = 0; col < pamP->width; ++col) {
@@ -100,15 +147,16 @@ pnm_readpamrown(const struct pam * const pamP,
             for (plane = 0; plane < pamP->depth; ++plane)
                 tuplenrow[col][plane] = tuplerow[col][plane] * scaler;
         }
-        pnm_freepamrow(tuplerow);
+        pm_setjmpbuf(origJmpbufP);
     }
+    pnm_freepamrow(tuplerow);
 }
 
 
 
 void 
-pnm_writepamrown(const struct pam * const pamP, 
-                 const tuplen *     const tuplenrow) {
+pnm_readpamrown(const struct pam * const pamP, 
+                tuplen *           const tuplenrow) {
 
     /* For speed, we don't check any of the inputs for consistency 
        here (unless it's necessary to avoid crashing).  Any consistency
@@ -121,20 +169,66 @@ pnm_writepamrown(const struct pam * const pamP,
        packed into one byte.
     */
     if (PAM_FORMAT_TYPE(pamP->format) == PBM_TYPE) {
-        int col;
-        bit *bitrow;
-        bitrow = pbm_allocrow(pamP->width);
-        for (col = 0; col < pamP->width; col++)
-            bitrow[col] = 
-                tuplenrow[col][0] < 0.5 ? PBM_BLACK : PBM_WHITE;
+        if (pamP->depth != 1)
+            pm_error("Invalid pam structure passed to pnm_readpamrow().  "
+                     "It says PBM format, but 'depth' member is not 1.");
+
+        readpbmrow(pamP, tuplenrow);
+    } else
+        readpamrow(pamP, tuplenrow);
+}
+
+
+
+static void
+writepbmrow(const struct pam * const pamP, 
+            const tuplen *     const tuplenrow) {
+
+    jmp_buf jmpbuf;
+    jmp_buf * origJmpbufP;
+    bit * bitrow;
+
+    bitrow = pbm_allocrow(pamP->width);
+
+    if (setjmp(jmpbuf) != 0) {
+        pbm_freerow(bitrow);
+        pm_setjmpbuf(origJmpbufP);
+        pm_longjmp();
+    } else {
+        unsigned int col;
+
+        pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
+
+        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, 
                         pamP->format == PBM_FORMAT);
-        pbm_freerow(bitrow);
+
+        pm_setjmpbuf(origJmpbufP);
+    }
+    pbm_freerow(bitrow);
+} 
+
+
+
+static void
+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);
+        pm_longjmp();
     } else {
-        tuple * tuplerow;
-        int col;
+        unsigned int col;
 
-        tuplerow = pnm_allocpamrow(pamP);
+        pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
 
         for (col = 0; col < pamP->width; ++col) {
             unsigned int plane;
@@ -143,8 +237,32 @@ pnm_writepamrown(const struct pam * const pamP,
                     (tuplenrow[col][plane] * pamP->maxval + 0.5);
         }    
         pnm_writepamrow(pamP, tuplerow);
-        pnm_freepamrow(tuplerow);
+
+        pm_setjmpbuf(origJmpbufP);
     }
+    pnm_freepamrow(tuplerow);
+}
+
+
+
+void 
+pnm_writepamrown(const struct pam * const pamP, 
+                 const tuplen *     const tuplenrow) {
+
+    /* For speed, we don't check any of the inputs for consistency 
+       here (unless it's necessary to avoid crashing).  Any consistency
+       checking should have been done by a prior call to 
+       pnm_writepaminit().
+    */
+    assert(pamP->maxval != 0);
+
+    /* Need a special case for raw PBM because it has multiple tuples (8)
+       packed into one byte.
+    */
+    if (PAM_FORMAT_TYPE(pamP->format) == PBM_TYPE)
+        writepbmrow(pamP, tuplenrow);
+    else
+        writepamrow(pamP, tuplenrow);
 }
 
 
@@ -152,8 +270,8 @@ pnm_writepamrown(const struct pam * const pamP,
 tuplen **
 pnm_allocpamarrayn(const struct pam * const pamP) {
     
-    tuplen **tuplenarray;
-    int row;
+    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.
@@ -161,12 +279,33 @@ pnm_allocpamarrayn(const struct pam * const pamP) {
     
     MALLOCARRAY(tuplenarray, pamP->height);
     if (tuplenarray == NULL) 
-        pm_error("Out of memory allocating the row pointer section of "
-                 "a %u row array", pamP->height);
-
-    for (row = 0; row < pamP->height; row++) {
-        tuplenarray[row] = pnm_allocpamrown(pamP);
+        asprintfN(&error,
+                  "Out of memory allocating the row pointer section of "
+                  "a %u row array", pamP->height);
+    else {
+        unsigned int rowsDone;
+
+        rowsDone = 0;
+        error = NULL;
+
+        while (rowsDone < pamP->height && !error) {
+            allocpamrown(pamP, &tuplenarray[rowsDone], &error);
+            if (!error)
+                ++rowsDone;
+        }
+        if (error) {
+            unsigned int row;
+            for (row = 0; row < rowsDone; ++row)
+                pnm_freepamrown(tuplenarray[rowsDone]);
+            free(tuplenarray);
+        }
+    }
+    if (error) {
+        pm_errormsg("pnm_allocpamarrayn() failed.  %s", error);
+        strfree(error);
+        pm_longjmp();
     }
+
     return(tuplenarray);
 }
 
@@ -191,16 +330,28 @@ pnm_readpamn(FILE *       const file,
              int          const size) {
 
     tuplen **tuplenarray;
-    int row;
+    jmp_buf jmpbuf;
+    jmp_buf * origJmpbufP;
 
     pnm_readpaminit(file, pamP, size);
     
     tuplenarray = pnm_allocpamarrayn(pamP);
     
-    for (row = 0; row < pamP->height; row++) 
-        pnm_readpamrown(pamP, tuplenarray[row]);
+    if (setjmp(jmpbuf) != 0) {
+        pnm_freepamarrayn(tuplenarray, pamP);
+        pm_setjmpbuf(origJmpbufP);
+        pm_longjmp();
+    } else {
+        unsigned int row;
 
-    return(tuplenarray);
+        pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
+
+        for (row = 0; row < pamP->height; ++row) 
+            pnm_readpamrown(pamP, tuplenarray[row]);
+
+        pm_setjmpbuf(origJmpbufP);
+    }
+    return tuplenarray;
 }
 
 
@@ -209,11 +360,11 @@ void
 pnm_writepamn(struct pam * const pamP, 
               tuplen **    const tuplenarray) {
 
-    int row;
+    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]);
 }
 
@@ -473,6 +624,8 @@ createUngammaMapOffset(const struct pam * const pamP,
    can be used in a reverse lookup to convert normalized ungamma'ed
    samplen values to integer sample values.  The 0.5 effectively does
    the rounding.
+
+   This never throws an error.  Return value NULL means failed.
 -----------------------------------------------------------------------------*/
     pnm_transformMap * retval;
     pnm_transformMap ungammaTransformMap;
diff --git a/lib/libpamread.c b/lib/libpamread.c
index 3d4b6448..0506d020 100644
--- a/lib/libpamread.c
+++ b/lib/libpamread.c
@@ -6,7 +6,7 @@
    raster (not the header).
 -----------------------------------------------------------------------------*/
 
-/* See libpm.c for the complicated explanation of this 32/64 bit file
+/* See pmfileio.c for the complicated explanation of this 32/64 bit file
    offset stuff.
 */
 #define _FILE_OFFSET_BITS 64
@@ -16,32 +16,48 @@
 #include <limits.h>
 #include <assert.h>
 
-#include "pam.h"
 #include "fileio.h"
+#include "nstring.h"
+#include "pam.h"
 
 
 static void
 readPbmRow(const struct pam * const pamP,
            tuple *            const tuplerow) {
 
-    unsigned char *bitrow;
     if (pamP->depth != 1)
         pm_error("Invalid pam structure passed to pnm_readpamrow().  "
                  "It says PBM format, but 'depth' member is not 1.");
+    else {
+        jmp_buf jmpbuf;
+        jmp_buf * origJmpbufP;
+        unsigned char * bitrow;
+
+        bitrow = (unsigned char *) pbm_allocrow(pbm_packed_bytes(pamP->width));
+
+        if (setjmp(jmpbuf) != 0) {
+            pbm_freerow(bitrow);
+            pm_setjmpbuf(origJmpbufP);
+            pm_longjmp();
+        } else {
+            pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
+
+            pbm_readpbmrow_packed(pamP->file, bitrow, pamP->width,
+                                  pamP->format);
     
-    bitrow = (unsigned char *) pbm_allocrow(pbm_packed_bytes(pamP->width));
-    pbm_readpbmrow_packed(pamP->file, bitrow, pamP->width, pamP->format);
-    
-    if (tuplerow) {
-        int col;
-        for (col = 0; col < pamP->width; ++col) {
-            tuplerow[col][0] = 
-                ( ((bitrow[col/8] >> (7-col%8)) & 1 ) == PBM_BLACK)
-                ? PAM_PBM_BLACK : PAM_PBM_WHITE
-                ;
+            if (tuplerow) {
+                unsigned int col;
+                for (col = 0; col < pamP->width; ++col) {
+                    tuplerow[col][0] = 
+                        ( ((bitrow[col/8] >> (7-col%8)) & 1 ) == PBM_BLACK)
+                        ? PAM_PBM_BLACK : PAM_PBM_WHITE
+                        ;
+                }
+            }
+            pm_setjmpbuf(origJmpbufP);
         }
-    }   
-    pbm_freerow(bitrow);
+        pbm_freerow(bitrow);
+    }
 }
 
 
@@ -190,6 +206,7 @@ readRawNonPbmRow(const struct pam * const pamP,
 
     unsigned char * inbuf;
     size_t bytesRead;
+    const char * error;
 
     inbuf = pnm_allocrowimage(pamP);
     
@@ -197,25 +214,34 @@ readRawNonPbmRow(const struct pam * const pamP,
 
     if (bytesRead != rowImageSize) {
         if (feof(pamP->file))
-            pm_error("End of file encountered when trying to read a row from "
-                     "input file.");
+            asprintfN(&error,
+                      "End of file encountered when trying to read a row from "
+                      "input file.");
         else 
-            pm_error("Error reading a row from input file.  "
-                     "fread() fails with errno=%d (%s)",
-                     errno, strerror(errno));
-    }
-    if (tuplerow) {
-        switch (pamP->bytes_per_sample) {
-        case 1: parse1BpsRow(pamP, tuplerow, inbuf); break;
-        case 2: parse2BpsRow(pamP, tuplerow, inbuf); break;
-        case 3: parse3BpsRow(pamP, tuplerow, inbuf); break;
-        case 4: parse4BpsRow(pamP, tuplerow, inbuf); break;
-        default:
-            pm_error("invalid bytes per sample passed to "
-                     "pnm_formatpamrow(): %u",  pamP->bytes_per_sample);
+            asprintfN(&error, "Error reading a row from input file.  "
+                      "fread() fails with errno=%d (%s)",
+                      errno, strerror(errno));
+    } else {
+        error = NULL;  /* initial assumption */
+        if (tuplerow) {
+            switch (pamP->bytes_per_sample) {
+            case 1: parse1BpsRow(pamP, tuplerow, inbuf); break;
+            case 2: parse2BpsRow(pamP, tuplerow, inbuf); break;
+            case 3: parse3BpsRow(pamP, tuplerow, inbuf); break;
+            case 4: parse4BpsRow(pamP, tuplerow, inbuf); break;
+            default:
+                asprintfN(&error, "invalid bytes per sample passed to "
+                          "pnm_formatpamrow(): %u",  pamP->bytes_per_sample);
+            }
         }
     }
     pnm_freerowimage(inbuf);
+
+    if (error) {
+        pm_errormsg("%s", error);
+        strfree(error);
+        pm_longjmp();
+    }
 }
 
 
@@ -268,15 +294,27 @@ pnm_readpam(FILE *       const fileP,
             struct pam * const pamP, 
             int          const size) {
 
-    tuple **tuplearray;
-    int row;
+    jmp_buf jmpbuf;
+    jmp_buf * origJmpbufP;
+    tuple ** tuplearray;
 
     pnm_readpaminit(fileP, pamP, size);
     
     tuplearray = pnm_allocpamarray(pamP);
     
-    for (row = 0; row < pamP->height; row++) 
-        pnm_readpamrow(pamP, tuplearray[row]);
-
+    if (setjmp(jmpbuf) != 0) {
+        pnm_freepamarray(tuplearray, pamP);
+        pm_setjmpbuf(origJmpbufP);
+        pm_longjmp();
+    } else {
+        unsigned int row;
+
+        pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
+            
+        for (row = 0; row < pamP->height; ++row) 
+            pnm_readpamrow(pamP, tuplearray[row]);
+
+        pm_setjmpbuf(origJmpbufP);
+    }
     return tuplearray;
 }
diff --git a/lib/libpamwrite.c b/lib/libpamwrite.c
index 9184a4b5..dd319d4a 100644
--- a/lib/libpamwrite.c
+++ b/lib/libpamwrite.c
@@ -6,7 +6,7 @@
    raster (not the header).
 -----------------------------------------------------------------------------*/
 
-/* See libpm.c for the complicated explanation of this 32/64 bit file
+/* See pmfileio.c for the complicated explanation of this 32/64 bit file
    offset stuff.
 */
 #define _FILE_OFFSET_BITS 64
@@ -308,22 +308,33 @@ writePamRawRow(const struct pam * const pamP,
    Write mutiple ('count') copies of the same row ('tuplerow') to the file,
    in raw (not plain) format.
 -----------------------------------------------------------------------------*/
+    jmp_buf jmpbuf;
+    jmp_buf * origJmpbufP;
     unsigned int rowImageSize;
-
     unsigned char * outbuf;  /* malloc'ed */
-    unsigned int i;
 
     outbuf = pnm_allocrowimage(pamP);
 
     pnm_formatpamrow(pamP, tuplerow, outbuf, &rowImageSize);
 
-    for (i = 0; i < count; ++i) {
-        size_t bytesWritten;
-
-        bytesWritten = fwrite(outbuf, 1, rowImageSize, pamP->file);
-        if (bytesWritten != rowImageSize)
-            pm_error("fwrite() failed to write an image row to the file.  "
-                     "errno=%d (%s)", errno, strerror(errno));
+    if (setjmp(jmpbuf) != 0) {
+        pnm_freerowimage(outbuf);
+        pm_setjmpbuf(origJmpbufP);
+        pm_longjmp();
+    } else {
+        unsigned int i;
+
+        pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
+        
+        for (i = 0; i < count; ++i) {
+            size_t bytesWritten;
+            
+            bytesWritten = fwrite(outbuf, 1, rowImageSize, pamP->file);
+            if (bytesWritten != rowImageSize)
+                pm_error("fwrite() failed to write an image row to the file.  "
+                         "errno=%d (%s)", errno, strerror(errno));
+        }
+        pm_setjmpbuf(origJmpbufP);
     }
     pnm_freerowimage(outbuf);
 }
diff --git a/lib/libpbm1.c b/lib/libpbm1.c
index 8dd491a7..fc20071c 100644
--- a/lib/libpbm1.c
+++ b/lib/libpbm1.c
@@ -10,20 +10,41 @@
 ** implied warranty.
 */
 
-/* See libpm.c for the complicated explanation of this 32/64 bit file
+/* See pmfileio.c for the complicated explanation of this 32/64 bit file
    offset stuff.
 */
 #define _FILE_OFFSET_BITS 64
 #define _LARGE_FILES  
 
 #include <stdio.h>
-#include "pbm.h"
-#include "libpbm.h"
+
+#include "pm_c_util.h"
+#include "mallocvar.h"
 #include "shhopt.h"
+#include "pbm.h"
+
+
+
+bit *
+pbm_allocrow(unsigned int const cols) {
+
+    bit * bitrow;
+
+    MALLOCARRAY(bitrow, cols);
+
+    if (bitrow == NULL)
+        pm_error("Unable to allocate space for a %u-column bit row", cols);
+
+    return bitrow;
+}
+
+
 
 void
-pbm_init(int *argcP, char *argv[]) {
-    pm_proginit(argcP, argv);
+pbm_init(int *   const argcP,
+         char ** const argv) {
+
+    pm_proginit(argcP, (const char **)argv);
 }
 
 
@@ -60,3 +81,92 @@ pbm_check(FILE * file, const enum pm_check_type check_type,
     }
 }
 
+
+
+static unsigned int
+bitpop8(unsigned char const x) {
+/*----------------------------------------------------------------------------
+   Return the number of 1 bits in 'x'
+-----------------------------------------------------------------------------*/
+static unsigned int const p[256] = {
+    0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
+    1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+    1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+    1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+    3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+    1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+    3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+    3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+    3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+    4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 };
+
+    return p[x];
+}
+
+
+
+static int
+bitpop(const unsigned char * const packedRow,
+       unsigned int          const cols) {
+/*----------------------------------------------------------------------------
+  Return the number of 1 bits in 'packedRow'.
+-----------------------------------------------------------------------------*/
+    unsigned int const colByteCnt  = pbm_packed_bytes(cols);
+    unsigned int const fullByteCnt = cols/8;
+
+    unsigned int i;
+    unsigned int sum;
+
+    sum = 0;  /* initial value */
+
+    for (i = 0; i < fullByteCnt; ++i)
+        sum += bitpop8(packedRow[i]);
+
+    if (colByteCnt > fullByteCnt)
+        sum += bitpop8(packedRow[i] >> (8-cols%8));
+
+    return sum;
+}
+
+
+
+bit
+pbm_backgroundbitrow(unsigned const char * const packedBits,
+                     unsigned int          const cols,
+                     unsigned int          const offset) {
+/*----------------------------------------------------------------------------
+  PBM version of pnm_backgroundxelrow() with additional offset parameter.
+  When offset == 0, produces the same return value as does
+  pnm_backgroundxelrow(promoted_bitrow, cols, ...)
+-----------------------------------------------------------------------------*/
+    const unsigned char * const row = &packedBits[offset/8];
+    unsigned int const rs = offset % 8;
+    unsigned int const last = pbm_packed_bytes(cols + rs) - 1;
+
+    unsigned int retval;
+
+    unsigned int firstbit, lastbit;
+    unsigned int totalBitpop, headBitpop;
+
+    firstbit = (row[0] >> (7-rs)) & 0x01;
+    lastbit  = (row[last] >> (7- (cols+rs-1)%8)) & 0x01;
+
+    if (firstbit == lastbit)
+        retval = firstbit;
+    else {
+        totalBitpop = bitpop(row, cols + rs);
+        headBitpop  = (rs == 0) ? 0 : bitpop(row, rs);
+
+        if (totalBitpop - headBitpop >= cols/2)
+            retval = PBM_BLACK;
+        else
+            retval = PBM_WHITE;
+    }
+    return retval;
+}
diff --git a/lib/libpbm2.c b/lib/libpbm2.c
index d04328ef..a8e4b0f6 100644
--- a/lib/libpbm2.c
+++ b/lib/libpbm2.c
@@ -10,9 +10,12 @@
 ** implied warranty.
 */
 
+#include <limits.h>
+
 #include "pbm.h"
 #include "libpbm.h"
 #include "fileio.h"
+#include "pam.h"
 
 static bit 
 getbit (FILE * const file) {
@@ -54,25 +57,65 @@ pbm_readpbminitrest( file, colsP, rowsP )
         pm_error("Number of columns in header is too large.");
     }
 
+
+
+static void
+validateComputableSize(unsigned int const cols,
+                       unsigned int const rows) {
+/*----------------------------------------------------------------------------
+   Validate that the dimensions of the image are such that it can be
+   processed in typical ways on this machine without worrying about
+   overflows.  Note that in C, arithmetic is always modulus
+   arithmetic, so if your values are too big, the result is not what
+   you expect.  That failed expectation can be disastrous if you use
+   it to allocate memory.
+
+   A common operation is adding 1 or 2 to the highest row or
+   column number in the image, so we make sure that's possible.
+-----------------------------------------------------------------------------*/
+    if (cols > INT_MAX - 2)
+        pm_error("image width (%u) too large to be processed", cols);
+    if (rows > INT_MAX - 2)
+        pm_error("image height (%u) too large to be processed", rows);
+}
+
+
+
 void
-pbm_readpbminit( file, colsP, rowsP, formatP )
-    FILE* file;
-    int* colsP;
-    int* rowsP;
-    int* formatP;
-    {
-    /* Check magic number. */
-    *formatP = pm_readmagicnumber( file );
-    switch ( PBM_FORMAT_TYPE(*formatP) )
-    {
-        case PBM_TYPE:
-    pbm_readpbminitrest( file, colsP, rowsP );
-    break;
+pbm_readpbminit(FILE * const ifP,
+                int *  const colsP,
+                int *  const rowsP,
+                int *  const formatP) {
+
+    *formatP = pm_readmagicnumber(ifP);
+
+    switch (PAM_FORMAT_TYPE(*formatP)) {
+    case PBM_TYPE:
+        pbm_readpbminitrest(ifP, colsP, rowsP);
+        break;
+
+    case PGM_TYPE:
+        pm_error("The input file is a PGM, not a PBM.  You may want to "
+                 "convert it to PBM with 'pamditherbw | pamtopnm' or "
+                 "'pamthreshold | pamtopnm'");
+
+    case PPM_TYPE:
+        pm_error("The input file is a PPM, not a PBM.  You may want to "
+                 "convert it to PBM with 'ppmtopgm', 'pamditherbw', and "
+                 "'pamtopnm'");
 
+    case PAM_TYPE:
+        pm_error("The input file is a PAM, not a PBM.  "
+                 "If it is a black and white image, you can convert it "
+                 "to PBM with 'pamtopnm'");
+        break;
     default:
-    pm_error( "bad magic number - not a pbm file" );
-    }
+        pm_error("bad magic number - not a Netpbm file");
     }
+    validateComputableSize(*colsP, *rowsP);
+}
+
+
 
 void
 pbm_readpbmrow( file, bitrow, cols, format )
@@ -114,8 +157,8 @@ pbm_readpbmrow( file, bitrow, cols, format )
 
 
 void
-pbm_readpbmrow_packed(FILE *          const file, 
-                      unsigned char * const packed_bits,
+pbm_readpbmrow_packed(FILE *          const fileP, 
+                      unsigned char * const packedBits,
                       int             const cols, 
                       int             const format) {
 
@@ -126,22 +169,22 @@ pbm_readpbmrow_packed(FILE *          const file,
 
         /* We first clear the return buffer, then set ones where needed */
         for (byteIndex = 0; byteIndex < pbm_packed_bytes(cols); ++byteIndex)
-            packed_bits[byteIndex] = 0x00;
+            packedBits[byteIndex] = 0x00;
 
         for (col = 0; col < cols; ++col) {
             unsigned char mask;
-            mask = getbit(file) << (7 - col % 8);
-            packed_bits[col / 8] |= mask;
+            mask = getbit(fileP) << (7 - col % 8);
+            packedBits[col / 8] |= mask;
         }
     }
     break;
 
     case RPBM_FORMAT: {
         int bytes_read;
-        bytes_read = fread(packed_bits, 1, pbm_packed_bytes(cols), file);
+        bytes_read = fread(packedBits, 1, pbm_packed_bytes(cols), fileP);
              
         if (bytes_read < pbm_packed_bytes(cols)) {
-            if (feof(file)) 
+            if (feof(fileP)) 
                 if (bytes_read == 0) 
                     pm_error("Attempt to read a raw PBM image row, but "
                              "no more rows left in file.");
@@ -160,6 +203,65 @@ pbm_readpbmrow_packed(FILE *          const file,
 
 
 
+void
+pbm_readpbmrow_bitoffset(FILE *          const ifP,
+                         unsigned char * const packedBits, 
+                         int             const cols,
+                         int             const format,
+                         unsigned int    const offset) {
+/*----------------------------------------------------------------------------
+   Read PBM packed bitrow from file 'ifP' (raster format given by
+   'cols' and 'format') and shift right 'offset' bits.
+
+   Read it into packedBits[], preserving surrounding image data.
+
+   Logic not tested for negative offsets.
+-----------------------------------------------------------------------------*/
+    unsigned int const rsh = offset % 8;
+    unsigned int const lsh = (8 - rsh) % 8;
+    unsigned char * const window = &packedBits[offset/8];
+        /* Area of packed row buffer into which we read the image data.
+           Aligned to nearest byte boundary to the left, so the first
+           few bits might contain original data, not output.
+        */
+    unsigned int const last = pbm_packed_bytes(cols+rsh) - 1;
+        /* Position within window of rightmost byte after shift */
+
+    /* The original leftmost and rightmost chars. */
+    unsigned char const origHead = window[0];
+    unsigned char const origEnd  = window[last];
+
+    pbm_readpbmrow_packed(ifP, window, cols, format);
+
+    if (rsh > 0) {
+        /* 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) {
+            unsigned char const t = window[i] << lsh;
+            window[i] = carryover | window[i] >> rsh;
+            carryover = t;
+        }
+    }
+  
+    if ((cols + rsh) % 8 > 0) {
+        /* Adjust rightmost char */
+        unsigned int  const trs = (cols + rsh) % 8;
+        unsigned int  const tls = 8 - trs;
+        unsigned char const rightBits =
+            ((unsigned char)(origEnd << trs) >> trs);
+        unsigned char const leftBits =
+            ((unsigned char)(window[last] >> tls) << tls);
+
+        window[last] =  leftBits | rightBits;
+    }
+} 
+
+
+
 bit**
 pbm_readpbm( file, colsP, rowsP )
     FILE* file;
diff --git a/lib/libpbm3.c b/lib/libpbm3.c
index 9c9bbd25..9200d30e 100644
--- a/lib/libpbm3.c
+++ b/lib/libpbm3.c
@@ -10,32 +10,23 @@
 ** implied warranty.
 */
 
+#include <assert.h>
+
+#include "pm_c_util.h"
 #include "pbm.h"
-#include "libpbm.h"
+
+#if HAVE_GCC_MMXSSE
 #include "bitreverse.h"
+#endif
 
-/* HAVE_MMX_SSE means we have the means to use MMX and SSE CPU facilities
-   to make PBM raster processing faster.
+/* HAVE_GCC_MMXSSE means we have the means to use MMX and SSE CPU facilities
+   to make PBM raster processing faster.  GCC only.
 
    The GNU Compiler -msse option makes SSE available.
+   For x86-32 with MMX/SSE, "-msse" must be explicitly given.
+   For x86-64 and AMD64, "-msse" is on by default.
 */
 
-#if defined(__GNUC__) && \
-  (__GNUC__ * 100 + __GNUC_MINOR__ >= 301) && \
-  (__GNUC__ * 100 + __GNUC_MINOR__ < 403) && \
-  defined (__SSE__)
-/* GCC 4.3 does have the facility, but it is different from what this
-   code knows how to use.  In particular, the calls to
-   __builtin_ia32_pcmpeqb() and __builtin_ia32_pmovmskb() fail to
-   compile, with complaints of improper argument types.
-*/
-
-#define HAVE_MMX_SSE 1
-#else
-#define HAVE_MMX_SSE 0
-#endif
-
-
 void
 pbm_writepbminit(FILE * const fileP, 
                  int    const cols, 
@@ -65,13 +56,13 @@ writePackedRawRow(FILE *                const fileP,
 } 
 
 
-
+#if HAVE_GCC_MMXSSE
 static void
 packBitsWithMmxSse(FILE *          const fileP,
                    const bit *     const bitrow,
                    unsigned char * const packedBits,
-                   int             const cols,
-                   int *           const nextColP) {
+                   unsigned int    const cols,
+                   unsigned int *  const nextColP) {
 /*----------------------------------------------------------------------------
    Pack the bits of bitrow[] into bytes at 'packedBits'.  Going left to right,
    stop when there aren't enough bits left to fill a whole byte.  Return
@@ -81,7 +72,6 @@ packBitsWithMmxSse(FILE *          const fileP,
    Use the Pentium MMX and SSE facilities to pack the bits quickly, but
    perform the exact same function as the simpler packBitsGeneric().
 -----------------------------------------------------------------------------*/
-#if HAVE_MMX_SSE
     /*
       We use MMX/SSE facilities that operate on 8 bytes at once to pack
       the bits quickly.
@@ -91,45 +81,37 @@ packBitsWithMmxSse(FILE *          const fileP,
       The key machine instructions are:
     
     
-      PCMPEQB  Packed CoMPare EQual Byte
+      PCMPGTB  Packed CoMPare Greater Than Byte
     
         Compares 8 bytes in parallel
-        Result is x00 if equal, xFF if unequal for each byte       
+        Result is x00 if greater than, xFF if not for each byte       
     
       PMOVMSKB Packed MOVe MaSK Byte 
     
         Result is a byte of the MSBs of 8 bytes
-        x00 xFF x00 xFF xFF xFF x00 x00 --> 01011100B = 0x5C     
-    
+        x00 xFF x00 xFF xFF xFF x00 x00 --> 01011100B = 0x5C
+        
+        The result is actually a 32 bit int, but the higher bits are
+        always 0.  (0x0000005C in the above case)
     
       EMMS     Empty MMx State
     
         Free MMX registers  
     
-    
-      Here's a one-statement version of the code in our foor loop.  It's harder 
-      to read, but if we find out this generates more efficient code, we could 
-      use this.
-    
-        packedBits[col/8] 
-          = bitreverse [ ~ (unsigned char) __builtin_ia32_pmovmskb (
-            __builtin_ia32_pcmpeqb ( *(v8qi*) (&bitrow[col]), *(v8qi*) &zero64)
-            ) ];
     */
 
-    typedef int v8qi __attribute__ ((mode(V8QI)));
-    typedef int di __attribute__ ((mode(DI)));
 
-    di const zero64 = 0;        /* to clear with PXOR */
+    typedef char v8qi __attribute__ ((vector_size(8)));
+    typedef int di __attribute__ ((mode(DI)));
 
     unsigned int col;
+    v8qi const zero64 =(v8qi)((di)0);  /* clear to zero */
 
     for (col = 0; col + 7 < cols; col += 8) {
+
         v8qi const compare =
-            __builtin_ia32_pcmpeqb(*(v8qi*) (&bitrow[col]), *(v8qi*) &zero64);
-        unsigned char const backwardWhiteMask = (unsigned char)
-            __builtin_ia32_pmovmskb(compare);
-        unsigned char const backwardBlackMask = ~backwardWhiteMask;
+            __builtin_ia32_pcmpgtb(*(v8qi*) (&bitrow[col]), (v8qi) zero64);
+        uint32_t const backwardBlackMask =  __builtin_ia32_pmovmskb(compare);
         unsigned char const blackMask = bitreverse[backwardBlackMask];
 
         packedBits[col/8] = blackMask;
@@ -138,9 +120,20 @@ packBitsWithMmxSse(FILE *          const fileP,
 
     __builtin_ia32_emms();
 
+}
 #else
-    if (bitreverse == bitreverse) {}; /* avoid unused vbl compiler warning */
+/* Avoid undefined function warning; never actually called */
+
+#define packBitsWithMmxSse(a,b,c,d,e) packBitsGeneric(a,b,c,d,e)
 #endif
+
+
+
+
+static unsigned int
+bitValue(unsigned char const byteValue) {
+
+    return byteValue == 0 ? 0 : 1;
 }
 
 
@@ -149,10 +142,10 @@ static void
 packBitsGeneric(FILE *          const fileP,
                 const bit *     const bitrow,
                 unsigned char * const packedBits,
-                int             const cols,
-                int *           const nextColP) {
+                unsigned int    const cols,
+                unsigned int *  const nextColP) {
 /*----------------------------------------------------------------------------
-   Pack the bits of bitrow[] into byts at 'packedBits'.  Going left to right,
+   Pack the bits of bitrow[] into bytes at 'packedBits'.  Going left to right,
    stop when there aren't enough bits left to fill a whole byte.  Return
    as *nextColP the number of the next column after the rightmost one we
    packed.
@@ -161,18 +154,16 @@ packBitsGeneric(FILE *          const fileP,
 -----------------------------------------------------------------------------*/
     unsigned int col;
 
-    #define iszero(x) ((x) == 0 ? 0 : 1)
-
     for (col = 0; col + 7 < cols; col += 8)
         packedBits[col/8] = (
-            iszero(bitrow[col+0]) << 7 |
-            iszero(bitrow[col+1]) << 6 |
-            iszero(bitrow[col+2]) << 5 |
-            iszero(bitrow[col+3]) << 4 |
-            iszero(bitrow[col+4]) << 3 |
-            iszero(bitrow[col+5]) << 2 |
-            iszero(bitrow[col+6]) << 1 |
-            iszero(bitrow[col+7]) << 0
+            bitValue(bitrow[col+0]) << 7 |
+            bitValue(bitrow[col+1]) << 6 |
+            bitValue(bitrow[col+2]) << 5 |
+            bitValue(bitrow[col+3]) << 4 |
+            bitValue(bitrow[col+4]) << 3 |
+            bitValue(bitrow[col+5]) << 2 |
+            bitValue(bitrow[col+6]) << 1 |
+            bitValue(bitrow[col+7]) << 0
             );
     *nextColP = col;
 }
@@ -180,50 +171,72 @@ packBitsGeneric(FILE *          const fileP,
 
 
 static void
+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;
+}
+
+
+
+static void
 writePbmRowRaw(FILE *      const fileP,
                const bit * const bitrow,
                int         const cols) {
 
-    int nextCol;
+    jmp_buf jmpbuf;
+    jmp_buf * origJmpbufP;
+    unsigned char * packedBits;
 
-    unsigned char * const packedBits = pbm_allocrow_packed(cols);
+    packedBits = pbm_allocrow_packed(cols);
 
-    if (HAVE_MMX_SSE)
-        packBitsWithMmxSse(fileP, bitrow, packedBits, cols, &nextCol);
-    else 
-        packBitsGeneric(fileP, bitrow, packedBits, cols, &nextCol);
+    if (setjmp(jmpbuf) != 0) {
+        pbm_freerow_packed(packedBits);
+        pm_setjmpbuf(origJmpbufP);
+        pm_longjmp();
+    } else {
+        unsigned int nextCol;
 
-    /* routine for partial byte at the end of packed_bits[]
-       Prior to addition of the above enhancement,
-       this method was used for the entire process
-     */                   
-
-    if (cols % 8 > 0) {
-        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
-                ;
+        pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
+
+        if (HAVE_GCC_MMXSSE)
+            packBitsWithMmxSse(fileP, bitrow, packedBits, cols, &nextCol);
+        else 
+            packBitsGeneric(fileP, bitrow, packedBits, cols, &nextCol);
+
+        if (cols % 8 > 0)
+            packPartialBytes(bitrow, cols, nextCol, packedBits);
         
-        packedBits[col/8] = item;
+        writePackedRawRow(fileP, packedBits, cols);
+
+        pm_setjmpbuf(origJmpbufP);
     }
-    
-    writePackedRawRow(fileP, packedBits, cols);
-    
     pbm_freerow_packed(packedBits);
 }
 
 
 
 static void
-writePbmRowPlain(FILE * const fileP,
-                 bit *  const bitrow, 
-                 int    const cols) {
+writePbmRowPlain(FILE *      const fileP,
+                 const bit * const bitrow, 
+                 int         const cols) {
     
     int col, charcount;
 
@@ -242,10 +255,10 @@ writePbmRowPlain(FILE * const fileP,
 
 
 void
-pbm_writepbmrow(FILE * const fileP, 
-                bit *  const bitrow, 
-                int    const cols, 
-                int    const forceplain) {
+pbm_writepbmrow(FILE *       const fileP, 
+                const bit *  const bitrow, 
+                int          const cols, 
+                int          const forceplain) {
 
     if (!forceplain && !pm_plain_output)
         writePbmRowRaw(fileP, bitrow, cols);
@@ -257,28 +270,119 @@ pbm_writepbmrow(FILE * const fileP,
 
 void
 pbm_writepbmrow_packed(FILE *                const fileP, 
-                       const unsigned char * const packed_bits,
+                       const unsigned char * const packedBits,
                        int                   const cols, 
                        int                   const forceplain) {
 
     if (!forceplain && !pm_plain_output)
-        writePackedRawRow(fileP, packed_bits, cols);
+        writePackedRawRow(fileP, packedBits, cols);
     else {
-        bit *bitrow;
-        int col;
+        jmp_buf jmpbuf;
+        jmp_buf * origJmpbufP;
+        bit * bitrow;
 
         bitrow = pbm_allocrow(cols);
 
-        for (col = 0; col < cols; ++col) 
-            bitrow[col] = 
-                packed_bits[col/8] & (0x80 >> (col%8)) ? PBM_BLACK : PBM_WHITE;
-        writePbmRowPlain(fileP, bitrow, cols);
+        if (setjmp(jmpbuf) != 0) {
+            pbm_freerow(bitrow);
+            pm_setjmpbuf(origJmpbufP);
+            pm_longjmp();
+        } else {
+            unsigned int col;
+            
+            pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
+
+            for (col = 0; col < cols; ++col) 
+                bitrow[col] = 
+                    packedBits[col/8] & (0x80 >> (col%8)) ?
+                    PBM_BLACK : PBM_WHITE;
+
+            writePbmRowPlain(fileP, bitrow, cols);
+
+            pm_setjmpbuf(origJmpbufP);
+        }
         pbm_freerow(bitrow);
     }
 }
 
 
 
+static unsigned char
+leftBits(unsigned char const x,
+         unsigned int  const n) {
+/*----------------------------------------------------------------------------
+   Clear rightmost (8-n) bits, retain leftmost (=high) n bits.
+-----------------------------------------------------------------------------*/
+    unsigned char buffer;
+
+    assert(n < 8);
+
+    buffer = x;
+
+    buffer >>= (8-n);
+    buffer <<= (8-n);
+
+    return buffer;
+}
+
+
+
+void
+pbm_writepbmrow_bitoffset(FILE *          const fileP,
+                          unsigned char * const packedBits,
+                          unsigned int    const cols,
+                          int             const format,
+                          unsigned int    const offset) {
+/*----------------------------------------------------------------------------
+   Write PBM row from a packed bit buffer 'packedBits, starting at the
+   specified offset 'offset' in the buffer.
+
+   We destroy the buffer.
+-----------------------------------------------------------------------------*/
+    unsigned int const rsh = offset % 8;
+    unsigned int const lsh = (8 - rsh) % 8;
+    unsigned int const csh = cols % 8;
+    unsigned char * const window = &packedBits[offset/8];
+        /* Area of packed row buffer from which we take the image data.
+           Aligned to nearest byte boundary to the left, so the first
+           few bits might be irrelvant.
+
+           Also our work buffer, in which we shift bits and from which we
+           ultimately write the bits to the file.
+        */
+    unsigned int const colByteCnt = pbm_packed_bytes(cols);
+    unsigned int const last = colByteCnt - 1;
+        /* Position within window of rightmost byte after shift */
+
+    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 
+           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;
+
+        unsigned int i;
+        for (i = 0; i < shiftBytes; ++i)
+            window[i] = window[i] << rsh | window[i+1] >> lsh;
+
+        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, 
diff --git a/lib/libpbmfont.c b/lib/libpbmfont.c
index 54cae478..1938e754 100644
--- a/lib/libpbmfont.c
+++ b/lib/libpbmfont.c
@@ -14,6 +14,7 @@
 ** implied warranty.
 */
 
+#include <assert.h>
 #include <string.h>
 
 #include "pm_c_util.h"
@@ -667,9 +668,9 @@ pbm_defaultfont(const char * const name) {
         unsigned int row;
 
         if (strcmp(name, "fixed") != 0)
-            pm_error( "built-in font name unknown, try 'bdf' or 'fixed'" );
+            pm_error( "built-in font name unknown, try 'bdf' or 'fixed'");
 
-        defaultfont = pbm_allocarray( DEFAULTFONT_COLS, DEFAULTFONT_ROWS );
+        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) {
@@ -689,104 +690,200 @@ pbm_defaultfont(const char * const name) {
                 }
             }
         }
-
         retval = 
-            pbm_dissectfont(defaultfont, DEFAULTFONT_ROWS, DEFAULTFONT_COLS);
+            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(bit ** const font,
-                int    const frows,
-                int    const fcols) {
+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 border you see is irrelevant.  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 dissection works by finding the first blank row and column; 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.
-    **
-    ** The difference between cell_height, cell_width and char_height,
-    ** char_width is that the first is the size of the cell including
-    ** spacing, while the second is just the actual maximum-size character.
-    */
-    int cell_width, cell_height, char_width, char_height;
-    int brow, bcol, row, col, d, ch, r, c, i;
-    struct font* fn;
-    struct glyph* glyph;
+       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;
-    bit b;
-
-    /* Find first blank row. */
-    for ( brow = 0; brow < frows / 6; ++brow ) {
-        b = font[brow][0];
-        for ( col = 1; col < fcols; ++col )
-            if ( font[brow][col] != b )
-                goto nextrow;
-        goto gotblankrow;
-    nextrow: ;
-    }
-    pm_error( "couldn't find blank row in font" );
-
- gotblankrow:
-    /* Find first blank col. */
-    for ( bcol = 0; bcol < fcols / 8; ++bcol ) {
-        b = font[0][bcol];
-        for ( row = 1; row < frows; ++row )
-            if ( font[row][bcol] != b )
-                goto nextcol;
-        goto gotblankcol;
-    nextcol: ;
-    }
-    pm_error( "couldn't find blank col in font" );
-
- gotblankcol:
-    /* Now compute character cell size. */
-    d = frows - brow;
-    cell_height = d / 11;
-    if ( cell_height * 11 != d )
-        pm_error( "problem computing character cell height" );
-    d = fcols - bcol;
-    cell_width = d / 15;
-    if ( cell_width * 15 != d )
-        pm_error( "problem computing character cell width" );
-    char_height = brow;
-    char_width = bcol;
+
+    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" );
+    if (fn == NULL)
+        pm_error("out of memory allocating font structure");
 
-    fn->maxwidth  = char_width;
-    fn->maxheight = char_height;
+    fn->maxwidth  = charWidth;
+    fn->maxheight = charHeight;
     fn->x = fn->y = 0;
 
     fn->oldfont = font;
@@ -808,8 +905,8 @@ pbm_dissectfont(bit ** const font,
         pm_error( "out of memory allocating glyph data" );
 
     /* Now fill in the 0,0 coords. */
-    row = cell_height * 2;
-    col = cell_width * 2;
+    row = cellHeight * 2;
+    col = cellWidth * 2;
     for (i = 0; i < firstCodePoint; ++i)
         fn->glyph[i] = NULL;
 
@@ -817,7 +914,7 @@ pbm_dissectfont(bit ** const font,
         glyph[ch].width = fn->maxwidth;
         glyph[ch].height = fn->maxheight;
         glyph[ch].x = glyph[ch].y = 0;
-        glyph[ch].xadd = cell_width;
+        glyph[ch].xadd = cellWidth;
 
         for ( r = 0; r < glyph[ch].height; ++r )
             for ( c = 0; c < glyph[ch].width; ++c )
@@ -828,10 +925,10 @@ pbm_dissectfont(bit ** const font,
 
         fn->glyph[firstCodePoint + ch] = &glyph[ch];
 
-        col += cell_width;
-        if ( col >= cell_width * 14 ) {
-            col = cell_width * 2;
-            row += cell_height;
+        col += cellWidth;
+        if ( col >= cellWidth * 14 ) {
+            col = cellWidth * 2;
+            row += cellHeight;
         }
     }
     for (i = firstCodePoint + nCharsInFont; i < 256; ++i)
@@ -868,18 +965,21 @@ pbm_loadfont(const char * const filename)
 
 
 
-struct font* pbm_loadpbmfont(const char * const filename)
-{
-    FILE* ifp;
-    bit** font;
+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( font, frows, fcols );
+    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;
@@ -971,178 +1071,418 @@ pbm_dumpfont( fn )
 
 /* Routines for loading a BDF font file */
 
-static int readline ARGS((FILE* fp, char* buf, char* arg[]));
-
-#define expect(str) if (readline(fp, line, arg) < 0 || strcmp(arg[0], (str))) \
-    { fclose(fp); return 0; }
 
-struct font* pbm_loadbdffont(const char * const name)
-{
-    FILE* fp;
-    char line[1024], *arg[32], *hex;
-    char *b;
-    int n, numchar, hdig, encoding;
-    struct font* font;
-    struct glyph* glyph;
+static unsigned int
+mk_argvn(char *        const s,
+         const char ** const vec,
+         unsigned int  const mk_max) {
 
-    if (!(fp = fopen(name, "rb")))
-        return 0;
+    int n;
+    char * p;
 
-    expect("STARTFONT");
+    p = &s[0];
+    n = 0;
 
-    MALLOCVAR(font);
-    if (font == NULL)
-        pm_error("no memory for font");
-    font->oldfont = 0;
-    { 
-        /* Initialize all characters to nonexistent; we will fill the ones we
-           find in the bdf file later.
-        */
-        int i;
-        for (i = 0; i < 256; i++) 
-            font->glyph[i] = NULL;
+    while (*p) {
+        if (ISSPACE(*p))
+            *p++ = '\0';
+        else {
+            vec[n++] = p;
+            if (n >= mk_max)
+                break;
+            while (*p && !ISSPACE(*p))
+                ++p;
+        }
     }
+    vec[n] = NULL;
 
-    while (readline(fp, line, arg) >= 0) {
-        if (!strcmp(arg[0], "COMMENT"))
-            continue;
-        if (!strcmp(arg[0], "SIZE"))
-            continue;
-        
-        if (!strcmp(arg[0], "STARTPROPERTIES")) {
-            n = atoi(arg[1]);
-            for (; n > 0 && readline(fp, line, arg) >= 0; n--)
-                ;
-        }
-        else if (!strcmp(arg[0], "FONTBOUNDINGBOX")) {
-            font->maxwidth = atoi(arg[1]);
-            font->maxheight = atoi(arg[2]);
-            font->x = atoi(arg[3]);
-            font->y = atoi(arg[4]);
+    return n;
+}
+
+
+
+static int
+readline(FILE *        const ifP,
+         char *        const line,
+         const char ** const wordList) {
+/*----------------------------------------------------------------------------
+   Read a nonblank line from file *ifP.  Return the value of the whole line
+   in *buf (must be at least 1024 bytes long), and parse it into words
+   in *wordList (must have at least 32 entries).
+
+   If there is no nonblank line before EOF, return rc == -1.
+-----------------------------------------------------------------------------*/
+    bool gotLine;
+    bool error;
+
+    for (gotLine = false, error = false; !gotLine && !error; ) {
+        char * rc;
+
+        rc = fgets(line, 1024, ifP);
+        if (rc == NULL)
+            error = true;
+        else {
+            mk_argvn(line, wordList, 32);
+            if (wordList[0] != NULL)
+                gotLine = true;
         }
-        else if (!strcmp(arg[0], "ENDFONT")) {
-            fclose(fp);
-            return font;
+    }
+    return error ? -1 : 0;
+}
+
+
+
+static void
+readBitmap(FILE *          const fp,
+           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) {
+        int i;  /* dot counter */
+        int rc;
+        char * hex;
+        char line[1024];
+        const char * arg[32];
+
+        rc = readline(fp, line, arg);
+
+        if (rc < 0)
+            pm_error("End of file in bitmap for character '%s' in BDF "
+                     "font file.", charName);
+
+        hex = line;
+        for (i = glyphWidth; i > 0; i -= 4) {
+            if (*hex == '\0')
+                pm_error("Premature end of line in line '%s' of "
+                         "bitmap for character '%s' "
+                         "in BDF font file", line, charName);
+            else {
+                char const hdig = *hex++;
+                unsigned int hdigValue;
+
+                if (hdig >= '0' && hdig <= '9')
+                    hdigValue = hdig - '0';
+                else if (hdig >= 'a' && hdig <= 'f')
+                    hdigValue = 10 + (hdig - 'a');
+                else if (hdig >= 'A' && hdig <= 'F')
+                    hdigValue = 10 + (hdig - 'A');
+                else 
+                    pm_error("Invalid hex digit '%c' in line '%s' of "
+                             "bitmap for character '%s' "
+                             "in BDF font file", hdig, line, charName);
+                        
+                if (i > 0)
+                    bmap[bmapIndex++] = hdigValue & 0x8 ? 1 : 0;
+                if (i > 1)
+                    bmap[bmapIndex++] = hdigValue & 0x4 ? 1 : 0;
+                if (i > 2)
+                    bmap[bmapIndex++] = hdigValue & 0x2 ? 1 : 0;
+                if (i > 3)
+                    bmap[bmapIndex++] = hdigValue & 0x1 ? 1 : 0;
+            }
         }
-        else if (!strcmp(arg[0], "CHARS")) {
-            numchar = atoi(arg[1]);
-            while (numchar > 0) {
-                if (readline(fp, line, arg) < 0) { fclose(fp); return 0; }
-                if (!strcmp(arg[0], "COMMENT"))
-                    continue;
-                if (strcmp(arg[0], "STARTCHAR")) { fclose(fp); return 0; }
-                MALLOCVAR(glyph);
-                if (glyph == NULL)
-                    pm_error("no memory for font glyph");
-
-                expect("ENCODING");
-                if ((encoding = atoi(arg[1])) < 0) {
-                    if (arg[2])
-                        encoding = atoi(arg[2]);
-                    else {
-                        while (readline(fp, line, arg) >= 0)
-                            if (!strcmp(arg[0], "ENDCHAR"))
-                                break;
+    }
+}
+
+
+
+static void
+createBmap(unsigned int  const glyphWidth,
+           unsigned int  const glyphHeight,
+           FILE *        const fp,
+           const char *  const charName,
+           const char ** const bmapP) {
+
+    char line[1024];
+    const char * arg[32];
+    unsigned char * bmap;
+    int rc;
+
+    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");
+
+    rc = readline(fp, line, arg);
+    if (rc < 0)
+        pm_error("End of file encountered reading font glyph byte map from "
+                 "BDF font file.");
+
+    if (streq(arg[0], "ATTRIBUTES")) {
+        rc = readline(fp, line, arg);
+        if (rc < 0)
+            pm_error("End of file encountered after ATTRIBUTES in BDF "
+                     "font file.");
+    }                
+    if (!streq(arg[0], "BITMAP"))
+        pm_error("'%s' found where BITMAP expected in definition of "
+                 "character '%s' in BDF font file.", line, charName);
+
+    assert(streq(arg[0], "BITMAP"));
+
+    readBitmap(fp, glyphWidth, glyphHeight, charName, bmap);
+
+    *bmapP = (char *)bmap;
+}
+
+
+
+static void
+expect(FILE *        const fp,
+       const char *  const expected,
+       const char ** const arg,
+       char *        const line) {
+
+    int rc;
+
+    rc = readline(fp, line, arg);
+
+    if (rc < 0)
+        pm_error("EOF in BDF font file where '%s' expected", expected);
+    else if (!streq(arg[0], expected))
+        pm_error("'%s' where '%s' expected in BDF font file",
+                 line, expected);
+}
+
+
+
+static void
+skipCharacter(FILE * const fp) {
+/*----------------------------------------------------------------------------
+  In BDF font file 'fp', skip through the end of the character we are
+  presently in.
+-----------------------------------------------------------------------------*/
+    bool endChar;
                         
-                        numchar--;
-                        continue;
-                    }
-                }
-                expect("SWIDTH");
-                expect("DWIDTH");
-                glyph->xadd = atoi(arg[1]);
-                expect("BBX");
-                glyph->width = atoi(arg[1]);
-                glyph->height = atoi(arg[2]);
-                glyph->x = atoi(arg[3]);
-                glyph->y = atoi(arg[4]);
-
-                b = (char*)malloc(glyph->width * glyph->height * sizeof(char));
-                glyph->bmap = b;
-                if (!glyph->bmap)
-                    pm_error("no memory for font glyph byte map");
-
-                if (readline(fp, line, arg) < 0) { fclose(fp); return 0; }
-                if (!strcmp(arg[0], "ATTRIBUTES"))
-                    if (readline(fp, line, arg) < 0) { fclose(fp); return 0; }
-                
-                for (n = glyph->height; n > 0; n--) {
-                    int i;  /* dot counter */
-                    if (readline(fp, line, arg) < 0) { fclose(fp); return 0; }
-                    hex = line;
-                    for (i = glyph->width; i > 0; i -= 4) {
-                        hdig = *hex++;
-                        if (hdig >= '0' && hdig <= '9')
-                            hdig -= '0';
-                        else if (hdig >= 'a' && hdig <= 'f')
-                            hdig -= 'a' - 10;
-                        else if (hdig >= 'A' && hdig <= 'F')
-                            hdig -= 'A' - 10;
+    endChar = FALSE;
                         
-                        *b++ = hdig & 8 ? 1 : 0;
-                        if (i > 1) *b++ = hdig & 4 ? 1 : 0;
-                        if (i > 2) *b++ = hdig & 2 ? 1 : 0;
-                        if (i > 3) *b++ = hdig & 1;
-                    }
-                }
+    while (!endChar) {
+        char line[1024];
+        const char * arg[32];
+        int rc;
+        rc = readline(fp, line, arg);
+        if (rc < 0)
+            pm_error("End of file in the middle of a character (before "
+                     "ENDCHAR) in BDF font file.");
+        endChar = streq(arg[0], "ENDCHAR");
+    }                        
+}
+
+
+
+static void
+validateEncoding(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 (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;
+}
+
+
 
-                expect("ENDCHAR");
 
-                if (encoding < 256)
-                    /* We ignore any characters with codes that don't fit 
-                       in 8 bits.  We may want to change this someday.
-                       */
-                    font->glyph[encoding] = glyph;
+static void
+processCharsLine(FILE *        const fp,
+                 const char ** const arg,
+                 struct font * const fontP) {
 
-                numchar--;
+    unsigned int const nCharacters = atoi(arg[1]);
+
+    unsigned int nCharsDone;
+
+    nCharsDone = 0;
+
+    while (nCharsDone < nCharacters) {
+        char line[1024];
+        const char * arg[32];
+        int rc;
+
+        rc = readline(fp, line, arg);
+        if (rc < 0)
+            pm_error("End of file after CHARS reading BDF font file");
+
+        if (streq(arg[0], "COMMENT")) {
+            /* ignore */
+        } else if (!streq(arg[0], "STARTCHAR"))
+            pm_error("no STARTCHAR after CHARS in BDF font file");
+        else {
+            const char * const charName = arg[1];
+            struct glyph * glyphP;
+            unsigned int codepoint;
+            bool badCodepoint;
+
+            assert(streq(arg[0], "STARTCHAR"));
+
+            MALLOCVAR(glyphP);
+
+            if (glyphP == NULL)
+                pm_error("no memory for font glyph for '%s' character",
+                         charName);
+
+            {
+                const char * arg[32];
+                expect(fp, "ENCODING", arg, line);
+
+                validateEncoding(arg, &codepoint, &badCodepoint);
             }
+            if (badCodepoint)
+                skipCharacter(fp);
+            else {
+                {
+                    const char * arg[32];
+                    expect(fp, "SWIDTH", arg, line);
+                }
+                {
+                    const char * arg[32];
+                    
+                    expect(fp, "DWIDTH", arg, line);
+                    glyphP->xadd = atoi(arg[1]);
+                }
+                {
+                    const char * arg[32];
+                    
+                    expect(fp, "BBX", arg, line);
+                    glyphP->width  = atoi(arg[1]);
+                    glyphP->height = atoi(arg[2]);
+                    glyphP->x      = atoi(arg[3]);
+                    glyphP->y      = atoi(arg[4]);
+                }
+                createBmap(glyphP->width, glyphP->height, fp, charName,
+                           &glyphP->bmap);
+                
+                {
+                    const char * arg[32];
+                    expect(fp, "ENDCHAR", arg, line);
+                }
+                assert(codepoint < 256); /* Ensured by validateEncoding() */
+
+                fontP->glyph[codepoint] = glyphP;
+            }
+            ++nCharsDone;
         }
     }
-    return font;
 }
 
-static int readline(fp, buf, arg)
-FILE* fp;
-char* buf;
-char* arg[];
-{
-    if (!fgets(buf, 1024, fp))
-        return -1;
-    
-    return mk_argvn(buf, arg, 32);
-}
 
-int mk_argvn(s, vec, mk_max)
-char* s;
-char* vec[];
-int mk_max;
-{
-    int n;
 
-    n = 0;
-    while (*s) {
-        if (ISSPACE(*s)) {
-            *s++ = '\0';
-            continue;
+static void
+processFontLine(FILE *        const fp,
+                const char *  const line,
+                const char ** const arg,
+                struct font * const fontP,
+                bool *        const endOfFontP) {
+
+    *endOfFontP = FALSE;  /* initial assumption */
+
+    if (streq(arg[0], "COMMENT")) {
+        /* ignore */
+    } else if (streq(arg[0], "SIZE")) {
+        /* ignore */
+    } else if (streq(arg[0], "STARTPROPERTIES")) {
+        unsigned int const propCount = atoi(arg[1]);
+        unsigned int i;
+        for (i = 0; i < propCount; ++i) {
+            char line[1024];
+            const char * arg[32];
+            int rc;
+            rc = readline(fp, line, arg);
+            if (rc < 0)
+                pm_error("End of file after STARTPROPERTIES in BDF font file");
         }
-        vec[n++] = s;
-        if (n >= mk_max)
-            break;
-        while (*s && !ISSPACE(*s))
-            s++;
+    } else if (streq(arg[0], "FONTBOUNDINGBOX")) {
+        fontP->maxwidth  = atoi(arg[1]);
+        fontP->maxheight = atoi(arg[2]);
+        fontP->x         = atoi(arg[3]);
+        fontP->y         = atoi(arg[4]);
+    } else if (streq(arg[0], "ENDFONT")) {
+        *endOfFontP = true;
+    } else if (!strcmp(arg[0], "CHARS"))
+        processCharsLine(fp, arg, fontP);
+}
+
+
+
+struct font *
+pbm_loadbdffont(const char * const name) {
+
+    FILE * fp;
+    char line[1024];
+    const char * arg[32];
+    struct font * fontP;
+    bool endOfFont;
+
+    fp = fopen(name, "rb");
+    if (!fp)
+        pm_error("Unable to open BDF font file name '%s'.  errno=%d (%s)",
+                 name, errno, strerror(errno));
+
+    expect(fp, "STARTFONT", arg, line);
+
+    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;
     }
-    vec[n] = 0;
+    fontP->x = fontP->y = 0;
 
-    /* Caller expects there to be at least one element in vec[], so we
-       can't return an empty line.  More advanced releases of Netpbm
-       just ignore blank lines.  If important, we can fairly easily port
-       the required code back to this release, by replacing readline()
-       with a current one, modified somewhat so it compiles here.
+    endOfFont = FALSE;
 
-       - Bryan 2010.03.23 
-    */
-    if (n < 1)
-        pm_error("Invalid font file -- contains a blank line");
+    while (!endOfFont) {
+        int rc;
+        rc = readline(fp, line, arg);
+        if (rc < 0)
+            pm_error("End of file before ENDFONT statement in BDF font file");
 
-    return n;
+        processFontLine(fp, line, arg, fontP, &endOfFont);
+    }
+    return fontP;
 }
diff --git a/lib/libpgm.h b/lib/libpgm.h
index ba51f9a9..7523faaf 100644
--- a/lib/libpgm.h
+++ b/lib/libpgm.h
@@ -12,13 +12,4 @@ pgm_readpgminitrest(FILE * const file,
                     int *  const rowsP, 
                     gray * const maxvalP);
 
-gray
-pgm_getrawsample(FILE * const file,
-                 gray   const maxval);
-
-void
-pgm_writerawsample(FILE * const fileP,
-                   gray   const val,
-                   gray   const maxval);
-
 #endif
diff --git a/lib/libpgm1.c b/lib/libpgm1.c
index 75fa365b..4d93e4be 100644
--- a/lib/libpgm1.c
+++ b/lib/libpgm1.c
@@ -10,7 +10,7 @@
 ** implied warranty.
 */
 
-/* See libpbm.c for the complicated explanation of this 32/64 bit file
+/* See pmfileio.c for the complicated explanation of this 32/64 bit file
    offset stuff.
 */
 #define _FILE_OFFSET_BITS 64
@@ -28,6 +28,7 @@
 #include "libpam.h"
 #include "fileio.h"
 #include "mallocvar.h"
+#include "nstring.h"
 
 
 gray *
@@ -49,13 +50,14 @@ void
 pgm_init(int *   const argcP,
          char ** const argv) {
 
-    pbm_init( argcP, argv );
+    pbm_init(argcP, argv);
 }
 
 
 
 void
-pgm_nextimage(FILE * const file, int * const eofP) {
+pgm_nextimage(FILE * const file,
+              int *  const eofP) {
     pm_nextimage(file, eofP);
 }
 
@@ -121,11 +123,6 @@ pgm_readpgminit(FILE * const fileP,
     /* Check magic number. */
     realFormat = pm_readmagicnumber(fileP);
     switch (PAM_FORMAT_TYPE(realFormat)) {
-    case PGM_TYPE:
-        *formatP = realFormat;
-        pgm_readpgminitrest(fileP, colsP, rowsP, maxvalP);
-        break;
-        
     case PBM_TYPE:
         *formatP = realFormat;
         pbm_readpbminitrest(fileP, colsP, rowsP);
@@ -149,6 +146,15 @@ pgm_readpgminit(FILE * const fileP,
         *maxvalP = PGM_MAXMAXVAL;
         break;
 
+    case PGM_TYPE:
+        *formatP = realFormat;
+        pgm_readpgminitrest(fileP, colsP, rowsP, maxvalP);
+        break;
+        
+    case PPM_TYPE:
+        pm_error("Input file is a PPM, which this program cannot process.  "
+                 "You may want to convert it to PGM with 'ppmtopgm'");
+
     case PAM_TYPE:
         pnm_readpaminitrestaspnm(fileP, colsP, rowsP, maxvalP, formatP);
 
@@ -158,40 +164,106 @@ pgm_readpgminit(FILE * const fileP,
         break;
 
     default:
-        pm_error("bad magic number - not a pgm or pbm file");
+        pm_error("bad magic number - not a Netpbm file");
     }
     validateComputableSize(*colsP, *rowsP);
 }
 
 
 
-gray
-pgm_getrawsample(FILE * const file,
-                 gray   const maxval) {
+static void
+readRpgmRow(FILE * const fileP,
+               gray * const grayrow, 
+               int    const cols,
+               gray   const maxval,
+               int    const format) {
+
+    unsigned int const bytesPerSample = maxval < 256 ? 1 : 2;
+    int          const bytesPerRow    = cols * bytesPerSample;
+    
+    unsigned char * rowBuffer;
+    const char * error;
+    
+    MALLOCARRAY(rowBuffer, bytesPerRow);
+    if (rowBuffer == NULL)
+        asprintfN(&error, "Unable to allocate memory for row buffer "
+                  "for %u columns", cols);
+    else {
+        ssize_t rc;
+        rc = fread(rowBuffer, 1, bytesPerRow, fileP);
+        if (rc == 0)
+            asprintfN(&error, "Error reading row.  fread() errno=%d (%s)",
+                      errno, strerror(errno));
+        else if (rc != bytesPerRow)
+            asprintfN(&error, "Error reading row.  Short read of %u bytes "
+                      "instead of %u", rc, bytesPerRow);
+        else {
+            error = NULL;
+            if (maxval < 256) {
+                unsigned int col;
+                for (col = 0; col < cols; ++col)
+                    grayrow[col] = (gray)rowBuffer[col];
+            } else {
+                unsigned int bufferCursor;
+                unsigned int col;
+                
+                bufferCursor = 0;  /* Start at beginning of rowBuffer[] */
+                
+                for (col = 0; col < cols; ++col) {
+                    gray g;
+                    
+                    g = rowBuffer[bufferCursor++] << 8;
+                    g |= rowBuffer[bufferCursor++];
+                    
+                    grayrow[col] = g;
+                }
+            }
+        }
+        free(rowBuffer);
+    }
+    if (error) {
+        pm_errormsg("%s", error);
+        strfree(error);
+        pm_longjmp();
+    }
+} 
+
+
 
-    if (maxval < 256) {
-        /* The sample is just one byte.  Read it. */
-        return(pm_getrawbyte(file));
+static void
+readPbmRow(FILE * const fileP,
+           gray * const grayrow, 
+           int    const cols,
+           gray   const maxval,
+           int    const format) {
+
+    jmp_buf jmpbuf;
+    jmp_buf * origJmpbufP;
+    bit * bitrow;
+    
+    bitrow = pbm_allocrow(cols);
+    if (setjmp(jmpbuf) != 0) {
+        pbm_freerow(bitrow);
+        pm_setjmpbuf(origJmpbufP);
+        pm_longjmp();
     } else {
-        /* The sample is two bytes.  Read both. */
-        unsigned char byte_pair[2];
-        size_t pairs_read;
-
-        pairs_read = fread(&byte_pair, 2, 1, file);
-        if (pairs_read == 0) 
-            pm_error("EOF /read error while reading a long sample");
-        /* This could be a few instructions faster if exploited the internal
-           format (i.e. endianness) of a pixval.  Then we might be able to
-           skip the shifting and oring.
-           */
-        return((byte_pair[0]<<8) | byte_pair[1]);
+        unsigned int col;
+
+        pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
+
+        pbm_readpbmrow(fileP, bitrow, cols, format);
+        for (col = 0; col < cols; ++col)
+            grayrow[col] = (bitrow[col] == PBM_WHITE ) ? maxval : 0;
+
+        pm_setjmpbuf(origJmpbufP);
     }
+    pbm_freerow(bitrow);
 }
 
 
 
 void
-pgm_readpgmrow(FILE * const file,
+pgm_readpgmrow(FILE * const fileP,
                gray * const grayrow, 
                int    const cols,
                gray   const maxval,
@@ -201,7 +273,7 @@ pgm_readpgmrow(FILE * const file,
     case PGM_FORMAT: {
         unsigned int col;
         for (col = 0; col < cols; ++col) {
-            grayrow[col] = pm_getuint(file);
+            grayrow[col] = pm_getuint(fileP);
             if (grayrow[col] > maxval)
                 pm_error("value out of bounds (%u > %u)",
                          grayrow[col], maxval);
@@ -209,86 +281,56 @@ pgm_readpgmrow(FILE * const file,
     }
     break;
         
-    case RPGM_FORMAT: {
-        unsigned int const bytesPerSample = maxval < 256 ? 1 : 2;
-        int          const bytesPerRow    = cols * bytesPerSample;
-
-        unsigned char * rowBuffer;
-        ssize_t rc;
-
-        MALLOCARRAY(rowBuffer, bytesPerRow);
-        if (rowBuffer == NULL)
-            pm_error("Unable to allocate memory for row buffer "
-                     "for %u columns", cols);
-
-        rc = fread(rowBuffer, 1, bytesPerRow, file);
-        if (rc == 0)
-            pm_error("Error reading row.  fread() errno=%d (%s)",
-                     errno, strerror(errno));
-        else if (rc != bytesPerRow)
-            pm_error("Error reading row.  Short read of %u bytes "
-                     "instead of %u", rc, bytesPerRow);
-
-        if (maxval < 256) {
-            unsigned int col;
-            for (col = 0; col < cols; ++col)
-                grayrow[col] = (gray)rowBuffer[col];
-        } else {
-            unsigned int bufferCursor;
-            unsigned int col;
-
-            bufferCursor = 0;  /* Start at beginning of rowBuffer[] */
-
-            for (col = 0; col < cols; ++col) {
-                gray g;
-
-                g = rowBuffer[bufferCursor++] << 8;
-                g |= rowBuffer[bufferCursor++];
-
-                grayrow[col] = g;
-            }
-        }
-        free(rowBuffer);
-    }
+    case RPGM_FORMAT:
+        readRpgmRow(fileP, grayrow, cols, maxval, format);
         break;
     
     case PBM_FORMAT:
-    case RPBM_FORMAT: {
-        bit * bitrow;
-        int col;
-
-        bitrow = pbm_allocrow(cols);
-        pbm_readpbmrow( file, bitrow, cols, format );
-        for (col = 0; col < cols; ++col)
-            grayrow[col] = (bitrow[col] == PBM_WHITE ) ? maxval : 0;
-        pbm_freerow(bitrow);
-    }
+    case RPBM_FORMAT:
+        readPbmRow(fileP, grayrow, cols, maxval, format);
         break;
         
     default:
-        pm_error( "can't happen" );
+        pm_error("can't happen");
     }
 }
 
 
 
 gray **
-pgm_readpgm(FILE * const file,
+pgm_readpgm(FILE * const fileP,
             int *  const colsP,
             int *  const rowsP, 
             gray * const maxvalP) {
 
-    gray** grays;
-    int row;
+    gray ** grays;
+    int rows, cols;
+    gray maxval;
     int format;
+    jmp_buf jmpbuf;
+    jmp_buf * origJmpbufP;
 
-    pgm_readpgminit( file, colsP, rowsP, maxvalP, &format );
+    pgm_readpgminit(fileP, &cols, &rows, &maxval, &format);
     
-    grays = pgm_allocarray( *colsP, *rowsP );
-    
-    for ( row = 0; row < *rowsP; ++row )
-        pgm_readpgmrow( file, grays[row], *colsP, *maxvalP, format );
+    grays = pgm_allocarray(cols, rows);
+
+    if (setjmp(jmpbuf) != 0) {
+        pgm_freearray(grays, rows);
+        pm_setjmpbuf(origJmpbufP);
+        pm_longjmp();
+    } else {
+        unsigned int row;
     
+        pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
+
+        for (row = 0; row < rows; ++row)
+            pgm_readpgmrow(fileP, grays[row], cols, maxval, format);
+
+        pm_setjmpbuf(origJmpbufP);
+    }
+    *colsP = cols;
+    *rowsP = rows;
+    *maxvalP = maxval;
     return grays;
 }
 
@@ -319,6 +361,5 @@ pgm_check(FILE *               const file,
         pm_filepos const need_raster_size = rows * bytes_per_row;
         
         pm_check(file, check_type, need_raster_size, retval_p);
-        
     }
 }
diff --git a/lib/libpgm2.c b/lib/libpgm2.c
index 7a04f09b..650d2cb5 100644
--- a/lib/libpgm2.c
+++ b/lib/libpgm2.c
@@ -16,7 +16,6 @@
 #include "pm_c_util.h"
 #include "mallocvar.h"
 #include "pgm.h"
-#include "libpgm.h"
 
 
 
@@ -57,35 +56,6 @@ putus(unsigned short const n,
 
 
 
-void
-pgm_writerawsample(FILE * const fileP,
-                   gray   const val,
-                   gray   const maxval) {
-
-    if (maxval < 256) {
-        /* Samples fit in one byte, so write just one byte */
-        int rc;
-        rc = putc(val, fileP);
-        if (rc == EOF)
-            pm_error("Error writing single byte sample to file");
-    } else {
-        /* Samples are too big for one byte, so write two */
-        int n_written;
-        unsigned char outval[2];
-        /* We could save a few instructions if we exploited the internal
-           format of a gray, i.e. its endianness.  Then we might be able
-           to skip the shifting and anding.
-           */
-        outval[0] = val >> 8;
-        outval[1] = val & 0xFF;
-        n_written = fwrite(&outval, 2, 1, fileP);
-        if (n_written == 0) 
-            pm_error("Error writing double byte sample to file");
-    }
-}
-
-
-
 static void
 format1bpsRow(const gray *    const grayrow,
               unsigned int    const cols,
diff --git a/lib/libpm.c b/lib/libpm.c
index f00f5d16..910d5666 100644
--- a/lib/libpm.c
+++ b/lib/libpm.c
@@ -1,69 +1,45 @@
 /**************************************************************************
                                   libpm.c
 ***************************************************************************
-  This is the most fundamental Netpbm library.  It contains routines
-  not specific to any particular Netpbm format.
+  This file contains fundamental libnetpbm services.
 
   Some of the subroutines in this library are intended and documented
   for use by Netpbm users, but most of them are just used by other
   Netpbm library subroutines.
-
-  Before May 2001, this function was served by the libpbm library
-  (in addition to being the library for handling the PBM format).
-
 **************************************************************************/
-#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
-       in some environments.
-    */
-#define _XOPEN_SOURCE 500    /* Make sure ftello, fseeko are defined */
-#define _LARGEFILE_SOURCE 1  /* Make sure ftello, fseeko are defined */
-#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 
-       case ftello() is still just ftello().
-
-       Likewise for all the other C library file functions.
 
-       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  
-    /* 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 */
+#define _XOPEN_SOURCE 500    /* Make sure ftello, fseeko are defined */
 
+#include <unistd.h>
 #include <stdio.h>
 #include <stdarg.h>
 #include <string.h>
 #include <errno.h>
 #include <setjmp.h>
-#ifdef __DJGPP__
-  #include <io.h>
-#endif
+#include <time.h>
+#include <limits.h>
 
 #include "pm_c_util.h"
+#include "mallocvar.h"
 #include "version.h"
 #include "compile.h"
 #include "nstring.h"
 #include "shhopt.h"
-#include "mallocvar.h"
+
 #include "pm.h"
 
 /* The following are set by pm_init(), then used by subsequent calls to other
    pm_xxx() functions.
    */
-static const char* pm_progname;
-static bool pm_showmessages;  
-    /* Programs should display informational messages (because the user didn't
-       specify the --quiet option).
-    */
+const char * pm_progname;
 
 int pm_plain_output;
     /* Boolean: programs should produce output in plain format */
 
+static bool pm_showmessages;  
+    /* Programs should display informational messages (because the user didn't
+       specify the --quiet option).
+    */
 static jmp_buf * pm_jmpbufP = NULL;
     /* A description of the point to which the program should hyperjump
        if a libnetpbm function encounters an error (libnetpbm functions
@@ -76,6 +52,17 @@ static jmp_buf * pm_jmpbufP = NULL;
        NULL, which is the default value, means when a libnetpbm function
        encounters an error, it causes the process to exit.
     */
+static pm_usererrormsgfn * userErrorMsgFn = NULL;
+    /* A function to call to issue an error message.
+
+       NULL means use the library default: print to Standard Error
+    */
+
+static pm_usermessagefn * userMessageFn = NULL;
+    /* A function to call to issue an error message.
+
+       NULL means use the library default: print to Standard Error
+    */
 
 
 
@@ -108,20 +95,24 @@ pm_longjmp(void) {
 
 
 void
-pm_usage(const char usage[]) {
-    pm_error("usage:  %s %s", pm_progname, usage);
+pm_setusererrormsgfn(pm_usererrormsgfn * fn) {
+
+    userErrorMsgFn = fn;
 }
 
 
 
 void
-pm_perror(const char reason[] ) {
+pm_setusermessagefn(pm_usermessagefn * fn) {
 
-    if (reason != NULL && strlen(reason) != 0)
-        pm_error("%s - errno=%d (%s)", reason, errno, strerror(errno));
-    else
-        pm_error("Something failed with errno=%d (%s)", 
-                 errno, strerror(errno));
+    userMessageFn = fn;
+}
+
+
+
+void
+pm_usage(const char usage[]) {
+    pm_error("usage:  %s %s", pm_progname, usage);
 }
 
 
@@ -134,43 +125,90 @@ pm_message(const char format[], ...) {
     va_start(args, format);
 
     if (pm_showmessages) {
-        fprintf(stderr, "%s: ", pm_progname);
-        vfprintf(stderr, format, args);
-        fputc('\n', stderr);
+        const char * msg;
+        vasprintfN(&msg, format, args);
+
+        if (userMessageFn)
+            userMessageFn(msg);
+        else
+            fprintf(stderr, "%s: %s\n", pm_progname, msg);
+
+        strfree(msg);
     }
     va_end(args);
 }
 
 
 
+static void
+errormsg(const char * const msg) {
+
+    if (userErrorMsgFn)
+        userErrorMsgFn(msg);
+    else
+        fprintf(stderr, "%s: %s\n", pm_progname, msg);
+}
+
+
+
+void PM_GNU_PRINTF_ATTR(1,2)
+pm_errormsg(const char format[], ...) {
+
+    va_list args;
+    const char * msg;
+
+    va_start(args, format);
+
+    vasprintfN(&msg, format, args);
+    
+    errormsg(msg);
+
+    strfree(msg);
+
+    va_end(args);
+}
+
+
+
 void PM_GNU_PRINTF_ATTR(1,2)
 pm_error(const char format[], ...) {
     va_list args;
+    const char * msg;
 
     va_start(args, format);
 
-    fprintf(stderr, "%s: ", pm_progname);
-    vfprintf(stderr, format, args);
-    fputc('\n', stderr);
+    vasprintfN(&msg, format, args);
+    
+    errormsg(msg);
+
+    strfree(msg);
+
     va_end(args);
 
     pm_longjmp();
 }
 
 
-/* Variable-sized arrays. */
 
-char *
+static void *
+mallocz(size_t const size) {
+
+    return malloc(MAX(1, size));
+}
+
+
+
+void *
 pm_allocrow(unsigned int const cols,
             unsigned int const size) {
 
-    char * itrow;
+    unsigned char * itrow;
 
-    if (UINT_MAX / cols < size)
+    if (cols != 0 && UINT_MAX / cols < size)
         pm_error("Arithmetic overflow multiplying %u by %u to get the "
                  "size of a row to allocate.", cols, size);
 
-    itrow = malloc(cols * size);
+    itrow = mallocz(cols * size);
     if (itrow == NULL)
         pm_error("out of memory allocating a row");
 
@@ -180,14 +218,70 @@ pm_allocrow(unsigned int const cols,
 
 
 void
-pm_freerow(char * const itrow) {
+pm_freerow(void * const itrow) {
     free(itrow);
 }
 
 
 
-char**
-pm_allocarray(int const cols, int const rows, int const size )  {
+static void
+allocarrayNoHeap(unsigned char ** const rowIndex,
+                 unsigned int     const cols,
+                 unsigned int     const rows,
+                 unsigned int     const size,
+                 const char **    const errorP) {
+
+    if (cols != 0 && UINT_MAX / cols < size)
+        asprintfN(errorP,
+                  "Arithmetic overflow multiplying %u by %u to get the "
+                  "size of a row to allocate.", cols, size);
+    else {
+        unsigned int rowsDone;
+
+        rowsDone = 0;
+        *errorP = NULL;
+
+        while (rowsDone < rows && !*errorP) {
+            unsigned char * const rowSpace = mallocz(cols * size);
+            if (rowSpace == NULL)
+                asprintfN(errorP,
+                          "Unable to allocate a %u-column by %u byte row",
+                          cols, size);
+            else
+                rowIndex[rowsDone++] = rowSpace;
+        }
+        if (*errorP) {
+            unsigned int row;
+            for (row = 0; row < rowsDone; ++row)
+                free(rowIndex[row]);
+        }
+    }
+}
+
+
+
+static unsigned char *
+allocRowHeap(unsigned int const cols,
+             unsigned int const rows,
+             unsigned int const size) {
+
+    unsigned char * retval;
+
+    if (cols != 0 && rows != 0 && UINT_MAX / cols / rows < size)
+        /* Too big even to request the memory ! */
+        retval = NULL;
+    else
+        retval = mallocz(rows * cols * size);
+
+    return retval;
+}
+
+
+
+char **
+pm_allocarray(int const cols,
+              int const rows,
+              int const size )  {
 /*----------------------------------------------------------------------------
    Allocate an array of 'rows' rows of 'cols' columns each, with each
    element 'size' bytes.
@@ -206,44 +300,46 @@ pm_allocarray(int const cols, int const rows, int const size )  {
    We use unfragmented format if possible, but if the allocation of the
    row heap fails, we fall back to fragmented.
 -----------------------------------------------------------------------------*/
-    char** rowIndex;
-    char * rowheap;
+    unsigned char ** rowIndex;
+    const char * error;
 
     MALLOCARRAY(rowIndex, rows + 1);
     if (rowIndex == NULL)
-        pm_error("out of memory allocating row index (%u rows) for an array",
-                 rows);
+        asprintfN(&error,
+                  "out of memory allocating row index (%u rows) for an array",
+                  rows);
+    else {
+        unsigned char * rowheap;
 
-    if (cols != 0 && rows != 0 && UINT_MAX / cols / rows < size)
-        /* Too big even to request the memory ! */
-        rowheap = NULL;
-    else
-        rowheap = malloc((unsigned int)rows * cols * size);
+        rowheap = allocRowHeap(cols, rows, size);
 
-    if (rowheap == NULL) {
-        /* We couldn't get the whole heap in one block, so try fragmented
-           format.
-        */
-        unsigned int row;
-        
-        rowIndex[rows] = NULL;   /* Declare it fragmented format */
-
-        for (row = 0; row < rows; ++row) {
-            rowIndex[row] = pm_allocrow(cols, size);
-            if (rowIndex[row] == NULL)
-                pm_error("out of memory allocating Row %u "
-                         "(%u columns, %u bytes per tuple) "
-                         "of an array", row, cols, size);
-        }
-    } else {
-        /* It's unfragmented format */
-        unsigned int row;
-        rowIndex[rows] = rowheap;  /* Declare it unfragmented format */
+        if (rowheap) {
+            /* It's unfragmented format */
 
-        for (row = 0; row < rows; ++row)
-            rowIndex[row] = &(rowheap[row * cols * size]);
+            rowIndex[rows] = rowheap;  /* Declare it unfragmented format */
+
+            if (rowheap) {
+                unsigned int row;
+                
+                for (row = 0; row < rows; ++row)
+                    rowIndex[row] = &(rowheap[row * cols * size]);
+            }
+            error = NULL;
+        } else {
+            /* We couldn't get the whole heap in one block, so try fragmented
+               format.
+            */
+            rowIndex[rows] = NULL;   /* Declare it fragmented format */
+            
+            allocarrayNoHeap(rowIndex, cols, rows, size, &error);
+        }
+    }
+    if (error) {
+        pm_errormsg("Couldn't allocate %u-row array.  %s", rows, error);
+        strfree(error);
+        pm_longjmp();
     }
-    return rowIndex;
+    return (char **)rowIndex;
 }
 
 
@@ -269,12 +365,12 @@ pm_freearray(char ** const rowIndex,
 /* Case-insensitive keyword matcher. */
 
 int
-pm_keymatch(char *       const strarg, 
+pm_keymatch(const char *       const strarg, 
             const char * const keywordarg, 
             int          const minchars) {
     int len;
-    const char *keyword;
-    char *str;
+    const char * keyword;
+    const char * str;
 
     str = strarg;
     keyword = keywordarg;
@@ -412,7 +508,8 @@ vmsProgname(int * const argcP, char * argv[]) {
 
 
 void
-pm_init(const char * const progname, unsigned int const flags) {
+pm_init(const char * const progname,
+        unsigned int const flags) {
 /*----------------------------------------------------------------------------
    Initialize static variables that Netpbm library routines use.
 
@@ -543,7 +640,7 @@ showNetpbmHelp(const char progname[]) {
 
 
 void
-pm_proginit(int * const argcP, char * argv[]) {
+pm_proginit(int * const argcP, const char * argv[]) {
 /*----------------------------------------------------------------------------
    Do various initialization things that all programs in the Netpbm package,
    and programs that emulate such programs, should do.
@@ -628,12 +725,13 @@ pm_setMessage(int const newState, int * const oldStateP) {
 }
 
 
+
 char *
 pm_arg0toprogname(const char arg0[]) {
 /*----------------------------------------------------------------------------
    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 is refers.
+   Netpbm program to which it refers.
 
    In the most ordinary case, this is simply the argument itself.
 
@@ -641,7 +739,7 @@ pm_arg0toprogname(const char arg0[]) {
    after the last slash, and if there is a .exe on it (as there is for
    DJGPP), that is removed.
 
-   The return value is in static storage within.  It is null-terminated,
+   The return value is in static storage within.  It is NUL-terminated,
    but truncated at 64 characters.
 -----------------------------------------------------------------------------*/
     static char retval[64+1];
@@ -667,831 +765,63 @@ pm_arg0toprogname(const char arg0[]) {
 
 
 
-/* File open/close that handles "-" as stdin/stdout and checks errors. */
-
-FILE*
-pm_openr(const char * const name) {
-    FILE* f;
-
-    if (strcmp(name, "-") == 0)
-        f = stdin;
-    else {
-#ifndef VMS
-        f = fopen(name, "rb");
-#else
-        f = fopen(name, "r", "ctx=stm");
-#endif
-        if (f == NULL) 
-            pm_error("Unable to open file '%s' for reading.  "
-                     "fopen() returns errno %d (%s)", 
-                     name, errno, strerror(errno));
-    }
-    return f;
-}
-
-
-
-FILE*
-pm_openw(const char * const name) {
-    FILE* f;
-
-    if (strcmp(name, "-") == 0)
-        f = stdout;
-    else {
-#ifndef VMS
-        f = fopen(name, "wb");
-#else
-        f = fopen(name, "w", "mbc=32", "mbf=2");  /* set buffer factors */
-#endif
-        if (f == NULL) 
-            pm_error("Unable to open file '%s' for writing.  "
-                     "fopen() returns errno %d (%s)", 
-                     name, errno, strerror(errno));
-    }
-    return f;
-}
-
-
-
-static const char *
-tmpDir(void) {
-/*----------------------------------------------------------------------------
-   Return the name of the directory in which we should create temporary
-   files.
-
-   The name is a constant in static storage.
------------------------------------------------------------------------------*/
-    const char * tmpdir;
-        /* running approximation of the result */
-
-    tmpdir = getenv("TMPDIR");   /* Unix convention */
-
-    if (!tmpdir || strlen(tmpdir) == 0)
-        tmpdir = getenv("TMP");  /* Windows convention */
-
-    if (!tmpdir || strlen(tmpdir) == 0)
-        tmpdir = getenv("TEMP"); /* Windows convention */
-
-    if (!tmpdir || strlen(tmpdir) == 0)
-        tmpdir = TMPDIR;
-
-    return tmpdir;
-}
-
-
-
-static int
-mkstempx(char * const filenameBuffer) {
-/*----------------------------------------------------------------------------
-  This is meant to be equivalent to POSIX mkstemp().
-
-  On some old systems, mktemp() is a security hazard that allows a hacker
-  to read or write our temporary file or cause us to read or write some
-  unintended file.  On other systems, mkstemp() does not exist.
-
-  A Windows/mingw environment is one which doesn't have mkstemp()
-  (2006.06.15).
-
-  We assume that if a system doesn't have mkstemp() that its mktemp()
-  is safe, or that the total situation is such that the problems of
-  mktemp() are not a problem for the user.
------------------------------------------------------------------------------*/
-    int retval;
-    int fd;
-    unsigned int attempts;
-    bool gotFile;
-    bool error;
-
-    for (attempts = 0, gotFile = FALSE, error = FALSE;
-         !gotFile && !error && attempts < 100;
-         ++attempts) {
-
-        char * rc;
-        rc = mktemp(filenameBuffer);
-
-        if (rc == NULL)
-            error = TRUE;
-        else {
-            int rc;
-
-            rc = open(filenameBuffer, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
-
-            if (rc == 0) {
-                fd = rc;
-                gotFile = TRUE;
-            } else {
-                if (errno == EEXIST) {
-                    /* We'll just have to keep trying */
-                } else 
-                    error = TRUE;
-            }
-        }
-    }    
-    if (gotFile)
-        retval = fd;
-    else
-        retval = -1;
-
-    return retval;
-}
-
-
-
-static int
-mkstemp2(char * const filenameBuffer) {
-
-#if HAVE_MKSTEMP
-    if (0)
-        mkstempx(NULL);  /* defeat compiler unused function warning */
-    return mkstemp(filenameBuffer);
-#else
-    return mkstempx(filenameBuffer);
-#endif
-}
-
-
-
-void
-pm_make_tmpfile(FILE **       const filePP,
-                const char ** const filenameP) {
-
-    int fd;
-    FILE * fileP;
-    const char * filenameTemplate;
-    char * filenameBuffer;  /* malloc'ed */
-    unsigned int fnamelen;
-    const char * tmpdir;
-    const char * dirseparator;
-
-    fnamelen = strlen (pm_progname) + 10; /* "/" + "_XXXXXX\0" */
-
-    tmpdir = tmpDir();
-
-    if (tmpdir[strlen(tmpdir) - 1] == '/')
-        dirseparator = "";
-    else
-        dirseparator = "/";
-    
-    asprintfN(&filenameTemplate, "%s%s%s%s", 
-              tmpdir, dirseparator, pm_progname, "_XXXXXX");
-
-    if (filenameTemplate == NULL)
-        pm_error("Unable to allocate storage for temporary file name");
-
-    filenameBuffer = strdup(filenameTemplate);
-
-    fd = mkstemp2(filenameBuffer);
-
-    if (fd < 0)
-        pm_error("Unable to create temporary file according to name "
-                 "pattern '%s'.  mkstemp() failed with "
-                 "errno %d (%s)", filenameTemplate, errno, strerror(errno));
-    else {
-        fileP = fdopen(fd, "w+b");
-
-        if (fileP == NULL)
-            pm_error("Unable to create temporary file.  fdopen() failed "
-                     "with errno %d (%s)", errno, strerror(errno));
-    }
-    strfree(filenameTemplate);
-
-    *filenameP = filenameBuffer;
-    *filePP = fileP;
-}
-
-
-
-FILE * 
-pm_tmpfile(void) {
-
-    FILE * fileP;
-    const char * tmpfile;
-
-    pm_make_tmpfile(&fileP, &tmpfile);
-
-    unlink(tmpfile);
+unsigned int
+pm_randseed(void) {
 
-    strfree(tmpfile);
+    return time(NULL) ^ getpid();
 
-    return fileP;
 }
 
 
 
-FILE *
-pm_openr_seekable(const char name[]) {
+unsigned int
+pm_parse_width(const char * const arg) {
 /*----------------------------------------------------------------------------
-  Open the file named by name[] such that it is seekable (i.e. it can be
-  rewound and read in multiple passes with fseek()).
-
-  If the file is actually seekable, this reduces to the same as
-  pm_openr().  If not, we copy the named file to a temporary file
-  and return that file's stream descriptor.
-
-  We use a file that the operating system recognizes as temporary, so
-  it picks the filename and deletes the file when Caller closes it.
+   Return the image width represented by the decimal ASCIIZ string
+   'arg'.  Fail if it doesn't validly represent a width or represents
+   a width that can't be conveniently used in computation.
 -----------------------------------------------------------------------------*/
-    int stat_rc;
-    int seekable;  /* logical: file is seekable */
-    struct stat statbuf;
-    FILE * original_file;
-    FILE * seekable_file;
-
-    original_file = pm_openr((char *) name);
-
-    /* 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
-       0 if the file is positioned at 0 but you can't actually back up
-       to 0.  I have seen documentation that says the errno for an
-       unseekable stream is EBADF and in practice seen ESPIPE.
-
-       On the other hand, regular files are always seekable and even if
-       some other file is, it doesn't hurt much to assume it isn't.
-    */
+    unsigned int width;
+    const char * error;
 
-    stat_rc = fstat(fileno(original_file), &statbuf);
-    if (stat_rc == 0 && S_ISREG(statbuf.st_mode))
-        seekable = TRUE;
-    else 
-        seekable = FALSE;
+    interpret_uint(arg, &width, &error);
 
-    if (seekable) {
-        seekable_file = original_file;
+    if (error) {
+        pm_error("'%s' is invalid as an image width.  %s", arg, error);
+        strfree(error);
     } else {
-        seekable_file = pm_tmpfile();
-        
-        /* Copy the input into the temporary seekable file */
-        while (!feof(original_file) && !ferror(original_file) 
-               && !ferror(seekable_file)) {
-            char buffer[4096];
-            int bytes_read;
-            bytes_read = fread(buffer, 1, sizeof(buffer), original_file);
-            fwrite(buffer, 1, bytes_read, seekable_file);
-        }
-        if (ferror(original_file))
-            pm_error("Error reading input file into temporary file.  "
-                     "Errno = %s (%d)", strerror(errno), errno);
-        if (ferror(seekable_file))
-            pm_error("Error writing input into temporary file.  "
-                     "Errno = %s (%d)", strerror(errno), errno);
-        pm_close(original_file);
-        {
-            int seek_rc;
-            seek_rc = fseek(seekable_file, 0, SEEK_SET);
-            if (seek_rc != 0)
-                pm_error("fseek() failed to rewind temporary file.  "
-                         "Errno = %s (%d)", strerror(errno), errno);
-        }
+        if (width > INT_MAX-10)
+            pm_error("Width %u is too large for computations.", width);
+        if (width == 0)
+            pm_error("Width argument must be a positive number.  You "
+                     "specified 0.");
     }
-    return seekable_file;
-}
-
-
-
-void
-pm_close(FILE * const f) {
-    fflush(f);
-    if (ferror(f))
-        pm_message("A file read or write error occurred at some point");
-    if (f != stdin)
-        if (fclose(f) != 0)
-            pm_error("close of file failed with errno %d (%s)",
-                     errno, strerror(errno));
-}
-
-
-
-/* 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.
-*/
-
-void
-pm_closer(FILE * const f) {
-    pm_close(f);
-}
-
-
-
-void
-pm_closew(FILE * const f) {
-    pm_close(f);
-}
-
-
-
-/* Endian I/O.
-
-   Before Netpbm 10.27 (March 2005), these would return failure on EOF
-   or I/O failure.  For backward compatibility, they still have the return
-   code, but it is always zero and the routines abort the program in case
-   of EOF or I/O failure.  A program that wants to handle failure differently
-   must use lower level (C library) interfaces.  But that level of detail
-   is uncharacteristic of a Netpbm program; the ease of programming that
-   comes with not checking a return code is more Netpbm.
-
-   It is also for historical reasons that these return signed values,
-   when clearly unsigned would make more sense.
-*/
-
-
-
-static void
-abortWithReadError(FILE * const ifP) {
-
-    if (feof(ifP))
-        pm_error("Unexpected end of input file");
-    else
-        pm_error("Error (not EOF) reading file.");
-}
-
-
-
-void
-pm_readchar(FILE * const ifP,
-            char * const cP) {
-    
-    int c;
-
-    c = getc(ifP);
-    if (c == EOF)
-        abortWithReadError(ifP);
-
-    *cP = c;
-}
-
-
-
-void
-pm_writechar(FILE * const ofP,
-             char   const c) {
-
-    putc(c, ofP);
-}
-
-
-
-int
-pm_readbigshort(FILE *  const ifP, 
-                short * const sP) {
-    int c;
-
-    unsigned short s;
-
-    c = getc(ifP);
-    if (c == EOF)
-        abortWithReadError(ifP);
-    s = (c & 0xff) << 8;
-    c = getc(ifP);
-    if (c == EOF)
-        abortWithReadError(ifP);
-    s |= c & 0xff;
-
-    *sP = s;
-
-    return 0;
-}
-
-
-
-int
-pm_writebigshort(FILE * const ofP, 
-                 short  const s) {
-
-    putc((s >> 8) & 0xff, ofP);
-    putc(s & 0xff, ofP);
-
-    return 0;
-}
-
-
-
-int
-pm_readbiglong(FILE * const ifP, 
-               long * const lP) {
-
-    int c;
-    unsigned long l;
-
-    c = getc(ifP);
-    if (c == EOF)
-        abortWithReadError(ifP);
-    l = c << 24;
-    c = getc(ifP);
-    if (c == EOF)
-        abortWithReadError(ifP);
-    l |= c << 16;
-    c = getc(ifP);
-    if (c == EOF)
-        abortWithReadError(ifP);
-    l |= c << 8;
-    c = getc(ifP);
-    if (c == EOF)
-        abortWithReadError(ifP);
-    l |= c;
-
-    *lP = l;
-
-    return 0;
-}
-
-
-
-int
-pm_writebiglong(FILE * const ofP, 
-                long   const l) {
-
-    putc((l >> 24) & 0xff, ofP);
-    putc((l >> 16) & 0xff, ofP);
-    putc((l >>  8) & 0xff, ofP);
-    putc((l >>  0) & 0xff, ofP);
-
-    return 0;
-}
-
-
-
-int
-pm_readlittleshort(FILE *  const ifP, 
-                   short * const sP) {
-    int c;
-    unsigned short s;
-
-    c = getc(ifP);
-    if (c == EOF)
-        abortWithReadError(ifP);
-    s = c & 0xff;
-
-    c = getc(ifP);
-    if (c == EOF)
-        abortWithReadError(ifP);
-    s |= (c & 0xff) << 8;
-
-    *sP = s;
-
-    return 0;
-}
-
-
-
-int
-pm_writelittleshort(FILE * const ofP, 
-                    short  const s) {
-
-    putc((s >> 0) & 0xff, ofP);
-    putc((s >> 8) & 0xff, ofP);
-
-    return 0;
-}
-
-
-
-int
-pm_readlittlelong(FILE * const ifP, 
-                  long * const lP) {
-    int c;
-    unsigned long l;
-
-    c = getc(ifP);
-    if (c == EOF)
-        abortWithReadError(ifP);
-    l = c;
-    c = getc(ifP);
-    if (c == EOF)
-        abortWithReadError(ifP);
-    l |= c << 8;
-    c = getc(ifP);
-    if (c == EOF)
-        abortWithReadError(ifP);
-    l |= c << 16;
-    c = getc(ifP);
-    if (c == EOF)
-        abortWithReadError(ifP);
-    l |= c << 24;
-
-    *lP = l;
-
-    return 0;
-}
-
-
-
-int
-pm_writelittlelong(FILE * const ofP, 
-                   long   const l) {
-
-    putc((l >>  0) & 0xff, ofP);
-    putc((l >>  8) & 0xff, ofP);
-    putc((l >> 16) & 0xff, ofP);
-    putc((l >> 24) & 0xff, ofP);
-
-    return 0;
-}
-
-
-
-int 
-pm_readmagicnumber(FILE * const ifP) {
-
-    int ich1, ich2;
-
-    ich1 = getc(ifP);
-    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." );
-
-    return ich1 * 256 + ich2;
-}
-
-
-
-/* Read a file of unknown size to a buffer. Return the number of bytes
-   read. Allocate more memory as we need it. The calling routine has
-   to free() the buffer.
-
-   Oliver Trepte, oliver@fysik4.kth.se, 930613 */
-
-#define PM_BUF_SIZE 16384      /* First try this size of the buffer, then
-                                   double this until we reach PM_MAX_BUF_INC */
-#define PM_MAX_BUF_INC 65536   /* Don't allocate more memory in larger blocks
-                                   than this. */
-
-char *
-pm_read_unknown_size(FILE * const file, 
-                     long * const nread) {
-    long nalloc;
-    char * buf;
-    bool eof;
-
-    *nread = 0;
-    nalloc = PM_BUF_SIZE;
-    MALLOCARRAY(buf, nalloc);
-
-    eof = FALSE;  /* initial value */
-
-    while(!eof) {
-        int val;
-
-        if (*nread >= nalloc) { /* We need a larger buffer */
-            if (nalloc > PM_MAX_BUF_INC)
-                nalloc += PM_MAX_BUF_INC;
-            else
-                nalloc += nalloc;
-            REALLOCARRAY_NOFAIL(buf, nalloc);
-        }
-
-        val = getc(file);
-        if (val == EOF)
-            eof = TRUE;
-        else 
-            buf[(*nread)++] = val;
-    }
-    return buf;
-}
-
-
-
-union cheat {
-    uint32n l;
-    short s;
-    unsigned char c[4];
-};
-
-
-
-short
-pm_bs_short(short const s) {
-    union cheat u;
-    unsigned char t;
-
-    u.s = s;
-    t = u.c[0];
-    u.c[0] = u.c[1];
-    u.c[1] = t;
-    return u.s;
-}
-
-
-
-long
-pm_bs_long(long const l) {
-    union cheat u;
-    unsigned char t;
-
-    u.l = l;
-    t = u.c[0];
-    u.c[0] = u.c[3];
-    u.c[3] = t;
-    t = u.c[1];
-    u.c[1] = u.c[2];
-    u.c[2] = t;
-    return u.l;
-}
-
-
-
-void
-pm_tell2(FILE *       const fileP, 
-         void *       const fileposP,
-         unsigned int const fileposSize) {
-/*----------------------------------------------------------------------------
-   Return the current file position as *filePosP, which is a buffer
-   'fileposSize' bytes long.  Abort the program if error, including if
-   *fileP isn't a file that has a position.
------------------------------------------------------------------------------*/
-    /* Note: FTELLO() is either ftello() or ftell(), depending on the
-       capabilities of the underlying C library.  It is defined in
-       pm_config.h.  ftello(), in turn, may be either ftell() or
-       ftello64(), as implemented by the C library.
-    */
-    pm_filepos const filepos = FTELLO(fileP);
-    if (filepos < 0)
-        pm_error("ftello() to get current file position failed.  "
-                 "Errno = %s (%d)\n", strerror(errno), errno);
-
-    if (fileposSize == sizeof(pm_filepos)) {
-        pm_filepos * const fileposP_filepos = fileposP;
-        *fileposP_filepos = filepos;
-    } else if (fileposSize == sizeof(long)) {
-        if (sizeof(pm_filepos) > sizeof(long) &&
-            filepos >= (pm_filepos) 1 << (sizeof(long)*8))
-            pm_error("File size is too large to represent in the %u bytes "
-                     "that were provided to pm_tell2()", fileposSize);
-        else {
-            long * const fileposP_long = fileposP;
-            *fileposP_long = (long)filepos;
-        }
-    } else
-        pm_error("File position size passed to pm_tell() is invalid: %u.  "
-                 "Valid sizes are %u and %u", 
-                 fileposSize, sizeof(pm_filepos), sizeof(long));
+    return width;
 }
 
 
 
 unsigned int
-pm_tell(FILE * const fileP) {
-    
-    long filepos;
-
-    pm_tell2(fileP, &filepos, sizeof(filepos));
-
-    return filepos;
-}
-
-
-
-void
-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)) 
-        /* 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
-           fseeko64(), as implemented by the C library.
-        */
-        FSEEKO(fileP, *fileposP, SEEK_SET);
-    else if (fileposSize == sizeof(long)) {
-        long const fileposLong = *(long *)fileposP;
-        fseek(fileP, fileposLong, SEEK_SET);
-    } else
-        pm_error("File position size passed to pm_seek() is invalid: %u.  "
-                 "Valid sizes are %u and %u", 
-                 fileposSize, sizeof(pm_filepos), sizeof(long));
-}
-
-
-
-void
-pm_seek(FILE * const fileP, unsigned long filepos) {
+pm_parse_height(const char * const arg) {
 /*----------------------------------------------------------------------------
+  Same as pm_parse_width(), but for height.
 -----------------------------------------------------------------------------*/
+    unsigned int height;
+    const char * error;
 
-    pm_filepos fileposBuff;
-
-    fileposBuff = filepos;
-
-    pm_seek2(fileP, &fileposBuff, sizeof(fileposBuff));
-}
-
-
+    interpret_uint(arg, &height, &error);
 
-void
-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, 
-   because the plain formats do permit white space after the raster.
-
-   Iff there is no next image, return *eofP == TRUE.
-
-   Note that in practice, we will not normally see white space here in
-   a plain PPM or plain PGM stream because the routine to read a
-   sample from the image reads one character of white space after the
-   sample in order to know where the sample ends.  There is not
-   normally more than one character of white space (a newline) after
-   the last sample in the raster.  But plain PBM is another story.  No white
-   space is required between samples of a plain PBM image.  But the raster
-   normally ends with a newline nonetheless.  Since the sample reading code
-   will not have read that newline, it is there for us to read now.
------------------------------------------------------------------------------*/
-    bool eof;
-    bool nonWhitespaceFound;
-
-    eof = FALSE;
-    nonWhitespaceFound = FALSE;
-
-    while (!eof && !nonWhitespaceFound) {
-        int c;
-        c = getc(file);
-        if (c == EOF) {
-            if (feof(file)) 
-                eof = TRUE;
-            else
-                pm_error("File error on getc() to position to image");
-        } else {
-            if (!isspace(c)) {
-                int rc;
-
-                nonWhitespaceFound = TRUE;
-
-                /* Have to put the non-whitespace character back in
-                   the stream -- it's part of the next image.  
-                */
-                rc = ungetc(c, file);
-                if (rc == EOF) 
-                    pm_error("File error doing ungetc() "
-                             "to position to image.");
-            }
-        }
+    if (error) {
+        pm_error("'%s' is invalid as an image height.  %s", arg, error);
+        strfree(error);
+    } else {
+        if (height > INT_MAX-10)
+            pm_error("Height %u is too large for computations.", height);
+        if (height == 0)
+            pm_error("Height argument must be a positive number.  You "
+                     "specified 0.");
     }
-    *eofP = eof;
-}
-
-
-
-void
-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) {
-/*----------------------------------------------------------------------------
-   This is not defined for use outside of libnetpbm.
------------------------------------------------------------------------------*/
-    struct stat statbuf;
-    pm_filepos curpos;  /* Current position of file; -1 if none */
-    int rc;
-
-#ifdef LARGEFILEDEBUG
-    pm_message("pm_filepos received by pm_check() is %u bytes.",
-               sizeof(pm_filepos));
-#endif
-    /* Note: FTELLO() is either ftello() or ftell(), depending on the
-       capabilities of the underlying C library.  It is defined in
-       pm_config.h.  ftello(), in turn, may be either ftell() or
-       ftello64(), as implemented by the C library.
-    */
-    curpos = FTELLO(file);
-    if (curpos >= 0) {
-        /* This type of file has a current position */
-            
-        rc = fstat(fileno(file), &statbuf);
-        if (rc != 0) 
-            pm_error("fstat() failed to get size of file, though ftello() "
-                     "successfully identified\n"
-                     "the current position.  Errno=%s (%d)",
-                     strerror(errno), errno);
-        else if (!S_ISREG(statbuf.st_mode)) {
-            /* Not a regular file; we can't know its size */
-            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) have_raster_size);
-            else if (have_raster_size > need_raster_size) {
-                if (retval_p) *retval_p = PM_CHECK_TOO_LONG;
-            } else {
-                if (retval_p) *retval_p = PM_CHECK_OK;
-            }
-        }
-    } else
-        if (retval_p) *retval_p = PM_CHECK_UNCHECKABLE;
+    return height;
 }
 
 
diff --git a/lib/libpnm1.c b/lib/libpnm1.c
index 82f99b93..536e5dc4 100644
--- a/lib/libpnm1.c
+++ b/lib/libpnm1.c
@@ -133,6 +133,74 @@ pnm_readpnminit(FILE *   const fileP,
 
 
 
+static void
+readpgmrow(FILE * const fileP,
+           xel *  const xelrow,
+           int    const cols,
+           xelval const maxval,
+           int    const format) {
+
+    jmp_buf jmpbuf;
+    jmp_buf * origJmpbufP;
+    gray * grayrow;
+
+    grayrow = pgm_allocrow(cols);
+
+    if (setjmp(jmpbuf) != 0) {
+        pgm_freerow(grayrow);
+        pm_setjmpbuf(origJmpbufP);
+        pm_longjmp();
+    } else {
+        unsigned int col;
+
+        pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
+
+        pgm_readpgmrow(fileP, grayrow, cols, (gray) maxval, format);
+
+        for (col = 0; col < cols; ++col)
+            PNM_ASSIGN1(xelrow[col], grayrow[col]);
+
+        pm_setjmpbuf(origJmpbufP);
+    }
+    pgm_freerow(grayrow);
+}
+
+
+
+static void
+readpbmrow(FILE * const fileP,
+               xel *  const xelrow,
+               int    const cols,
+               xelval const maxval,
+               int    const format) {
+
+    jmp_buf jmpbuf;
+    jmp_buf * origJmpbufP;
+    bit * bitrow;
+
+    bitrow = pbm_allocrow(cols);
+
+    if (setjmp(jmpbuf) != 0) {
+        pbm_freerow(bitrow);
+        pm_setjmpbuf(origJmpbufP);
+        pm_longjmp();
+    } else {
+        unsigned int col;
+
+        pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
+
+        pbm_readpbmrow(fileP, bitrow, cols, format);
+
+        for (col = 0; col < cols; ++col)
+            PNM_ASSIGN1(xelrow[col], bitrow[col] == PBM_BLACK ? 0 : maxval);
+
+        pm_setjmpbuf(origJmpbufP);
+    }
+    pbm_freerow(bitrow);
+}
+
+
+
 void
 pnm_readpnmrow(FILE * const fileP,
                xel *  const xelrow,
@@ -145,28 +213,13 @@ pnm_readpnmrow(FILE * const fileP,
         ppm_readppmrow(fileP, (pixel*) xelrow, cols, (pixval) maxval, format);
         break;
 
-    case PGM_TYPE: {
-        gray * grayrow;
-        unsigned int col;
-
-        grayrow = pgm_allocrow(cols);
-        pgm_readpgmrow(fileP, grayrow, cols, (gray) maxval, format);
-        for (col = 0; col < cols; ++col)
-            PNM_ASSIGN1(xelrow[col], grayrow[col]);
-        pgm_freerow(grayrow);
-    }
-    break;
+    case PGM_TYPE:
+        readpgmrow(fileP, xelrow, cols, maxval, format);
+        break;
         
-    case PBM_TYPE: {
-        bit * bitrow;
-        unsigned int col;
-        bitrow = pbm_allocrow(cols);
-        pbm_readpbmrow(fileP, bitrow, cols, format);
-        for (col = 0; col < cols; ++col)
-            PNM_ASSIGN1(xelrow[col], bitrow[col] == PBM_BLACK ? 0: maxval);
-        pbm_freerow(bitrow);
-    }
-    break;
+    case PBM_TYPE:
+        readpbmrow(fileP, xelrow, cols, maxval, format);
+        break;
 
     default:
         pm_error("INTERNAL ERROR.  Impossible format.");
@@ -182,15 +235,35 @@ pnm_readpnm(FILE *   const fileP,
             xelval * const maxvalP,
             int *    const formatP) {
 
+    jmp_buf jmpbuf;
+    jmp_buf * origJmpbufP;
+    int cols, rows;
+    xelval maxval;
+    int format;
     xel ** xels;
-    int row;
 
-    pnm_readpnminit(fileP, colsP, rowsP, maxvalP, formatP);
+    pnm_readpnminit(fileP, &cols, &rows, &maxval, &format);
+
+    xels = pnm_allocarray(cols, rows);
 
-    xels = pnm_allocarray(*colsP, *rowsP);
+    if (setjmp(jmpbuf) != 0) {
+        pnm_freearray(xels, rows);
+        pm_setjmpbuf(origJmpbufP);
+        pm_longjmp();
+    } else {
+        unsigned int row;
 
-    for (row = 0; row < *rowsP; ++row)
-        pnm_readpnmrow(fileP, xels[row], *colsP, *maxvalP, *formatP);
+        pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
+
+        for (row = 0; row < rows; ++row)
+            pnm_readpnmrow(fileP, xels[row], cols, maxval, format);
+
+        pm_setjmpbuf(origJmpbufP);
+    }
+    *colsP = cols;
+    *rowsP = rows;
+    *maxvalP = maxval;
+    *formatP = format;
 
     return xels;
 }
diff --git a/lib/libpnm2.c b/lib/libpnm2.c
index aae78d52..01ffa389 100644
--- a/lib/libpnm2.c
+++ b/lib/libpnm2.c
@@ -15,13 +15,10 @@
 #include "pnm.h"
 
 #include "ppm.h"
-#include "libppm.h"
 
 #include "pgm.h"
-#include "libpgm.h"
 
 #include "pbm.h"
-#include "libpbm.h"
 
 void
 pnm_writepnminit(FILE * const fileP, 
@@ -55,51 +52,97 @@ pnm_writepnminit(FILE * const fileP,
 
 
 
-void
-pnm_writepnmrow(FILE * const fileP, 
-                xel *  const xelrow, 
-                int    const cols, 
-                xelval const maxval, 
-                int    const format, 
-                int    const forceplain) {
-
-    bool const plainFormat = forceplain || pm_plain_output;
+static void
+writepgmrow(FILE *       const fileP, 
+            const xel *  const xelrow, 
+            unsigned int const cols, 
+            xelval       const maxval, 
+            int          const format, 
+            bool         const plainFormat) {
     
-    switch (PNM_FORMAT_TYPE(format)) {
-    case PPM_TYPE:
-        ppm_writeppmrow(fileP, (pixel*) xelrow, cols, (pixval) maxval, 
-                        plainFormat);
-        break;
-
-    case PGM_TYPE: {
-        gray* grayrow;
+    jmp_buf jmpbuf;
+    jmp_buf * origJmpbufP;
+    gray * grayrow;
+    
+    grayrow = pgm_allocrow(cols);
+    
+    if (setjmp(jmpbuf) != 0) {
+        pgm_freerow(grayrow);
+        pm_setjmpbuf(origJmpbufP);
+        pm_longjmp();
+    } else {
         unsigned int col;
 
-        grayrow = pgm_allocrow(cols);
-
+        pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
+        
         for (col = 0; col < cols; ++col)
             grayrow[col] = PNM_GET1(xelrow[col]);
-
+    
         pgm_writepgmrow(fileP, grayrow, cols, (gray) maxval, plainFormat);
 
-        pgm_freerow( grayrow );
+        pm_setjmpbuf(origJmpbufP);
     }
-    break;
+    pgm_freerow(grayrow);
+}
+
+
 
-    case PBM_TYPE: {
-        bit* bitrow;
+static void
+writepbmrow(FILE *       const fileP,
+            const xel *  const xelrow,
+            unsigned int const cols,
+            bool         const plainFormat) {
+
+    jmp_buf jmpbuf;
+    jmp_buf * origJmpbufP;
+    bit * bitrow;
+
+    bitrow = pbm_allocrow(cols);
+    
+    if (setjmp(jmpbuf) != 0) {
+        pbm_freerow(bitrow);
+        pm_setjmpbuf(origJmpbufP);
+        pm_longjmp();
+    } else {
         unsigned int col;
 
-        bitrow = pbm_allocrow(cols);
+        pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
 
         for (col = 0; col < cols; ++col)
             bitrow[col] = PNM_GET1(xelrow[col]) == 0 ? PBM_BLACK : PBM_WHITE;
-
+    
         pbm_writepbmrow(fileP, bitrow, cols, plainFormat);
 
-        pbm_freerow(bitrow);
-    }    
-    break;
+        pm_setjmpbuf(origJmpbufP);
+    }
+    pbm_freerow(bitrow);
+}    
+
+
+
+void
+pnm_writepnmrow(FILE *      const fileP, 
+                const xel * const xelrow, 
+                int         const cols, 
+                xelval      const maxval, 
+                int         const format, 
+                int         const forceplain) {
+
+    bool const plainFormat = forceplain || pm_plain_output;
+    
+    switch (PNM_FORMAT_TYPE(format)) {
+    case PPM_TYPE:
+        ppm_writeppmrow(fileP, (pixel*) xelrow, cols, (pixval) maxval, 
+                        plainFormat);
+        break;
+
+    case PGM_TYPE:
+        writepgmrow(fileP, xelrow, cols, maxval, format, plainFormat);
+        break;
+
+    case PBM_TYPE:
+        writepbmrow(fileP, xelrow, cols, plainFormat);
+        break;
     
     default:
         pm_error("invalid format argument received by pnm_writepnmrow(): %d"
diff --git a/lib/libpnm3.c b/lib/libpnm3.c
index 99d35dc0..c41a2108 100644
--- a/lib/libpnm3.c
+++ b/lib/libpnm3.c
@@ -11,27 +11,54 @@
 */
 
 #include "pnm.h"
-
 #include "ppm.h"
-#include "libppm.h"
-
 #include "pgm.h"
-#include "libpgm.h"
-
 #include "pbm.h"
-#include "libpbm.h"
 
-#if __STDC__
-xel
-pnm_backgroundxel( xel** xels, int cols, int rows, xelval maxval, int format )
-#else /*__STDC__*/
+
+
+static xel
+mean4(int const format,
+      xel const a,
+      xel const b,
+      xel const c,
+      xel const d) {
+/*----------------------------------------------------------------------------
+   Return cartesian mean of the 4 colors.
+-----------------------------------------------------------------------------*/
+    xel retval;
+
+    switch (PNM_FORMAT_TYPE(format)) {
+    case PPM_TYPE:
+        PPM_ASSIGN(
+            retval,
+            (PPM_GETR(a) + PPM_GETR(b) + PPM_GETR(c) + PPM_GETR(d)) / 4,
+            (PPM_GETG(a) + PPM_GETG(b) + PPM_GETG(c) + PPM_GETG(d)) / 4,
+            (PPM_GETB(a) + PPM_GETB(b) + PPM_GETB(c) + PPM_GETB(d)) / 4);
+        break;
+
+    case PGM_TYPE:
+    case PBM_TYPE:
+        PNM_ASSIGN1(
+            retval,
+            (PNM_GET1(a) + PNM_GET1(b) + PNM_GET1(c) + PNM_GET1(d))/4);
+        break;
+
+    default:
+        pm_error( "Invalid format passed to pnm_backgroundxel()");
+    }
+    return retval;
+}
+
+
+
 xel
-pnm_backgroundxel( xels, cols, rows, maxval, format )
-    xel** xels;
-    int cols, rows, format;
-    xelval maxval;
-#endif /*__STDC__*/
-    {
+pnm_backgroundxel(xel**  const xels,
+                  int    const cols,
+                  int    const rows,
+                  xelval const maxval,
+                  int    const format) {
+
     xel bgxel, ul, ur, ll, lr;
 
     /* Guess a good background value. */
@@ -40,192 +67,143 @@ pnm_backgroundxel( xels, cols, rows, maxval, format )
     ll = xels[rows-1][0];
     lr = xels[rows-1][cols-1];
 
-    /* First check for three corners equal. */
-    if ( PNM_EQUAL( ul, ur ) && PNM_EQUAL( ur, ll ) )
-    bgxel = ul;
-    else if ( PNM_EQUAL( ul, ur ) && PNM_EQUAL( ur, lr ) )
-    bgxel = ul;
-    else if ( PNM_EQUAL( ul, ll ) && PNM_EQUAL( ll, lr ) )
-    bgxel = ul;
-    else if ( PNM_EQUAL( ur, ll ) && PNM_EQUAL( ll, lr ) )
-    bgxel = ur;
-    /* Nope, check for two corners equal. */
-    else if ( PNM_EQUAL( ul, ur ) || PNM_EQUAL( ul, ll ) ||
-          PNM_EQUAL( ul, lr ) )
-    bgxel = ul;
-    else if ( PNM_EQUAL( ur, ll ) || PNM_EQUAL( ur, lr ) )
-    bgxel = ur;
-    else if ( PNM_EQUAL( ll, lr ) )
-    bgxel = ll;
+    /* We first recognize three corners equal.  If not, we look for any
+       two.  If not, we just average all four.
+    */
+    if (PNM_EQUAL(ul, ur) && PNM_EQUAL(ur, ll))
+        bgxel = ul;
+    else if (PNM_EQUAL(ul, ur) && PNM_EQUAL(ur, lr))
+        bgxel = ul;
+    else if (PNM_EQUAL(ul, ll) && PNM_EQUAL(ll, lr))
+        bgxel = ul;
+    else if (PNM_EQUAL(ur, ll) && PNM_EQUAL(ll, lr))
+        bgxel = ur;
+    else if (PNM_EQUAL(ul, ur))
+        bgxel = ul;
+    else if (PNM_EQUAL(ul, ll))
+        bgxel = ul;
+    else if (PNM_EQUAL(ul, lr))
+        bgxel = ul;
+    else if (PNM_EQUAL(ur, ll))
+        bgxel = ur;
+    else if (PNM_EQUAL(ur, lr))
+        bgxel = ur;
+    else if (PNM_EQUAL(ll, lr))
+        bgxel = ll;
     else
-    {
-    /* Nope, we have to average the four corners.  This breaks the
-    ** rules of pnm, but oh well.  Let's try to do it portably. */
-    switch ( PNM_FORMAT_TYPE(format) )
-        {
-        case PPM_TYPE:
-        PPM_ASSIGN( bgxel,
-        (PPM_GETR(ul) + PPM_GETR(ur) + PPM_GETR(ll) + PPM_GETR(lr)) / 4,
-        (PPM_GETG(ul) + PPM_GETG(ur) + PPM_GETG(ll) + PPM_GETG(lr)) / 4,
-        (PPM_GETB(ul) + PPM_GETB(ur) + PPM_GETB(ll) + PPM_GETB(lr)) / 4 );
-        break;
+        bgxel = mean4(format, ul, ur, ll, lr);
 
-        case PGM_TYPE:
-        {
-        gray gul, gur, gll, glr;
-        gul = (gray) PNM_GET1( ul );
-        gur = (gray) PNM_GET1( ur );
-        gll = (gray) PNM_GET1( ll );
-        glr = (gray) PNM_GET1( lr );
-        PNM_ASSIGN1( bgxel, ( ( gul + gur + gll + glr ) / 4 ) );
-        break;
-        }
+    return bgxel;
+}
 
-        case PBM_TYPE:
-        pm_error(
-        "pnm_backgroundxel: four bits no two of which equal each other??" );
 
-        default:
-        pm_error( "Invalid format passed to pnm_backgroundxel()");
-        }
-    }
 
-    return bgxel;
-    }
-
-#if __STDC__
-xel
-pnm_backgroundxelrow( xel* xelrow, int cols, xelval maxval, int format )
-#else /*__STDC__*/
 xel
-pnm_backgroundxelrow( xelrow, cols, maxval, format )
-    xel* xelrow;
-    int cols, format;
-    xelval maxval;
-#endif /*__STDC__*/
-    {
+pnm_backgroundxelrow(xel *  const xelrow,
+                     int    const cols,
+                     xelval const maxval,
+                     int    const format) {
+
     xel bgxel, l, r;
 
     /* Guess a good background value. */
     l = xelrow[0];
     r = xelrow[cols-1];
 
-    /* First check for both corners equal. */
-    if ( PNM_EQUAL( l, r ) )
-    bgxel = l;
-    else
-    {
-    /* Nope, we have to average the two corners.  This breaks the
-    ** rules of pnm, but oh well.  Let's try to do it portably. */
-    switch ( PNM_FORMAT_TYPE(format) )
-        {
+    if (PNM_EQUAL(l, r))
+        /* Both corners are same color, so that's the background color,
+           without any extra computation.
+        */
+        bgxel = l;
+    else {
+        /* Corners are different, so use cartesian mean of them */
+        switch (PNM_FORMAT_TYPE(format)) {
         case PPM_TYPE:
-        PPM_ASSIGN(bgxel,
-                   (PPM_GETR(l) + PPM_GETR(r)) / 2,
-                   (PPM_GETG(l) + PPM_GETG(r)) / 2,
-                   (PPM_GETB(l) + PPM_GETB(r)) / 2
-            );
-        break;
+            PPM_ASSIGN(bgxel,
+                       (PPM_GETR(l) + PPM_GETR(r)) / 2,
+                       (PPM_GETG(l) + PPM_GETG(r)) / 2,
+                       (PPM_GETB(l) + PPM_GETB(r)) / 2
+                );
+            break;
 
         case PGM_TYPE:
-        {
-        gray gl, gr;
-        gl = (gray) PNM_GET1( l );
-        gr = (gray) PNM_GET1( r );
-        PNM_ASSIGN1( bgxel, ( ( gl + gr ) / 2 ) );
-        break;
-        }
-
-        case PBM_TYPE:
-        {
-        int col, blacks;
-
-        /* One black, one white.  Gotta count. */
-        for ( col = 0, blacks = 0; col < cols; ++col )
-        {
-        if ( PNM_GET1( xelrow[col] ) == 0 )
-            ++blacks;
-        }
-        if ( blacks >= cols / 2 )
-        PNM_ASSIGN1( bgxel, 0 );
-        else
-        PNM_ASSIGN1( bgxel, maxval );
-        break;
+            PNM_ASSIGN1(bgxel, (PNM_GET1(l) + PNM_GET1(r)) / 2);
+            break;
+
+        case PBM_TYPE: {
+            unsigned int col, blackCnt;
+
+            /* One black, one white.  Gotta count. */
+            for (col = 0, blackCnt = 0; col < cols; ++col) {
+                if (PNM_GET1(xelrow[col] ) == 0)
+                    ++blackCnt;
+            }
+            if (blackCnt >= cols / 2)
+                PNM_ASSIGN1(bgxel, 0);
+            else
+                PNM_ASSIGN1(bgxel, maxval);
+            break;
         }
 
         default:
-        pm_error( "Invalid format passed to pnm_backgroundxelrow()");
+            pm_error("Invalid format passed to pnm_backgroundxelrow()");
         }
     }
 
     return bgxel;
-    }
+}
+
+
 
-#if __STDC__
-xel
-pnm_whitexel( xelval maxval, int format )
-#else /*__STDC__*/
 xel
-pnm_whitexel( maxval, format )
-    xelval maxval;
-    int format;
-#endif /*__STDC__*/
-    {
-    xel x;
+pnm_whitexel(xelval const maxval,
+             int    const format) {
 
-    switch ( PNM_FORMAT_TYPE(format) )
-    {
+    xel retval;
+
+    switch (PNM_FORMAT_TYPE(format)) {
     case PPM_TYPE:
-    PPM_ASSIGN( x, maxval, maxval, maxval );
-    break;
+        PPM_ASSIGN(retval, maxval, maxval, maxval);
+        break;
 
     case PGM_TYPE:
-    PNM_ASSIGN1( x, maxval );
-    break;
-
     case PBM_TYPE:
-    PNM_ASSIGN1( x, maxval );
-    break;
+        PNM_ASSIGN1(retval, maxval);
+        break;
 
     default:
-    pm_error( "Invalid format passed to pnm_whitexel()");
+        pm_error("Invalid format %d passed to pnm_whitexel()", format);
     }
 
-    return x;
-    }
+    return retval;
+}
+
+
 
-#if __STDC__
-xel
-pnm_blackxel( xelval maxval, int format )
-#else /*__STDC__*/
 xel
-pnm_blackxel( maxval, format )
-    xelval maxval;
-    int format;
-#endif /*__STDC__*/
-    {
-    xel x;
+pnm_blackxel(xelval const maxval,
+             int    const format) {
 
-    switch ( PNM_FORMAT_TYPE(format) )
-    {
+    xel retval;
+
+    switch (PNM_FORMAT_TYPE(format)) {
     case PPM_TYPE:
-    PPM_ASSIGN( x, 0, 0, 0 );
-    break;
+        PPM_ASSIGN(retval, 0, 0, 0);
+        break;
 
     case PGM_TYPE:
-    PNM_ASSIGN1( x, (xelval) 0 );
-    break;
-
     case PBM_TYPE:
-    PNM_ASSIGN1( x, (xelval) 0 );
-    break;
+        PNM_ASSIGN1(retval, 0);
+        break;
 
     default:
-    pm_error( "Invalid format passed to pnm_blackxel(): %d", format);
+        pm_error("Invalid format %d passed to pnm_blackxel()", format);
     }
+    
+    return retval;
+}
+
 
-    return x;
-    }
 
 void
 pnm_invertxel(xel*   const xP, 
@@ -255,16 +233,8 @@ pnm_invertxel(xel*   const xP,
 
 
 
-#if __STDC__
 void
 pnm_promoteformat( xel** xels, int cols, int rows, xelval maxval, int format, xelval newmaxval, int newformat )
-#else /*__STDC__*/
-void
-pnm_promoteformat( xels, cols, rows, maxval, format, newmaxval, newformat )
-    xel** xels;
-    xelval maxval, newmaxval;
-    int cols, rows, format, newformat;
-#endif /*__STDC__*/
     {
     int row;
 
@@ -273,16 +243,8 @@ pnm_promoteformat( xels, cols, rows, maxval, format, newmaxval, newformat )
         xels[row], cols, maxval, format, newmaxval, newformat );
     }
 
-#if __STDC__
 void
 pnm_promoteformatrow( xel* xelrow, int cols, xelval maxval, int format, xelval newmaxval, int newformat )
-#else /*__STDC__*/
-void
-pnm_promoteformatrow( xelrow, cols, maxval, format, newmaxval, newformat )
-    xel* xelrow;
-    xelval maxval, newmaxval;
-    int cols, format, newformat;
-#endif /*__STDC__*/
     {
     register int col;
     register xel* xP;
@@ -412,3 +374,45 @@ pnm_xeltopixel(xel const inputxel,
 
     return outputpixel;
 }
+
+
+
+xel
+pnm_parsecolorxel(const char * const colorName,
+                  xelval       const maxval,
+                  int          const format) {
+
+    pixel const bgColor = ppm_parsecolor(colorName, maxval);
+
+    xel retval;
+
+    switch(PNM_FORMAT_TYPE(format)) {
+    case PPM_TYPE:
+        PNM_ASSIGN(retval,
+                   PPM_GETR(bgColor), PPM_GETG(bgColor), PPM_GETB(bgColor));
+        break;
+    case PGM_TYPE:
+        if (PPM_ISGRAY(bgColor))
+            PNM_ASSIGN1(retval, PPM_GETB(bgColor));
+        else
+            pm_error("Non-gray color '%s' specified for a "
+                     "grayscale (PGM) image",
+                     colorName);
+                   break;
+    case PBM_TYPE:
+        if (PPM_EQUAL(bgColor, ppm_whitepixel(maxval)))
+            PNM_ASSIGN1(retval, maxval);
+        else if (PPM_EQUAL(bgColor, ppm_blackpixel()))
+            PNM_ASSIGN1(retval, 0);
+        else
+            pm_error ("Color '%s', which is neither black nor white, "
+                      "specified for a black and white (PBM) image",
+                      colorName);
+        break;
+    default:
+        pm_error("Invalid format code %d passed to pnm_parsecolorxel()",
+                 format);
+    }
+    
+    return retval;
+}
diff --git a/lib/libppm1.c b/lib/libppm1.c
index 40fbd377..97110730 100644
--- a/lib/libppm1.c
+++ b/lib/libppm1.c
@@ -10,7 +10,7 @@
 ** implied warranty.
 */
 
-/* See libpbm.c for the complicated explanation of this 32/64 bit file
+/* See pmfileio.c for the complicated explanation of this 32/64 bit file
    offset stuff.
 */
 #define _FILE_OFFSET_BITS 64
@@ -19,6 +19,7 @@
 #include <string.h>
 #include <stdio.h>
 #include <errno.h>
+
 #include "ppm.h"
 #include "libppm.h"
 #include "pgm.h"
@@ -29,6 +30,7 @@
 #include "libpam.h"
 #include "fileio.h"
 #include "mallocvar.h"
+#include "nstring.h"
 
 
 pixel *
@@ -133,7 +135,8 @@ ppm_readppminit(FILE *   const fileP,
 
     case PBM_TYPE:
         *formatP = realFormat;
-        *maxvalP = 1;
+        /* See comment in pgm_readpgminit() about this maxval */
+        *maxvalP = PPM_MAXMAXVAL;
         pbm_readpbminitrest(fileP, colsP, rowsP);
         break;
 
@@ -150,124 +153,211 @@ ppm_readppminit(FILE *   const fileP,
 
 
 
-void
-ppm_readppmrow(FILE*  const fileP, 
-               pixel* const pixelrow, 
-               int    const cols, 
-               pixval const maxval, 
-               int    const format) {
-
-    switch (format) {
-    case PPM_FORMAT: {
-        unsigned int col;
-        for (col = 0; col < cols; ++col) {
-            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);
-            if (g > maxval)
-                pm_error("Green sample value %u is greater than maxval (%u)",
-                         g, maxval);
-            if (b > maxval)
-                pm_error("Blue sample value %u is greater than maxval (%u)",
-                         b, maxval);
-
-            PPM_ASSIGN(pixelrow[col], r, g, b);
-        }
+static void
+readPpmRow(FILE *       const fileP, 
+           pixel *      const pixelrow, 
+           unsigned int const cols, 
+           pixval       const maxval, 
+           int          const format) {
+
+    unsigned int col;
+
+    for (col = 0; col < cols; ++col) {
+        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);
+        if (g > maxval)
+            pm_error("Green sample value %u is greater than maxval (%u)",
+                     g, maxval);
+        if (b > maxval)
+            pm_error("Blue sample value %u is greater than maxval (%u)",
+                     b, maxval);
+        
+        PPM_ASSIGN(pixelrow[col], r, g, b);
     }
-    break;
+}
 
-    /* For PAM, we require a depth of 3, which means the raster format
-       is identical to Raw PPM!  How convenient.
-    */
-    case PAM_FORMAT:
-    case RPPM_FORMAT: {
-        unsigned int const bytesPerSample = maxval < 256 ? 1 : 2;
-        unsigned int const bytesPerRow    = cols * 3 * bytesPerSample;
-        
-        unsigned int bufferCursor;
-        unsigned char * rowBuffer;
-        ssize_t rc;
 
-        MALLOCARRAY(rowBuffer, bytesPerRow);
+
+static void
+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;
         
-        if (rowBuffer == NULL)
-            pm_error("Unable to allocate memory for row buffer "
-                     "for %u columns", cols);
+    unsigned char * rowBuffer;
+    const char * error;
+    
+    MALLOCARRAY(rowBuffer, bytesPerRow);
+        
+    if (rowBuffer == NULL)
+        asprintfN(&error, "Unable to allocate memory for row buffer "
+                  "for %u columns", cols);
+    else {
+        ssize_t rc;
 
         rc = fread(rowBuffer, 1, bytesPerRow, fileP);
     
         if (feof(fileP))
-            pm_error("Unexpected EOF reading row of PPM image.");
+            asprintfN(&error, "Unexpected EOF reading row of PPM image.");
         else if (ferror(fileP))
-            pm_error("Error reading row.  fread() errno=%d (%s)",
-                     errno, strerror(errno));
+            asprintfN(&error, "Error reading row.  fread() errno=%d (%s)",
+                      errno, strerror(errno));
         else if (rc != bytesPerRow)
-            pm_error("Error reading row.  Short read of %u bytes "
-                     "instead of %u", rc, bytesPerRow);
-    
-        bufferCursor = 0;  /* start at beginning of rowBuffer[] */
+            asprintfN(&error, "Error reading row.  Short read of %u bytes "
+                      "instead of %u", rc, bytesPerRow);
+        else {
+            unsigned int bufferCursor;
+
+            error = NULL;
+
+            bufferCursor = 0;  /* start at beginning of rowBuffer[] */
         
-        if (bytesPerSample == 1) {
-            unsigned int col;
-            for (col = 0; col < cols; ++col) {
-                pixval const r = rowBuffer[bufferCursor++];
-                pixval const g = rowBuffer[bufferCursor++];
-                pixval const b = rowBuffer[bufferCursor++];
-                PPM_ASSIGN(pixelrow[col], r, g, b);
-            }
-        } else  {
-            /* two byte samples */
-            unsigned int col;
-            for (col = 0; col < cols; ++col) {
-                pixval r, g, b;
-
-                r = rowBuffer[bufferCursor++] << 8;
-                r |= rowBuffer[bufferCursor++];
-
-                g = rowBuffer[bufferCursor++] << 8;
-                g |= rowBuffer[bufferCursor++];
-
-                b = rowBuffer[bufferCursor++] << 8;
-                b |= rowBuffer[bufferCursor++];
-                
-                PPM_ASSIGN(pixelrow[col], r, g, b);
+            if (bytesPerSample == 1) {
+                unsigned int col;
+                for (col = 0; col < cols; ++col) {
+                    pixval const r = rowBuffer[bufferCursor++];
+                    pixval const g = rowBuffer[bufferCursor++];
+                    pixval const b = rowBuffer[bufferCursor++];
+                    PPM_ASSIGN(pixelrow[col], r, g, b);
+                }
+            } else  {
+                /* two byte samples */
+                unsigned int col;
+                for (col = 0; col < cols; ++col) {
+                    pixval r, g, b;
+                    
+                    r = rowBuffer[bufferCursor++] << 8;
+                    r |= rowBuffer[bufferCursor++];
+                    
+                    g = rowBuffer[bufferCursor++] << 8;
+                    g |= rowBuffer[bufferCursor++];
+                    
+                    b = rowBuffer[bufferCursor++] << 8;
+                    b |= rowBuffer[bufferCursor++];
+                    
+                    PPM_ASSIGN(pixelrow[col], r, g, b);
+                }
             }
         }
         free(rowBuffer);
     }
-    break;
+    if (error) {
+        pm_errormsg("%s", error);
+        strfree(error);
+        pm_longjmp();
+    }
+}
 
-    case PGM_FORMAT:
-    case RPGM_FORMAT: {
-        gray * const grayrow = pgm_allocrow(cols);
+
+
+static void
+readPgmRow(FILE *       const fileP, 
+           pixel *      const pixelrow, 
+           unsigned int const cols, 
+           pixval       const maxval, 
+           int          const format) {
+
+    jmp_buf jmpbuf;
+    jmp_buf * origJmpbufP;
+    gray * grayrow;
+
+    grayrow = pgm_allocrow(cols);
+
+    if (setjmp(jmpbuf) != 0) {
+        pgm_freerow(grayrow);
+        pm_setjmpbuf(origJmpbufP);
+        pm_longjmp();
+    } else {
         unsigned int col;
+    
+        pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
 
         pgm_readpgmrow(fileP, grayrow, cols, maxval, format);
+
         for (col = 0; col < cols; ++col) {
             pixval const g = grayrow[col];
             PPM_ASSIGN(pixelrow[col], g, g, g);
         }
-        pgm_freerow(grayrow);
+        pm_setjmpbuf(origJmpbufP);
     }
-    break;
+    pgm_freerow(grayrow);
+}
 
-    case PBM_FORMAT:
-    case RPBM_FORMAT: {
-        bit * const bitrow = pbm_allocrow(cols);
+
+
+static void
+readPbmRow(FILE *       const fileP, 
+           pixel *      const pixelrow, 
+           unsigned int const cols, 
+           pixval       const maxval, 
+           int          const format) {
+
+    jmp_buf jmpbuf;
+    jmp_buf * origJmpbufP;
+    bit * bitrow;
+
+    bitrow = pbm_allocrow(cols);
+
+    if (setjmp(jmpbuf) != 0) {
+        pbm_freerow(bitrow);
+        pm_setjmpbuf(origJmpbufP);
+        pm_longjmp();
+    } else {
         unsigned int col;
 
+        pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
+
         pbm_readpbmrow(fileP, bitrow, cols, format);
+
         for (col = 0; col < cols; ++col) {
             pixval const g = (bitrow[col] == PBM_WHITE) ? maxval : 0;
             PPM_ASSIGN(pixelrow[col], g, g, g);
         }
-        pbm_freerow(bitrow);
+        pm_setjmpbuf(origJmpbufP);
     }
-    break;
+    pbm_freerow(bitrow);
+}
+
+
+
+void
+ppm_readppmrow(FILE *  const fileP, 
+               pixel * const pixelrow, 
+               int     const cols, 
+               pixval  const maxval, 
+               int     const format) {
+
+    switch (format) {
+    case PPM_FORMAT:
+        readPpmRow(fileP, pixelrow, cols, maxval, format);
+        break;
+
+    /* For PAM, we require a depth of 3, which means the raster format
+       is identical to Raw PPM!  How convenient.
+    */
+    case PAM_FORMAT:
+    case RPPM_FORMAT:
+        readRppmRow(fileP, pixelrow, cols, maxval, format);
+        break;
+
+    case PGM_FORMAT:
+    case RPGM_FORMAT:
+        readPgmRow(fileP, pixelrow, cols, maxval, format);
+        break;
+
+    case PBM_FORMAT:
+    case RPBM_FORMAT:
+        readPbmRow(fileP, pixelrow, cols, maxval, format);
+        break;
 
     default:
         pm_error("Invalid format code");
@@ -281,17 +371,36 @@ ppm_readppm(FILE *   const fileP,
             int *    const colsP, 
             int *    const rowsP, 
             pixval * const maxvalP) {
-    pixel** pixels;
-    int row;
+
+    jmp_buf jmpbuf;
+    jmp_buf * origJmpbufP;
+    pixel ** pixels;
+    int cols, rows;
+    pixval maxval;
     int format;
 
-    ppm_readppminit(fileP, colsP, rowsP, maxvalP, &format);
+    ppm_readppminit(fileP, &cols, &rows, &maxval, &format);
 
-    pixels = ppm_allocarray(*colsP, *rowsP);
+    pixels = ppm_allocarray(cols, rows);
 
-    for (row = 0; row < *rowsP; ++row)
-        ppm_readppmrow(fileP, pixels[row], *colsP, *maxvalP, format);
+    if (setjmp(jmpbuf) != 0) {
+        ppm_freearray(pixels, rows);
+        pm_setjmpbuf(origJmpbufP);
+        pm_longjmp();
+    } else {
+        unsigned int row;
 
+        pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
+
+        for (row = 0; row < rows; ++row)
+            ppm_readppmrow(fileP, pixels[row], cols, maxval, format);
+
+        *colsP = cols;
+        *rowsP = rows;
+        *maxvalP = maxval;
+
+        pm_setjmpbuf(origJmpbufP);
+    }
     return pixels;
 }
 
diff --git a/lib/libppm2.c b/lib/libppm2.c
index 52c4ab16..3bf74bd4 100644
--- a/lib/libppm2.c
+++ b/lib/libppm2.c
@@ -17,7 +17,6 @@
 #include "pm_c_util.h"
 #include "mallocvar.h"
 #include "ppm.h"
-#include "libppm.h"
 
 void
 ppm_writeppminit(FILE*  const fileP, 
@@ -145,10 +144,10 @@ ppm_writeppmrowraw(FILE *        const fileP,
 
 
 static void
-ppm_writeppmrowplain(FILE *       const fileP,
-                     pixel *      const pixelrow,
-                     unsigned int const cols,
-                     pixval       const maxval) {
+ppm_writeppmrowplain(FILE *        const fileP,
+                     const pixel * const pixelrow,
+                     unsigned int  const cols,
+                     pixval        const maxval) {
 
     unsigned int col;
     unsigned int charcount;
@@ -178,11 +177,11 @@ ppm_writeppmrowplain(FILE *       const fileP,
 
 
 void
-ppm_writeppmrow(FILE *  const fileP, 
-                pixel * const pixelrow, 
-                int     const cols, 
-                pixval  const maxval, 
-                int     const forceplain) {
+ppm_writeppmrow(FILE *        const fileP, 
+                const pixel * const pixelrow, 
+                int           const cols, 
+                pixval        const maxval, 
+                int           const forceplain) {
 
     if (forceplain || pm_plain_output || maxval >= 1<<16) 
         ppm_writeppmrowplain(fileP, pixelrow, cols, maxval);
diff --git a/lib/libppmcmap.c b/lib/libppmcmap.c
index a9efccbc..055bfc8b 100644
--- a/lib/libppmcmap.c
+++ b/lib/libppmcmap.c
@@ -12,9 +12,10 @@
 ** implied warranty.
 */
 
-#include "ppm.h"
-#include "libppm.h"
+#include "pm_c_util.h"
+#include "nstring.h"
 #include "mallocvar.h"
+#include "ppm.h"
 #include "ppmcmap.h"
 
 #define HASH_SIZE 20023
@@ -110,94 +111,124 @@ ppm_addtocolorhist( colorhist_vector chv,
 
 
 
-colorhash_table
-ppm_alloccolorhash(void)  {
+static colorhash_table
+alloccolorhash(void)  {
     colorhash_table cht;
     int i;
 
     MALLOCARRAY(cht, HASH_SIZE);
+    if (cht) {
+        for (i = 0; i < HASH_SIZE; ++i)
+            cht[i] = NULL;
+    }
+    return cht;
+}
+
+
+
+colorhash_table
+ppm_alloccolorhash(void)  {
+    colorhash_table cht;
+
+    cht = alloccolorhash();
+
     if (cht == NULL)
         pm_error( "out of memory allocating hash table" );
 
-    for (i = 0; i < HASH_SIZE; ++i)
-        cht[i] = NULL;
-
     return cht;
 }
 
 
 
-static colorhash_table
-computecolorhash(pixel ** const pixels, 
-                 const int cols, const int rows, 
-                 const int maxcolors, int * const colorsP,
-                 FILE * const ifp, pixval const maxval, int const format) {
-/*----------------------------------------------------------------------------
-   Compute a color histogram from an image.  The input is one of two types:
+static void
+readppmrow(FILE *        const fileP, 
+           pixel *       const pixelrow, 
+           int           const cols, 
+           pixval        const maxval, 
+           int           const format,
+           const char ** const errorP) {
 
-   1) a two-dimensional array of pixels 'pixels';  In this case, 'pixels'
-      is non-NULL and 'ifp' is NULL.
+    jmp_buf jmpbuf;
+    jmp_buf * origJmpbufP;
+    
+    if (setjmp(jmpbuf) != 0) {
+        pm_setjmpbuf(origJmpbufP);
+        asprintfN(errorP, "Failed to read row of image.");
+    } else {
+        pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
 
-   2) an open file, positioned to the image data.  In this case,
-      'pixels' is NULL and 'ifp' is non-NULL.  ifp is the stream
-      descriptor for the input file, and 'maxval' and 'format' are
-      parameters of the image data in it.
-      
-      We return with the file still open and its position undefined.  
+        ppm_readppmrow(fileP, pixelrow, cols, maxval, format);
 
-   In either case, the image is 'cols' by 'rows'.
+        *errorP = NULL; /* Would have longjmped if anything went wrong */
+                
+        pm_setjmpbuf(origJmpbufP);
+    }
+}
 
-   Return the number of colors found as *colorsP.
 
-   However, if 'maxcolors' is nonzero and the number of colors is
-   greater than 'maxcolors', return a null return value and *colorsP
-   undefined.
+
+static void
+buildHashTable(FILE *          const ifP,
+               pixel **        const pixels,
+               unsigned int    const cols,
+               unsigned int    const rows,
+               pixval          const maxval,
+               int             const format,
+               unsigned int    const maxcolors,
+               colorhash_table const cht,
+               pixel *         const rowbuffer,
+               int *           const nColorsP,
+               bool *          const tooManyColorsP,
+               const char **   const errorP) {
+/*----------------------------------------------------------------------------
+  Look at all the colors in the file *ifP or array pixels[][] and add
+  them to the hash table 'cht'.
+
+  Even if we fail, we may add some colors to 'cht'.
+
+  As soon as we've seen more that 'maxcolors' colors, we quit.  In that
+  case, only, we return *tooManyColorsP == true.  That is not a failure.
+  'maxcolors' == 0 means infinity.
 -----------------------------------------------------------------------------*/
-    colorhash_table cht;
-    int row;
-    pixel * rowbuffer;  /* malloc'ed */
-        /* Buffer for a row read from the input file; undefined (but still
-           allocated) if input is not from a file.
-        */
-    
-    cht = ppm_alloccolorhash( );
-    *colorsP = 0;   /* initial value */
+    unsigned int row;
+    unsigned int nColors;
 
-    rowbuffer = ppm_allocrow(cols);
+    nColors = 0;   /* initial value */
+    *tooManyColorsP = FALSE; /* initial value */
+    *errorP = NULL;  /* initial value */
 
     /* Go through the entire image, building a hash table of colors. */
-    for (row = 0; row < rows; ++row) {
-        int col;
+    for (row = 0; row < rows && !*tooManyColorsP && !*errorP; ++row) {
+        unsigned int col;
         pixel * pixelrow;  /* The row of pixels we are processing */
 
-        if (ifp) {
-            ppm_readppmrow(ifp, rowbuffer, cols, maxval, format);
+        if (ifP) {
+            readppmrow(ifP, rowbuffer, cols, maxval, format, errorP);
             pixelrow = rowbuffer;
         } else 
             pixelrow = pixels[row];
 
-        for (col = 0; col < cols; ++col) {
+        for (col = 0; col < cols && !*tooManyColorsP && !*errorP; ++col) {
             const pixel apixel = pixelrow[col];
             const int hash = ppm_hashpixel(apixel);
             colorhist_list chl; 
 
             for (chl = cht[hash]; 
-                 chl != (colorhist_list) 0 && 
-                     !PPM_EQUAL(chl->ch.color, apixel);
+                 chl && !PPM_EQUAL(chl->ch.color, apixel);
                  chl = chl->next);
 
             if (chl)
-                chl->ch.value++;
+                ++chl->ch.value;
             else {
                 /* It's not in the hash yet, so add it (if allowed) */
-                ++(*colorsP);
-                if (maxcolors > 0 && *colorsP > maxcolors) {
-                    ppm_freecolorhash(cht);
-                    return NULL;
-                } else {
+                ++nColors;
+                if (maxcolors > 0 && nColors > maxcolors)
+                    *tooManyColorsP = TRUE;
+                else {
                     MALLOCVAR(chl);
                     if (chl == NULL)
-                        pm_error("out of memory computing hash table");
+                        asprintfN(errorP,
+                                  "out of memory computing hash table");
                     chl->ch.color = apixel;
                     chl->ch.value = 1;
                     chl->next = cht[hash];
@@ -206,31 +237,124 @@ computecolorhash(pixel ** const pixels,
             }
         }
     }
-    ppm_freerow(rowbuffer);
-    return cht;
+    *nColorsP = nColors;
+}
+
+
+
+static void
+computecolorhash(pixel **          const pixels, 
+                 unsigned int      const cols,
+                 unsigned int      const rows, 
+                 unsigned int      const maxcolors,
+                 int *             const nColorsP,
+                 FILE *            const ifP,
+                 pixval            const maxval,
+                 int               const format,
+                 colorhash_table * const chtP,
+                 const char **     const errorP) {
+/*----------------------------------------------------------------------------
+   Compute a color histogram from an image.  The input is one of two types:
+
+   1) a two-dimensional array of pixels 'pixels';  In this case, 'pixels'
+      is non-NULL and 'ifP' is NULL.
+
+   2) an open file, positioned to the image data.  In this case,
+      'pixels' is NULL and 'ifP' is non-NULL.  ifP is the stream
+      descriptor for the input file, and 'maxval' and 'format' are
+      parameters of the image data in it.
+      
+      We return with the file still open and its position undefined.  
+
+   In either case, the image is 'cols' by 'rows'.
+
+   Return the number of colors found as *colorsP.
+
+   However, if 'maxcolors' is nonzero and the number of colors is
+   greater than 'maxcolors', return a null return value and *colorsP
+   undefined.
+-----------------------------------------------------------------------------*/
+    pixel * rowbuffer;  /* malloc'ed */
+        /* Buffer for a row read from the input file; undefined (but still
+           allocated) if input is not from a file.
+        */
+
+    MALLOCARRAY(rowbuffer, cols);
+        
+    if (rowbuffer == NULL)
+        asprintfN(errorP, "Unable to allocate %u-column row buffer.", cols);
+    else {
+        colorhash_table cht;
+        bool tooManyColors;
+
+        cht = alloccolorhash();
+
+        if (cht == NULL)
+            asprintfN(errorP, "Unable to allocate color hash.");
+        else {
+            buildHashTable(ifP, pixels, cols, rows, maxval, format, maxcolors,
+                           cht, rowbuffer,
+                           nColorsP, &tooManyColors, errorP);
+                
+            if (tooManyColors) {
+                ppm_freecolorhash(cht);
+                *chtP = NULL;
+            } else
+                *chtP = cht;
+
+            if (*errorP)
+                ppm_freecolorhash(cht);
+        }
+        free(rowbuffer);
+    }
 }
 
 
 
 colorhash_table
 ppm_computecolorhash(pixel ** const pixels, 
-                     const int cols, const int rows, 
-                     const int maxcolors, int * const colorsP) {
+                     int      const cols,
+                     int      const rows, 
+                     int      const maxcolors,
+                     int *    const colorsP) {
 
-    return computecolorhash(pixels, cols, rows, maxcolors, colorsP, 
-                            NULL, 0, 0);
+    colorhash_table cht;
+    const char * error;
+
+    computecolorhash(pixels, cols, rows, maxcolors, colorsP, 
+                     NULL, 0, 0, &cht, &error);
+
+    if (error) {
+        pm_errormsg("%s", error);
+        strfree(error);
+        pm_longjmp();
+    }
+    return cht;
 }
 
 
 
 colorhash_table
-ppm_computecolorhash2(FILE * const ifp,
-                      const int cols, const int rows, 
-                      const pixval maxval, const int format, 
-                      const int maxcolors, int * const colorsP ) {
+ppm_computecolorhash2(FILE * const ifP,
+                      int    const cols,
+                      int    const rows, 
+                      pixval const maxval,
+                      int    const format, 
+                      int    const maxcolors,
+                      int *  const colorsP ) {
 
-    return computecolorhash(NULL, cols, rows, maxcolors, colorsP,
-                            ifp, maxval, format);
+    colorhash_table cht;
+    const char * error;
+
+    computecolorhash(NULL, cols, rows, maxcolors, colorsP,
+                     ifP, maxval, format, &cht, &error);
+
+    if (error) {
+        pm_errormsg("%s", error);
+        strfree(error);
+        pm_longjmp();
+    }
+    return cht;
 }
 
 
@@ -353,30 +477,50 @@ ppm_colorhashtocolorhist(colorhash_table const cht, int const maxcolors) {
 colorhash_table
 ppm_colorhisttocolorhash(colorhist_vector const chv, 
                          int              const colors) {
+
+    colorhash_table retval;
     colorhash_table cht;
-    int i, hash;
-    pixel color;
-    colorhist_list chl;
+    const char * error;
 
-    cht = ppm_alloccolorhash( );  /* Initializes to NULLs */
-
-    for (i = 0; i < colors; ++i) {
-        color = chv[i].color;
-        hash = ppm_hashpixel(color);
-        for (chl = cht[hash]; chl != (colorhist_list) 0; chl = chl->next)
-            if (PPM_EQUAL(chl->ch.color, color))
-                pm_error(
-                    "same color found twice - %d %d %d", PPM_GETR(color),
-                    PPM_GETG(color), PPM_GETB(color) );
-        MALLOCVAR(chl);
-        if (chl == NULL)
-            pm_error("out of memory");
-        chl->ch.color = color;
-        chl->ch.value = i;
-        chl->next = cht[hash];
-        cht[hash] = chl;
+    cht = alloccolorhash( );  /* Initializes to NULLs */
+    if (cht == NULL)
+        asprintfN(&error, "Unable to allocate color hash");
+    else {
+        unsigned int i;
+
+        for (i = 0, error = NULL; i < colors && !error; ++i) {
+            pixel const color = chv[i].color;
+            int const hash = ppm_hashpixel(color);
+            
+            colorhist_list chl;
+
+            for (chl = cht[hash]; chl && !error; chl = chl->next)
+                if (PPM_EQUAL(chl->ch.color, color))
+                    asprintfN(&error, "same color found twice: (%u %u %u)",
+                              PPM_GETR(color),
+                              PPM_GETG(color),
+                              PPM_GETB(color));
+            MALLOCVAR(chl);
+            if (chl == NULL)
+                asprintfN(&error, "out of memory");
+            else {
+                chl->ch.color = color;
+                chl->ch.value = i;
+                chl->next = cht[hash];
+                cht[hash] = chl;
+            }
+        }
+        if (error)
+            ppm_freecolorhash(cht);
     }
-    return cht;
+    if (error) {
+        pm_errormsg("%s", error);
+        strfree(error);
+        pm_longjmp();
+    } else
+        retval = cht;
+
+    return retval;
 }
 
 
@@ -629,15 +773,7 @@ ppm_findclosestcolor(const pixel * const colormap,
 
 
 void
-#if __STDC__
 ppm_colorrowtomapfile(FILE *ofp, pixel *colormap, int ncolors, pixval maxval)
-#else
-ppm_colorrowtomapfile(ofp, colormap, ncolors, maxval)
-    FILE *ofp;
-    pixel *colormap;
-    int ncolors;
-    pixval maxval;
-#endif
 {
     int i;
 
diff --git a/lib/libppmcolor.c b/lib/libppmcolor.c
index 36cbff73..58663c94 100644
--- a/lib/libppmcolor.c
+++ b/lib/libppmcolor.c
@@ -19,6 +19,7 @@
 
 #include "pm_c_util.h"
 #include "mallocvar.h"
+#include "nstring.h"
 #include "ppm.h"
 #include "colorname.h"
 
@@ -144,8 +145,8 @@ parseHexDigits(const char *   const string,
     unsigned int digitCount;
     pixval n;
     
-    digitCount = 0;
-    n = 0;
+    digitCount = 0;  /* initial value */
+    n = 0;           /* initial value */
     while (string[digitCount] != delim) {
         char const digit = string[digitCount];
         if (digit == '\0')
@@ -248,22 +249,6 @@ parseNewDecX11(char       const colorname[],
 
 
 
-static bool
-isHexString(char const string[],
-            int  const hexit[]) {
-
-    bool retval;
-    const char * p;
-
-    for (p = &string[0], retval = true; *p && retval == true; ++p) {
-        if (hexit[(unsigned int)*p] == -1)
-            retval = false;
-    }
-    return retval;
-}
-
-
-
 static void
 parseOldX11(char       const colorname[], 
             pixval     const maxval,
@@ -279,7 +264,7 @@ parseOldX11(char       const colorname[],
     
     computeHexTable(hexit);
 
-    if (!isHexString(&colorname[1], hexit))
+    if (!strishex(&colorname[1]))
         pm_error("Non-hexadecimal characters in #-type color specification");
 
     switch (strlen(colorname) - 1 /* (Number of hex digits) */) {
@@ -414,13 +399,13 @@ ppm_parsecolor(const char * const colorname,
 
 
 
-char*
-ppm_colorname(const pixel* const colorP, 
-              pixval       const maxval, 
-              int          const hexok)   {
+char *
+ppm_colorname(const pixel * const colorP, 
+              pixval        const maxval, 
+              int           const hexok)   {
 
     int r, g, b;
-    FILE* f;
+    FILE * f;
     static char colorname[200];
 
     if (maxval == 255) {
@@ -460,8 +445,7 @@ ppm_colorname(const pixel* const colorP,
        hex specifier, so return that.
     */
     sprintf(colorname, "#%02x%02x%02x", r, g, b);
-    return colorname;
-}
+    return colorname;}
 
 
 
@@ -472,11 +456,12 @@ processColorfileEntry(struct colorfile_entry const ce,
                       colorhash_table        const cht,
                       const char **          const colornames,
                       pixel *                const colors,
-                      unsigned int *         const colornameIndexP) {
+                      unsigned int *         const colornameIndexP,
+                      const char **          const errorP) {
 
     if (*colornameIndexP >= MAXCOLORNAMES)
-        pm_error("Too many colors in colorname dictionary.  "
-                 "Max allowed is %u", MAXCOLORNAMES);
+        asprintfN(errorP, "Too many colors in colorname dictionary.  "
+                  "Max allowed is %u", MAXCOLORNAMES);
     else {
         pixel color;
 
@@ -488,13 +473,17 @@ processColorfileEntry(struct colorfile_entry const ce,
                file gives for each color, so we just ignore the
                current entry.  
             */
+            *errorP = NULL;
         } else {
             ppm_addtocolorhash(cht, &color, *colornameIndexP);
             colornames[*colornameIndexP] = strdup(ce.colorname);
             colors[*colornameIndexP] = color;
             if (colornames[*colornameIndexP] == NULL)
-                pm_error("Unable to allocate space for color name");
-            ++(*colornameIndexP);
+                asprintfN(errorP, "Unable to allocate space for color name");
+            else {
+                *errorP = NULL;
+                ++(*colornameIndexP);
+            }
         }
     }
 }
@@ -502,44 +491,178 @@ processColorfileEntry(struct colorfile_entry const ce,
 
 
 static void
-readcolordict(const char *    const fileName,
+openColornameFile(const char *  const fileName,
+                  bool          const mustOpen,
+                  FILE **       const filePP,
+                  const char ** const errorP) {
+
+    jmp_buf jmpbuf;
+    jmp_buf * origJmpbufP;
+
+    if (setjmp(jmpbuf) != 0) {
+        asprintfN(errorP, "Failed to open color name file");
+        pm_setjmpbuf(origJmpbufP);
+        pm_longjmp();
+    } else {
+        *filePP = pm_openColornameFile(fileName, mustOpen);
+
+        *errorP = NULL;  /* Would have longjmped if there were a problem */
+
+        pm_setjmpbuf(origJmpbufP);
+    }
+}
+
+
+
+static void
+readOpenColorFile(FILE *          const colorFileP,
+                  unsigned int *  const nColorsP,
+                  const char **   const colornames,
+                  pixel *         const colors,
+                  colorhash_table const cht,
+                  const char **   const errorP) {
+/*----------------------------------------------------------------------------
+   Read the color dictionary file *colorFileP and add the colors in it
+   to colornames[], colors[], and 'cht'.
+
+   We may add colors to 'cht' even if we fail.
+-----------------------------------------------------------------------------*/
+    unsigned int nColorsDone;
+    bool done;
+
+    nColorsDone = 0;
+    done = FALSE;
+    *errorP = NULL;
+
+    while (!done && !*errorP) {
+        struct colorfile_entry const ce = pm_colorget(colorFileP);
+        
+        if (!ce.colorname)  
+            done = TRUE;
+        else 
+            processColorfileEntry(ce, cht, colornames, colors,
+                                  &nColorsDone, errorP);
+    }
+    if (!*errorP) {
+        *nColorsP = nColorsDone;
+        
+        while (nColorsDone < MAXCOLORNAMES)
+            colornames[nColorsDone++] = NULL;
+    }
+    
+    if (*errorP) {
+        unsigned int colorIndex;
+
+        for (colorIndex = 0; colorIndex < nColorsDone; ++colorIndex)
+            strfree(colornames[colorIndex]);
+    }
+}
+
+
+
+static colorhash_table
+allocColorHash(void) {
+
+    colorhash_table cht;
+    jmp_buf jmpbuf;
+    jmp_buf * origJmpbufP;
+
+    if (setjmp(jmpbuf) != 0)
+        cht = NULL;
+    else {
+        pm_setjmpbufsave(&jmpbuf, &origJmpbufP);
+        cht = ppm_alloccolorhash();
+    }
+    pm_setjmpbuf(origJmpbufP);
+
+    return cht;
+}
+
+
+
+static void
+readColorFile(const char *    const fileName,
               bool            const mustOpen,
               unsigned int *  const nColorsP,
               const char **   const colornames,
-              pixel * const   colors,
-              colorhash_table const cht) {
+              pixel *         const colors,
+              colorhash_table const cht,
+              const char **   const errorP) {
 
-    FILE * colorFile;
+    FILE * colorFileP;
 
-    colorFile = pm_openColornameFile(fileName, mustOpen);
+    openColornameFile(fileName, mustOpen, &colorFileP, errorP);
+    if (!*errorP) {
+        if (colorFileP == NULL) {
+            /* Couldn't open it, but Caller says treat same as
+               empty file
+            */
+            *nColorsP = 0;
+            {
+                unsigned int i;
+                for (i = 0; i < MAXCOLORNAMES; ++i)
+                    colornames[i] = NULL;
+            }
+            *errorP = NULL;
+        } else {
+            readOpenColorFile(colorFileP, nColorsP, colornames, colors, cht,
+                              errorP);
+            
+            fclose(colorFileP);
+        }
+    }
+}
 
-    if (colorFile != NULL) {
-        unsigned int colornameIndex;
-        bool done;
+    
 
-        colornameIndex = 0;  /* initial value */
-        done = FALSE;
-        while (!done) {
-            struct colorfile_entry const ce = pm_colorget(colorFile);
+static void
+readcolordict(const char *      const fileName,
+              bool              const mustOpen,
+              unsigned int *    const nColorsP,
+              const char ***    const colornamesP,
+              pixel **          const colorsP,
+              colorhash_table * const chtP,
+              const char **     const errorP) {
 
-            if (!ce.colorname)  
-                done = TRUE;
-            else 
-                processColorfileEntry(ce, cht, colornames, colors,
-                                      &colornameIndex);
-        }
+    const char ** colornames;
 
-        *nColorsP = colornameIndex;
+    MALLOCARRAY(colornames, MAXCOLORNAMES);
 
-        while (colornameIndex < MAXCOLORNAMES)
-            colornames[colornameIndex++] = NULL;
+    if (colornames == NULL)
+        asprintfN(errorP, "Unable to allocate space for colorname table.");
+    else {
+        pixel * colors;
 
-        fclose(colorFile);
-    } else {
-        unsigned int i;
-        *nColorsP = 0;
-        for (i = 0; i < MAXCOLORNAMES; ++i)
-            colornames[i] = NULL;
+        MALLOCARRAY(colors, MAXCOLORNAMES);
+        
+        if (colors == NULL)
+            asprintfN(errorP, "Unable to allocate space for color table.");
+        else {
+            colorhash_table cht;
+
+            cht = allocColorHash();
+            
+            if (cht == NULL)
+                asprintfN(errorP, "Unable to allocate space for color hash");
+            else {
+                readColorFile(fileName, mustOpen,
+                              nColorsP, colornames, colors, cht,
+                              errorP);
+
+                if (*errorP)
+                    ppm_freecolorhash(cht);
+                else
+                    *chtP = cht;
+            }
+            if (*errorP)
+                free(colors);
+            else
+                *colorsP = colors;
+        }
+        if (*errorP)
+            free(colornames);
+        else
+            *colornamesP = colornames;
     }
 }
 
@@ -557,32 +680,31 @@ ppm_readcolordict(const char *      const fileName,
     const char ** colornames;
     pixel * colors;
     unsigned int nColors;
+    const char * error;
 
-    cht = ppm_alloccolorhash();
-
-    MALLOCARRAY(colornames, MAXCOLORNAMES);
-
-    colors = ppm_allocrow(MAXCOLORNAMES);
+    readcolordict(fileName, mustOpen, &nColors, &colornames, &colors, &cht,
+                  &error);
 
-    if (colornames == NULL)
-        pm_error("Unable to allocate space for colorname table.");
-
-    readcolordict(fileName, mustOpen, &nColors, colornames, colors, cht);
-
-    if (chtP)
-        *chtP = cht;
-    else
+    if (error) {
+        pm_errormsg("%s", error);
+        strfree(error);
         ppm_freecolorhash(cht);
-    if (colornamesP)
-        *colornamesP = colornames;
-    else
-        ppm_freecolornames(colornames);
-    if (colorsP)
-        *colorsP = colors;
-    else
-        ppm_freerow(colors);
-    if (nColorsP)
-        *nColorsP = nColors;
+    } else {
+        if (chtP)
+            *chtP = cht;
+        else
+            ppm_freecolorhash(cht);
+        if (colornamesP)
+            *colornamesP = colornames;
+        else
+            ppm_freecolornames(colornames);
+        if (colorsP)
+            *colorsP = colors;
+        else
+            ppm_freerow(colors);
+        if (nColorsP)
+            *nColorsP = nColors;
+    }
 }
 
 
diff --git a/lib/libppmd.c b/lib/libppmd.c
index 892794a5..2325dd68 100644
--- a/lib/libppmd.c
+++ b/lib/libppmd.c
@@ -32,6 +32,70 @@ struct penpos {
     int y;
 };
 
+struct rectangle {
+    /* ((0,0),(0,0)) means empty. */
+    /* 'lr' is guaranteed not to be left of or above 'ul' */
+    struct penpos ul;
+    struct penpos lr;
+};
+
+static struct rectangle const emptyRectangle = {
+    {0, 0},
+    {0, 0},
+};
+
+
+static ppmd_point
+makePoint(int const x,
+          int const y) {
+
+    return ppmd_makePoint(x, y);
+}
+
+
+
+static ppmd_point
+middlePoint(ppmd_point const a,
+            ppmd_point const b) {
+
+    ppmd_point retval;
+
+    retval.x = (a.x + b.x) / 2;
+    retval.y = (a.y + b.y) / 2;
+
+    return retval;
+}
+
+
+
+static bool
+pointsEqual(ppmd_point const a,
+            ppmd_point const b) {
+
+    return a.x == b.x && a.y == b.y;
+}
+
+
+
+static bool
+pointIsWithinBounds(ppmd_point   const p,
+                    unsigned int const cols,
+                    unsigned int const rows) {
+
+    return (p.x >= 0 && p.x < cols && p.y >= 0 && p.y < rows);
+}
+
+        
+
+static ppmd_point
+vectorSum(ppmd_point const a,
+          ppmd_point const b) {
+
+    return makePoint(a.x + b.x, a.y + b.y);
+}
+
+
+
 static long int const DDA_SCALE = 8192;
 
 #define PPMD_MAXCOORD 32767
@@ -53,28 +117,35 @@ static long int const DDA_SCALE = 8192;
 
 
 
-static void
-ppmd_validateCoords(int const x,
-                    int const y) {
+void
+ppmd_validateCoord(int const c) {
 
-    if (x < -PPMD_MAXCOORD || x > PPMD_MAXCOORD)
-        pm_error("x coordinate out of bounds: %d", x);
+    if (c < -PPMD_MAXCOORD || c > PPMD_MAXCOORD)
+        pm_error("Coordinate out of bounds: %d", c);
+}
 
-    if (y < -PPMD_MAXCOORD || y > PPMD_MAXCOORD)
-        pm_error("y coordinate out of bounds: %d", y);
+
+
+void
+ppmd_validatePoint(ppmd_point const p) {
+
+    if (p.x < -PPMD_MAXCOORD || p.x > PPMD_MAXCOORD)
+        pm_error("x coordinate of (%d, %d) out of bounds", p.x, p.y);
+
+    if (p.y < -PPMD_MAXCOORD || p.y > PPMD_MAXCOORD)
+        pm_error("y coordinate of (%d, %d) out of bounds", p.x, p.y);
 }
 
 
 
 static void
-drawPoint(ppmd_drawproc       drawproc,
-          const void *  const clientdata,
-          pixel **      const pixels, 
-          int           const cols, 
-          int           const rows, 
-          pixval        const maxval, 
-          int           const x, 
-          int           const y) {
+drawPoint(ppmd_drawprocp       drawproc,
+          const void *   const clientdata,
+          pixel **       const pixels, 
+          int            const cols, 
+          int            const rows, 
+          pixval         const maxval, 
+          ppmd_point     const p) {
 /*----------------------------------------------------------------------------
    Draw a single point, assuming that it is within the bounds of the
    image.
@@ -82,12 +153,66 @@ drawPoint(ppmd_drawproc       drawproc,
     if (drawproc == PPMD_NULLDRAWPROC) {
         const pixel * const pixelP = clientdata;
         
-        assert(x >= 0); assert(x < cols);
-        assert(y >= 0); assert(y < rows);
+        assert(p.x >= 0); assert(p.x < cols);
+        assert(p.y >= 0); assert(p.y < rows);
 
-        pixels[y][x] = *pixelP;
+        pixels[p.y][p.x] = *pixelP;
     } else
-        drawproc(pixels, cols, rows, maxval, x, y, clientdata);
+        drawproc(pixels, cols, rows, maxval, p, clientdata);
+}
+
+
+
+struct drawProcXY {
+    ppmd_drawproc * drawProc;
+    const void *    clientData;
+};
+
+static struct drawProcXY
+makeDrawProcXY(ppmd_drawproc * const drawProc,
+               const void *    const clientData) {
+
+    struct drawProcXY retval;
+
+    retval.drawProc   = drawProc;
+    retval.clientData = clientData;
+    
+    return retval;
+}
+
+
+
+static ppmd_drawprocp drawProcPointXY;
+
+static void
+drawProcPointXY(pixel **     const pixels,
+                unsigned int const cols,
+                unsigned int const rows,
+                pixval       const maxval,
+                ppmd_point   const p,
+                const void * const clientdata) {
+
+    const struct drawProcXY * const xyP = clientdata;
+
+    if (xyP->drawProc == PPMD_NULLDRAWPROC)
+        drawPoint(PPMD_NULLDRAWPROC, xyP->clientData,
+                  pixels, cols, rows, maxval, p);
+    else
+        xyP->drawProc(pixels, cols, rows, maxval, p.x, p.y, xyP->clientData);
+}
+
+
+
+void
+ppmd_point_drawprocp(pixel **     const pixels, 
+                     unsigned int const cols, 
+                     unsigned int const rows, 
+                     pixval       const maxval, 
+                     ppmd_point   const p,
+                     const void * const clientdata) {
+
+    if (p.x >= 0 && p.x < cols && p.y >= 0 && p.y < rows)
+        pixels[p.y][p.x] = *((pixel*)clientdata);
 }
 
 
@@ -101,12 +226,42 @@ ppmd_point_drawproc(pixel**     const pixels,
                     int         const y, 
                     const void* const clientdata) {
 
-    if (x >= 0 && x < cols && y >= 0 && y < rows)
-        pixels[y][x] = *((pixel*)clientdata);
+    ppmd_point p;
+    
+    p.x = x;
+    p.y = y;
+
+    ppmd_point_drawprocp(pixels, cols, rows, maxval, p, clientdata);
+}
+
+
+
+static void
+findRectangleIntersection(struct rectangle   const rect1,
+                          struct rectangle   const rect2,
+                          struct rectangle * const intersectionP) {
+/*----------------------------------------------------------------------------
+   Find the intersection between rectangles 'rect1' and 'rect2'.
+   Return it as *intersectionP.
+-----------------------------------------------------------------------------*/
+    struct penpos tentativeUl, tentativeLr;
+
+    tentativeUl.x = MAX(rect1.ul.x, rect2.ul.x);
+    tentativeUl.y = MAX(rect1.ul.y, rect2.ul.y);
+    tentativeLr.x = MIN(rect1.lr.x, rect2.lr.x);
+    tentativeLr.y = MIN(rect1.lr.y, rect2.lr.y);
+
+    if (tentativeLr.x <= tentativeUl.x ||
+        tentativeLr.y <= tentativeUl.y) {
+        /* No intersection */
+        *intersectionP = emptyRectangle;
+    } else {
+        intersectionP->ul = tentativeUl;
+        intersectionP->lr = tentativeLr;
+    }
 }
 
 
-/* Simple fill routine. */
 
 void
 ppmd_filledrectangle(pixel **      const pixels, 
@@ -117,37 +272,43 @@ ppmd_filledrectangle(pixel **      const pixels,
                      int           const y, 
                      int           const width, 
                      int           const height, 
-                     ppmd_drawproc      drawProc,
+                     ppmd_drawproc       drawProc,
                      const void *  const clientdata) {
 
-    int cx, cy, cwidth, cheight, row;
+    struct drawProcXY const xy = makeDrawProcXY(drawProc, clientdata);
 
-    /* Clip. */
-    cx = x;
-    cy = y;
-    cwidth = width;
-    cheight = height;
+    struct rectangle image, request, intersection;
+    unsigned int row;
 
-    if (cx < 0) {
-        cx = 0;
-        cwidth += x;
-    }
-    if (cy < 0) {
-        cy = 0;
-        cheight += y;
-    }
-    if (cx + cwidth > cols)
-        cwidth = cols - cx;
+    if (width < 0)
+        pm_error("negative width %d passed to ppmd_filledrectangle", width);
+    if (height < 0)
+        pm_error("negative height %d passed to ppmd_filledrectangle", height);
+    if (cols < 0)
+        pm_error("negative image width %d passed to ppmd_filledrectangle",
+                 cols);
+    if (rows < 0)
+        pm_error("negative image height %d passed to ppmd_filledrectangle",
+                 rows);
+
+    request.ul.x = x;
+    request.ul.y = y;
+    request.lr.x = x + width;
+    request.lr.y = y + height;
+
+    image.ul.x = 0;
+    image.ul.y = 0;
+    image.lr.x = cols;
+    image.lr.y = rows;
 
-    if (cy + cheight > rows)
-        cheight = rows - cy;
+    findRectangleIntersection(image, request, &intersection);
 
     /* Draw. */
-    for (row = cy; row < cy + cheight; ++row) {
-        int col;
-        for (col = cx; col < cx + cwidth; ++col)
-            drawPoint(drawProc, clientdata,
-                      pixels, cols, rows, maxval, col, row);
+    for (row = intersection.ul.y; row < intersection.lr.y; ++row) {
+        unsigned int col;
+        for (col = intersection.ul.x; col < intersection.lr.x; ++col)
+            drawPoint(drawProcPointXY, &xy,
+                      pixels, cols, rows, maxval, makePoint(col, row));
     }
 }
 
@@ -187,20 +348,16 @@ ppmd_setlineclip(int const newSetting) {
 
 
 static void
-clipEnd0(int    const x0,
-         int    const y0,
-         int    const x1,
-         int    const y1,
-         int    const cols,
-         int    const rows,
-         int *  const cx0P,
-         int *  const cy0P,
-         bool * const noLineP) {
+clipEnd0(ppmd_point   const p0,
+         ppmd_point   const p1,
+         int          const cols,
+         int          const rows,
+         ppmd_point * const c0P,
+         bool *       const noLineP) {
 /*----------------------------------------------------------------------------
-   Given a line that goes from (x0, y0) to (x1, y1), where any of
-   these coordinates may be anywhere in space -- not just in the frame,
-   clip the (x0, y0) end to bring it into the frame.  
-   Return the clipped-to location as (*cx0P, *cy0P).
+   Given a line that goes from p0 to p1, where any of these coordinates may be
+   anywhere in space -- not just in the frame, clip the p0 end to bring it
+   into the frame.  Return the clipped-to location as *c0P.
 
    Iff this is not possible because the entire line described is
    outside the frame, return *nolineP == true.
@@ -208,45 +365,43 @@ clipEnd0(int    const x0,
    The frame is 'cols' columns starting at 0, by 'rows' rows starting
    at 0.
 -----------------------------------------------------------------------------*/
-
-    int cx0, cy0;
+    ppmd_point c0;
     bool noLine;
 
-    cx0 = x0;        /* initial value */
-    cy0 = y0;        /* initial value */
+    c0 = p0;         /* initial value */
     noLine = FALSE;  /* initial value */
 
     /* Clip End 0 of the line horizontally */
-    if (cx0 < 0) {
-        if (x1 < 0)
+    if (c0.x < 0) {
+        if (p1.x < 0)
             noLine = TRUE;
         else {
-            cy0 = cy0 + (y1 - cy0) * (-cx0) / (x1 - cx0);
-            cx0 = 0;
+            c0.y = c0.y + (p1.y - c0.y) * (-c0.x) / (p1.x - c0.x);
+            c0.x = 0;
         }
-    } else if (cx0 >= cols) {
-        if (x1 >= cols)
+    } else if (c0.x >= cols) {
+        if (p1.x >= cols)
             noLine = TRUE;
         else {
-            cy0 = cy0 + (y1 - cy0) * (cols - 1 - cx0) / (x1 - cx0);
-            cx0 = cols - 1;
+            c0.y = c0.y + (p1.y - c0.y) * (cols - 1 - c0.x) / (p1.x - c0.x);
+            c0.x = cols - 1;
         }
     }
 
     /* Clip End 0 of the line vertically */
-    if (cy0 < 0) {
-        if (y1 < 0)
+    if (c0.y < 0) {
+        if (p1.y < 0)
             noLine = TRUE;
         else {
-            cx0 = cx0 + (x1 - cx0) * (-cy0) / (y1 - cy0);
-            cy0 = 0;
+            c0.x = c0.x + (p1.x - c0.x) * (-c0.y) / (p1.y - c0.y);
+            c0.y = 0;
         }
-    } else if (cy0 >= rows) {
-        if (y1 >= rows)
+    } else if (c0.y >= rows) {
+        if (p1.y >= rows)
             noLine = TRUE;
         else {
-            cx0 = cx0 + (x1 - cx0) * (rows - 1 - cy0) / (y1 - cy0);
-            cy0 = rows - 1;
+            c0.x = c0.x + (p1.x - c0.x) * (rows - 1 - c0.y) / (p1.y - c0.y);
+            c0.y = rows - 1;
         }
     }
 
@@ -254,140 +409,127 @@ clipEnd0(int    const x0,
        horizontally.  If so, we know the other endpoint is also out of
        frame horizontally and the line misses the frame entirely.
     */
-    if (cx0 < 0 || cx0 >= cols) {
-        assert(x1 < 0 || x1 >= cols);
+    if (c0.x < 0 || c0.x >= cols) {
+        assert(p1.x < 0 || p1.x >= cols);
         noLine = TRUE;
     }
-    *cx0P = cx0;
-    *cy0P = cy0;
+    *c0P = c0;
     *noLineP = noLine;
 }
 
 
 
 static void
-clipEnd1(int    const x0,
-         int    const y0,
-         int    const x1,
-         int    const y1,
-         int    const cols,
-         int    const rows,
-         int *  const cx1P,
-         int *  const cy1P) {
+clipEnd1(ppmd_point   const p0,
+         ppmd_point   const p1,
+         int          const cols,
+         int          const rows,
+         ppmd_point * const c1P) {
 /*----------------------------------------------------------------------------
-   Given a line that goes from (x0, y0) to (x1, y1), where (x0, y0) is
-   within the frame, but (x1, y1) can be anywhere in space, clip the
-   (x1, y1) end to bring it into the frame.  Return the clipped-to
-   location as (*cx1P, *cy1P).
+   Given a line that goes from p0 to p1, where p0 is within the frame, but p1
+   can be anywhere in space, clip the p1 end to bring it into the frame.
+   Return the clipped-to location as *c1P.
 
-   This is guaranteed to be possible, since we already know at least one
-   point (i.e. (x0, y0)) is in the frame.
+   This is guaranteed to be possible, since we already know at least one point
+   (i.e. p0) is in the frame.
 
    The frame is 'cols' columns starting at 0, by 'rows' rows starting
    at 0.
 -----------------------------------------------------------------------------*/
-    int cx1, cy1;
-
-    assert(x0 >= 0 && y0 < cols);
-    assert(y0 >= 0 && y0 < rows);
+    ppmd_point c1;
+        /* The current clipped location of p1; we clip it multile times
+           to get the final location.
+        */
+    /* p0 is in the frame: */
+    assert(p0.x >= 0 && p0.x < cols);
+    assert(p0.y >= 0 && p0.y < rows);
     
     /* Clip End 1 of the line horizontally */
-    cx1 = x1;  /* initial value */
-    cy1 = y1;  /* initial value */
+    c1 = p1;  /* initial value */
     
-    if (cx1 < 0) {
+    if (c1.x < 0) {
         /* We know the line isn't vertical, since End 0 is in the frame
            and End 1 is left of frame.
         */
-        cy1 = cy1 + (y0 - cy1) * (-cx1) / (x0 - cx1);
-        cx1 = 0;
-    } else if (cx1 >= cols) {
+        c1.y = c1.y + (p0.y - c1.y) * (-c1.x) / (p0.x - c1.x);
+        c1.x = 0;
+    } else if (c1.x >= cols) {
         /* We know the line isn't vertical, since End 0 is in the frame
            and End 1 is right of frame.
         */
-        cy1 = cy1 + (y0 - cy1) * (cols - 1 - cx1) / (x0 - cx1);
-        cx1 = cols - 1;
+        c1.y = c1.y + (p0.y - c1.y) * (cols - 1 - c1.x) / (p0.x - c1.x);
+        c1.x = cols - 1;
     }
     
     /* Clip End 1 of the line vertically */
-    if (cy1 < 0) {
+    if (c1.y < 0) {
         /* We know the line isn't horizontal, since End 0 is in the frame
            and End 1 is above frame.
         */
-        cx1 = cx1 + (x0 - cx1) * (-cy1) / (y0 - cy1);
-        cy1 = 0;
-    } else if (cy1 >= rows) {
+        c1.x = c1.x + (p0.x - c1.x) * (-c1.y) / (p0.y - c1.y);
+        c1.y = 0;
+    } else if (c1.y >= rows) {
         /* We know the line isn't horizontal, since End 0 is in the frame
            and End 1 is below frame.
         */
-        cx1 = cx1 + (x0 - cx1) * (rows - 1 - cy1) / (y0 - cy1);
-        cy1 = rows - 1;
+        c1.x = c1.x + (p0.x - c1.x) * (rows - 1 - c1.y) / (p0.y - c1.y);
+        c1.y = rows - 1;
     }
 
-    *cx1P = cx1;
-    *cy1P = cy1;
+    *c1P = c1;
 }
 
 
 
 static void
-clipLine(int    const x0,
-         int    const y0,
-         int    const x1,
-         int    const y1,
-         int    const cols,
-         int    const rows,
-         int *  const cx0P,
-         int *  const cy0P,
-         int *  const cx1P,
-         int *  const cy1P,
-         bool * const noLineP) {
+clipLine(ppmd_point   const p0,
+         ppmd_point   const p1,
+         int          const cols,
+         int          const rows,
+         ppmd_point * const c0P,
+         ppmd_point * const c1P,
+         bool *       const noLineP) {
 /*----------------------------------------------------------------------------
-   Clip the line that goes from (x0, y0) to (x1, y1) so that none of it
-   is outside the boundaries of the raster with width 'cols' and height
-   'rows'
+   Clip the line that goes from p0 to p1 so that none of it is outside the
+   boundaries of the raster with width 'cols' and height 'rows'
 
-   The clipped line goes from (*cx0P, *cy0P) to (*cx1P, *cy1P).
+   The clipped line goes from *c0P to *c1P.
 
    But if the entire line is outside the boundaries (i.e. we clip the
    entire line), return *noLineP true and the other values undefined.
 -----------------------------------------------------------------------------*/
-    int cx0, cy0, cx1, cy1;
+    ppmd_point c0, c1;
         /* The line we successively modify.  Starts out as the input
            line and ends up as the output line.
         */
     bool noLine;
 
-    clipEnd0(x0, y0, x1, y1, cols, rows, &cx0, &cy0, &noLine);
+    clipEnd0(p0, p1, cols, rows, &c0, &noLine);
 
     if (!noLine) {
-        /* (cx0, cy0) is in the frame: */
-        assert(cx0 >= 0 && cy0 < cols);
-        assert(cy0 >= 0 && cy0 < rows);
+        /* p0 is in the frame: */
+        assert(c0.x >= 0 && c0.x < cols);
+        assert(c0.y >= 0 && c0.y < rows);
 
-        clipEnd1(cx0, cy0, x1, y1, cols, rows, &cx1, &cy1);
+        clipEnd1(c0, p1, cols, rows, &c1);
     }
 
-    *cx0P = cx0;
-    *cy0P = cy0;
-    *cx1P = cx1;
-    *cy1P = cy1;
+    *c0P = c0;
+    *c1P = c1;
     *noLineP = noLine;
 }
 
 
 
 static void
-drawShallowLine(ppmd_drawproc       drawProc,
-                const void *  const clientdata,
-                pixel **      const pixels, 
-                int           const cols, 
-                int           const rows, 
-                pixval        const maxval, 
-                int           const x0, 
-                int           const y0,
-                int           const x1,
-                int           const y1) {
+drawShallowLine(ppmd_drawprocp       drawProc,
+                const void *   const clientdata,
+                pixel **       const pixels, 
+                int            const cols, 
+                int            const rows, 
+                pixval         const maxval, 
+                ppmd_point     const p0,
+                ppmd_point     const p1) {
 /*----------------------------------------------------------------------------
    Draw a line that is more horizontal than vertical.
 
@@ -396,27 +538,27 @@ drawShallowLine(ppmd_drawproc       drawProc,
    Assume the line has distinct start and end points (i.e. it's at least
    two points).
 -----------------------------------------------------------------------------*/
-
     /* Loop over X domain. */
     long dy, srow;
     int dx, col, row, prevrow;
 
-    if (x1 > x0)
+    if (p1.x > p0.x)
         dx = 1;
     else
         dx = -1;
-    dy = (y1 - y0) * DDA_SCALE / abs(x1 - x0);
-    prevrow = row = y0;
+    dy = (p1.y - p0.y) * DDA_SCALE / abs(p1.x - p0.x);
+    prevrow = row = p0.y;
     srow = row * DDA_SCALE + DDA_SCALE / 2;
-    col = x0;
+    col = p0.x;
     for ( ; ; ) {
         if (linetype == PPMD_LINETYPE_NODIAGS && row != prevrow) {
             drawPoint(drawProc, clientdata,
-                      pixels, cols, rows, maxval, col, prevrow);
+                      pixels, cols, rows, maxval, makePoint(col, prevrow));
             prevrow = row;
         }
-        drawPoint(drawProc, clientdata, pixels, cols, rows, maxval, col, row);
-        if (col == x1)
+        drawPoint(drawProc, clientdata, pixels, cols, rows, maxval,
+                  makePoint(col, row));
+        if (col == p1.x)
             break;
         srow += dy;
         row = srow / DDA_SCALE;
@@ -427,16 +569,14 @@ drawShallowLine(ppmd_drawproc       drawProc,
 
 
 static void
-drawSteepLine(ppmd_drawproc       drawProc,
-              const void *  const clientdata,
-              pixel **      const pixels, 
-              int           const cols, 
-              int           const rows, 
-              pixval        const maxval, 
-              int           const x0, 
-              int           const y0,
-              int           const x1,
-              int           const y1) {
+drawSteepLine(ppmd_drawprocp       drawProc,
+              const void *   const clientdata,
+              pixel **       const pixels, 
+              int            const cols, 
+              int            const rows, 
+              pixval         const maxval, 
+              ppmd_point     const p0,
+              ppmd_point     const p1) {
 /*----------------------------------------------------------------------------
    Draw a line that is more vertical than horizontal.
 
@@ -450,22 +590,23 @@ drawSteepLine(ppmd_drawproc       drawProc,
     long dx, scol;
     int dy, col, row, prevcol;
 
-    if (y1 > y0)
+    if (p1.y > p0.y)
         dy = 1;
     else
         dy = -1;
-    dx = (x1 - x0) * DDA_SCALE / abs(y1 - y0);
-    row = y0;
-    prevcol = col = x0;
+    dx = (p1.x - p0.x) * DDA_SCALE / abs(p1.y - p0.y);
+    row = p0.y;
+    prevcol = col = p0.x;
     scol = col * DDA_SCALE + DDA_SCALE / 2;
     for ( ; ; ) {
         if (linetype == PPMD_LINETYPE_NODIAGS && col != prevcol) {
             drawPoint(drawProc, clientdata,
-                      pixels, cols, rows, maxval, prevcol, row);
+                      pixels, cols, rows, maxval, makePoint(prevcol, row));
             prevcol = col;
         }
-        drawPoint(drawProc, clientdata, pixels, cols, rows, maxval, col, row);
-        if (row == y1)
+        drawPoint(drawProc, clientdata, pixels, cols, rows, maxval,
+                  makePoint(col, row));
+        if (row == p1.y)
             break;
         row += dy;
         scol += dx;
@@ -476,55 +617,128 @@ drawSteepLine(ppmd_drawproc       drawProc,
 
 
 void
-ppmd_line(pixel **      const pixels, 
-          int           const cols, 
-          int           const rows, 
-          pixval        const maxval, 
-          int           const x0, 
-          int           const y0, 
-          int           const x1, 
-          int           const y1, 
-          ppmd_drawproc       drawProc,
-          const void *  const clientdata) {
-
-    int cx0, cy0, cx1, cy1;
+ppmd_linep(pixel **       const pixels, 
+           int            const cols, 
+           int            const rows, 
+           pixval         const maxval, 
+           ppmd_point     const p0,
+           ppmd_point     const p1,
+           ppmd_drawprocp       drawProc,
+           const void *   const clientdata) {
+
+    ppmd_point c0, c1;
     bool noLine;  /* There's no line left after clipping */
 
-    ppmd_validateCoords(cols, rows);
-    ppmd_validateCoords(x0, y0);
-    ppmd_validateCoords(x1, y1);
+    ppmd_validateCoord(cols);
+    ppmd_validateCoord(rows);
+    ppmd_validatePoint(p0);
+    ppmd_validatePoint(p1);
 
     if (lineclip) {
-        clipLine(x0, y0, x1, y1, cols, rows, &cx0, &cy0, &cx1, &cy1, &noLine);
+        clipLine(p0, p1, cols, rows, &c0, &c1, &noLine);
     } else {
-        cx0 = x0;
-        cy0 = y0;
-        cx1 = x1;
-        cy1 = y1;
+        c0 = p0;
+        c1 = p1;
         noLine = FALSE;
     }
 
     if (noLine) {
         /* Nothing to draw */
-    } else if (cx0 == cx1 && cy0 == cy1) {
+    } else if (pointsEqual(c0, c1)) {
         /* This line is just a point.  Because there aren't two
            distinct endpoints, we have a special case.
         */
-        drawPoint(drawProc, clientdata, pixels, cols, rows, maxval, cx0, cy0);
+        drawPoint(drawProc, clientdata, pixels, cols, rows, maxval, c0);
     } else {
         /* Draw, using a simple DDA. */
-        if (abs(cx1 - cx0) > abs(cy1 - cy0))
+        if (abs(c1.x - c0.x) > abs(c1.y - c0.y))
             drawShallowLine(drawProc, clientdata, pixels, cols, rows, maxval,
-                            cx0, cy0, cx1, cy1);
+                            c0, c1);
         else
             drawSteepLine(drawProc, clientdata, pixels, cols, rows, maxval,
-                          cx0, cy0, cx1, cy1);
+                          c0, c1);
+    }
+}
+
+
+void
+ppmd_line(pixel **      const pixels, 
+          int           const cols, 
+          int           const rows, 
+          pixval        const maxval, 
+          int           const x0, 
+          int           const y0, 
+          int           const x1, 
+          int           const y1, 
+          ppmd_drawproc       drawProc,
+          const void *  const clientData) {
+
+    struct drawProcXY const xy = makeDrawProcXY(drawProc, clientData);
+
+    ppmd_linep(pixels, cols, rows, maxval,
+               makePoint(x0, y0), makePoint(x1, y1), drawProcPointXY, &xy);
+}
+
+
+
+static unsigned int
+    distanceFromLine(ppmd_point const p,
+                     ppmd_point const l0,
+                     ppmd_point const l1) {
+/*----------------------------------------------------------------------------
+  Compute, sort of, the distance between point 'p' and the line through
+  'l0' and 'l1'.
+
+  I don't really know the signficance of this measurement.
+-----------------------------------------------------------------------------*/
+
+    ppmd_point const middle = middlePoint(l0, l1);
+
+    return (abs(p.x - middle.x) + abs(p.y - middle.y));
+}
+
+
+
+
+void
+ppmd_spline3p(pixel **       const pixels, 
+              int            const cols, 
+              int            const rows, 
+              pixval         const maxval, 
+              ppmd_point     const p0,
+              ppmd_point     const ctl,
+              ppmd_point     const p1,
+              ppmd_drawprocp       drawProc,
+              const void *   const clientdata) {
+
+    static unsigned int const splineThresh = 3;
+        /* The limit of recursion */
+
+    if (distanceFromLine(ctl, p0, p1) <= splineThresh) {
+        /* The control point is pretty close to the straight line that
+           joins the endpoints, so we'll just draw a straight line.
+        */
+        ppmd_linep(
+            pixels, cols, rows, maxval, p0, p1, drawProc, clientdata);
+    } else {
+        /* We want some curvature, so pick a point (b) sort of between the
+           two endpoints and the control point and then draw a spline
+           between each of the endpoints and (b):
+        */
+        ppmd_point const a = middlePoint(p0, ctl);
+        ppmd_point const c = middlePoint(ctl, p1);
+        ppmd_point const b = middlePoint(a, c);
+
+        ppmd_spline3p(
+            pixels, cols, rows, maxval, p0, a, b, drawProc, clientdata);
+
+        ppmd_spline3p(
+            pixels, cols, rows, maxval, b, c, p1, drawProc, clientdata);
     }
 }
 
 
 
-#define SPLINE_THRESH 3
 void
 ppmd_spline3(pixel **      const pixels, 
              int           const cols, 
@@ -539,35 +753,44 @@ ppmd_spline3(pixel **      const pixels,
              ppmd_drawproc       drawProc,
              const void *  const clientdata) {
 
-    register int xa, ya, xb, yb, xc, yc, xp, yp;
-
-    xa = ( x0 + x1 ) / 2;
-    ya = ( y0 + y1 ) / 2;
-    xc = ( x1 + x2 ) / 2;
-    yc = ( y1 + y2 ) / 2;
-    xb = ( xa + xc ) / 2;
-    yb = ( ya + yc ) / 2;
-
-    xp = ( x0 + xb ) / 2;
-    yp = ( y0 + yb ) / 2;
-    if ( abs( xa - xp ) + abs( ya - yp ) > SPLINE_THRESH )
-        ppmd_spline3(
-            pixels, cols, rows, maxval, x0, y0, xa, ya, xb, yb, drawProc,
-            clientdata );
-    else
-        ppmd_line(
-            pixels, cols, rows, maxval, x0, y0, xb, yb, drawProc, clientdata);
-
-    xp = ( x2 + xb ) / 2;
-    yp = ( y2 + yb ) / 2;
-    if ( abs( xc - xp ) + abs( yc - yp ) > SPLINE_THRESH )
-        ppmd_spline3(
-            pixels, cols, rows, maxval, xb, yb, xc, yc, x2, y2, drawProc,
-            clientdata );
-    else
-        ppmd_line(
-            pixels, cols, rows, maxval, xb, yb, x2, y2,
-            drawProc, clientdata );
+    struct drawProcXY const xy = makeDrawProcXY(drawProc, clientdata);
+
+    ppmd_spline3p(pixels, cols, rows, maxval,
+                  makePoint(x0, y0),
+                  makePoint(x1, y1),
+                  makePoint(x2, y2),
+                  drawProcPointXY, &xy);
+}
+
+
+
+void
+ppmd_polysplinep(pixel **       const pixels, 
+                 unsigned int   const cols, 
+                 unsigned int   const rows, 
+                 pixval         const maxval, 
+                 ppmd_point     const p0,
+                 unsigned int   const nc,
+                 ppmd_point *   const c,
+                 ppmd_point     const p1,
+                 ppmd_drawprocp       drawProc,
+                 const void *   const clientdata) {
+
+    ppmd_point p;
+    
+    unsigned int i;
+
+    assert(nc > 0);
+
+    p = p0;
+    for (i = 0; i < nc - 1; ++i) {
+        ppmd_point const n = middlePoint(c[i], c[i+1]);
+        ppmd_spline3p(
+            pixels, cols, rows, maxval, p, c[i], n, drawProc, clientdata);
+        p = n;
+    }
+    ppmd_spline3p(
+        pixels, cols, rows, maxval, p, c[nc - 1], p1, drawProc, clientdata);
 }
 
 
@@ -587,50 +810,132 @@ ppmd_polyspline(pixel **      const pixels,
                 ppmd_drawproc       drawProc,
                 const void *  const clientdata) {
 
-    register int i, x, y, xn, yn;
-
-    x = x0;
-    y = y0;
-    for ( i = 0; i < nc - 1; ++i )
-    {
-        xn = ( xc[i] + xc[i + 1] ) / 2;
-        yn = ( yc[i] + yc[i + 1] ) / 2;
-        ppmd_spline3(
-            pixels, cols, rows, maxval, x, y, xc[i], yc[i], xn, yn, drawProc,
-            clientdata );
-        x = xn;
-        y = yn;
+    ppmd_point const p1 = makePoint(x1, y1);
+    ppmd_point const p0 = makePoint(x0, y0);
+    struct drawProcXY const xy = makeDrawProcXY(drawProc, clientdata);
+
+    ppmd_point p;
+    unsigned int i;
+
+    p = p0;  /* initial value */
+
+    assert(nc > 0);
+
+    for (i = 0; i < nc - 1; ++i) {
+        ppmd_point const n = middlePoint(makePoint(xc[i], yc[i]),
+                                         makePoint(xc[i+1], yc[i+1]));
+        ppmd_spline3p(
+            pixels, cols, rows, maxval, p, makePoint(xc[i], yc[i]), n,
+            drawProcPointXY, &xy);
+        p = n;
     }
-    ppmd_spline3(
-        pixels, cols, rows, maxval, x, y, xc[nc - 1], yc[nc - 1], x1, y1,
-        drawProc, clientdata );
+    ppmd_spline3p(
+        pixels, cols, rows, maxval, p, makePoint(xc[nc - 1], yc[nc - 1]), p1,
+        drawProcPointXY, &xy);
 }
 
 
 
 void
-ppmd_spline4(pixel **      const pixels, 
-             int           const cols, 
-             int           const rows, 
-             pixval        const maxval, 
-             int           const x0, 
-             int           const y0, 
-             int           const x1, 
-             int           const y1, 
-             int           const x2, 
-             int           const y2, 
-             int           const x3, 
-             int           const y3, 
-             ppmd_drawproc       drawproc,
-             const void *  const clientdata) {
+ppmd_spline4p(pixel **       const pixels, 
+              unsigned int   const cols, 
+              unsigned int   const rows, 
+              pixval         const maxval, 
+              ppmd_point     const endPt0,
+              ppmd_point     const endPt1,
+              ppmd_point     const ctlPt0,
+              ppmd_point     const ctlPt1,
+              ppmd_drawprocp       drawproc,
+              const void *   const clientdata) {
+/*----------------------------------------------------------------------------
+   Draw a cubic spline from 'endPt0' to 'endPt1', using 'ctlPt0' and
+   'ctlPt1' as control points in the classic way: a line through
+   'endPt0' and 'ctlPt0' is tangent to the curve at 'entPt0' and the
+   length of that line controls "enthusiasm," whatever that is.
+   Same for 'endPt1' and 'ctlPt1'.
+-----------------------------------------------------------------------------*/
 
-    pm_error("ppmd_spline4() has not been written yet!");
+    pm_error("ppmd_spline4p() has not been written yet!");
 
 }
 
 
 
 void
+ppmd_circlep(pixel **       const pixels, 
+             unsigned int   const cols, 
+             unsigned int   const rows, 
+             pixval         const maxval, 
+             ppmd_point     const center,
+             unsigned int   const radius, 
+             ppmd_drawprocp       drawProc,
+             const void *   const clientData) {
+/*----------------------------------------------------------------------------
+  If lineclip mode is on, draw only points within the image.
+  If lineclip is off, "draw" all points (by designated drawproc).  Note
+  that the drawproc can't actually draw a point outside the image, but
+  it might maintain state that is affected by imaginary points outside
+  the image.
+
+  Initial point is 3 o'clock. 
+-----------------------------------------------------------------------------*/
+    if (radius >= DDA_SCALE)
+        pm_error("Error drawing circle.  Radius %d is too large.", radius);
+
+    ppmd_validateCoord(center.x + radius);
+    ppmd_validateCoord(center.y + radius);
+    ppmd_validateCoord(center.x - radius);
+    ppmd_validateCoord(center.y - radius);
+
+    if (radius > 0) {
+        long const e = DDA_SCALE / radius;
+
+        ppmd_point const p0 = makePoint(radius, 0);  /* 3 o'clock */
+            /* The starting point around the circle, assuming (0, 0) center */
+        ppmd_point p;
+            /* Current drawing position in the circle, assuming (0,0) center */
+        bool onFirstPoint;
+        bool prevPointExists;
+        ppmd_point prevPoint;
+            /* Previous drawing position, assuming (0, 0) center*/
+        long sx, sy;  /* 'p', scaled by DDA_SCALE */
+
+        p = p0;
+
+        sx = p.x * DDA_SCALE + DDA_SCALE / 2;
+        sy = p.y * DDA_SCALE + DDA_SCALE / 2;
+
+        onFirstPoint = TRUE;
+        prevPointExists = FALSE;
+
+        while (onFirstPoint || !pointsEqual(p, p0)) {
+            if (prevPointExists && pointsEqual(p, prevPoint)) {
+                /* We're on the same point we were on last time (we moved less
+                   than a point's worth).  Just keep moving.
+                */
+            } else {
+                ppmd_point const imagePoint = vectorSum(center,p);
+                if (!lineclip || pointIsWithinBounds(imagePoint, cols, rows))
+                    drawPoint(drawProc, clientData,
+                              pixels, cols, rows, maxval, imagePoint);
+
+                prevPoint = p;
+                prevPointExists = TRUE;
+            }
+
+            if (!pointsEqual(p, p0))
+                onFirstPoint = FALSE;
+
+            sx += e * sy / DDA_SCALE;
+            sy -= e * sx / DDA_SCALE;
+            p = makePoint(sx / DDA_SCALE, sy / DDA_SCALE);
+        }
+    }
+}
+
+
+
+void
 ppmd_circle(pixel **      const pixels, 
             int           const cols, 
             int           const rows, 
@@ -639,54 +944,16 @@ ppmd_circle(pixel **      const pixels,
             int           const cy, 
             int           const radius, 
             ppmd_drawproc       drawProc,
-            const void *  const clientdata) {
-
-    int x0, y0, x, y, prevx, prevy, nopointsyet;
-    long sx, sy, e;
+            const void *  const clientData) {
 
     if (radius < 0)
-        pm_error("Error drawing circle.  Radius must be positive: %d", radius);
-    else if (radius == 0)
-        return;
-    else if (radius >= DDA_SCALE)
-        pm_error("Error drawing circle.  Radius too large: %d", radius);
-
-    ppmd_validateCoords(cx + radius, cy + radius);
-    ppmd_validateCoords(cx - radius, cy - radius);
-
-    x0 = x = radius;
-    y0 = y = 0;
-    sx = x * DDA_SCALE + DDA_SCALE / 2;
-    sy = y * DDA_SCALE + DDA_SCALE / 2;
-    e = DDA_SCALE / radius;
-
-    /* If lineclip is on, draw only points within pixmap.
-       Initial point is 3 o'clock. 
-       If lineclip is off, "draw" all points (by designated drawproc).
-    */
+        pm_error("Error drawing circle.  Radius %d is negative.", radius);
+    else {
+        struct drawProcXY const xy = makeDrawProcXY(drawProc, clientData);
 
-    if ((x + cx >= 0 && x + cx < cols && y + cy >= 0 && y + cy < rows) ||
-        !lineclip)
-        drawPoint(drawProc, clientdata,
-                  pixels, cols, rows, maxval, x + cx, y + cy);
-    nopointsyet = 1;
-
-    do {
-        prevx = x;
-        prevy = y;
-        sx += e * sy / DDA_SCALE;
-        sy -= e * sx / DDA_SCALE;
-        x = sx / DDA_SCALE;
-        y = sy / DDA_SCALE;
-        if (x != prevx || y != prevy) {
-            nopointsyet = 0;
-            if ((x + cx >= 0 && x + cx < cols && y + cy >= 0 && y + cy < rows)
-                || !lineclip) 
-                drawPoint(drawProc, clientdata,
-                          pixels, cols, rows, maxval, x + cx, y + cy);
-        }
+        ppmd_circlep(pixels, cols, rows, maxval, makePoint(cx, cy), radius,
+                     drawProcPointXY, &xy);
     }
-    while (nopointsyet || x != x0 || y != y0);
 }
 
 
@@ -695,8 +962,7 @@ ppmd_circle(pixel **      const pixels,
 
 typedef struct
 {
-    int x;
-    int y;
+    ppmd_point point;
     int edge;
 } coord;
 
@@ -762,36 +1028,33 @@ ppmd_fill_destroy(struct fillobj * fillObjP) {
 }
 
 
+
 void
-ppmd_fill_drawproc(pixel**      const pixels, 
-                   int          const cols, 
-                   int          const rows, 
-                   pixval       const maxval, 
-                   int          const x, 
-                   int          const y, 
-                   const void * const clientdata) {
+ppmd_fill_drawprocp(pixel **     const pixels, 
+                    unsigned int const cols, 
+                    unsigned int const rows, 
+                    pixval       const maxval, 
+                    ppmd_point   const p,
+                    const void * const clientdata) {
 
     fillobj * fh;
     coord * cp;
-    coord * ocp;
 
     fh = (fillobj*) clientdata;
 
+    /* If these are the same coords we saved last time, don't bother. */
     if (fh->n > 0) {
-        /* If these are the same coords we saved last time, don't bother. */
-        ocp = &(fh->coords[fh->n - 1]);
-        if ( x == ocp->x && y == ocp->y )
+        ppmd_point const lastPoint = fh->coords[fh->n - 1].point;
+        if (pointsEqual(p, lastPoint))
             return;
     }
 
-    /* Ok, these are new; check if there's room for two more coords. */
+    /* Ok, these are new; make room for two more coords. */
     if (fh->n + 1 >= fh->size) {
         fh->size += SOME;
         REALLOCARRAY(fh->coords, fh->size);
         if (fh->coords == NULL)
-            pm_error( "out of memory enlarging a fillhandle" );
-
-        ocp = &(fh->coords[fh->n - 1]);
+            pm_error("out of memory enlarging a fillhandle");
     }
 
     /* Check for extremum and set the edge number. */
@@ -801,10 +1064,11 @@ ppmd_fill_drawproc(pixel**      const pixels,
         fh->ydir = 0;
         fh->startydir = 0;
     } else {
+        coord * const ocp = &(fh->coords[fh->n - 1]);
         int dx, dy;
 
-        dx = x - ocp->x;
-        dy = y - ocp->y;
+        dx = p.x - ocp->point.x;
+        dy = p.y - ocp->point.y;
         if (dx < -1 || dx > 1 || dy < -1 || dy > 1) {
             /* Segment break.  Close off old one. */
             if (fh->startydir != 0 && fh->ydir != 0)
@@ -836,8 +1100,7 @@ ppmd_fill_drawproc(pixel**      const pixels,
                     */
                     ++fh->curedge;
                     cp = &fh->coords[fh->n];
-                    cp->x = ocp->x;
-                    cp->y = ocp->y;
+                    cp->point = ocp->point;
                     cp->edge = fh->curedge;
                     ++fh->n;
                 }
@@ -850,14 +1113,28 @@ ppmd_fill_drawproc(pixel**      const pixels,
 
     /* Save this coord. */
     cp = &fh->coords[fh->n];
-    cp->x = x;
-    cp->y = y;
+    cp->point = p;
     cp->edge = fh->curedge;
     ++fh->n;
 }
 
 
 
+void
+ppmd_fill_drawproc(pixel**      const pixels, 
+                   int          const cols, 
+                   int          const rows, 
+                   pixval       const maxval, 
+                   int          const x, 
+                   int          const y, 
+                   const void * const clientData) {
+
+    ppmd_fill_drawprocp(pixels, cols, rows, maxval, makePoint(x, y),
+                        clientData);
+}
+
+
+
 
 #ifndef LITERAL_FN_DEF_MATCH
 static qsort_comparison_fn yx_compare;
@@ -870,15 +1147,18 @@ yx_compare(const void * const c1Arg,
     const coord * const c1P = c1Arg;
     const coord * const c2P = c2Arg;
 
+    ppmd_point const p1 = c1P->point;
+    ppmd_point const p2 = c2P->point;
+
     int retval;
     
-    if (c1P->y > c2P->y)
+    if (p1.y > p2.y)
         retval = 1;
-    else if (c1P->y < c2P->y)
+    else if (p1.y < p2.y)
         retval = -1;
-    else if (c1P->x > c2P->x)
+    else if (p1.x > p2.x)
         retval = 1;
-    else if (c1P->x < c2P->x)
+    else if (p1.x < p2.x)
         retval = -1;
     else
         retval = 0;
@@ -937,7 +1217,7 @@ ppmd_fill(pixel **         const pixels,
             fh->coords[i-2] = t;
         }
         if (i > 0) {
-            if (cp->x == lx && cp->y == py) {
+            if (cp->point.x == lx && cp->point.y == py) {
                 eq = TRUE;
                 if (cp->edge != edge && cp->edge == pedge) {
                     /* Swap . and .-1. */
@@ -950,8 +1230,8 @@ ppmd_fill(pixel **         const pixels,
             } else
                 eq = FALSE;
         }
-        lx    = cp->x;
-        py    = cp->y;
+        lx    = cp->point.x;
+        py    = cp->point.y;
         pedge = edge;
         edge  = cp->edge;
     }
@@ -960,35 +1240,35 @@ ppmd_fill(pixel **         const pixels,
     for (i = 0; i < fh->n; ++i) {
         cp = &fh->coords[i];
         if (i == 0) {
-            lx       = rx = cp->x;
-            py       = cp->y;
+            lx       = rx = cp->point.x;
+            py       = cp->point.y;
             edge     = cp->edge;
             leftside = TRUE;
         } else {
-            if (cp->y != py) {
+            if (cp->point.y != py) {
                 /* Row changed.  Emit old span and start a new one. */
                 ppmd_filledrectangle(
                     pixels, cols, rows, maxval, lx, py, rx - lx + 1, 1,
                     drawProc, clientdata);
-                lx       = rx = cp->x;
-                py       = cp->y;
+                lx       = rx = cp->point.x;
+                py       = cp->point.y;
                 edge     = cp->edge;
                 leftside = TRUE;
             } else {
                 if (cp->edge == edge) {
                     /* Continuation of side. */
-                    rx = cp->x;
+                    rx = cp->point.x;
                 } else {
                     /* Edge changed.  Is it a span? */
                     if (leftside) {
-                        rx       = cp->x;
+                        rx       = cp->point.x;
                         leftside = FALSE;
                     } else {
                         /* Got a span to fill. */
                         ppmd_filledrectangle(
                             pixels, cols, rows, maxval, lx, py, rx - lx + 1,
                             1, drawProc, clientdata);
-                        lx       = rx = cp->x;
+                        lx       = rx = cp->point.x;
                         leftside = TRUE;
                     }
                     edge = cp->edge;
@@ -1024,12 +1304,7 @@ static int extleft, exttop, extright, extbottom;  /* To accumulate extents */
 /*  ISIN  --  Return sine of an angle in integral degrees.  The
           value returned is 65536 times the sine.  */
 
-#if __STDC__
 static long isin(int deg)
-#else
-    static long isin(deg)
-    int deg;
-#endif
 {
     /* Domain reduce to 0 to 360 degrees. */
 
@@ -1054,58 +1329,145 @@ static long isin(int deg)
 /*  ICOS  --  Return cosine of an angle in integral degrees.  The
           value returned is 65536 times the cosine.  */
 
-#if __STDC__
 static long icos(int deg)
-#else
-    static long icos(deg)
-    int deg;
-#endif
 {
     return isin(deg + 90);
 }  
 
-#define SCHAR(x) (u = (x), (((u) & 0x80) ? ((u) | (-1 ^ 0xFF)) : (u)))
+static int
+twosCompByteValue(unsigned char const c) {
+/*----------------------------------------------------------------------------
+   E.g. if 'c' is 0x5, return 5.  If 'c' is 0xF0, return -16.
+-----------------------------------------------------------------------------*/
+    return (char)c;
+}
+
+static int
+glyphSkipBefore(const struct ppmd_glyph * const glyphP) {
+
+    return twosCompByteValue(glyphP->header.skipBefore);
+}
+
+static int
+glyphWidth(const struct ppmd_glyph * const glyphP) {
+
+    return twosCompByteValue(glyphP->header.skipAfter) -
+        twosCompByteValue(glyphP->header.skipBefore);
+}
+
+
+static ppmd_point
+commandPoint(const struct ppmd_glyphCommand * const commandP) {
+/*----------------------------------------------------------------------------
+   Return the point which is the argument of glyph drawing command
+   *commandP.  The origin of the coordinate system for this point
+   is the center of the glyph cell and the scale is the scale of the
+   font, so (-10, -10) means the upper left corner of the glyph cell.
+-----------------------------------------------------------------------------*/
+    return makePoint(twosCompByteValue(commandP->x),
+                     twosCompByteValue(commandP->y));
+}
 
 #define Scalef 21       /* Font design size */
 #define Descend 9       /* Descender offset */
 
 
+static ppmd_point
+textPosFromFontPos(ppmd_point   const fontPos,
+                   ppmd_point   const textBoxOrigin,
+                   ppmd_point   const center,
+                   ppmd_point   const glyphOrigin,
+                   unsigned int const height,
+                   long         const rotcos,
+                   long         const rotsin) {
+/*----------------------------------------------------------------------------
+   'fontPos' is a position within a glyph as told by the font definition.
+   It is relative to the center of the glyph, in units of font pixels
+   (1/21 of a glyph cell).
+
+   We return the position on the canvas of that point.
+
+   That takes into account where in the text box we are, where the text box
+   is on the canvas, the size of the characters, and the rotation of the
+   text box.
+-----------------------------------------------------------------------------*/
+    ppmd_point const ptl = vectorSum(center, fontPos);
+        /* Position relative to the top left of the standard glyph cell */
+
+    ppmd_point const pl = vectorSum(glyphOrigin, ptl);
+        /* Position relative to the top left of the whole text box,
+           assuming the text box is horizontal and has font scale.
+        */
+    
+    ppmd_point const ps = makePoint((pl.x * (int)height) / Scalef,
+                                    (pl.y * (int)height) / Scalef);
+         /* Same as above, but with the text box its actual size */
+
+    ppmd_point const retval =
+        makePoint(textBoxOrigin.x +
+                  (ps.x * rotcos - (ps.y-(int)height) * rotsin) / 65536,
+                  textBoxOrigin.y +
+                  (ps.x * rotsin + (ps.y-(int)height) * rotcos) / 65536);
+
+    ppmd_validatePoint(retval);
+
+    return retval;
+}
+
+
 
 static void
 drawGlyph(const struct ppmd_glyph * const glyphP,
-          int *                     const xP,
-          int                       const y,
+          ppmd_point                const glyphOrigin,
           pixel **                  const pixels,
           unsigned int              const cols,
           unsigned int              const rows,
           pixval                    const maxval,
           int                       const height,
-          int                       const xpos,
-          int                       const ypos,
+          ppmd_point                const textBoxOrigin,
           long                      const rotcos,
           long                      const rotsin,
-          ppmd_drawproc                   drawProc,
-          const void *              const clientdata
-          ) {
+          ppmd_drawprocp                  drawProc,
+          const void *              const clientdata,
+          unsigned int *            const cursorAdvanceP
+    ) {
 /*----------------------------------------------------------------------------
-   *xP is the column number of the left side of the glyph in the
-   output upon entry, and we update it to the left side of the next
-   glyph.
-
-   'y' is the row number of either the top or the bottom of the glyph
-   (I can't tell which right now) in the output.
+  'glyphOrigin' is the position relative to the upper left corner of the text
+  box of the upper left corner of this glyph cell.  It is in units of font
+  pixels (so you have to scale it by the font size to actual distance on
+  the canvas).
+
+  We return as *cursorAdvanceP the amount to the right of this glyph cell
+  the next glyph cell on the line (if any) should be.
+
+  The actual glyph cell may be a little to the left of the nominal position
+  because of kerning.  The font says how much to shift the cell left.
+
+  'textBoxOrigin' is the left end of the baseline of the top line in the
+  text box, in the coordinate system of the canvas.  'rotcos' and 'rotsin'
+  tell how that text box is rotated with respect to the horizontal on the
+  canvas.
+  
+  'height' is the height in canvas pixels of a glyph.  This is a scale factor
+  to convert font coordinates to canvas coordinates.
 -----------------------------------------------------------------------------*/
-    struct penpos penPos;
+    ppmd_point const center = makePoint(-glyphSkipBefore(glyphP), Scalef/2);
+        /* This is what you have to add to the coordinates in a glyph
+           command, which are relative to the center of the glyph, to get
+           coordinates relative to the upper left corner of the glyph
+        */
+    ppmd_point p;
+        /* Current drawing position within the glyph.  Origin is the top
+           left of the glyph cell.  Units are font pixels.
+        */
     unsigned int commandNum;
-    int x;
-    int u;  /* Used by the SCHAR macro */
-
-    x = *xP;  /* initial value */
 
-    x -= SCHAR(glyphP->header.skipBefore);
-
-    penPos.x = x;
-    penPos.y = y;
+    p = textPosFromFontPos(makePoint(0, 0),
+                           textBoxOrigin,
+                           center,
+                           glyphOrigin,
+                           height,
+                           rotcos, rotsin);   /* initial value */
 
     for (commandNum = 0;
          commandNum < glyphP->header.commandCount;
@@ -1119,84 +1481,68 @@ drawGlyph(const struct ppmd_glyph * const glyphP,
             break;
         case CMD_DRAWLINE:
         {
-            int const nx = x + SCHAR(commandP->x);
-            int const ny = y + SCHAR(commandP->y);
-
-            int mx1, my1, mx2, my2;
-            int tx1, ty1, tx2, ty2;
-
-            /* Note that up until this  moment  we've  been
-               working  in  an  arbitrary model co-ordinate
-               system with  fixed  size  and  no  rotation.
-               Before  drawing  the  stroke,  transform  to
-               viewing co-ordinates to  honour  the  height
-               and angle specifications.
-            */
-
-            mx1 = (penPos.x * height) / Scalef;
-            my1 = ((penPos.y - Descend) * height) / Scalef;
-            mx2 = (nx * height) / Scalef;
-            my2 = ((ny - Descend) * height) / Scalef;
-            tx1 = xpos + (mx1 * rotcos - my1 * rotsin) / 65536;
-            ty1 = ypos + (mx1 * rotsin + my1 * rotcos) / 65536;
-            tx2 = xpos + (mx2 * rotcos - my2 * rotsin) / 65536;
-            ty2 = ypos + (mx2 * rotsin + my2 * rotcos) / 65536;
-
-            ppmd_validateCoords(tx1, ty1);
-            ppmd_validateCoords(tx2, ty2);
-            
-            ppmd_line(pixels, cols, rows, maxval, tx1, ty1, tx2, ty2,
-                      drawProc, clientdata);
-
-            penPos.x = nx;
-            penPos.y = ny;
+            ppmd_point const n = textPosFromFontPos(commandPoint(commandP),
+                                                    textBoxOrigin,
+                                                    center,
+                                                    glyphOrigin,
+                                                    height,
+                                                    rotcos, rotsin);
+                                                    
+            ppmd_linep(pixels, cols, rows, maxval, p, n,
+                       drawProc, clientdata);
+
+            p = n;
         }
-            break;
+        break;
         case CMD_MOVEPEN:
-            penPos.x = x + SCHAR(commandP->x);
-            penPos.y = y + SCHAR(commandP->y);
+            p = textPosFromFontPos(commandPoint(commandP),
+                                   textBoxOrigin,
+                                   center,
+                                   glyphOrigin,
+                                   height,
+                                   rotcos, rotsin);
             break;
         }
     }
-    x += glyphP->header.skipAfter; 
-
-    *xP = x;
+    *cursorAdvanceP = glyphWidth(glyphP);
 }
 
 
-/* PPMD_TEXT  --  Draw the zero-terminated  string  s,  with  its  baseline
-          starting  at  point  (x, y), inclined by angle degrees to
-          the X axis, with letters height pixels  high  (descenders
-          will  extend below the baseline).  The supplied drawproc
-          and cliendata are passed to ppmd_line which performs  the
-          actual drawing. */
 
 void
-ppmd_text(pixel**       const pixels, 
-          int           const cols, 
-          int           const rows, 
-          pixval        const maxval, 
-          int           const xpos, 
-          int           const ypos, 
-          int           const height, 
-          int           const angle, 
-          const char *  const sArg, 
-          ppmd_drawproc       drawProc,
-          const void *  const clientdata) {
-
+ppmd_textp(pixel**        const pixels, 
+           int            const cols, 
+           int            const rows, 
+           pixval         const maxval, 
+           ppmd_point     const pos,
+           int            const height, 
+           int            const angle, 
+           const char *   const sArg, 
+           ppmd_drawprocp       drawProc,
+           const void *   const clientdata) {
+/*----------------------------------------------------------------------------
+   Draw the zero-terminated string 'sArg', with its baseline starting at point
+   'pos', inclined by 'angle' degrees to the X axis, with letters 'height'
+   pixels high (descenders will extend below the baseline).  We pass the
+   supplied drawproc and clientdata to ppmd_linep, which performs the actual
+   drawing.
+
+   There may be multiple lines of text.  The baseline of the topmost line
+   starts at 'pos'.
+-----------------------------------------------------------------------------*/
     const struct ppmd_font * const fontP = ppmd_get_font();
+
     long rotsin, rotcos;
-    int x, y;
+    ppmd_point p;
     const char * s;
 
-    ppmd_validateCoords(xpos, ypos);
+    ppmd_validatePoint(pos);
 
-    x = y = 0;
+    p = makePoint(0, 0);
     rotsin = isin(-angle);
     rotcos = icos(-angle);
 
-    s = sArg;
-    while (*s) {
+    for (s = &sArg[0]; *s; ) {
         unsigned char const ch = *s++;
 
         if (ch >= fontP->header.firstCodePoint &&
@@ -1205,19 +1551,45 @@ ppmd_text(pixel**       const pixels,
             const struct ppmd_glyph * const glyphP =
                 &fontP->glyphTable[ch - fontP->header.firstCodePoint];
 
-            ppmd_validateCoords(x, y); 
+            unsigned int cursorAdvance;
+
+            ppmd_validatePoint(p); 
 
-            drawGlyph(glyphP, &x, y, pixels, cols, rows, maxval,
-                      height, xpos, ypos, rotcos, rotsin,
-                      drawProc, clientdata);
+            drawGlyph(glyphP, p, pixels, cols, rows, maxval,
+                      height, pos, rotcos, rotsin,
+                      drawProc, clientdata, &cursorAdvance);
+            p.x += cursorAdvance;
         } else if (ch == '\n') {
             /* Move to the left edge of the next line down */
-            y += Scalef + Descend;
-            x = 0;
+            p.y += Scalef + Descend;
+            p.x = 0;
         }
     }
 }
 
+
+
+void
+ppmd_text(pixel**       const pixels, 
+          int           const cols, 
+          int           const rows, 
+          pixval        const maxval, 
+          int           const xpos, 
+          int           const ypos, 
+          int           const height, 
+          int           const angle, 
+          const char *  const sArg, 
+          ppmd_drawproc       drawProc,
+          const void *  const clientData) {
+
+    struct drawProcXY const xy = makeDrawProcXY(drawProc, clientData);
+
+    ppmd_textp(pixels, cols, rows, maxval, makePoint(xpos, ypos),
+               height, angle, sArg, drawProcPointXY, &xy);
+}
+
+
+
 /* EXTENTS_DRAWPROC  --  Drawproc which just accumulates the extents
              rectangle bounding the text. */
 
diff --git a/lib/libppmfloyd.c b/lib/libppmfloyd.c
index 071c3c36..ec6256ff 100644
--- a/lib/libppmfloyd.c
+++ b/lib/libppmfloyd.c
@@ -85,35 +85,36 @@ allocateFi(int const cols) {
 
 
 ppm_fs_info *
-ppm_fs_init(int cols, pixval maxval, int flags) {
+ppm_fs_init(unsigned int const cols,
+            pixval       const maxval,
+            unsigned int const flags) {
 
-    ppm_fs_info *fi;
+    ppm_fs_info * fiP;
     
-    fi = allocateFi(cols);
+    fiP = allocateFi(cols);
 
-    fi->lefttoright = 1;
-    fi->cols = cols;
-    fi->maxval = maxval;
-    fi->flags = flags;
-    
-    if( flags & FS_RANDOMINIT ) {
+    fiP->lefttoright = 1;
+    fiP->cols        = cols;
+    fiP->maxval      = maxval;
+    fiP->flags       = flags;
+
+    if (flags & FS_RANDOMINIT) {
         unsigned int i;
-        srand((int)(time(0) ^ getpid()));
-        for( i = 0; i < cols +2; i++ ) {
+        srand(pm_randseed());
+        for (i = 0; i < cols +2; ++i) {
             /* random errors in [-1..+1] */
-            fi->thisrederr[i]   = rand() % 32 - 16;
-            fi->thisgreenerr[i] = rand() % 32 - 16;
-            fi->thisblueerr[i]  = rand() % 32 - 16;
+            fiP->thisrederr[i]   = rand() % 32 - 16;
+            fiP->thisgreenerr[i] = rand() % 32 - 16;
+            fiP->thisblueerr[i]  = rand() % 32 - 16;
         }
-    }
-    else {
+    } else {
         unsigned int i;
 
-        for( i = 0; i < cols + 2; i++ )
-            fi->thisrederr[i] = fi->thisgreenerr[i] = 
-                fi->thisblueerr[i] = 0;
+        for (i = 0; i < cols + 2; ++i)
+            fiP->thisrederr[i] = fiP->thisgreenerr[i] = 
+                fiP->thisblueerr[i] = 0;
     }
-    return fi;
+    return fiP;
 }
 
 
diff --git a/lib/libppmfuzzy.c b/lib/libppmfuzzy.c
index 6127d5d5..2a54e896 100644
--- a/lib/libppmfuzzy.c
+++ b/lib/libppmfuzzy.c
@@ -84,7 +84,7 @@ memberTrapez(fzLog const x1,
 static fzLog
 hueIsAround000(double const hue) {
 
-    return memberZ(10, 30, hue);
+    return memberZ(10, 20, hue);
 }
 
 
@@ -92,7 +92,7 @@ hueIsAround000(double const hue) {
 static fzLog
 hueIsAround015(double const hue) {
 
-    return memberZ(30, 40, hue);
+    return memberZ(20, 40, hue);
 }
 
 
@@ -100,7 +100,7 @@ hueIsAround015(double const hue) {
 static fzLog
 hueIsAround030(double const hue) {
 
-    return memberTrapez(10, 30, 40, 60, hue);
+    return memberTrapez(10, 20, 40, 60, hue);
 }
 
 
@@ -108,7 +108,7 @@ hueIsAround030(double const hue) {
 static fzLog
 hueIsAround060(double const hue) {
 
-    return memberTrapez(40, 60, 60, 80, hue);
+    return memberTrapez(40, 50, 60, 70, hue);
 }
 
 
@@ -116,7 +116,7 @@ hueIsAround060(double const hue) {
 static fzLog
 hueIsAround120(double const hue) {
 
-    return memberTrapez(60, 80, 150, 180, hue);
+    return memberTrapez(60, 70, 150, 180, hue);
 }
 
 
@@ -160,14 +160,14 @@ hueIsAround360(double const hue) {
 static fzLog
 satIsVeryLow(double const sat) {
 
-    return memberZ(0.02, 0.1, sat);
+    return memberZ(0.03, 0.08, sat);
 }
 
 
 
 static fzLog
 satIsLow(double const sat) {
-    return memberTrapez(0.02, 0.1, 0.2, 0.3, sat);
+    return memberTrapez(0.03, 0.08, 0.17, 0.2, sat);
 }
 
 
@@ -175,7 +175,7 @@ satIsLow(double const sat) {
 static fzLog
 satIsMedium(double const sat) {
 
-    return memberTrapez(0.2, 0.3, 0.6, 0.7, sat);
+    return memberTrapez(0.17, 0.2, 0.6, 0.8, sat);
 }
 
 
@@ -183,7 +183,7 @@ satIsMedium(double const sat) {
 static fzLog
 satIsHigh(double const sat) {
 
-    return memberS(0.6, 0.7, sat);
+    return memberS(0.6, 0.8, sat);
 }
 
 
@@ -195,7 +195,7 @@ satIsHigh(double const sat) {
 static fzLog
 valIsVeryLow(double const val) {
 
-    return memberZ(0.1, 0.2, val);
+    return memberZ(0.05, 0.2, val);
 }
 
 
@@ -203,7 +203,7 @@ valIsVeryLow(double const val) {
 static fzLog
 valIsLow(double const val) {
 
-    return memberTrapez(0.1, 0.2, 0.3, 0.6, val);
+    return memberTrapez(0.05, 0.2, 0.25, 0.3, val);
 }
 
 
@@ -211,7 +211,7 @@ valIsLow(double const val) {
 static fzLog
 valIsMedium(double const val) {
 
-    return memberTrapez(0.3, 0.6, 0.7, 0.8, val);
+    return memberTrapez(0.25, 0.3, 0.6, 0.7, val);
 }
 
 
@@ -219,7 +219,15 @@ valIsMedium(double const val) {
 static fzLog
 valIsHigh(double const val) {
 
-    return memberS(0.7, 0.8, val);
+    return memberTrapez(0.6, 0.7, 0.95, 0.97, val);
+}
+
+
+
+static fzLog
+valIsVeryHigh(double const val) {
+
+    return memberS(0.95, 0.97, val);
 }
 
 
@@ -269,10 +277,11 @@ matchBk(pixel     const color,
     fzLog const satMedium  = satIsMedium(hsv.s);
     fzLog const satHigh    = satIsHigh(hsv.s);
 
-    fzLog const valVeryLow = valIsVeryLow(hsv.v);
-    fzLog const valLow     = valIsLow(hsv.v);
-    fzLog const valMedium  = valIsMedium(hsv.v);
-    fzLog const valHigh    = valIsHigh(hsv.v);
+    fzLog const valVeryLow  = valIsVeryLow(hsv.v);
+    fzLog const valLow      = valIsLow(hsv.v);
+    fzLog const valMedium   = valIsMedium(hsv.v);
+    fzLog const valHigh     = valIsHigh(hsv.v);
+    fzLog const valVeryHigh = valIsVeryHigh(hsv.v);
 
     fzLog const hueAround000 = hueIsAround000(hsv.h);
     fzLog const hueAround015 = hueIsAround015(hsv.h);
@@ -285,13 +294,13 @@ matchBk(pixel     const color,
     fzLog const hueAround360 = hueIsAround360(hsv.h);
 
     (*bkMatchP)[BKCOLOR_BLACK]  =
-        fzAnd(fzOr(satVeryLow, satLow), valVeryLow);
+        fzAnd(fzOr(satVeryLow, satLow), fzOr(valVeryLow, valLow));
 
     (*bkMatchP)[BKCOLOR_GRAY]   =
-        fzAnd(fzOr(satVeryLow, satLow), fzOr(valLow, valMedium));
+        fzAnd(satVeryLow, fzAnd(fzNot(valVeryLow), fzNot(valVeryHigh)));
 
     (*bkMatchP)[BKCOLOR_WHITE]  =
-        fzAnd(fzOr(satVeryLow, satLow), valHigh);
+        fzAnd(satVeryLow, valVeryHigh);
     
     (*bkMatchP)[BKCOLOR_RED]    =
         fzAnd(fzAnd(fzOr(hueAround000, hueAround360), fzNot(satVeryLow)),
@@ -300,38 +309,40 @@ matchBk(pixel     const color,
 
     (*bkMatchP)[BKCOLOR_ORANGE] =
         fzAnd(fzAnd(hueAround030, fzOr(satMedium, satHigh)),
-              fzOr(valMedium, valHigh)
+              fzOr(fzOr(valMedium, valHigh), valVeryHigh)
              );
 
     (*bkMatchP)[BKCOLOR_YELLOW] =
         fzAnd(fzAnd(hueAround060, fzOr(satMedium, satHigh)),
-              fzOr(valMedium, valHigh)
+              fzOr(valHigh, valVeryHigh)
              );
 
     (*bkMatchP)[BKCOLOR_GREEN]  =
-        fzAnd(fzAnd(hueAround120, fzNot(satVeryLow)),
-              fzOr(valMedium, valHigh)
+        fzAnd(fzAnd(hueAround120, fzOr(satMedium, satHigh)),
+              fzAnd(fzNot(valVeryLow), fzNot(valLow))
              );
 
     (*bkMatchP)[BKCOLOR_BLUE]   =
-        fzAnd(fzAnd(hueAround180, fzAnd(fzNot(satVeryLow), fzNot(satLow))),
-              fzOr(valMedium, valHigh)
+        fzAnd(fzAnd(hueAround180, fzNot(satVeryLow)),
+              fzNot(valVeryLow)
              );
 
     (*bkMatchP)[BKCOLOR_VIOLET] =
-        fzAnd(fzAnd(hueAround270, fzNot(satVeryLow)),
+        fzAnd(fzAnd(hueAround270, fzOr(satMedium, satHigh)),
               fzOr(valMedium, valHigh)
              );
 
     (*bkMatchP)[BKCOLOR_PURPLE] =
-        fzAnd(fzAnd(hueAround320, fzNot(satVeryLow)),
+        fzAnd(fzAnd(hueAround320, fzOr(satMedium, satHigh)),
               fzOr(valMedium, valHigh)
              );
 
     (*bkMatchP)[BKCOLOR_BROWN]  =
-        fzAnd(fzOr(hueAround015, hueAround360),
-              fzAnd(fzNot(satVeryLow), fzNot(valHigh))
-             );
+	fzOr(
+             fzAnd(fzOr(hueAround015, hueAround360),
+                   fzAnd(fzNot(satVeryLow), fzOr(valLow, valMedium))),
+             fzAnd(hueAround015, satLow)
+	    );
 }
 
 
@@ -359,17 +370,17 @@ ppm_bk_color_from_color(pixel  const color,
 
 
 static pixel const bkColorMap[BKCOLOR_COUNT] = {
-    {  0,   0,   0}, /* BKCOLOR_BLACK  */
     {174, 174, 174}, /* BKCOLOR_GRAY   */
-    {255, 255, 255}, /* BKCOLOR_WHITE  */
-    {255,   0,   0}, /* BKCOLOR_RED    */
+    {128,  42,  42}, /* BKCOLOR_BROWN  */
     {255, 128,   0}, /* BKCOLOR_ORANGE */
+    {255,   0,   0}, /* BKCOLOR_RED    */
     {255, 255,   0}, /* BKCOLOR_YELLOW */
     {  0, 255,   0}, /* BKCOLOR_GREEN  */
     {  0,   0, 255}, /* BKCOLOR_BLUE   */
     {143,  94, 153}, /* BKCOLOR_VIOLET */
     {160,  32, 240}, /* BKCOLOR_PURPLE */
-    {128,  42,  42}  /* BKCOLOR_BROWN  */
+    {255, 255, 255}, /* BKCOLOR_WHITE  */
+    {  0,   0,   0}  /* BKCOLOR_BLACK  */
 };
 
 
@@ -393,17 +404,17 @@ ppm_color_from_bk_color(bk_color const bkColor,
 
 
 static const char * const bkColorNameMap[BKCOLOR_COUNT] = {
-    "black",
     "gray",
-    "white",
-    "red",
+    "brown",
     "orange",
+    "red",
     "yellow",
     "green",
     "blue",
     "violet",
     "purple",
-    "brown"
+    "white",
+    "black"
 };
 
 
@@ -414,7 +425,7 @@ ppm_bk_color_from_name(const char * const name) {
     bk_color i;
 
     for (i = 0; i < BKCOLOR_COUNT; ++i) {
-        if (STREQ(name, bkColorNameMap[i]))
+        if (streq(name, bkColorNameMap[i]))
             return i;
     }
     pm_error("Invalid Berlin-Kay color name: '%s'", name);
diff --git a/lib/libsystem.c b/lib/libsystem.c
index b2f78ed4..92495fe5 100644
--- a/lib/libsystem.c
+++ b/lib/libsystem.c
@@ -14,6 +14,7 @@
 =============================================================================*/
 #define _XOPEN_SOURCE
 
+#include <stdarg.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <stdio.h>
@@ -22,6 +23,8 @@
 #include <signal.h>
 #include <sys/wait.h>
 
+#include "pm_c_util.h"
+#include "mallocvar.h"
 #include "pm.h"
 #include "pm_system.h"
 
@@ -30,35 +33,69 @@
 
 
 static void
-execProgram(const char * const shellCommand,
-            int          const stdinFd,
-            int          const stdoutFd) {
+closeUninheritableFds(int const stdinFd,
+                      int const stdoutFd) {
 /*----------------------------------------------------------------------------
-   Run the shell command 'shellCommand', supplying to the shell
-   'inputPipeFd' as its Standard Input and 'outputPipeFd' as its 
-   Standard Output.
+  Close all the file descriptors that we declare uninheritable -- files Parent
+  has open that Child has no business accessing.
 
-   But leave Standard Input and Standard Output as we found them.
+  Closing an extra file descriptor is essential to allow the file to close
+  when Parent closes it.
+
+  We define uninheritable as less than 64 and not Standard Input, Output,
+  or Error, or 'stdinFd' or 'stdoutFd'.
+-----------------------------------------------------------------------------*/
+    int fd;
+
+    for (fd = 0; fd < 64; ++fd) {
+        if (fd == stdinFd) {
+        } else if (fd == stdoutFd) {
+        } else if (fd == STDIN_FILENO) {
+        } else if (fd == STDOUT_FILENO) {
+        } else if (fd == STDERR_FILENO) {
+        } else {
+            close(fd);
+        }
+    }
+}
+
+
+
+static void
+execProgram(const char *  const progName,
+            const char ** const argArray,
+            int           const stdinFd,
+            int           const stdoutFd) {
+/*----------------------------------------------------------------------------
+   Exec the program 'progName' with arguments argArray[], with 'stdinFd' as
+   its Standard Input and 'stdoutFd' as its Standard Output.
+
+   But if the exec fails, leave all file descriptors as we found them.
+
+   Note that stdinFd or stdoutFd may actually be Standard Input and
+   Standard Output already.
 -----------------------------------------------------------------------------*/
     int stdinSaveFd, stdoutSaveFd;
     int rc;
     int execErrno;
 
-    /* Make inputPipeFd Standard Input.
-       Make outputPipeFd Standard Output.
+    /* Make stdinFd Standard Input.
+       Make stdoutFd Standard Output.
     */
     if (stdinFd != STDIN) {
         stdinSaveFd  = dup(STDIN);
         close(STDIN);
         dup2(stdinFd, STDIN);
+        close(stdinFd);
     }
     if (stdoutFd != STDOUT) {
         stdoutSaveFd = dup(STDOUT);
         close(STDOUT);
         dup2(stdoutFd, STDOUT);
+        close(stdoutFd);
     }
 
-    rc = execl("/bin/sh", "sh", "-c", shellCommand, NULL);
+    rc = execvp(progName, (char **)argArray);
 
     execErrno = errno;
 
@@ -73,10 +110,12 @@ execProgram(const char * const shellCommand,
         close(stdoutSaveFd);
     }
     if (rc < 0)
-        pm_error("Unable to exec the shell.  Errno=%d (%s)",
-                 execErrno, strerror(execErrno));
+        pm_error("Unable to exec '%s' "
+                 "(i.e. the program did not run at all).  "
+                 "execvp() errno=%d (%s)",
+                 progName, execErrno, strerror(execErrno));
     else
-        pm_error("INTERNAL ERROR.  execl() returns, but does not fail.");
+        pm_error("INTERNAL ERROR.  execvp() returns, but does not fail.");
 }
 
 
@@ -92,21 +131,21 @@ createPipeFeeder(void          pipeFeederRtn(int, void *),
    other end of the pipe as *fdP.
 -----------------------------------------------------------------------------*/
     int pipeToFeed[2];
-    pid_t feederPid;
+    pid_t rc;
 
     pipe(pipeToFeed);
-    feederPid = fork();
-    if (feederPid < 0) {
+    rc = fork();
+    if (rc < 0) {
         pm_error("fork() of stdin feeder failed.  errno=%d (%s)", 
                  errno, strerror(errno));
-    } else if (feederPid == 0) {
+    } else if (rc == 0) {
         /* This is the child -- the stdin feeder process */
         close(pipeToFeed[0]);
         (*pipeFeederRtn)(pipeToFeed[1], feederParm);
         exit(0);
-    }
-    else {
+    } else {
         /* This is the parent */
+        pid_t const feederPid = rc;
         close(pipeToFeed[1]);
         *fdP = pipeToFeed[0];
         *pidP = feederPid;
@@ -116,39 +155,62 @@ createPipeFeeder(void          pipeFeederRtn(int, void *),
 
 
 static void
-spawnProcessor(const char * const shellCommand, 
-               int          const stdinFd,
-               int *        const stdoutFdP,
-               pid_t *      const pidP) {
+spawnProcessor(const char *  const progName,
+               const char ** const argArray,
+               int           const stdinFd,
+               int *         const stdoutFdP,
+               pid_t *       const pidP) {
 /*----------------------------------------------------------------------------
-   Create a process to run a shell that runs command 'shellCommand'.
-   Pass file descriptor 'stdinFd' to the shell as Standard Input.
-   Set up a pipe and pass it to the shell as Standard Output.  Return
-   as *stdoutFdP the file descriptor of the other end of that pipe,
-   from which Caller can suck the shell's Standard Output.
+   Create a process to run program 'progName' with arguments
+   argArray[] (terminated by NULL element).  Pass file descriptor
+   'stdinFd' to the shell as Standard Input.
+
+   if 'stdoutFdP' is NULL, have that process write its Standard Output to
+   the current process' Standard Output.
+
+   If 'stdoutFdP' is non-NULL, set up a pipe and pass it to the new
+   process as Standard Output.  Return as *stdoutFdP the file
+   descriptor of the other end of that pipe, from which Caller can
+   suck the program's Standard Output.
 -----------------------------------------------------------------------------*/
+    bool const pipeStdout = !!stdoutFdP;
+
     int stdoutpipe[2];
-    pid_t processorpid;
-        
-    pipe(stdoutpipe);
+    pid_t rc;
 
-    processorpid = fork();
-    if (processorpid < 0) {
+    if (pipeStdout)
+        pipe(stdoutpipe);
+
+    rc = fork();
+    if (rc < 0) {
         pm_error("fork() of processor process failed.  errno=%d (%s)", 
                  errno, strerror(errno));
-    } else if (processorpid == 0) {
-        /* The second child */
-        close(stdoutpipe[0]);
+    } else if (rc == 0) {
+        /* The program child */
+
+        int stdoutFd;
+        
+        if (pipeStdout) {
+            close(stdoutpipe[0]);
+            stdoutFd = stdoutpipe[1];
+        } else
+            stdoutFd = STDOUT;
 
-        execProgram(shellCommand, stdinFd, stdoutpipe[1]);
+        closeUninheritableFds(stdinFd, stdoutFd);
+
+        execProgram(progName, argArray, stdinFd, stdoutFd);
 
         close(stdinFd);
         close(stdoutpipe[1]);
         pm_error("INTERNAL ERROR: execProgram() returns.");
     } else {
         /* The parent */
-        close(stdoutpipe[1]);
-        *stdoutFdP = stdoutpipe[0];
+        pid_t const processorpid = rc;
+
+        if (pipeStdout) {
+            close(stdoutpipe[1]);
+            *stdoutFdP = stdoutpipe[0];
+        }
         *pidP = processorpid;
     }
 }
@@ -235,7 +297,7 @@ cleanupProcessorProcess(pid_t const processorPid) {
         int const exitStatus = WEXITSTATUS(terminationStatus);
 
         if (exitStatus != 0)
-            pm_message("Shell process exited with abnormal exist status %u.  ",
+            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.",
@@ -281,18 +343,19 @@ cleanupFeederProcess(pid_t const feederPid) {
 
 
 void
-pm_system(void stdinFeeder(int, void *),
-          void *          const feederParm,
-          void stdoutAccepter(int, void *),
-          void *          const accepterParm,
-          const char *    const shellCommand) {
+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) {
 /*----------------------------------------------------------------------------
-   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'.
+   Run a program in a child process.  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
+   But if 'stdinFeeder' is NULL, just feed the program our own Standard
    Input.  And if 'stdoutFeeder' is NULL, just send its Standard Output
    to our own Standard Output.
 
@@ -300,77 +363,132 @@ pm_system(void stdinFeeder(int, void *),
    element).  That includes arg0.
 -----------------------------------------------------------------------------*/
     /* If 'stdinFeeder' is non-NULL, we create a child process to run
-       'stdinFeeder' and create a pipe between from that process as the
-       shell's Standard Input.
+       'stdinFeeder' and create a pipe from that process as the
+       program's Standard Input.
+
+       We create another child process to run the program.
 
-       If 'stdoutFeeder' is non-NULL, we create a child process to run
-       the shell and create a pipe between the shell's Standard Output
-       and this process, and then this process runs 'stdoutAccepter'
-       to read the data from that pipe.
+       If 'stdoutFeeder' is non-NULL, we create a pipe between the
+       program process and the current process and have the program
+       write its Standard Output to that pipe.  The current process
+       runs 'stdoutAccepter' to read the data from that pipe.
        
-       But if 'stdoutFeeder' is NULL, we just run the shell in this
-       process.
+       But if 'stdoutFeeder' is NULL, we just tell the program process
+       to write to the current process' Standard Output.
 
-       So there can be 1, 2, or 3 processes involved depending on 
-       parameters.
+       So there are two processes when stdinFeeder is NULL and three when
+       stdinFeeder is non-null.
     */
     
-    int shellStdinFd;
+    int progStdinFd;
     pid_t feederPid;
+    pid_t processorPid;
 
     if (stdinFeeder) 
-        createPipeFeeder(stdinFeeder, feederParm, &shellStdinFd, &feederPid);
+        createPipeFeeder(stdinFeeder, feederParm, &progStdinFd, &feederPid);
     else {
-        shellStdinFd = STDIN;
+        progStdinFd = STDIN;
         feederPid = 0;
     }
 
     if (stdoutAccepter) {
-        int shellStdoutFd;
-        pid_t processorPid;
+        int progStdoutFd;
 
-        /* Make a child process to run the shell and pipe back to us its
+        /* Make a child process to run the program and pipe back to us its
            Standard Output 
         */
-        spawnProcessor(shellCommand, shellStdinFd, 
-                       &shellStdoutFd, &processorPid);
+        spawnProcessor(progName, argArray, progStdinFd, 
+                       &progStdoutFd, &processorPid);
 
-        /* The shell process has cloned our 'shellStdinFd'; we have no
+        /* The child process has cloned our 'progStdinFd'; we have no
            more use for our copy.
         */
-        close(shellStdinFd);
-        /* Dispose of the stdout from that shell */
-        (*stdoutAccepter)(shellStdoutFd, accepterParm);
-        close(shellStdoutFd);
-
-        cleanupProcessorProcess(processorPid);
+        close(progStdinFd);
+        /* Dispose of the stdout from that child */
+        (*stdoutAccepter)(progStdoutFd, accepterParm);
+        close(progStdoutFd);
     } else {
-        /* Run a child process for the shell that sends its Standard Output
+        /* Run a child process for the program that sends its Standard Output
            to our Standard Output
         */
-        int const stdinSaveFd = dup(STDIN);
-        int rc;
+        spawnProcessor(progName, argArray, progStdinFd, NULL, &processorPid);
+    }
 
-        dup2(shellStdinFd, STDIN);
-        
-        rc = system(shellCommand);
+    cleanupProcessorProcess(processorPid);
 
-        close(STDIN);
-        dup2(stdinSaveFd, STDIN);
+    if (feederPid) 
+        cleanupFeederProcess(feederPid);
+}
+
+
+
+void
+pm_system_lp(const char *    const progName,
+             void stdinFeeder(int, void *),
+             void *          const feederParm,
+             void stdoutAccepter(int, void *),
+             void *          const accepterParm,
+             ...) {
+/*----------------------------------------------------------------------------
+  same as pm_system_vp() except with arguments as variable arguments
+  instead of an array.
+-----------------------------------------------------------------------------*/
+    va_list args;
+    bool endOfArgs;
+    const char ** argArray;
+    unsigned int n;
+
+    va_start(args, accepterParm);
+
+    endOfArgs = FALSE;
+    argArray = NULL;
+
+    for (endOfArgs = FALSE, argArray = NULL, n = 0;
+         !endOfArgs;
+        ) {
+        const char * const arg = va_arg(args, const char *);
         
-        if (rc < 0)
-            pm_error("Unable to invoke the shell.  Errno=%d (%s)",
-                     errno, strerror(errno));
-        else if (rc != 0)
-            pm_message("WARNING: Shell process completion code = %d", rc);
+        REALLOCARRAY(argArray, n+1);
+
+        argArray[n++] = arg;
+
+        if (!arg)
+            endOfArgs = TRUE;
     }
 
-    if (feederPid) 
-        cleanupFeederProcess(feederPid);
+    va_end(args);
+
+    pm_system_vp(progName, argArray,
+                 stdinFeeder, feederParm, stdoutAccepter, accepterParm);
+
+    free(argArray);
 }
 
 
 
+void
+pm_system(void stdinFeeder(int, void *),
+          void *          const feederParm,
+          void stdoutAccepter(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.
+-----------------------------------------------------------------------------*/
+
+    pm_system_lp("/bin/sh", 
+                 stdinFeeder, feederParm, stdoutAccepter, accepterParm,
+                 "sh", "-c", shellCommand, NULL);
+}
+
+
 
 void
 pm_feed_from_memory(int    const pipeToFeedFd,
diff --git a/lib/libsystem_dummy.c b/lib/libsystem_dummy.c
index afe20dde..2787ce67 100644
--- a/lib/libsystem_dummy.c
+++ b/lib/libsystem_dummy.c
@@ -15,6 +15,24 @@
 #include "pm_system.h"
 
 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) {
+}
+
+void
+pm_system_lp(const char *    const progName,
+             void stdinFeeder(int, void *),
+             void *          const feederParm,
+             void stdoutAccepter(int, void *),
+             void *          const accepterParm,
+             ...) {
+}
+
+void
 pm_system(void                  stdinFeeder(int, void *),
           void *          const feederParm,
           void                  stdoutAccepter(int, void *),
diff --git a/lib/mkstemp.c b/lib/mkstemp.c
deleted file mode 100644
index cd9140c7..00000000
--- a/lib/mkstemp.c
+++ /dev/null
@@ -1,8 +0,0 @@
-#include <stdlib.h>
-#include <stdio.h>
-#include <fcntl.h>
-
-#include "pm_c_util.h"
-
-
-
diff --git a/lib/pam.h b/lib/pam.h
index 3bad05e8..c28c5c2c 100644
--- a/lib/pam.h
+++ b/lib/pam.h
@@ -6,8 +6,8 @@
 #ifndef PAM_H
 #define PAM_H
 
-#include "pm.h"
-#include "pnm.h"
+#include <netpbm/pm.h>
+#include <netpbm/pnm.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -23,16 +23,16 @@ typedef unsigned long sample;
     */
 
 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.  
-
-   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
-   easy.
-*/
+    /* 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.  
+       
+       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
+       easy.
+    */
 
     /* 'size' and 'len' are necessary in order to provide forward and
        backward compatibility between library functions and calling programs
@@ -44,7 +44,9 @@ struct pam {
         /* 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'
-           */
+
+           Use PAM_STRUCT_SIZE() to compute or interpret a value for this.
+        */
     FILE * file;
     int format;
         /* The format code of the raw image.  This is PAM_FORMAT
@@ -52,7 +54,7 @@ struct pam {
            image.  Then it's PBM_FORMAT, RPBM_FORMAT, etc.
            */
     unsigned int plainformat;
-        /* Logical: On output, use the plain version of the format type
+        /* Logical: On output, use plain version of the format type
            indicated by 'format'.  Otherwise, use the raw version.
            (i.e., on output, the plainness information in 'format' is
            irrelevant).  Input functions set this to FALSE, for the
@@ -102,7 +104,7 @@ struct pam {
 
            On output, NULL means no comments.
 
-           On input, libnetpbm mallocs storage for the comments and placed
+           On input, libnetpbm mallocs storage for the comments and places
            the pointer at *comment_p.  Caller must free it.  NULL means
            libnetpbm does not return comments and does not allocate any
            storage.
@@ -457,6 +459,11 @@ tuple
 pnm_parsecolor(const char * const colorname,
                sample       const maxval);
 
+const char *
+pnm_colorname(struct pam * const pamP,
+              tuple        const color,
+              int          const hexok);
+
 extern double 
 pnm_lumin_factor[3];
 
diff --git a/lib/pammap.h b/lib/pammap.h
index fa054deb..c9c1deed 100644
--- a/lib/pammap.h
+++ b/lib/pammap.h
@@ -9,8 +9,8 @@
 
 #ifndef PAMMAP_H
 #define PAMMAP_H
-#include "colorname.h"
-#include "pam.h"
+#include <netpbm/colorname.h>
+#include <netpbm/pam.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -72,10 +72,12 @@ tupletable
 pnm_alloctupletable(const struct pam * const pamP, unsigned int const size);
 
 void
-pnm_freetupletable(struct pam * const pamP, tupletable const tupletable);
+pnm_freetupletable(const struct pam * const pamP,
+                   tupletable         const tupletable);
 
 void
-pnm_freetupletable2(struct pam * const pamP, tupletable2 const tupletable);
+pnm_freetupletable2(const struct pam * const pamP,
+                    tupletable2        const tupletable);
 
 tuplehash
 pnm_createtuplehash(void);
@@ -96,6 +98,14 @@ pnm_computetuplefreqtable2(struct pam *   const pamP,
                            sample         const newMaxval,
                            unsigned int * const sizeP);
 
+tupletable
+pnm_computetuplefreqtable3(struct pam *   const pamP,
+                           tuple **       const tupleArray,
+                           unsigned int   const maxsize,
+                           unsigned int   const newDepth,
+                           sample         const newMaxval,
+                           unsigned int * const countP);
+
 tuplehash
 pnm_computetuplefreqhash(struct pam *   const pamP,
                          tuple **       const tupleArray,
diff --git a/lib/pbm.h b/lib/pbm.h
index 1591c77f..24574d07 100644
--- a/lib/pbm.h
+++ b/lib/pbm.h
@@ -1,7 +1,7 @@
 #ifndef PBM_H_INCLUDED
 #define PBM_H_INCLUDED
 
-#include "pm.h"
+#include <netpbm/pm.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -33,13 +33,18 @@ typedef unsigned char bit;
 
 /* Declarations of routines. */
 
-void pbm_init ARGS(( int* argcP, char* argv[] ));
+void
+pbm_init(int *   const argcP,
+         char ** const argv);
+
 void
 pbm_nextimage(FILE *file, int * const eofP);
 
+bit *
+pbm_allocrow(unsigned int const cols);
+
 #define pbm_allocarray(cols, rows) \
   ((bit**) pm_allocarray(cols, rows, sizeof(bit)))
-#define pbm_allocrow(cols) ((bit*) pm_allocrow(cols, sizeof(bit)))
 #define pbm_freearray(bits, rows) pm_freearray((char**) bits, rows)
 #define pbm_freerow(bitrow) pm_freerow((char*) bitrow)
 #define pbm_packed_bytes(cols) (((cols)+7)/8)
@@ -56,9 +61,19 @@ pbm_nextimage(FILE *file, int * const eofP);
 bit** pbm_readpbm(FILE* file, int* colsP, int* rowsP);
 void pbm_readpbminit(FILE* file, int* colsP, int* rowsP, int* formatP);
 void pbm_readpbmrow(FILE* file, bit* bitrow, int cols, int format);
-void pbm_readpbmrow_packed(
-    FILE* const file, unsigned char * const packed_bits, 
-    const int cols, const int format);
+
+void
+pbm_readpbmrow_packed(FILE *          const file, 
+                      unsigned char * const packedBits,
+                      int             const cols, 
+                      int             const format);
+
+void
+pbm_readpbmrow_bitoffset(FILE *          const fileP,
+                         unsigned char * const packedBits, 
+                         int             const cols,
+                         int             const format,
+                         unsigned int    const offset);
 
 void
 pbm_writepbminit(FILE * const fileP, 
@@ -74,10 +89,10 @@ pbm_writepbm(FILE * const fileP,
              int    const forceplain);
 
 void
-pbm_writepbmrow(FILE * const fileP, 
-                bit *  const bitrow, 
-                int    const cols, 
-                int    const forceplain);
+pbm_writepbmrow(FILE *      const fileP, 
+                const bit * const bitrow, 
+                int         const cols, 
+                int         const forceplain);
 
 void
 pbm_writepbmrow_packed(FILE *                const fileP, 
@@ -86,10 +101,22 @@ pbm_writepbmrow_packed(FILE *                const fileP,
                        int                   const forceplain);
 
 void
+pbm_writepbmrow_bitoffset(FILE *          const ifP,
+                          unsigned char * const packedBits,
+                          unsigned int    const cols,
+                          int             const format,
+                          unsigned int    const offset);
+
+void
 pbm_check(FILE * file, const enum pm_check_type check_type, 
           const int format, const int cols, const int rows,
           enum pm_check_code * const retval_p);
 
+bit
+pbm_backgroundbitrow(const unsigned char * const packedBits,
+                     unsigned int          const cols,
+                     unsigned int          const offset);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/pbmfont.h b/lib/pbmfont.h
index 432aea3c..a977a11f 100644
--- a/lib/pbmfont.h
+++ b/lib/pbmfont.h
@@ -13,9 +13,10 @@ struct glyph {
        can be anything, but normally does not have white borders because
        it's more efficient to represent white borders explicitly.
     */
-	int width, height;
+    unsigned int width;
+    unsigned int height;
         /* The dimensions of the central glyph, i.e. the 'bmap' array */
-	int x;
+    int x;
         /* Width in pixels of the white left border of this glyph.
            This can actually be negative to indicate that the central
            glyph backs up over the previous character in the line.  In
@@ -23,12 +24,14 @@ struct glyph {
            is as if 'x' is 0.
         */
     int y;
-        /* Height in pixels of the white bottom border of this glyph */
-	int xadd;
+        /* Height in pixels of the white bottom border of this glyph.
+           Can be negative.
+        */
+    unsigned int xadd;
         /* Width of glyph -- white left border plus central glyph
            plus white right border
         */
-	const char * bmap;
+    const char * bmap;
         /* The raster of the central glyph.  It is an 'width' by
            'height' array in row major order, with each byte being 1
            for black; 0 for white.  E.g. if 'width' is 20 pixels and
@@ -44,31 +47,30 @@ struct font {
        an code point in the range 0..255, this structure describes the
        glyph for that character.
     */
-	int maxwidth, maxheight;
-	int x;
+    int maxwidth, maxheight;
+    int x;
         /* ?? Not used by Pbmtext */
     int y;
         /* Amount of white space that should be added between lines of
-           this font.
+           this font.  Can be negative.
         */
-	struct glyph * glyph[256];
+    struct glyph * glyph[256];
         /* glyph[i] is the glyph for code point i */
-	bit** oldfont;
-	    /* for compatibility with old pbmtext routines */
-	    /* oldfont is 0 if the font is BDF derived */
-	int fcols, frows;
+    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*
-pbm_dissectfont(bit ** const font,
-                int    const frows,
-                int    const fcols);
+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 ARGS(( struct font* fn ));
-int mk_argvn ARGS(( char* s, char* vec[], int max ));
+void pbm_dumpfont(struct font * const fnP);
 
 #ifdef __cplusplus
 }
diff --git a/lib/pgm.h b/lib/pgm.h
index 86935307..2de8d531 100644
--- a/lib/pgm.h
+++ b/lib/pgm.h
@@ -4,7 +4,8 @@
 #ifndef _PGM_H_
 #define _PGM_H_
 
-#include "pbm.h"
+#include <netpbm/pm.h>
+#include <netpbm/pbm.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -68,7 +69,7 @@ pgm_init(int *   const argcP,
 gray *
 pgm_allocrow(unsigned int const cols);
 
-#define pgm_freerow(grayrow) free(grayrow)
+#define pgm_freerow(grayrow) pm_freerow(grayrow)
 
 #define pgm_allocarray( cols, rows ) \
   ((gray**) pm_allocarray( cols, rows, sizeof(gray) ))
diff --git a/lib/pm.h b/lib/pm.h
index 9c3c087e..a0c2005e 100644
--- a/lib/pm.h
+++ b/lib/pm.h
@@ -13,7 +13,7 @@
 #ifndef PM_H_INCLUDED
 #define PM_H_INCLUDED
 
-#include "pm_config.h"
+#include <netpbm/pm_config.h>
 
 #include <sys/types.h>
 #include <ctype.h>
@@ -21,6 +21,7 @@
 #include <errno.h>
 #include <setjmp.h>
 #include <sys/stat.h>
+#include <fcntl.h>
 
 #ifdef VMS
 #include <perror.h>
@@ -79,6 +80,26 @@ extern "C" {
 #define PURE_FN_ATTR
 #endif
 
+
+/* S_IRUSR is POSIX, defined in <sys/stat.h> Some old BSD systems and Windows
+   systems have S_IREAD instead.  Most Unix today (2011) has both.  In 2011,
+   Android has S_IRUSR and not S_IREAD.
+
+   Some Windows has _S_IREAD.
+
+   We're ignoring S_IREAD now to see if anyone misses it.  If there are still
+   users that need it, we can handle it here.
+*/
+#ifdef WIN32
+  #define PM_S_IWUSR _S_IWRITE
+  #define PM_S_IRUSR _S_IREAD
+#else
+  #define PM_S_IWUSR S_IWUSR
+  #define PM_S_IRUSR S_IRUSR
+#endif
+
+
+
 typedef struct {
     /* Coordinates of a pixel within an image.  Row 0 is the top row.
        Column 0 is the left column.
@@ -91,12 +112,13 @@ extern int pm_plain_output;
     /* Output functions are to produce plain (as opposed to raw) format
        regardless of their 'plainformat' arguments.
     */
+extern const char * pm_progname;
 
 void 
 pm_init(const char * const progname, unsigned int const flags);
 
 void 
-pm_proginit(int* const argcP, char* argv[]);
+pm_proginit(int * const argcP, const char * argv[]);
 
 void
 pm_setMessage(int const newState, int * const oldStateP);
@@ -104,11 +126,18 @@ pm_setMessage(int const newState, int * const oldStateP);
 FILE * 
 pm_tmpfile(void);
 
+int
+pm_tmpfile_fd(void);
+
 void
 pm_make_tmpfile(FILE **       const filePP,
                 const char ** const filenameP);
 
 void
+pm_make_tmpfile_fd(int *         const fdP,
+                   const char ** const filenameP);
+
+void
 pm_nextimage(FILE * const file, int * const eofP);
 
 /* Variable-sized arrays definitions. */
@@ -116,7 +145,7 @@ pm_nextimage(FILE * const file, int * const eofP);
 char** 
 pm_allocarray (int const cols, int const rows, int const size );
 
-char * 
+void * 
 pm_allocrow(unsigned int const cols,
             unsigned int const size);
 
@@ -124,12 +153,14 @@ void
 pm_freearray (char** const its, int const rows);
 
 void 
-pm_freerow(char* const itrow);
+pm_freerow(void * const row);
 
 
 /* Obsolete -- use shhopt instead */
 int 
-pm_keymatch (char* const str, const char* const keyword, int const minchars);
+pm_keymatch(const char * const str,
+            const char * const keyword,
+            int          const minchars);
 
 
 int PURE_FN_ATTR
@@ -154,15 +185,25 @@ pm_setjmpbufsave(jmp_buf *  const jmpbufP,
 void
 pm_longjmp(void);
 
+
+typedef void pm_usermessagefn(const char * msg);
+
+void
+pm_setusermessagefn(pm_usermessagefn * fn);
+
+typedef void pm_usererrormsgfn(const char * msg);
+
+void
+pm_setusererrormsgfn(pm_usererrormsgfn * fn);
+
 void PM_GNU_PRINTF_ATTR(1,2)
 pm_message (const char format[], ...);     
 
 void PM_GNU_PRINTF_ATTR(1,2)
-pm_error (const char reason[], ...);       
+pm_errormsg(const char format[], ...);
 
-/* Obsolete - use helpful error message instead */
-void
-pm_perror (const char reason[]);           
+void PM_GNU_PRINTF_ATTR(1,2)
+pm_error (const char reason[], ...);       
 
 /* Obsolete - use shhopt and user's manual instead */
 void 
@@ -335,9 +376,23 @@ pm_check(FILE *               const file,
          pm_filepos           const need_raster_size,
          enum pm_check_code * const retval_p);
 
+void
+pm_drain(FILE *         const fileP,
+         unsigned int   const limit,
+         unsigned int * const bytesReadP);
+
 char *
 pm_arg0toprogname(const char arg0[]);
 
+unsigned int
+pm_randseed(void);
+
+unsigned int
+pm_parse_width(const char * const arg);
+
+unsigned int
+pm_parse_height(const char * const arg);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/pm_gamma.h b/lib/pm_gamma.h
index 92b34145..6630e05c 100644
--- a/lib/pm_gamma.h
+++ b/lib/pm_gamma.h
@@ -1,7 +1,7 @@
 #ifndef _PM_GAMMA_H_
 #define _PM_GAMMA_H_
 
-#include "pm_config.h"
+#include <netpbm/pm_config.h>
 
 #include <math.h>
 
diff --git a/lib/pm_system.h b/lib/pm_system.h
index 0605f888..a7560f48 100644
--- a/lib/pm_system.h
+++ b/lib/pm_system.h
@@ -10,13 +10,28 @@ extern "C" {
 
 
 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);
+
+void
+pm_system_lp(const char *    const progName,
+             void stdinFeeder(int, void *),
+             void *          const feederParm,
+             void stdoutAccepter(int, void *),
+             void *          const accepterParm,
+             ...);
+
+void
 pm_system(void                  stdinFeeder(int, void *),
           void *          const feederParm,
           void                  stdoutAccepter(int, void *),
           void *          const accepterParm,
           const char *    const shellCommand);
 
-
 struct bufferDesc {
     /* This is just a parameter for the routines below */
     unsigned int    size;
diff --git a/lib/pmfileio.c b/lib/pmfileio.c
new file mode 100644
index 00000000..84ab53ab
--- /dev/null
+++ b/lib/pmfileio.c
@@ -0,0 +1,966 @@
+/**************************************************************************
+                                 pmfileio.c
+***************************************************************************
+  This file contains fundamental file I/O stuff for libnetpbm.
+  These are external functions, unlike 'fileio.c', but are not
+  particular to any Netpbm format.
+**************************************************************************/
+#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
+       in some environments.
+    */
+#define _XOPEN_SOURCE 500    /* Make sure ftello, fseeko are defined */
+#define _LARGEFILE_SOURCE 1  /* Make sure ftello, fseeko are defined */
+#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 
+       case ftello() is still just ftello().
+
+       Likewise for all the other C library file functions.
+
+       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  
+    /* 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 */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#ifdef __DJGPP__
+  #include <io.h>
+#endif
+
+#include "pm_c_util.h"
+#include "mallocvar.h"
+#include "nstring.h"
+
+#include "pm.h"
+
+
+
+/* File open/close that handles "-" as stdin/stdout and checks errors. */
+
+FILE*
+pm_openr(const char * const name) {
+    FILE* f;
+
+    if (strcmp(name, "-") == 0)
+        f = stdin;
+    else {
+#ifndef VMS
+        f = fopen(name, "rb");
+#else
+        f = fopen(name, "r", "ctx=stm");
+#endif
+        if (f == NULL) 
+            pm_error("Unable to open file '%s' for reading.  "
+                     "fopen() returns errno %d (%s)", 
+                     name, errno, strerror(errno));
+    }
+    return f;
+}
+
+
+
+FILE*
+pm_openw(const char * const name) {
+    FILE* f;
+
+    if (strcmp(name, "-") == 0)
+        f = stdout;
+    else {
+#ifndef VMS
+        f = fopen(name, "wb");
+#else
+        f = fopen(name, "w", "mbc=32", "mbf=2");  /* set buffer factors */
+#endif
+        if (f == NULL) 
+            pm_error("Unable to open file '%s' for writing.  "
+                     "fopen() returns errno %d (%s)", 
+                     name, errno, strerror(errno));
+    }
+    return f;
+}
+
+
+
+static const char *
+tmpDir(void) {
+/*----------------------------------------------------------------------------
+   Return the name of the directory in which we should create temporary
+   files.
+
+   The name is a constant in static storage.
+-----------------------------------------------------------------------------*/
+    const char * tmpdir;
+        /* running approximation of the result */
+
+    tmpdir = getenv("TMPDIR");   /* Unix convention */
+
+    if (!tmpdir || strlen(tmpdir) == 0)
+        tmpdir = getenv("TMP");  /* Windows convention */
+
+    if (!tmpdir || strlen(tmpdir) == 0)
+        tmpdir = getenv("TEMP"); /* Windows convention */
+
+    if (!tmpdir || strlen(tmpdir) == 0)
+        tmpdir = TMPDIR;
+
+    return tmpdir;
+}
+
+
+
+static int
+tempFileOpenFlags(void) {
+/*----------------------------------------------------------------------------
+  Open flags (argument to open()) suitable for a new temporary file  
+-----------------------------------------------------------------------------*/
+    int retval;
+
+    retval = 0
+        | O_CREAT
+        | O_RDWR
+#ifndef WIN32
+        | O_EXCL
+#endif
+#ifdef WIN32
+        | O_BINARY
+#endif
+        ;
+
+    return retval;
+}
+
+
+
+
+static int
+mkstempx(char * const filenameBuffer) {
+/*----------------------------------------------------------------------------
+  This is meant to be equivalent to POSIX mkstemp().
+
+  On some old systems, mktemp() is a security hazard that allows a hacker
+  to read or write our temporary file or cause us to read or write some
+  unintended file.  On other systems, mkstemp() does not exist.
+
+  A Windows/mingw environment is one which doesn't have mkstemp()
+  (2006.06.15).
+
+  We assume that if a system doesn't have mkstemp() that its mktemp()
+  is safe, or that the total situation is such that the problems of
+  mktemp() are not a problem for the user.
+-----------------------------------------------------------------------------*/
+    int retval;
+    int fd;
+    unsigned int attempts;
+    bool gotFile;
+    bool error;
+
+    for (attempts = 0, gotFile = FALSE, error = FALSE;
+         !gotFile && !error && attempts < 100;
+         ++attempts) {
+
+        char * rc;
+        rc = mktemp(filenameBuffer);
+
+        if (rc == NULL)
+            error = TRUE;
+        else {
+            int rc;
+
+            rc = open(filenameBuffer, tempFileOpenFlags(),
+                      PM_S_IWUSR | PM_S_IRUSR);
+
+            if (rc >= 0) {
+                fd = rc;
+                gotFile = TRUE;
+            } else {
+                if (errno == EEXIST) {
+                    /* We'll just have to keep trying */
+                } else 
+                    error = TRUE;
+            }
+        }
+    }    
+    if (gotFile)
+        retval = fd;
+    else
+        retval = -1;
+
+    return retval;
+}
+
+
+
+static int
+mkstemp2(char * const filenameBuffer) {
+
+#if HAVE_MKSTEMP
+    if (0)
+        mkstempx(NULL);  /* defeat compiler unused function warning */
+    return mkstemp(filenameBuffer);
+#else
+    return mkstempx(filenameBuffer);
+#endif
+}
+
+
+
+static void
+makeTmpfileWithTemplate(const char *  const filenameTemplate,
+                        int *         const fdP,
+                        const char ** const filenameP,
+                        const char ** const errorP) {
+    
+    char * filenameBuffer;  /* malloc'ed */
+
+    filenameBuffer = strdup(filenameTemplate);
+
+    if (filenameBuffer == NULL)
+        asprintfN(errorP, "Unable to allocate storage for temporary "
+                  "file name");
+    else {
+        int rc;
+        
+        rc = mkstemp2(filenameBuffer);
+        
+        if (rc < 0)
+            asprintfN(errorP,
+                      "Unable to create temporary file according to name "
+                      "pattern '%s'.  mkstemp() failed with errno %d (%s)",
+                      filenameTemplate, errno, strerror(errno));
+        else {
+            *fdP = rc;
+            *filenameP = filenameBuffer;
+            *errorP = NULL;
+        }
+        if (*errorP)
+            strfree(filenameBuffer);
+    }
+}
+
+
+
+void
+pm_make_tmpfile_fd(int *         const fdP,
+                   const char ** const filenameP) {
+
+    const char * filenameTemplate;
+    unsigned int fnamelen;
+    const char * tmpdir;
+    const char * dirseparator;
+    const char * error;
+
+    fnamelen = strlen(pm_progname) + 10; /* "/" + "_XXXXXX\0" */
+
+    tmpdir = tmpDir();
+
+    if (tmpdir[strlen(tmpdir) - 1] == '/')
+        dirseparator = "";
+    else
+        dirseparator = "/";
+    
+    asprintfN(&filenameTemplate, "%s%s%s%s", 
+              tmpdir, dirseparator, pm_progname, "_XXXXXX");
+
+    if (filenameTemplate == strsol)
+        asprintfN(&error,
+                  "Unable to allocate storage for temporary file name");
+    else {
+        makeTmpfileWithTemplate(filenameTemplate, fdP, filenameP, &error);
+
+        strfree(filenameTemplate);
+    }
+    if (error) {
+        pm_errormsg("%s", error);
+        strfree(error);
+        pm_longjmp();
+    }
+}
+
+
+
+void
+pm_make_tmpfile(FILE **       const filePP,
+                const char ** const filenameP) {
+
+    int fd;
+
+    pm_make_tmpfile_fd(&fd, filenameP);
+
+    *filePP = fdopen(fd, "w+b");
+    
+    if (*filePP == NULL) {
+        close(fd);
+        unlink(*filenameP);
+        strfree(*filenameP);
+
+        pm_error("Unable to create temporary file.  "
+                 "fdopen() failed with errno %d (%s)",
+                 errno, strerror(errno));
+    }
+}
+
+
+
+FILE * 
+pm_tmpfile(void) {
+
+    FILE * fileP;
+    const char * tmpfile;
+
+    pm_make_tmpfile(&fileP, &tmpfile);
+
+    unlink(tmpfile);
+
+    strfree(tmpfile);
+
+    return fileP;
+}
+
+
+
+int
+pm_tmpfile_fd(void) {
+
+    int fd;
+    const char * tmpfile;
+
+    pm_make_tmpfile_fd(&fd, &tmpfile);
+
+    unlink(tmpfile);
+
+    strfree(tmpfile);
+
+    return fd;
+}
+
+
+
+FILE *
+pm_openr_seekable(const char name[]) {
+/*----------------------------------------------------------------------------
+  Open the file named by name[] such that it is seekable (i.e. it can be
+  rewound and read in multiple passes with fseek()).
+
+  If the file is actually seekable, this reduces to the same as
+  pm_openr().  If not, we copy the named file to a temporary file
+  and return that file's stream descriptor.
+
+  We use a file that the operating system recognizes as temporary, so
+  it picks the filename and deletes the file when Caller closes it.
+-----------------------------------------------------------------------------*/
+    int stat_rc;
+    int seekable;  /* logical: file is seekable */
+    struct stat statbuf;
+    FILE * original_file;
+    FILE * seekable_file;
+
+    original_file = pm_openr((char *) name);
+
+    /* 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
+       0 if the file is positioned at 0 but you can't actually back up
+       to 0.  I have seen documentation that says the errno for an
+       unseekable stream is EBADF and in practice seen ESPIPE.
+
+       On the other hand, regular files are always seekable and even if
+       some other file is, it doesn't hurt much to assume it isn't.
+    */
+
+    stat_rc = fstat(fileno(original_file), &statbuf);
+    if (stat_rc == 0 && S_ISREG(statbuf.st_mode))
+        seekable = TRUE;
+    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) 
+               && !ferror(seekable_file)) {
+            char buffer[4096];
+            int bytes_read;
+            bytes_read = fread(buffer, 1, sizeof(buffer), original_file);
+            fwrite(buffer, 1, bytes_read, seekable_file);
+        }
+        if (ferror(original_file))
+            pm_error("Error reading input file into temporary file.  "
+                     "Errno = %s (%d)", strerror(errno), errno);
+        if (ferror(seekable_file))
+            pm_error("Error writing input into temporary file.  "
+                     "Errno = %s (%d)", strerror(errno), errno);
+        pm_close(original_file);
+        {
+            int seek_rc;
+            seek_rc = fseek(seekable_file, 0, SEEK_SET);
+            if (seek_rc != 0)
+                pm_error("fseek() failed to rewind temporary file.  "
+                         "Errno = %s (%d)", strerror(errno), errno);
+        }
+    }
+    return seekable_file;
+}
+
+
+
+void
+pm_close(FILE * const f) {
+    fflush(f);
+    if (ferror(f))
+        pm_message("A file read or write error occurred at some point");
+    if (f != stdin)
+        if (fclose(f) != 0)
+            pm_error("close of file failed with errno %d (%s)",
+                     errno, strerror(errno));
+}
+
+
+
+/* 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.
+*/
+
+void
+pm_closer(FILE * const f) {
+    pm_close(f);
+}
+
+
+
+void
+pm_closew(FILE * const f) {
+    pm_close(f);
+}
+
+
+
+/* Endian I/O.
+
+   Before Netpbm 10.27 (March 2005), these would return failure on EOF
+   or I/O failure.  For backward compatibility, they still have the return
+   code, but it is always zero and the routines abort the program in case
+   of EOF or I/O failure.  A program that wants to handle failure differently
+   must use lower level (C library) interfaces.  But that level of detail
+   is uncharacteristic of a Netpbm program; the ease of programming that
+   comes with not checking a return code is more Netpbm.
+
+   It is also for historical reasons that these return signed values,
+   when clearly unsigned would make more sense.
+*/
+
+
+
+static void
+abortWithReadError(FILE * const ifP) {
+
+    if (feof(ifP))
+        pm_error("Unexpected end of input file");
+    else
+        pm_error("Error (not EOF) reading file.");
+}
+
+
+
+static unsigned char
+getcNofail(FILE * const ifP) {
+
+    int c;
+
+    c = getc(ifP);
+
+    if (c == EOF)
+        abortWithReadError(ifP);
+
+    return (unsigned char)c;
+}
+
+
+
+void
+pm_readchar(FILE * const ifP,
+            char * const cP) {
+    
+    *cP = (char)getcNofail(ifP);
+}
+
+
+
+void
+pm_writechar(FILE * const ofP,
+             char   const c) {
+
+    putc(c, ofP);
+}
+
+
+
+int
+pm_readbigshort(FILE *  const ifP, 
+                short * const sP) {
+
+    unsigned short s;
+
+    s  = getcNofail(ifP) << 8;
+    s |= getcNofail(ifP) << 0;
+
+    *sP = s;
+
+    return 0;
+}
+
+
+
+int
+pm_writebigshort(FILE * const ofP, 
+                 short  const s) {
+
+    putc((s >> 8) & 0xff, ofP);
+    putc(s & 0xff, ofP);
+
+    return 0;
+}
+
+
+
+int
+pm_readbiglong(FILE * const ifP, 
+               long * const lP) {
+
+    unsigned long l;
+
+    l  = getcNofail(ifP) << 24;
+    l |= getcNofail(ifP) << 16;
+    l |= getcNofail(ifP) <<  8;
+    l |= getcNofail(ifP) <<  0;
+
+    *lP = l;
+
+    return 0;
+}
+
+
+
+int
+pm_writebiglong(FILE * const ofP, 
+                long   const l) {
+
+    putc((l >> 24) & 0xff, ofP);
+    putc((l >> 16) & 0xff, ofP);
+    putc((l >>  8) & 0xff, ofP);
+    putc((l >>  0) & 0xff, ofP);
+
+    return 0;
+}
+
+
+
+int
+pm_readlittleshort(FILE *  const ifP, 
+                   short * const sP) {
+    unsigned short s;
+
+    s  = getcNofail(ifP) << 0;
+    s |= getcNofail(ifP) << 8;
+
+    *sP = s;
+
+    return 0;
+}
+
+
+
+int
+pm_writelittleshort(FILE * const ofP, 
+                    short  const s) {
+
+    putc((s >> 0) & 0xff, ofP);
+    putc((s >> 8) & 0xff, ofP);
+
+    return 0;
+}
+
+
+
+int
+pm_readlittlelong(FILE * const ifP, 
+                  long * const lP) {
+    unsigned long l;
+
+    l  = getcNofail(ifP) <<  0;
+    l |= getcNofail(ifP) <<  8;
+    l |= getcNofail(ifP) << 16;
+    l |= getcNofail(ifP) << 24;
+
+    *lP = l;
+
+    return 0;
+}
+
+
+
+int
+pm_writelittlelong(FILE * const ofP, 
+                   long   const l) {
+
+    putc((l >>  0) & 0xff, ofP);
+    putc((l >>  8) & 0xff, ofP);
+    putc((l >> 16) & 0xff, ofP);
+    putc((l >> 24) & 0xff, ofP);
+
+    return 0;
+}
+
+
+
+int 
+pm_readmagicnumber(FILE * const ifP) {
+
+    int ich1, ich2;
+
+    ich1 = getc(ifP);
+    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." );
+
+    return ich1 * 256 + ich2;
+}
+
+
+
+/* Read a file of unknown size to a buffer. Return the number of bytes
+   read. Allocate more memory as we need it. The calling routine has
+   to free() the buffer.
+
+   Oliver Trepte, oliver@fysik4.kth.se, 930613
+*/
+
+#define PM_BUF_SIZE 16384      /* First try this size of the buffer, then
+                                  double this until we reach PM_MAX_BUF_INC */
+#define PM_MAX_BUF_INC 65536   /* Don't allocate more memory in larger blocks
+                                  than this. */
+
+char *
+pm_read_unknown_size(FILE * const file, 
+                     long * const nread) {
+    long nalloc;
+    char * buf;
+    bool eof;
+
+    *nread = 0;
+    nalloc = PM_BUF_SIZE;
+    MALLOCARRAY(buf, nalloc);
+
+    eof = FALSE;  /* initial value */
+
+    while(!eof) {
+        int val;
+
+        if (*nread >= nalloc) { /* We need a larger buffer */
+            if (nalloc > PM_MAX_BUF_INC)
+                nalloc += PM_MAX_BUF_INC;
+            else
+                nalloc += nalloc;
+            REALLOCARRAY_NOFAIL(buf, nalloc);
+        }
+
+        val = getc(file);
+        if (val == EOF)
+            eof = TRUE;
+        else 
+            buf[(*nread)++] = val;
+    }
+    return buf;
+}
+
+
+
+union cheat {
+    uint32_t l;
+    short s;
+    unsigned char c[4];
+};
+
+
+
+short
+pm_bs_short(short const s) {
+    union cheat u;
+    unsigned char t;
+
+    u.s = s;
+    t = u.c[0];
+    u.c[0] = u.c[1];
+    u.c[1] = t;
+    return u.s;
+}
+
+
+
+long
+pm_bs_long(long const l) {
+    union cheat u;
+    unsigned char t;
+
+    u.l = l;
+    t = u.c[0];
+    u.c[0] = u.c[3];
+    u.c[3] = t;
+    t = u.c[1];
+    u.c[1] = u.c[2];
+    u.c[2] = t;
+    return u.l;
+}
+
+
+
+void
+pm_tell2(FILE *       const fileP, 
+         void *       const fileposP,
+         unsigned int const fileposSize) {
+/*----------------------------------------------------------------------------
+   Return the current file position as *filePosP, which is a buffer
+   'fileposSize' bytes long.  Abort the program if error, including if
+   *fileP isn't a file that has a position.
+-----------------------------------------------------------------------------*/
+    /* Note: FTELLO() is either ftello() or ftell(), depending on the
+       capabilities of the underlying C library.  It is defined in
+       pm_config.h.  ftello(), in turn, may be either ftell() or
+       ftello64(), as implemented by the C library.
+    */
+    pm_filepos const filepos = FTELLO(fileP);
+    if (filepos < 0)
+        pm_error("ftello() to get current file position failed.  "
+                 "Errno = %s (%d)\n", strerror(errno), errno);
+
+    if (fileposSize == sizeof(pm_filepos)) {
+        pm_filepos * const fileposP_filepos = fileposP;
+        *fileposP_filepos = filepos;
+    } else if (fileposSize == sizeof(long)) {
+        if (sizeof(pm_filepos) > sizeof(long) &&
+            filepos >= (pm_filepos) 1 << (sizeof(long)*8))
+            pm_error("File size is too large to represent in the %u bytes "
+                     "that were provided to pm_tell2()", fileposSize);
+        else {
+            long * const fileposP_long = fileposP;
+            *fileposP_long = (long)filepos;
+        }
+    } else
+        pm_error("File position size passed to pm_tell() is invalid: %u.  "
+                 "Valid sizes are %u and %u", 
+                 fileposSize, (unsigned int)sizeof(pm_filepos),
+                 (unsigned int) sizeof(long));
+}
+
+
+
+unsigned int
+pm_tell(FILE * const fileP) {
+    
+    long filepos;
+
+    pm_tell2(fileP, &filepos, sizeof(filepos));
+
+    return filepos;
+}
+
+
+
+void
+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)) 
+        /* 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
+           fseeko64(), as implemented by the C library.
+        */
+        FSEEKO(fileP, *fileposP, SEEK_SET);
+    else if (fileposSize == sizeof(long)) {
+        long const fileposLong = *(long *)fileposP;
+        fseek(fileP, fileposLong, SEEK_SET);
+    } else
+        pm_error("File position size passed to pm_seek() is invalid: %u.  "
+                 "Valid sizes are %u and %u", 
+                 fileposSize, (unsigned int)sizeof(pm_filepos),
+                 (unsigned int) sizeof(long));
+}
+
+
+
+void
+pm_seek(FILE * const fileP, unsigned long filepos) {
+/*----------------------------------------------------------------------------
+-----------------------------------------------------------------------------*/
+
+    pm_filepos fileposBuff;
+
+    fileposBuff = filepos;
+
+    pm_seek2(fileP, &fileposBuff, sizeof(fileposBuff));
+}
+
+
+
+void
+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, 
+   because the plain formats do permit white space after the raster.
+
+   Iff there is no next image, return *eofP == TRUE.
+
+   Note that in practice, we will not normally see white space here in
+   a plain PPM or plain PGM stream because the routine to read a
+   sample from the image reads one character of white space after the
+   sample in order to know where the sample ends.  There is not
+   normally more than one character of white space (a newline) after
+   the last sample in the raster.  But plain PBM is another story.  No white
+   space is required between samples of a plain PBM image.  But the raster
+   normally ends with a newline nonetheless.  Since the sample reading code
+   will not have read that newline, it is there for us to read now.
+-----------------------------------------------------------------------------*/
+    bool eof;
+    bool nonWhitespaceFound;
+
+    eof = FALSE;
+    nonWhitespaceFound = FALSE;
+
+    while (!eof && !nonWhitespaceFound) {
+        int c;
+        c = getc(file);
+        if (c == EOF) {
+            if (feof(file)) 
+                eof = TRUE;
+            else
+                pm_error("File error on getc() to position to image");
+        } else {
+            if (!isspace(c)) {
+                int rc;
+
+                nonWhitespaceFound = TRUE;
+
+                /* Have to put the non-whitespace character back in
+                   the stream -- it's part of the next image.  
+                */
+                rc = ungetc(c, file);
+                if (rc == EOF) 
+                    pm_error("File error doing ungetc() "
+                             "to position to image.");
+            }
+        }
+    }
+    *eofP = eof;
+}
+
+
+
+void
+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) {
+/*----------------------------------------------------------------------------
+   This is not defined for use outside of libnetpbm.
+-----------------------------------------------------------------------------*/
+    struct stat statbuf;
+    pm_filepos curpos;  /* Current position of file; -1 if none */
+    int rc;
+
+#ifdef LARGEFILEDEBUG
+    pm_message("pm_filepos received by pm_check() is %u bytes.",
+               sizeof(pm_filepos));
+#endif
+    /* Note: FTELLO() is either ftello() or ftell(), depending on the
+       capabilities of the underlying C library.  It is defined in
+       pm_config.h.  ftello(), in turn, may be either ftell() or
+       ftello64(), as implemented by the C library.
+    */
+    curpos = FTELLO(file);
+    if (curpos >= 0) {
+        /* This type of file has a current position */
+            
+        rc = fstat(fileno(file), &statbuf);
+        if (rc != 0) 
+            pm_error("fstat() failed to get size of file, though ftello() "
+                     "successfully identified\n"
+                     "the current position.  Errno=%s (%d)",
+                     strerror(errno), errno);
+        else if (!S_ISREG(statbuf.st_mode)) {
+            /* Not a regular file; we can't know its size */
+            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) have_raster_size);
+            else if (have_raster_size > need_raster_size) {
+                if (retval_p) *retval_p = PM_CHECK_TOO_LONG;
+            } else {
+                if (retval_p) *retval_p = PM_CHECK_OK;
+            }
+        }
+    } else
+        if (retval_p) *retval_p = PM_CHECK_UNCHECKABLE;
+}
+
+
+
+void
+pm_drain(FILE *         const fileP,
+         unsigned int   const limit,
+         unsigned int * const bytesReadP) {
+/*----------------------------------------------------------------------------
+  Read bytes from *fileP until EOF and return as *bytesReadP how many there
+  were.
+
+  But don't read any more than 'limit'.
+
+  This is a good thing to call after reading an input file to be sure you
+  didn't leave some input behind, which could mean you didn't properly
+  interpret the file.
+-----------------------------------------------------------------------------*/
+    unsigned int bytesRead;
+    bool eof;
+
+    for (bytesRead = 0, eof = false; !eof && bytesRead < limit;) {
+
+        int rc;
+
+        rc = fgetc(fileP);
+
+        eof = (rc == EOF);
+        if (!eof)
+            ++bytesRead;
+    }
+    *bytesReadP = bytesRead;
+}
diff --git a/lib/pnm.h b/lib/pnm.h
index ed6983f4..e4bd34a8 100644
--- a/lib/pnm.h
+++ b/lib/pnm.h
@@ -4,7 +4,8 @@
 #ifndef _PNM_H_
 #define _PNM_H_
 
-#include "ppm.h"
+#include <netpbm/pm.h>
+#include <netpbm/ppm.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -28,7 +29,9 @@ typedef pixval xelval;
 
 /* Declarations of routines. */
 
-void pnm_init ARGS(( int* argcP, char* argv[] ));
+void
+pnm_init(int *   const argcP,
+         char ** const argv);
 
 void
 pnm_nextimage(FILE *file, int * const eofP);
@@ -36,7 +39,7 @@ pnm_nextimage(FILE *file, int * const eofP);
 xel *
 pnm_allocrow(unsigned int const cols);
 
-#define pnm_freerow(xelrow) free(xelrow)
+#define pnm_freerow(xelrow) pm_freerow(xelrow)
 
 #define pnm_allocarray( cols, rows ) \
   ((xel**) pm_allocarray( cols, rows, sizeof(xel) ))
@@ -83,12 +86,12 @@ pnm_writepnminit(FILE * const fileP,
                  int    const forceplain);
 
 void
-pnm_writepnmrow(FILE * const fileP, 
-                xel *  const xelrow, 
-                int    const cols, 
-                xelval const maxval, 
-                int    const format, 
-                int    const forceplain);
+pnm_writepnmrow(FILE *      const fileP, 
+                const xel * const xelrow, 
+                int         const cols, 
+                xelval      const maxval, 
+                int         const format, 
+                int         const forceplain);
 
 void
 pnm_writepnm(FILE * const fileP,
@@ -100,13 +103,13 @@ pnm_writepnm(FILE * const fileP,
              int    const forceplain);
 
 xel 
-pnm_backgroundxel (xel** xels, int cols, int rows, xelval maxval, int format);
+pnm_backgroundxel(xel** xels, int cols, int rows, xelval maxval, int format);
 
 xel 
-pnm_backgroundxelrow (xel* xelrow, int cols, xelval maxval, int format);
+pnm_backgroundxelrow(xel* xelrow, int cols, xelval maxval, int format);
 
 xel 
-pnm_whitexel (xelval maxval, int format);
+pnm_whitexel(xelval maxval, int format);
 
 xel 
 pnm_blackxel(xelval maxval, int format);
@@ -127,6 +130,11 @@ pixel
 pnm_xeltopixel(xel const inputxel,
                int const format);
 
+xel
+pnm_parsecolorxel(const char * const colorName,
+                  xelval       const maxval,
+                  int          const format);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/ppm.h b/lib/ppm.h
index 8471e767..719189bf 100644
--- a/lib/ppm.h
+++ b/lib/ppm.h
@@ -3,7 +3,8 @@
 #ifndef _PPM_H_
 #define _PPM_H_
 
-#include "pgm.h"
+#include <netpbm/pm.h>
+#include <netpbm/pgm.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -50,7 +51,7 @@ typedef struct {
 #define PPM_TYPE PPM_FORMAT
 
 
-#include "ppmcmap.h"
+#include <netpbm/ppmcmap.h>
 
 /* Macro for turning a format number into a type number. */
 
@@ -58,7 +59,22 @@ typedef struct {
   ((f) == PPM_FORMAT || (f) == RPPM_FORMAT ? PPM_TYPE : PGM_FORMAT_TYPE(f))
 
 
-/* Declarations of routines. */
+static __inline__ pixel
+ppm_whitepixel(pixval maxval) {
+
+    pixel retval;
+    PPM_ASSIGN(retval, maxval, maxval, maxval);
+
+    return retval;
+}
+
+static __inline__ pixel
+ppm_blackpixel(void) {
+
+    pixel const retval = {0, 0, 0};
+
+    return retval;
+}
 
 void ppm_init(int * argcP, char* argv[]);
 
@@ -70,7 +86,7 @@ ppm_allocrow(unsigned int const cols);
 
 #define ppm_freearray(pixels, rows) pm_freearray((char**) pixels, rows)
 
-#define ppm_freerow(pixelrow) free(pixelrow);
+#define ppm_freerow(pixelrow) pm_freerow(pixelrow);
 
 pixel**
 ppm_readppm(FILE *   const fileP, 
@@ -108,11 +124,11 @@ ppm_writeppminit(FILE*  const fileP,
                  int    const forceplain);
 
 void
-ppm_writeppmrow(FILE *  const fileP, 
-                pixel * const pixelrow, 
-                int     const cols, 
-                pixval  const maxval, 
-                int     const forceplain);
+ppm_writeppmrow(FILE *        const fileP, 
+                const pixel * const pixelrow, 
+                int           const cols, 
+                pixval        const maxval, 
+                int           const forceplain);
 
 void
 ppm_check(FILE *               const fileP, 
@@ -259,22 +275,25 @@ ppm_saturation(pixel const p,
 
 typedef enum {
     /* A color from the set of universally understood colors developed
-       by Brent Berlin and Paul Kay
+       by Brent Berlin and Paul Kay.
+
+       Algorithms in libnetpbm depend on the numerical representations
+       of these values being as follows.
     */
-    BKCOLOR_BLACK = 0,
-    BKCOLOR_GRAY,
-    BKCOLOR_WHITE,
-    BKCOLOR_RED,
+    BKCOLOR_GRAY = 0,
+    BKCOLOR_BROWN,
     BKCOLOR_ORANGE,
+    BKCOLOR_RED,
     BKCOLOR_YELLOW,
     BKCOLOR_GREEN,
     BKCOLOR_BLUE,
     BKCOLOR_VIOLET,
     BKCOLOR_PURPLE,
-    BKCOLOR_BROWN
+    BKCOLOR_WHITE,
+    BKCOLOR_BLACK
 } bk_color;
 
-#define BKCOLOR_COUNT (BKCOLOR_BROWN+1)
+#define BKCOLOR_COUNT (BKCOLOR_BLACK+1)
 
 bk_color
 ppm_bk_color_from_color(pixel  const color,
diff --git a/lib/ppmcmap.h b/lib/ppmcmap.h
index b44dcbea..dd3e5c14 100644
--- a/lib/ppmcmap.h
+++ b/lib/ppmcmap.h
@@ -36,10 +36,10 @@ ppm_computecolorhist2(FILE * const ifp,
                       const int maxcolors, int * const colorsP );
 
 void
-ppm_addtocolorhist( colorhist_vector chv, 
-                    int * const colorsP, const int maxcolors, 
-                    const pixel * const colorP, 
-                    const int value, const int position );
+ppm_addtocolorhist(colorhist_vector chv, 
+                   int * const colorsP, const int maxcolors, 
+                   const pixel * const colorP, 
+                   const int value, const int position );
 
 void
 ppm_freecolorhist(colorhist_vector const chv);
@@ -89,13 +89,39 @@ void
 ppm_freecolorhash(colorhash_table const cht);
 
 
-colorhash_table ppm_colorrowtocolorhash ARGS((pixel *colorrow, int ncolors));
-pixel * ppm_computecolorrow ARGS((pixel **pixels, int cols, int rows, int maxcolors, int *ncolorsP));
-pixel * ppm_mapfiletocolorrow ARGS((FILE *file, int maxcolors, int *ncolorsP, pixval *maxvalP));
-void    ppm_colorrowtomapfile ARGS((FILE *ofp, pixel *colormap, int ncolors, pixval maxval));
-void    ppm_sortcolorrow (pixel * const colorrow, const int ncolors, 
-                          int (*cmpfunc)(pixel *, pixel *) );
-int     ppm_addtocolorrow ARGS((pixel *colorrow, int *ncolorsP, int maxcolors, pixel *pixelP));
+colorhash_table
+ppm_colorrowtocolorhash(pixel * const colorrow,
+                        int     const ncolors);
+
+pixel *
+ppm_computecolorrow(pixel ** const pixels,
+                    int      const cols,
+                    int      const rows,
+                    int      const maxcolors,
+                    int *    const ncolorsP);
+
+pixel *
+ppm_mapfiletocolorrow(FILE *   const fileP,
+                      int      const maxcolors,
+                      int *    const ncolorsP,
+                      pixval * const maxvalP);
+
+void
+ppm_colorrowtomapfile(FILE *  const ofP,
+                      pixel * const colormap,
+                      int     const ncolors,
+                      pixval  const maxval);
+
+void
+ppm_sortcolorrow(pixel * const colorrow,
+                 int     const ncolors, 
+                 int (*cmpfunc)(pixel *, pixel *));
+
+int
+ppm_addtocolorrow(pixel * const colorrow,
+                  int *   const ncolorsP,
+                  int     const maxcolors,
+                  pixel * const pixelP);
 
 int
 ppm_findclosestcolor(const pixel * const colormap, 
diff --git a/lib/ppmdraw.h b/lib/ppmdraw.h
index 7441cc43..d7a02e79 100644
--- a/lib/ppmdraw.h
+++ b/lib/ppmdraw.h
@@ -16,15 +16,33 @@ extern "C" {
 #endif
 
 
+typedef struct {
+    int x;
+    int y;
+} ppmd_point;
+
+static __inline__ ppmd_point
+ppmd_makePoint(int const x,
+               int const y) {
+
+    ppmd_point p;
+
+    p.x = x;
+    p.y = y;
+
+    return p;
+}
+
+void
+ppmd_validateCoord(int const c);
+
+void
+ppmd_validatePoint(ppmd_point const p);
+
 typedef enum {
     PPMD_PATHLEG_LINE
 } ppmd_pathlegtype;
 
-typedef struct {
-    unsigned int x;
-    unsigned int y;
-} ppmd_point;
-
 struct ppmd_linelegparms {
     ppmd_point end;
 };
@@ -59,8 +77,11 @@ typedef struct {
 
 
 
-typedef void ppmd_drawproc(pixel ** const, int const, int const, pixval const, int const, int const, const void *const);
+typedef void ppmd_drawprocp(pixel **, unsigned int, unsigned int,
+                            pixval, ppmd_point, const void *);
+typedef void ppmd_drawproc(pixel **, int, int, pixval, int, int, const void *);
 
+ppmd_drawprocp ppmd_point_drawprocp;
 ppmd_drawproc ppmd_point_drawproc;
 
 /*
@@ -111,6 +132,18 @@ ppmd_setlineclip(int const clip);
 */
 
 void
+ppmd_linep(pixel **       const pixels, 
+           int            const cols, 
+           int            const rows, 
+           pixval         const maxval, 
+           ppmd_point     const p0,
+           ppmd_point     const p1,
+           ppmd_drawprocp       drawProc,
+           const void *   const clientdata);
+
+    /* Draws a line from p0 to p1.  */
+
+void
 ppmd_line(pixel**       const pixels, 
           int           const cols, 
           int           const rows, 
@@ -121,8 +154,23 @@ ppmd_line(pixel**       const pixels,
           int           const y1, 
           ppmd_drawproc       drawproc,
           const void *  const clientdata);
-/* Draws a line from (x0, y0) to (x1, y1).
-*/
+    /* Draws a line from (x0, y0) to (x1, y1). */
+
+void
+ppmd_spline3p(pixel **       const pixels, 
+              int            const cols, 
+              int            const rows, 
+              pixval         const maxval, 
+              ppmd_point     const p0,
+              ppmd_point     const p1,
+              ppmd_point     const p2,
+              ppmd_drawprocp       drawProc,
+              const void *   const clientdata);
+
+    /* Draws a three-point spline from p0 to p2, with p1
+       as the control point.  All drawing is done via ppmd_linep(),
+       so the routines that control it control ppmd_spline3p() as well. 
+    */
 
 void
 ppmd_spline3(pixel **      const pixels, 
@@ -137,9 +185,22 @@ ppmd_spline3(pixel **      const pixels,
              int           const y2, 
              ppmd_drawproc       drawproc,
              const void *  const clientdata);
-    /* Draws a three-point spline from (x0, y0) to (x2, y2), with (x1,
-       y1) as the control point.  All drawing is done via ppmd_line(),
-       so the routines that control it control ppmd_spline3() as well. 
+
+void
+ppmd_polysplinep(pixel **       const pixels, 
+                 unsigned int   const cols, 
+                 unsigned int   const rows, 
+                 pixval         const maxval, 
+                 ppmd_point     const p0,
+                 unsigned int   const nc,
+                 ppmd_point *   const c,
+                 ppmd_point     const p1,
+                 ppmd_drawprocp       drawProc,
+                 const void *   const clientdata);
+
+    /* Draws a bunch of splines end to end.  p0 and p1 are the initial and
+       final points, and the c[] are the intermediate control points.  nc is
+       the number of these control points.
     */
 
 void
@@ -156,32 +217,29 @@ ppmd_polyspline(pixel **     const pixels,
                 int          const y1, 
                 ppmd_drawproc      drawProc,
                 const void * const clientdata);
-    /* Draws a bunch of splines end to end.  (x0, y0) and (x1, y1) are
-       the initial and final points, and the xc and yc are the
-       intermediate control points.  nc is the number of these control
-       points.
-    */
 
 void
-ppmd_spline4(pixel **      const pixels, 
-             int           const cols, 
-             int           const rows, 
-             pixval        const maxval, 
-             int           const x0, 
-             int           const y0, 
-             int           const x1, 
-             int           const y1, 
-             int           const x2, 
-             int           const y2, 
-             int           const x3, 
-             int           const y3, 
-             ppmd_drawproc       drawproc,
-             const void *  const clientdata);
-    /* Draws a four-point spline from (x0, y0) to (x3, y3), with (x1,
-       y1) and (x2, y2) as the control points.  All drawing is done
-       via ppmd_line(), so the routines that control it control this
-       as well.
-    */
+ppmd_spline4p(pixel **       const pixels, 
+              unsigned int   const cols, 
+              unsigned int   const rows, 
+              pixval         const maxval, 
+              ppmd_point     const endPt0,
+              ppmd_point     const endPt1,
+              ppmd_point     const ctlPt0,
+              ppmd_point     const ctlPt1,
+              ppmd_drawprocp       drawproc,
+              const void *   const clientdata);
+
+void
+ppmd_circlep(pixel **       const pixels, 
+             unsigned int   const cols, 
+             unsigned int   const rows, 
+             pixval         const maxval, 
+             ppmd_point     const center,
+             unsigned int   const radius, 
+             ppmd_drawprocp       drawProc,
+             const void *   const clientData);
+    /* Draws a circle centered at 'center' with radius 'radius' */
 
 void
 ppmd_circle(pixel **     const pixels, 
@@ -193,7 +251,6 @@ ppmd_circle(pixel **     const pixels,
             int          const radius, 
             ppmd_drawproc      drawProc,
             const void * const clientdata);
-    /* Draws a circle centered at (cx, cy) with the specified radius. */
 
 
 /* Simple filling routines.  */
@@ -242,16 +299,25 @@ char *
 ppmd_fill_init(void);
 
 void
-ppmd_fill_drawproc(pixel ** const pixels, 
-                   int      const cols, 
-                   int      const rows, 
-                   pixval   const maxval, 
-                   int      const x, 
-                   int      const y, 
-                   const void * const clientdata);
+ppmd_fill_drawprocp(pixel **     const pixels, 
+                    unsigned int const cols, 
+                    unsigned int const rows, 
+                    pixval       const maxval, 
+                    ppmd_point   const p,
+                    const void * const clientdata);
     /* Use this drawproc to trace the outline you want filled.  Use
        the fillhandle as the clientdata.
     */
+
+void
+ppmd_fill_drawproc(pixel **     const pixels, 
+                   int          const cols, 
+                   int          const rows, 
+                   pixval       const maxval, 
+                   int          const x, 
+                   int          const y, 
+                   const void * const clientdata);
+
 void
 ppmd_fill(pixel **         const pixels, 
           int              const cols, 
@@ -269,17 +335,29 @@ ppmd_fill(pixel **         const pixels,
 /* Text drawing routines. */
 
 void
-ppmd_text(pixel**      const pixels, 
-          int          const cols, 
-          int          const rows, 
-          pixval       const maxval, 
-          int          const xpos, 
-          int          const ypos, 
-          int          const height, 
-          int          const angle, 
-          const char * const sArg, 
-	  ppmd_drawproc,
-    const void*  const clientdata);
+ppmd_textp(pixel**        const pixels, 
+           int            const cols, 
+           int            const rows, 
+           pixval         const maxval, 
+           ppmd_point     const pos,
+           int            const height, 
+           int            const angle, 
+           const char *   const sArg, 
+           ppmd_drawprocp       drawProc,
+           const void *   const clientdata);
+
+void
+ppmd_text(pixel**       const pixels, 
+          int           const cols, 
+          int           const rows, 
+          pixval        const maxval, 
+          int           const xpos, 
+          int           const ypos, 
+          int           const height, 
+          int           const angle, 
+          const char *  const sArg, 
+          ppmd_drawproc       drawProc,
+          const void *  const clientdata);
 /* Draws the null-terminated string 's' left justified at the point
    ('x', 'y').  The text will be 'height' pixels high and will be aligned on a
    baseline inclined 'angle' degrees with the X axis.  The supplied
diff --git a/lib/ppmfloyd.h b/lib/ppmfloyd.h
index e16ad651..264fc0b6 100644
--- a/lib/ppmfloyd.h
+++ b/lib/ppmfloyd.h
@@ -15,17 +15,17 @@ struct ppm_fs_info {
     /* thisXerr and nextXerr are dynamically allocated arrays each of whose
        dimension is the width of the image plus 2
        */
-    long *thisrederr;
-    long *thisgreenerr;
-    long *thisblueerr;
-    long *nextrederr;
-    long *nextgreenerr;
-    long *nextblueerr;
+    long * thisrederr;
+    long * thisgreenerr;
+    long * thisblueerr;
+    long * nextrederr;
+    long * nextgreenerr;
+    long * nextblueerr;
     int lefttoright;
     int cols;
     pixval maxval;
     int flags;
-    pixel *pixrow;
+    pixel * pixrow;
     int col_end;
     pixval red, green, blue;
 };
@@ -37,7 +37,9 @@ typedef struct ppm_fs_info ppm_fs_info;
 #define FS_ALTERNATE  0x02
 
 ppm_fs_info *
-ppm_fs_init(int cols, pixval maxval, int flags);
+ppm_fs_init(unsigned int const cols,
+            pixval       const maxval,
+            unsigned int const flags);
 
 void
 ppm_fs_free(ppm_fs_info *fi);
diff --git a/lib/util/Makefile b/lib/util/Makefile
index 8f461f28..8b3fa1c1 100644
--- a/lib/util/Makefile
+++ b/lib/util/Makefile
@@ -5,25 +5,23 @@ endif
 SUBDIR = lib/util
 VPATH=.:$(SRCDIR)/$(SUBDIR)
 
-include $(BUILDDIR)/Makefile.config
-
-INCLUDES = -I $(BUILDDIR) -I $(SRCDIR)/$(SUBDIR)/..
+include $(BUILDDIR)/config.mk
 
 # nstring is required for asprintf(), etc.  Also some systems don't have
 # snprintf(), e.g. Solaris 2.5.1.  2002.03.29.
-UTILOBJECTS = shhopt.o nstring.o filename.o
+UTILOBJECTS = shhopt.o nstring.o vasprintf.o filename.o nsleep.o
 
 MERGE_OBJECTS =
 
 all: $(UTILOBJECTS)
 
-include $(SRCDIR)/Makefile.common
+include $(SRCDIR)/common.mk
+
+INCLUDES = -I$(SRCDIR)/$(SUBDIR) -I. -Iimportinc
 
 $(UTILOBJECTS):%.o:%.c importinc
-	$(CC) -c $(INCLUDES) -DNDEBUG $(CFLAGS) $(CFLAGS_SHLIB) \
+	$(CC) -c $(INCLUDES) -DNDEBUG $(CPPFLAGS) $(CFLAGS) $(CFLAGS_SHLIB) \
 	  $(CFLAGS_PERSONAL) $(CADD) -o $@ $<
 
 testnstring: test.c nstring.h nstring.o
 	$(CC) $(CFLAGS) $(CADD) -o $@ nstring.o $<
-
-include Makefile.depend
diff --git a/lib/util/bitarith.h b/lib/util/bitarith.h
new file mode 100644
index 00000000..4ed6c4c3
--- /dev/null
+++ b/lib/util/bitarith.h
@@ -0,0 +1,42 @@
+#ifndef BITARITH_H_INCLUDED
+#define BITARITH_H_INCLUDED
+
+#include "pm_config.h"
+
+static __inline__ unsigned char
+pm_byteLeftBits(unsigned char const x,
+                unsigned int  const n) {
+/*----------------------------------------------------------------------------
+  Clear rightmost (8-n) bits, retain leftmost (=high) n bits.
+
+  Return arbitrary value if n > 8.
+-----------------------------------------------------------------------------*/
+    unsigned char retval;
+
+    retval = x;
+    retval >>= (8-n);
+    retval <<= (8-n);
+
+    return retval;
+}
+
+
+
+static __inline__ unsigned char
+pm_byteRightBits(unsigned char const x,
+                 unsigned int  const n){
+/*----------------------------------------------------------------------------
+  Return rightmost (=low) n bits of x.
+
+  Return arbitrary value if n > 8.
+-----------------------------------------------------------------------------*/
+    unsigned char retval;
+
+    retval = x;
+    retval <<= (8-n);
+    retval >>= (8-n);
+
+    return retval;
+}
+
+#endif
diff --git a/lib/util/floatcode.h b/lib/util/floatcode.h
new file mode 100644
index 00000000..23c57e9b
--- /dev/null
+++ b/lib/util/floatcode.h
@@ -0,0 +1,185 @@
+#ifndef FLOATCODE_H_INCLUDED
+#define FLOATCODE_H_INCLUDED
+
+#include "pm_config.h"  /* BYTE_ORDER */
+
+unsigned int const pm_byteOrder = BYTE_ORDER;
+
+
+typedef struct {
+/*----------------------------------------------------------------------------
+   This is a big-endian representation of a 32 bit floating point number.
+   I.e. bytes[0] contains the sign bit, etc.
+
+   On a big-endian machines, this is bit for bit identical to 'float'.
+   On a little-endian machine, it isn't.
+
+   This is an important data type because decent file formats use
+   big-endian -- they don't care if some CPU happens to use some other
+   code for its own work.
+-----------------------------------------------------------------------------*/
+    unsigned char bytes[4];
+} pm_bigendFloat;
+
+
+static __inline__ float
+pm_floatFromBigendFloat(pm_bigendFloat const arg) {
+
+    float retval;
+
+    switch (pm_byteOrder) {
+    case BIG_ENDIAN: {
+        union {
+            pm_bigendFloat bigend;
+            float native;
+        } converter;
+        
+        converter.bigend = arg;
+        
+        retval = converter.native;
+    }; break;
+    case LITTLE_ENDIAN: {
+        union {
+            unsigned char bytes[4];
+            float native;
+        } converter;
+
+        converter.bytes[0] = arg.bytes[3];
+        converter.bytes[1] = arg.bytes[2];
+        converter.bytes[2] = arg.bytes[1];
+        converter.bytes[3] = arg.bytes[0];
+
+        retval = converter.native;
+    } break;
+    }
+    return retval;
+}
+
+
+
+static __inline__ pm_bigendFloat
+pm_bigendFloatFromFloat(float const arg) {
+
+    pm_bigendFloat retval;
+
+    switch (pm_byteOrder) {
+    case BIG_ENDIAN: {
+        union {
+            pm_bigendFloat bigend;
+            float native;
+        } converter;
+        
+        converter.native = arg;
+
+        retval = converter.bigend;
+    } break;
+    case LITTLE_ENDIAN: {
+        union {
+            unsigned char bytes[4];
+            float native;
+        } converter;
+
+        converter.native = arg;
+
+        retval.bytes[0] = converter.bytes[3];
+        retval.bytes[1] = converter.bytes[2];
+        retval.bytes[2] = converter.bytes[1];
+        retval.bytes[3] = converter.bytes[0];
+    } break;
+    }
+    return retval;
+}
+
+typedef struct {
+/*----------------------------------------------------------------------------
+   This is a big-endian representation of a 64 bit floating point number.
+   I.e. bytes[0] contains the sign bit, etc.
+
+   On a big-endian machines, this is bit for bit identical to 'float'.
+   On a little-endian machine, it isn't.
+
+   This is an important data type because decent file formats use
+   big-endian -- they don't care if some CPU happens to use some other
+   code for its own work.
+-----------------------------------------------------------------------------*/
+    unsigned char bytes[8];
+} pm_bigendDouble;
+
+
+static __inline__ double
+pm_doubleFromBigendDouble(pm_bigendDouble const arg) {
+
+    double retval;
+
+    switch (pm_byteOrder) {
+    case BIG_ENDIAN: {
+        union {
+            pm_bigendDouble bigend;
+            double native;
+        } converter;
+        
+        converter.bigend = arg;
+        
+        retval = converter.native;
+    }; break;
+    case LITTLE_ENDIAN: {
+        union {
+            unsigned char bytes[4];
+            double native;
+        } converter;
+
+        converter.bytes[0] = arg.bytes[7];
+        converter.bytes[1] = arg.bytes[6];
+        converter.bytes[2] = arg.bytes[5];
+        converter.bytes[3] = arg.bytes[4];
+        converter.bytes[4] = arg.bytes[3];
+        converter.bytes[5] = arg.bytes[2];
+        converter.bytes[6] = arg.bytes[1];
+        converter.bytes[7] = arg.bytes[0];
+
+        retval = converter.native;
+    } break;
+    }
+    return retval;
+}
+
+
+
+static __inline__ pm_bigendDouble
+pm_bigendDoubleFromDouble(double const arg) {
+
+    pm_bigendDouble retval;
+
+    switch (pm_byteOrder) {
+    case BIG_ENDIAN: {
+        union {
+            pm_bigendDouble bigend;
+            double native;
+        } converter;
+        
+        converter.native = arg;
+
+        retval = converter.bigend;
+    } break;
+    case LITTLE_ENDIAN: {
+        union {
+            unsigned char bytes[4];
+            double native;
+        } converter;
+
+        converter.native = arg;
+
+        retval.bytes[0] = converter.bytes[7];
+        retval.bytes[1] = converter.bytes[6];
+        retval.bytes[2] = converter.bytes[5];
+        retval.bytes[3] = converter.bytes[4];
+        retval.bytes[4] = converter.bytes[3];
+        retval.bytes[5] = converter.bytes[2];
+        retval.bytes[6] = converter.bytes[1];
+        retval.bytes[7] = converter.bytes[0];
+    } break;
+    }
+    return retval;
+}
+
+#endif
diff --git a/lib/util/intcode.h b/lib/util/intcode.h
index 4d9c83aa..dd42f669 100644
--- a/lib/util/intcode.h
+++ b/lib/util/intcode.h
@@ -1,7 +1,78 @@
 #ifndef INTCODE_H_INCLUDED
 #define INTCODE_H_INCLUDED
 
-#include "pm_config.h"  /* For uint32_t, BYTE_ORDER */
+#include "pm_config.h"  /* For uint32_t, BYTE_ORDER, HAVE_INT64 */
+
+static unsigned int const pm_byteOrder = BYTE_ORDER;
+
+typedef struct {
+/*----------------------------------------------------------------------------
+   This is a big-endian representation of a 16 bit integer.  I.e.
+   bytes[0] is the most significant 8 bits; bytes[1] is the least
+   significant 8 bits of the number in pure binary.
+
+   On a big-endian machines, this is bit for bit identical to uint16_t.
+   On a little-endian machine, it isn't.
+
+   This is an important data type because decent file formats use
+   big-endian -- they don't care if some CPU happens to use some other
+   code for its own work.
+-----------------------------------------------------------------------------*/
+    unsigned char bytes[2];
+} bigend16;
+
+
+static __inline__ uint16_t
+pm_uintFromBigend16(bigend16 const arg) {
+
+    uint16_t retval;
+
+    switch (pm_byteOrder) {
+    case BIG_ENDIAN: {
+        union {
+            bigend16 bigend;
+            uint16_t native;
+        } converter;
+        converter.bigend = arg;
+        retval = converter.native;
+    } break;
+
+    default: {
+        retval =
+            (arg.bytes[0] << 8) |
+            (arg.bytes[1] << 0);
+    }
+    }
+    return retval;
+}
+
+
+
+static __inline__ bigend16
+pm_bigendFromUint16(uint16_t const arg) {
+
+    bigend16 retval;
+
+    switch (pm_byteOrder) {
+    case BIG_ENDIAN: {
+        union {
+            bigend16 bigend;
+            uint16_t native;
+        } converter;
+        converter.native = arg;
+        retval = converter.bigend;
+    } break;
+
+    default: {
+        uint16_t shift;
+        shift = arg;
+        retval.bytes[1] = shift;  /* Takes lower 8 bits */
+        shift >>= 8;
+        retval.bytes[0] = shift;  /* Takes lower 8 bits */
+    }
+    }
+    return retval;
+}
 
 typedef struct {
 /*----------------------------------------------------------------------------
@@ -20,9 +91,6 @@ typedef struct {
 } bigend32;
 
 
-unsigned int const pm_byteOrder = BYTE_ORDER;
-
-
 static __inline__ uint32_t
 pm_uintFromBigend32(bigend32 const arg) {
 
@@ -34,19 +102,30 @@ pm_uintFromBigend32(bigend32 const arg) {
             bigend32 bigend;
             uint32_t native;
         } converter;
-        
         converter.bigend = arg;
-        
         retval = converter.native;
-    }; break;
+    } break;
+
+#if HAVE_GCC_BSWAP
     case LITTLE_ENDIAN: {
+        /* Use GCC built-in */  
+        union {
+            bigend32 bigend;
+            uint32_t native;
+        } converter;
+        converter.bigend = arg;
+        retval = __builtin_bswap32(converter.native);
+    } break;
+#endif
+    
+    default:
         retval =
             (arg.bytes[0] << 24) |
             (arg.bytes[1] << 16) |
             (arg.bytes[2] <<  8) |
             (arg.bytes[3] <<  0);
-    } break;
     }
+
     return retval;
 }
 
@@ -63,12 +142,23 @@ pm_bigendFromUint32(uint32_t const arg) {
             bigend32 bigend;
             uint32_t native;
         } converter;
-        
         converter.native = arg;
-
         retval = converter.bigend;
     } break;
+
+#if HAVE_GCC_BSWAP    
     case LITTLE_ENDIAN: {
+        /* Use GCC built-in */  
+        union {
+            bigend32 bigend;
+            uint32_t native;
+        } converter;
+        converter.native = __builtin_bswap32(arg);
+        retval = converter.bigend;
+    } break;
+#endif    
+
+    default: {
         uint32_t shift;
         shift = arg;
         retval.bytes[3] = shift;  /* Takes lower 8 bits */
@@ -78,9 +168,127 @@ pm_bigendFromUint32(uint32_t const arg) {
         retval.bytes[1] = shift;  /* Takes lower 8 bits */
         shift >>= 8;
         retval.bytes[0] = shift;  /* Takes lower 8 bits */
+    }
+    }
+    return retval;
+}
+
+
+#if HAVE_INT64
+
+typedef struct {
+/*----------------------------------------------------------------------------
+   This is a big-endian representation of a 64 bit integer.  I.e.
+   bytes[0] is the most significant 8 bits; bytes[7] is the least
+   significant 8 bits of the number in pure binary.
+
+   On a big-endian machines, this is bit for bit identical to uint64_t.
+   On a little-endian machine, it isn't.
+
+   This is an important data type because decent file formats use
+   big-endian -- they don't care if some CPU happens to use some other
+   code for its own work.
+
+   uint64_t is supported only when available.
+-----------------------------------------------------------------------------*/
+    unsigned char bytes[8];
+} bigend64;
+
+
+static __inline__ uint64_t
+pm_uintFromBigend64(bigend64 const arg) {
+
+    uint64_t retval;
+
+    switch (pm_byteOrder) {
+    case BIG_ENDIAN: {
+        union {
+            bigend64 bigend;
+            uint64_t native;
+        } converter;
+        converter.bigend = arg;
+        retval = converter.native;
     } break;
+
+#if HAVE_GCC_BSWAP    
+    case LITTLE_ENDIAN: {
+        /* Use GCC built-in */  
+        union {
+            bigend64 bigend;
+            uint64_t native;
+        } converter;
+        converter.bigend = arg;
+        retval = __builtin_bswap64(converter.native);
+    } break;
+#endif
+    default:
+        retval =
+            ((uint64_t)arg.bytes[0] << 56) | ((uint64_t)arg.bytes[1] << 48) |
+            ((uint64_t)arg.bytes[2] << 40) | ((uint64_t)arg.bytes[3] << 32) |
+            ((uint64_t)arg.bytes[4] << 24) | ((uint64_t)arg.bytes[5] << 16) |
+            ((uint64_t)arg.bytes[6] <<  8) | ((uint64_t)arg.bytes[7] <<  0);
     }
     return retval;
 }
 
+
+
+static __inline__ bigend64
+pm_bigendFromUint64(uint64_t const arg) {
+
+    bigend64 retval;
+
+    switch (pm_byteOrder) {
+    case BIG_ENDIAN: {
+        union {
+            bigend64 bigend;
+            uint64_t native;
+        } converter;
+        converter.native = arg;
+        retval = converter.bigend;
+    } break;
+
+#if HAVE_GCC_BSWAP
+    case LITTLE_ENDIAN: {
+
+        /* Use GCC built-in */  
+        union {
+            bigend64 bigend;
+            uint64_t native;
+        } converter;
+        converter.native = __builtin_bswap64(arg);
+        retval = converter.bigend;
+    } break;
 #endif
+    
+    default: {
+        uint64_t shift;
+        shift = arg;
+        retval.bytes[7] = shift;  /* Takes lower 8 bits */
+        shift >>= 8;
+        retval.bytes[6] = shift;  /* Takes lower 8 bits */
+        shift >>= 8;
+        retval.bytes[5] = shift;  /* Takes lower 8 bits */
+        shift >>= 8;
+        retval.bytes[4] = shift;  /* Takes lower 8 bits */
+        shift >>= 8;
+        retval.bytes[3] = shift;  /* Takes lower 8 bits */
+        shift >>= 8;
+        retval.bytes[2] = shift;  /* Takes lower 8 bits */
+        shift >>= 8;
+        retval.bytes[1] = shift;  /* Takes lower 8 bits */
+        shift >>= 8;
+        retval.bytes[0] = shift;  /* Takes lower 8 bits */
+    }
+    }
+    return retval;
+}
+
+#endif /* HAVE_INT64 */
+
+#endif
+
+
+
+
+
diff --git a/lib/util/mallocvar.h b/lib/util/mallocvar.h
index a26d007b..1f2be127 100644
--- a/lib/util/mallocvar.h
+++ b/lib/util/mallocvar.h
@@ -57,11 +57,22 @@ static __inline__ void
 reallocProduct(void **      const blockP,
                unsigned int const factor1,
                unsigned int const factor2) {
+
+    void * const oldBlockP = *blockP;
+
+    void * newBlockP;
     
     if (UINT_MAX / factor2 < factor1) 
-        *blockP = NULL;
+        newBlockP = NULL;
     else 
-        *blockP = realloc(*blockP, factor1 * factor2); 
+        newBlockP = realloc(oldBlockP, factor1 * factor2); 
+
+    if (newBlockP)
+        *blockP = newBlockP;
+    else {
+        free(oldBlockP);
+        *blockP = NULL;
+    }
 }
 
 
@@ -72,10 +83,12 @@ reallocProduct(void **      const blockP,
     arrayName = array; \
 } while (0)
 
-#define REALLOCARRAY(arrayName, nElements) { \
+#define REALLOCARRAY(arrayName, nElements) do { \
     void * array; \
     array = arrayName; \
     reallocProduct(&array, nElements, sizeof(arrayName[0])); \
+    if (!array) \
+        free(arrayName); \
     arrayName = array; \
 } while (0)
 
diff --git a/lib/util/nsleep.c b/lib/util/nsleep.c
new file mode 100644
index 00000000..943b8c77
--- /dev/null
+++ b/lib/util/nsleep.c
@@ -0,0 +1,27 @@
+#ifdef WIN32
+  #include <windows.h>
+  #include <process.h>
+#else
+  #include <unistd.h>
+#endif
+
+#include "nsleep.h"
+
+
+
+void
+sleepN(unsigned int const milliseconds) {
+
+#ifdef WIN32
+    SleepEx(milliseconds, TRUE);
+#else
+
+    /* We could use usleep() here if millisecond resolution is really
+       important, but since Netpbm has no need for it today, we don't
+       want to deal with the possibility that usleep() doesn't exist.
+       08.08.01.
+    */
+
+    sleep((milliseconds + 999)/1000);
+#endif
+}
diff --git a/lib/util/nsleep.h b/lib/util/nsleep.h
new file mode 100644
index 00000000..372b8008
--- /dev/null
+++ b/lib/util/nsleep.h
@@ -0,0 +1,7 @@
+#ifndef NSLEEP_H_INCLUDED
+#define NSLEEP_H_INCLUDED
+
+void
+sleepN(unsigned int const milliseconds);
+
+#endif
diff --git a/lib/util/nstring.c b/lib/util/nstring.c
index 66d636d5..8842aa05 100644
--- a/lib/util/nstring.c
+++ b/lib/util/nstring.c
@@ -740,15 +740,6 @@ const char * const strsol = "NO MEMORY TO CREATE STRING!";
 
 
 
-/* We would like to have vasprintfN(), but it is difficult because you
-   can't run through a va_list twice, which we would want to do: once
-   to measure the length; once actually to build the string.  On some
-   machines, you can simply make two copies of the va_list variable in
-   normal C fashion, but on others you need va_copy, which is a
-   relatively recent invention.  In particular, the simple va_list copy
-   failed on an AMD64 Gcc Linux system in March 2006.
-*/
-
 void PM_GNU_PRINTF_ATTR(2,3)
 asprintfN(const char ** const resultP,
           const char *  const fmt, 
@@ -887,20 +878,80 @@ stripeq(const char * const comparand,
 
 
 
-const char *
-memmemN(const char * const haystack,
+const void *
+memmemN(const void * const haystackArg,
         size_t       const haystacklen,
-        const char * const needle,
+        const void * const needleArg,
         size_t       const needlelen) {
 
+    const unsigned char * const haystack = haystackArg;
+    const unsigned char * const needle   = needleArg;
+
     /* This does the same as the function of the same name in the GNU
        C library
     */
-    const char * p;
+    const unsigned char * p;
 
     for (p = haystack; p <= haystack + haystacklen - needlelen; ++p)
-        if (MEMEQ(p, needle, needlelen))
+        if (memeq(p, needle, needlelen))
             return p;
 
     return NULL;
 }
+
+
+
+bool
+strishex(const char * const subject) {
+
+    bool retval;
+    unsigned int i;
+
+    retval = TRUE;  /* initial assumption */
+
+    for (i = 0; i < strlen(subject); ++i)
+        if (!ISXDIGIT(subject[i]))
+            retval = FALSE;
+
+    return retval;
+}
+
+
+
+void
+interpret_uint(const char *   const string,
+               unsigned int * const valueP,
+               const char **  const errorP) {
+
+    if (string[0] == '\0')
+        asprintfN(errorP, "Null string.");
+    else {
+        /* strtoul() does a bizarre thing where if the number is out
+           of range, it returns a clamped value but tells you about it
+           by setting errno = ERANGE.  If it is not out of range,
+           strtoul() 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')
+            asprintfN(errorP, "Non-digit stuff in string: %s", tail);
+        else if (errno == ERANGE)
+            asprintfN(errorP, "Number too large");
+        else if (ulongValue > UINT_MAX)
+            asprintfN(errorP, "Number too large");
+        else if (string[0] == '-')
+            asprintfN(errorP, "Negative number");
+            /* Sleazy code; string may have leading spaces. */
+        else {
+            *valueP = ulongValue;
+            *errorP = NULL;
+        }
+    }
+}
+
+
diff --git a/lib/util/nstring.h b/lib/util/nstring.h
index 9ed20051..53f1e4c0 100644
--- a/lib/util/nstring.h
+++ b/lib/util/nstring.h
@@ -5,6 +5,7 @@
 #include <string.h>
 #include <ctype.h>
 
+#include "pm_c_util.h"
 #include "pm.h"  /* For PM_GNU_PRINTF_ATTR, __inline__ */
 
 #ifdef __cplusplus
@@ -14,7 +15,7 @@ extern "C" {
 } /* to fake out automatic code indenters */
 #endif
 
-/* Here is are string functions that respect the size of the array
+/* Here are string functions that respect the size of the array
    into which you are copying -- E.g. STRSCPY truncates the source string as
    required so that it fits, with the terminating null, in the destination
    array.
@@ -25,22 +26,18 @@ extern "C" {
 	(strncmp((A), (B), sizeof(A)))
 #define STRSCAT(A,B) \
     (strncpy(A+strlen(A), B, sizeof(A)-strlen(A)), *((A)+sizeof(A)-1) = '\0')
-
-#define STREQ(A, B) \
-    (strcmp((A), (B)) == 0)
-#define STRNEQ(A, B, C) \
-    (strncmp((A), (B), (C)) == 0)
-#define STRCASEEQ(A, B) \
-    (strcasecmp((A), (B)) == 0)
-#define STRNCASEEQ(A, B, C) \
-    (strncasecmp((A), (B), (C)) == 0)
 #define STRSEQ(A, B) \
-	(strncmp((A), (B), sizeof(A)) == 0)
+	(strneq((A), (B), sizeof(A)))
+
+#define MEMEQ(a,b,c) (memcmp(a, b, c) == 0)
+
+#define MEMSEQ(a,b) (memeq(a, b, sizeof(*(a))) == 0)
+
+#define MEMSSET(a,b) (memset(a, b, sizeof(*(a))))
 
-#define MEMEQ(A, B, C) \
-    (memcmp((A), (B), (C)) == 0)
-#define MEMSZERO(A) \
-    bzero((A), sizeof(A))
+#define MEMSCPY(a,b) (memcpy(a, b, sizeof(*(a))))
+
+#define MEMSZERO(a) (MEMSSET(a, 0))
 
 
 static __inline__ int
@@ -50,6 +47,42 @@ streq(const char * const comparand,
     return strcmp(comparand, comparator) == 0;
 }
 
+static __inline__ int
+strneq(const char * const comparand,
+       const char * const comparator,
+       size_t       const size) {
+
+    return strncmp(comparand, comparator, size) == 0;
+}
+
+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.
+*/
+#ifdef _BSD_SOURCE
+static __inline__ int
+strcaseeq(const char * const comparand,
+          const char * const comparator) {
+
+    return strcasecmp(comparand, comparator) == 0;
+}
+
+static __inline__ int
+strncaseeq(const char * const comparand,
+           const char * const comparator,
+           size_t       const size) {
+
+    return strncasecmp(comparand, comparator, size) == 0;
+}
+#endif
 
 
 /* The standard C library routines isdigit(), for some weird 
@@ -134,6 +167,11 @@ asprintfN(const char ** const resultP,
           const char *  const fmt,
           ...) PM_GNU_PRINTF_ATTR(2,3);
 
+void
+vasprintfN(const char ** const resultP,
+           const char *  const format,
+           va_list             args);
+
 void 
 strfree(const char * const string);
 
@@ -144,12 +182,20 @@ int
 stripeq(const char * const comparand,
         const char * const comparator);
 
-const char *
-memmemN(const char * const haystack,
+const void *
+memmemN(const void * const haystackArg,
         size_t       const haystacklen,
-        const char * const needle,
+        const void * const needleArg,
         size_t       const needlelen);
 
+bool
+strishex(const char * const subject);
+
+void
+interpret_uint(const char *   const string,
+               unsigned int * const valueP,
+               const char **  const errorP);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/util/pm_c_util.h b/lib/util/pm_c_util.h
index f21a2f82..07913f30 100644
--- a/lib/util/pm_c_util.h
+++ b/lib/util/pm_c_util.h
@@ -15,6 +15,16 @@
 #define ROUND(X) (((X) >= 0) ? (int)((X)+0.5) : (int)((X)-0.5))
 #undef ROUNDU
 #define ROUNDU(X) ((unsigned int)((X)+0.5))
+
+/* ROUNDUP rounds up to a specified multiple.  E.g. ROUNDUP(22, 8) == 24 */
+
+#undef ROUNDUP
+#define ROUNDUP(X,M) (((X)+(M)-1)/(M)*(M))
+#undef ROUNDDN
+#define ROUNDDN(X,M) ((X)/(M)*(M))
+
+#define ROUNDDIV(DIVIDEND,DIVISOR) (((DIVIDEND) + (DIVISOR)/2)/(DIVISOR))
+
 #undef SQR
 #define SQR(a) ((a)*(a))
 
diff --git a/lib/util/shhopt.c b/lib/util/shhopt.c
index 7722b5d5..718186fa 100644
--- a/lib/util/shhopt.c
+++ b/lib/util/shhopt.c
@@ -89,9 +89,13 @@ optStructCount(const optEntry opt[])
     return ret;
 }
 
+
+
+static int
+optMatch(optEntry     const opt[],
+         const char * const s,
+         int          const lng) {
 /*------------------------------------------------------------------------
- |  NAME          optMatch
- |
  |  FUNCTION      Find a matching option.
  |
  |  INPUT         opt     array of possible options.
@@ -103,35 +107,39 @@ optStructCount(const optEntry opt[])
  |  DESCRIPTION   Short options are matched from the first character in
  |                the given string.
  */
-static int
-optMatch(const optEntry opt[], const char *s, int lng)
-{
-    int        nopt, q, matchlen = 0;
-    const char *p;
 
-    nopt = optStructCount(opt);
+    unsigned int const nopt = optStructCount(opt);
+
+    unsigned int q;
+    unsigned int matchlen;
+    const char * p;
+
+    matchlen = 0;  /* initial value */
+
     if (lng) {
         if ((p = strchr(s, '=')) != NULL)
             matchlen = p - s;
         else
             matchlen = strlen(s);
     }
-    for (q = 0; q < nopt; q++) {
+    for (q = 0; q < nopt; ++q) {
         if (lng) {
-            if (!opt[q].longName)
-                continue;
-            if (strncmp(s, opt[q].longName, matchlen) == 0)
-                return q;
+            if (opt[q].longName) {
+                if (strncmp(s, opt[q].longName, matchlen) == 0)
+                    return q;
+            }
         } else {
-            if (!opt[q].shortName)
-                continue;
-            if (*s == opt[q].shortName)
-                return q;
+            if (opt[q].shortName) {
+                if (s[0] == opt[q].shortName)
+                    return q;
+            }
         }
     }
     return -1;
 }
 
+
+
 /*------------------------------------------------------------------------
  |  NAME          optString
  |
@@ -422,13 +430,17 @@ optExecute(optEntry  const opt, char *arg, int lng)
     case OPT_UINT:
     case OPT_ULONG: {
         unsigned long tmp;
-        char *e;
+        char * tailPtr;
         
         if (arg == NULL)
             optFatal("internal error: optExecute() called with NULL argument "
                      "'%s'", optString(opt, lng));
-        tmp = strtoul(arg, &e, 10);
-        if (*e)
+
+        if (arg[0] == '-' || arg[1] == '+')
+            optFatal("unsigned number '%s' has a sign ('%c')",
+                     arg, arg[0]);
+        tmp = strtoul(arg, &tailPtr, 10);
+        if (*tailPtr)
             optFatal("invalid number `%s'", arg);
         if (errno == ERANGE
             || (opt.type == OPT_UINT && tmp > UINT_MAX))
@@ -643,7 +655,15 @@ static void
 parse_short_option_token(char *argv[], const int argc, const int ai,
                          const optEntry opt_table[], 
                          int * const tokens_consumed_p) {
+/*----------------------------------------------------------------------------
+   Parse a cluster of short options, e.g. -walne .
+
+   The last option in the cluster might take an argument, and we parse
+   that as well.  e.g. -cf myfile or -cfmyfile .
 
+   argv[] and argc describe the whole program argument set.  'ai' is the
+   index of the argument that is the short option cluster.
+-----------------------------------------------------------------------------*/
     char *o;  /* A short option character */
     char *arg;
     int mi;   /* index into option table */
@@ -685,11 +705,60 @@ parse_short_option_token(char *argv[], const int argc, const int ai,
 
 
 static void
-parse_long_option(char *argv[], const int argc, const int ai,
-                  const int namepos,
-                  const optEntry opt_table[], 
-                  int * const tokens_consumed_p) {
+fatalUnrecognizedLongOption(const char * const optionName,
+                            optEntry     const optTable[]) {
+
+    unsigned int const nopt = optStructCount(optTable);
+
+    unsigned int q;
+
+    char optList[1024];
+
+    optList[0] = '\0';  /* initial value */
+
+    for (q = 0;
+         q < nopt && strlen(optList) + 1 <= sizeof(optList);
+         ++q) {
+
+        const optEntry * const optEntryP = &optTable[q];
+        const char * entry;
+
+        if (optEntryP->longName)
+            asprintfN(&entry, "-%s ", optEntryP->longName);
+        else
+            asprintfN(&entry, "-%c ", optEntryP->shortName);
+
+        strncat(optList, entry, sizeof(optList) - strlen(optList) - 1);
+
+        strfree(entry);
+
+        if (strlen(optList) + 1 == sizeof(optList)) {
+            /* Buffer is full.  Overwrite end of list with ellipsis */
+            strcpy(&optList[sizeof(optList) - 4], "...");
+        }
+    }
+    optFatal("unrecognized option '%s'.  Recognized options are: %s",
+             optionName, optList);
+}
+
+
+
+static void
+parse_long_option(char *   const argv[],
+                  int      const argc,
+                  int      const ai,
+                  int      const namepos,
+                  optEntry const opt_table[], 
+                  int *    const tokens_consumed_p) {
+/*----------------------------------------------------------------------------
+   Parse a long option, e.g. -verbose or --verbose.
+
+   The option might take an argument, and we parse
+   that as well.  e.g. -file=myfile or -file myfile .
 
+   argv[] and argc describe the whole program argument set.  'ai' is the
+   index of the argument that is the long option.
+-----------------------------------------------------------------------------*/
     char *equals_arg;
       /* The argument of an option, included in the same token, after a
          "=".  NULL if no "=" in the token.
@@ -703,8 +772,8 @@ parse_long_option(char *argv[], const int argc, const int ai,
     *tokens_consumed_p = 1;  /* initial assumption */
     /* find matching option */
     if ((mi = optMatch(opt_table, &argv[ai][namepos], 1)) < 0)
-        optFatal("unrecognized option `%s'", argv[ai]);
-            
+        fatalUnrecognizedLongOption(argv[ai], opt_table);
+
     /* possibly locate the argument to this option. */
     { 
         char *p;
@@ -727,7 +796,8 @@ parse_long_option(char *argv[], const int argc, const int ai,
         }
     } else {
         if (equals_arg)
-            optFatal("option `%s' doesn't allow an argument",
+            optFatal("option `%s' doesn't allow an argument, but you "
+                     "have specified it in the form name=value",
                      optString(opt_table[mi], 1));
         else 
             arg = NULL;
diff --git a/lib/util/shhopt.h b/lib/util/shhopt.h
index 0c3c3ba4..99096a76 100644
--- a/lib/util/shhopt.h
+++ b/lib/util/shhopt.h
@@ -1,4 +1,4 @@
-/*==============================================================================
+/*=============================================================================
 HERE IS AN EXAMPLE OF THE USE OF SHHOPT:
 
 
@@ -6,6 +6,8 @@ HERE IS AN EXAMPLE OF THE USE OF SHHOPT:
 int 
 main ( int argc, char **argv ) {
 
+    // initial values here are just to demonstrate what gets set and
+    // what doesn't by the code below.
     int help_flag = 7;
     unsigned int help_spec =7;
     unsigned int height_spec =7;
@@ -186,12 +188,13 @@ typedef struct {
 
 /* OPTENT3 is the same as OPTENTRY except that it also sets the "specified"
    element of the table entry (so it assumes OPTION_DEF is a table of
-   optEntry instead of optStruct).
+   optEntry instead of optStruct).  It sets it to the number of times that
+   the option appears in the command line.
 
    Here is an example:
 
        unsigned int option_def_index = 0;
-       optEntry *option_def = malloc(100*sizeof(optEntry));
+       MALLOCARRAY_NOFAIL(option_def, 100);
        OPTENT3('h', "help",     OPT_FLAG, &help_flag, 0);
        OPTENT3(0,   "alphaout", OPT_STRING, &alpha_filename, 0);
 */
@@ -229,14 +232,4 @@ optDestroyNameValueList(struct optNameValue * const list);
 }
 #endif
 
-/* Here's a hack to help us introduce pm_c_util.h.  That should be
-   #included in nearly every Netpbm program, but we're too lazy to insert
-   it into hundreds of files right now.  But shhopt.h is included into
-   most of those files, so we #include it here!
-
-   Before 2005.12.03, the stuff that is now in pm_c_util.h was in pm.h,
-   but that's a bad place for it because pm.h is an external header file.
-*/
-#include "pm_c_util.h"
-
 #endif
diff --git a/lib/util/vasprintf.c b/lib/util/vasprintf.c
new file mode 100644
index 00000000..47b4079d
--- /dev/null
+++ b/lib/util/vasprintf.c
@@ -0,0 +1,58 @@
+#define _GNU_SOURCE
+   /* Due to conditional compilation, this is GNU source only if the C library
+      is GNU.
+   */
+#include <stdlib.h>
+#include <string.h>
+
+#include "pm_config.h"
+#include "nstring.h"
+
+
+
+void
+vasprintfN(const char ** const resultP,
+           const char *  const format,
+           va_list             varargs) {
+
+    char * result;
+
+#if HAVE_VASPRINTF
+    int rc;
+
+    rc = vasprintf(&result, format, varargs);
+
+    if (rc < 0)
+        *resultP = strsol;
+    else
+        *resultP = result;
+#else
+    /* We have a big compromise here.  To do this right, without a
+       huge amount of work, we need to go through the variable
+       arguments twice: once to determine how much memory to allocate,
+       and once to format the string.  On some machines, you can
+       simply make two copies of the va_list variable in normal C
+       fashion, but on others you need va_copy, which is a relatively
+       recent invention.  In particular, the simple va_list copy
+       failed on an AMD64 Gcc Linux system in March 2006.  
+
+       So instead, we just allocate 4K and truncate or waste as
+       necessary.
+    */
+    size_t const allocSize = 4096;
+    result = malloc(allocSize);
+    
+    if (result == NULL)
+        *resultP = strsol;
+    else {
+        size_t realLen;
+
+        vsnprintfN(result, allocSize, format, varargs, &realLen);
+        
+        if (realLen >= allocSize)
+            strcpy(result + allocSize - 15, "<<<TRUNCATED");
+
+        *resultP = result;
+    }
+#endif
+}
diff --git a/lib/util/wordaccess.h b/lib/util/wordaccess.h
index 28963aee..2eaa2b24 100644
--- a/lib/util/wordaccess.h
+++ b/lib/util/wordaccess.h
@@ -36,28 +36,39 @@
    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.
 */
-#if (!defined(WORDACCESS_GENERIC) \
-     && defined(__GNUC__) && defined(__GLIBC__) \
-     && (__GNUC__ * 100 + __GNUC_MINOR__ >= 304) )
 
-    #if BYTE_ORDER==BIG_ENDIAN    /* defined by GCC */
+#include "pm_config.h"
 
-        #include "wordaccess_gcc3_be.h"
+#if (!defined(WORDACCESS_GENERIC) && HAVE_GCC_BITCOUNT )
 
-    #elif defined(__ia64__) || defined(__amd64__) || defined(__x86_64__)
-         /* all these macros are defined by GCC */
+    #if BYTE_ORDER == BIG_ENDIAN    /* See pm_config.h */
+        /* Sun Sparc 64, etc */ 
+        #include "wordaccess_gcc3_be.h"
 
+    #elif (BITS_PER_LONG == 64)
+        /* AMD Athlon 64, Intel x86_64, Intel Itanium, etc. */
         #include "wordaccess_64_le.h"
 
-    #else
+    #elif (BITS_PER_LONG == 32)
+        /* Intel x86_32 (80386, 80486, Pentium), etc. */
+        #include "wordaccess_generic.h"
 
-        #include "wordaccess_gcc3_le.h"
+    #else
+        /* Extremely rare case.
+           If long is neither 32 nor 64 bits, (say, 128) it comes here.
+        */
+        #define WORDACCESS_GENERIC
+        #include "wordaccess_generic.h"
 
     #endif
 
 #else
-
+    /* Non GCC, GCC prior to v.3.4 or WORDACCESS_GENERIC defined  */
     #include "wordaccess_generic.h"
 
 #endif
diff --git a/lib/util/wordaccess_64_le.h b/lib/util/wordaccess_64_le.h
index 4bb52b2e..4d148ad2 100644
--- a/lib/util/wordaccess_64_le.h
+++ b/lib/util/wordaccess_64_le.h
@@ -2,26 +2,21 @@
   This file is the part of wordaccess.h for use under these
   conditions:
 
-  * GCC (>=3.4), GLIBC
-  * 64 bit Little-Endian machines (IA64, X86-64, AMD64)
-=============================================================================*/
-   
-/*  
-    64 bit hton and ntoh do not exist.  Here we use bswap_64.
-    
-    While bswap_64 works on 64 bit data, __builtin_clzl works on "long" which
-    may or may not be 64 bits.  Code provided to find the right data type and
-    file off any extra when necessary.
-*/
-
-#include <byteswap.h>  /* See note above on bswap_64 */
+  * 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 bytes) {
-    return ((wordint) bswap_64(*(wordint *)bytes));
+bytesToWordint(wordintBytes const bytes) {
+    return (wordint) pm_uintFromBigend64(*(bigend64*)bytes);
 }
 
 
@@ -29,24 +24,5 @@ bytesToWordint(wordintBytes bytes) {
 static __inline__ void
 wordintToBytes(wordintBytes * const bytesP,
                wordint        const wordInt) {
-    *(wordint *)bytesP = bswap_64(wordInt);
-}
-
-
-
-static __inline__ unsigned int
-wordintClz(wordint const x){
-
-    unsigned int s;
-
-    if (x == 0)
-        return sizeof(wordint) * 8;
-
-    /* Find the data type closest to 64 bits, and file off any extra. */
-    else if ((s=sizeof(long int)) >= 8)
-        return (__builtin_clzl((long int)x << (s - 8) * 8));
-    else if ((s=sizeof(long long int)) >= 8)
-        return (__builtin_clzll((long long int)x << (s - 8) * 8));
-    else
-        pm_error("Long long int is less than 64 bits on this machine"); 
+     *(bigend64*)bytesP = pm_bigendFromUint64(wordInt);
 }
diff --git a/lib/util/wordaccess_gcc3_be.h b/lib/util/wordaccess_gcc3_be.h
index 6f5d86fc..5aa63521 100644
--- a/lib/util/wordaccess_gcc3_be.h
+++ b/lib/util/wordaccess_gcc3_be.h
@@ -4,16 +4,6 @@
 
   * GCC (>=3.4), GLIBC
   * Big-Endian machines
-  
-  __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.
 *===========================================================================*/
 
 typedef unsigned long int wordint;
@@ -31,10 +21,3 @@ wordintToBytes(wordintBytes * const bytesP,
                wordint        const wordInt) {
     *(wordint *)bytesP = wordInt;
 }
-
-
-
-static __inline__ unsigned int
-wordintClz(wordint const x) {
-    return (x==0 ? sizeof(wordint)*8 : __builtin_clzl(x));
-}
diff --git a/lib/util/wordaccess_gcc3_le.h b/lib/util/wordaccess_gcc3_le.h
deleted file mode 100644
index 7db218db..00000000
--- a/lib/util/wordaccess_gcc3_le.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*=============================================================================
-
-  This file is the part of wordaccess.h for use under these
-  conditions:
-
-  * GCC (>=3.4), GLIBC
-  * 32 bit Little-Endian machines (intel MPUs 80386, Pentium, etc.)
-  * Other non-Big-Endian machines (very rare)
-  
-=============================================================================*/
-
-typedef uint32_t wordint;
-typedef unsigned char wordintBytes[sizeof(wordint)];
-
-#include <sys/types.h>
-#include <netinet/in.h>
-
-/*
-  Here we use the more widely used functions htonl and ntohl instead of
-  bswap_32.  This makes possible the handling of weird byte ordering
-  (neither Big-Endian nor Little-Endian) schemes, if any.
-*/
-
-static __inline__ wordint
-bytesToWordint(wordintBytes const bytes) {
-    return (wordint) ntohl(*(wordint *)bytes);
-}
-
-
-
-static __inline__ void
-wordintToBytes(wordintBytes * const bytesP,
-               wordint        const wordInt) {
-
-    *(wordint *)bytesP = htonl(wordInt);
-}
-
-
-
-static __inline__ unsigned int
-wordintClz(wordint const x) {
-
-    /* Find the data type closest to 32 bits, and file off any extra.  */
-
-    if (x == 0)
-        return sizeof(wordint) * 8;
-    else if (sizeof(int) >= 4)
-        return __builtin_clz((int)x << (sizeof(int) - 4) * 8);
-    else if (sizeof(long int) >= 4)
-        return __builtin_clzl((long int)x << (sizeof(long int) - 4) * 8);
-    else
-        pm_error("Long int is less than 32 bits on this machine"); 
-}
-
diff --git a/lib/util/wordaccess_generic.h b/lib/util/wordaccess_generic.h
index 7f27ef74..94cc8124 100644
--- a/lib/util/wordaccess_generic.h
+++ b/lib/util/wordaccess_generic.h
@@ -5,85 +5,25 @@
 
   * Compilers other than GCC
   * GCC before version 3.4
-  * c libraries other than Glibc
   * Specified 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) {
-    wordint retval;
-    unsigned int i;
+bytesToWordint(wordintBytes  const bytes) {
 
-    /* Note that 'bytes' is a pointer, due to C array degeneration.
-       That means sizeof(bytes) isn't what you think it is.
-    */
-    
-    for (i = 1, retval = bytes[0]; i < sizeof(wordint); ++i) {
-        retval = (retval << 8) + bytes[i];
-    }
-    return retval;
+    return (wordint)  pm_uintFromBigend32( * (bigend32*) bytes);
 }
 
 
-
 static __inline__ void
 wordintToBytes(wordintBytes * const bytesP,
-               wordint        const wordInt) {
-
-    wordint buffer;
-    int i;
-
-    for (i = sizeof(*bytesP)-1, buffer = wordInt; i >= 0; --i) {
-        (*bytesP)[i] = buffer & 0xFF;
-        buffer >>= 8;
-    }
-}
-    
-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;
-}
-
-
+               wordint    const wordInt) {
 
-static __inline__  unsigned int
-wordintClz(wordint const x) {
-    return clz32(x);
+    * (bigend32*) bytesP = pm_bigendFromUint32((uint32_t)wordInt);
 }
diff --git a/lib/util/wordintclz.h b/lib/util/wordintclz.h
new file mode 100644
index 00000000..32e6ade8
--- /dev/null
+++ b/lib/util/wordintclz.h
@@ -0,0 +1,93 @@
+#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/other/Makefile b/other/Makefile
index 9aaa9d93..f7333c64 100644
--- a/other/Makefile
+++ b/other/Makefile
@@ -5,13 +5,14 @@ endif
 SUBDIR = other
 VPATH=.:$(SRCDIR)/$(SUBDIR)
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
 SUBDIRS = pamx
 
+EXTERN_INCLUDES =
 ifneq ($(LINUXSVGALIB),NONE)
   ifneq ($(LINUXSVGAHDR_DIR),)
-    INCLUDES += -I$(LINUXSVGAHDR_DIR)
+    EXTERN_INCLUDES += -I$(LINUXSVGAHDR_DIR)
   endif
 endif
 
@@ -23,7 +24,7 @@ endif
 # build.
 
 PORTBINARIES = pamarith pambayer pamchannel pamdepth \
-	pamendian pamlookup pampick pamsplit \
+	pamendian pamfixtrunc pamlookup pampick pamsplit \
 	pamstack pamsummcol pnmcolormap \
 	ppmdcfont ppmddumpfont ppmdmkfont 
 
@@ -47,7 +48,7 @@ MERGE_OBJECTS = $(MERGEBINARIES:%=%.o2)
 .PHONY: all
 all: $(BINARIES) $(SUBDIRS:%=%/all)
 
-include $(SRCDIR)/Makefile.common
+include $(SRCDIR)/common.mk
 
 ppmsvgalib: %: %.o $(NETPBMLIB) $(LIBOPT)
 	$(LD) -o $@ $< \
@@ -60,15 +61,15 @@ install.bin.local: $(PKGDIR)/bin
 # Remember that $(SYMLINK) might just be a copy command.
 # In July 2002, pamarith replaced pnmarith
 	cd $(PKGDIR)/bin ; \
-	  rm -f pnmarith ; \
-	  $(SYMLINK) pamarith$(EXE) pnmarith
+	  rm -f pnmarith$(EXE) ; \
+	  $(SYMLINK) pamarith$(EXE) pnmarith$(EXE)
 # In December 2005, pamsplit replaced pnmsplit
 	cd $(PKGDIR)/bin ; \
-	  rm -f pnmsplit ; \
-	  $(SYMLINK) pamsplit$(EXE) pnmsplit
+	  rm -f pnmsplit$(EXE) ; \
+	  $(SYMLINK) pamsplit$(EXE) pnmsplit$(EXE)
 # In February 2006, pamdepth replaced pnmdepth
 	cd $(PKGDIR)/bin ; \
-	  rm -f pnmdepth ; \
-	  $(SYMLINK) pamdepth$(EXE) pnmdepth
+	  rm -f pnmdepth$(EXE) ; \
+	  $(SYMLINK) pamdepth$(EXE) pnmdepth$(EXE)
 
 FORCE:
diff --git a/other/pamarith.c b/other/pamarith.c
index c1e7f1ed..4374ee1c 100644
--- a/other/pamarith.c
+++ b/other/pamarith.c
@@ -1,6 +1,9 @@
 #include <assert.h>
 #include <string.h>
+#include <limits.h>
 
+#include "pm_c_util.h"
+#include "mallocvar.h"
 #include "shhopt.h"
 #include "pam.h"
 
@@ -10,25 +13,59 @@ enum function {FN_ADD, FN_SUBTRACT, FN_MULTIPLY, FN_DIVIDE, FN_DIFFERENCE,
                FN_SHIFTLEFT, FN_SHIFTRIGHT
               };
 
+
+
+static bool
+isDyadic(enum function const function) {
+
+    bool retval;
+    
+    switch (function) {
+    case FN_ADD:
+    case FN_MEAN:
+    case FN_AND:
+    case FN_OR:
+    case FN_XOR:
+        retval = FALSE;
+        break;
+    case FN_SUBTRACT:
+    case FN_DIFFERENCE:
+    case FN_MINIMUM:
+    case FN_MAXIMUM:
+    case FN_COMPARE:
+    case FN_MULTIPLY:
+    case FN_DIVIDE:
+    case FN_NAND:
+    case FN_NOR:
+    case FN_SHIFTLEFT:
+    case FN_SHIFTRIGHT:
+        retval = TRUE;
+        break;
+    }
+    return retval;
+}
+
+
+
 struct cmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    const char *input1Filespec;  
-    const char *input2Filespec;  
     enum function function;
+    unsigned int operandCt;
+    const char ** operandFileNames;
 };
 
 
 
 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
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
-    optEntry *option_def = malloc(100*sizeof(optEntry));
+    optEntry * option_def;
         /* Instructions to OptParseOptions2 on how to parse our options.
          */
     optStruct3 opt;
@@ -41,6 +78,8 @@ parseCommandLine(int argc, char ** const argv,
         andSpec, orSpec, nandSpec, norSpec, xorSpec,
         shiftleftSpec, shiftrightSpec;
 
+    MALLOCARRAY_NOFAIL(option_def, 100);
+
     option_def_index = 0;   /* incremented by OPTENTRY */
     OPTENT3(0, "add",         OPT_FLAG,   NULL, &addSpec,        0);
     OPTENT3(0, "subtract",    OPT_FLAG,   NULL, &subtractSpec,   0);
@@ -63,7 +102,7 @@ parseCommandLine(int argc, char ** const argv,
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+    optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
     if (addSpec + subtractSpec + multiplySpec + divideSpec + differenceSpec +
@@ -72,15 +111,6 @@ parseCommandLine(int argc, char ** const argv,
         shiftleftSpec + shiftrightSpec > 1)
         pm_error("You may specify only one function");
 
-    if (argc-1 != 2)
-        pm_error("You must specify two arguments:  the files which are "
-                 "the operands of the "
-                 "dyadic function.  You specified %d", argc-1);
-    else {
-        cmdlineP->input1Filespec = argv[1];
-        cmdlineP->input2Filespec = argv[2];
-    }
-
     if (addSpec)
         cmdlineP->function = FN_ADD;
     else if (subtractSpec)
@@ -115,6 +145,21 @@ parseCommandLine(int argc, char ** const argv,
         cmdlineP->function = FN_SHIFTRIGHT;
     else
         pm_error("You must specify a function (e.g. '-add')");
+
+    if (argc-1 < 2)
+        pm_error("You must specify at least two arguments: the files which "
+                 "are the operands of the function.  You specified %u",
+                 argc-1);
+    else {
+        if (isDyadic(cmdlineP->function) && argc-1 > 2)
+            pm_error("You specified %u arguments, but a dyadic function.  "
+                     "For a dyadic function, you must specify 2 arguments:  "
+                     "the operands of the function", argc-1);
+        else {
+            cmdlineP->operandCt = argc-1;
+            cmdlineP->operandFileNames = &argv[1];
+        }
+    }
 }        
 
 
@@ -220,43 +265,124 @@ computeOutputType(struct pam *  const outpamP,
 
 
 
+static sample
+samplenSum(samplen      const operands[],
+           unsigned int const operandCt) {
+
+    unsigned int i;
+    samplen total;
+
+    for (i = 1, total = operands[0]; i < operandCt; ++i) {
+        total += operands[1];
+        if (total > 1.)
+            total = 1.;
+    }
+    return total;
+}
+
+
+
+static sample
+samplenMin(samplen      const operands[],
+           unsigned int const operandCt) {
+
+    unsigned int i;
+    samplen min;
+
+    for (i = 1, min = operands[0]; i < operandCt; ++i) {
+        if (operands[i] < min)
+            min = operands[i];
+    }
+    return min;
+}
+
+
+
+static sample
+samplenMax(samplen      const operands[],
+           unsigned int const operandCt) {
+
+    unsigned int i;
+    samplen max;
+
+    for (i = 1, max = operands[0]; i < operandCt; ++i) {
+        if (operands[i] > max)
+            max = operands[i];
+    }
+    return max;
+}
+
+
+
+static sample
+samplenMean(samplen      const operands[],
+            unsigned int const operandCt) {
+
+    unsigned int i;
+    double sum;
+
+    for (i = 0, sum = 0.; i < operandCt; ++i)
+        sum += operands[i];
+
+    return sum / operandCt;
+}
+
+
+
+static sample
+samplenProduct(samplen      const operands[],
+               unsigned int const operandCt) {
+
+    unsigned int i;
+    double product;
+
+    for (i = 1, product = operands[0]; i < operandCt; ++i)
+        product *= operands[i];
+
+    return product;
+}
+
+
+
 static samplen
 applyNormalizedFunction(enum function const function,
-                        samplen       const leftArg,
-                        samplen       const rightArg) {
+                        samplen       const operands[],
+                        unsigned int  const operandCt) {
 
     samplen result;
 
     switch (function) {
     case FN_ADD:
-        result = MIN(1., leftArg + rightArg);
+        result = samplenSum(operands, operandCt);
         break;
     case FN_SUBTRACT:
-        result = MAX(0., leftArg - rightArg);
+        result = MAX(0., operands[0] - operands[1]);
         break;
     case FN_MULTIPLY:
-        result = leftArg * rightArg;
+        result = samplenProduct(operands, operandCt);
         break;
     case FN_DIVIDE:
-        result = (rightArg > leftArg) ?
-        leftArg / rightArg : 1.;
+        result = (operands[1] > operands[0]) ?
+        operands[0] / operands[1] : 1.;
         break;
     case FN_DIFFERENCE:
-        result = leftArg > rightArg ? 
-            leftArg - rightArg : rightArg - leftArg;
+        result = operands[0] > operands[1] ? 
+            operands[0] - operands[1] : operands[1] - operands[0];
         break;
     case FN_MINIMUM:
-        result = MIN(leftArg, rightArg);
+        result = samplenMin(operands, operandCt);
         break;
     case FN_MAXIMUM:
-        result = MAX(leftArg, rightArg);
+        result = samplenMax(operands, operandCt);
         break;
     case FN_MEAN:
-        result = (leftArg + rightArg) / 2.0;
+        result = samplenMean(operands, operandCt);
         break;
     case FN_COMPARE:
         result = 
-            leftArg > rightArg ? 1. : leftArg < rightArg ? 0. : .5;
+            operands[0] > operands[1] ?
+            1. : operands[0] < operands[1] ?
+            0. : .5;
         break;
     default:
         pm_error("Internal error.  applyNormalizedFunction() called "
@@ -274,10 +400,15 @@ doNormalizedArith(struct pam *  const inpam1P,
                   struct pam *  const outpamP,
                   enum function const function) {
 
+    unsigned int const operandCt = 2;
+
     tuplen * tuplerown1;
     tuplen * tuplerown2;
     tuplen * tuplerownOut;
     unsigned int row;
+    samplen * operands;
+
+    MALLOCARRAY_NOFAIL(operands, operandCt);
 
     tuplerown1   = pnm_allocpamrown(inpam1P);
     tuplerown2   = pnm_allocpamrown(inpam2P);
@@ -295,10 +426,11 @@ doNormalizedArith(struct pam *  const inpam1P,
                 unsigned int const plane1 = MIN(outplane, inpam1P->depth-1);
                 unsigned int const plane2 = MIN(outplane, inpam2P->depth-1);
 
+                operands[0] = tuplerown1[col][plane1];
+                operands[1] = tuplerown2[col][plane2];
+
                 tuplerownOut[col][outplane] = 
-                    applyNormalizedFunction(function, 
-                                            tuplerown1[col][plane1], 
-                                            tuplerown2[col][plane2]);
+                    applyNormalizedFunction(function, operands, operandCt); 
                 assert(tuplerownOut[col][outplane] >= 0.);
                 assert(tuplerownOut[col][outplane] <= 1.);
 
@@ -310,75 +442,244 @@ doNormalizedArith(struct pam *  const inpam1P,
     pnm_freepamrown(tuplerown1);
     pnm_freepamrown(tuplerown2);
     pnm_freepamrown(tuplerownOut);
+    free(operands);
+}
+
+
+
+static sample
+sampleSum(sample       const operands[],
+          unsigned int const operandCt,
+          sample       const maxval) {
+
+    unsigned int i;
+    sample total;
+
+    for (i = 1, total = operands[0]; i < operandCt; ++i) {
+        total += operands[i];
+        if (total > maxval)
+            total = maxval;
+    }
+    return total;
+}
+
+
+
+static sample
+sampleMin(sample       const operands[],
+          unsigned int const operandCt) {
+
+    unsigned int i;
+    sample min;
+
+    for (i = 1, min = operands[0]; i < operandCt; ++i) {
+        if (operands[i] < min)
+            min = operands[i];
+    }
+    return min;
+}
+
+
+
+static sample
+sampleMax(sample       const operands[],
+          unsigned int const operandCt) {
+
+    unsigned int i;
+    sample max;
+
+    for (i = 1, max = operands[0]; i < operandCt; ++i) {
+        if (operands[i] > max)
+            max = operands[i];
+    }
+    return max;
+}
+
+
+
+static sample
+sampleMean(sample       const operands[],
+           unsigned int const operandCt) {
+
+    unsigned int i;
+    unsigned int sum;
+
+    for (i = 0, sum = 0; i < operandCt; ++i) {
+        sum += operands[i];
+        if (UINT_MAX - operands[i] < sum)
+            pm_error("Arithmetic overflow adding samples for mean");
+    }
+    return ROUNDDIV(sum, operandCt);
+}
+
+
+
+static sample
+sampleProduct(sample       const operands[],
+              unsigned int const operandCt,
+              sample       const maxval) {
+
+    unsigned int i;
+    double product;
+
+    for (i = 0, product = 1.0; i < operandCt; ++i) {
+        product *= ((double)operands[i]/maxval);
+    }
+    return (sample)(product * maxval + 0.5);
+}
+
+
+
+static sample
+sampleAnd(sample       const operands[],
+          unsigned int const operandCt) {
+
+    unsigned int i;
+    sample accum;
+
+    for (i = 1, accum = operands[0]; i < operandCt; ++i) {
+        accum &= operands[i];
+    }
+    return accum;
+}
+
+
+
+static sample
+sampleOr(sample       const operands[],
+         unsigned int const operandCt) {
+
+    unsigned int i;
+    sample accum;
+
+    for (i = 1, accum = operands[0]; i < operandCt; ++i) {
+        accum |= operands[i];
+    }
+    return accum;
+}
+
+
+
+static sample
+sampleNand(sample       const operands[],
+           unsigned int const operandCt,
+           sample       const maxval) {
+
+    unsigned int i;
+    sample accum;
+
+    for (i = 1, accum = operands[0]; i < operandCt; ++i) {
+        accum &= operands[i];
+    }
+    return ~accum & maxval;
+}
+
+
+
+static sample
+sampleNor(sample       const operands[],
+          unsigned int const operandCt,
+          sample       const maxval) {
+
+    unsigned int i;
+    sample accum;
+
+    for (i = 1, accum = operands[0]; i < operandCt; ++i) {
+        accum |= operands[i];
+    }
+    return ~accum & maxval;
+}
+
+
+
+static sample
+sampleXor(sample       const operands[],
+          unsigned int const operandCt) {
+
+    unsigned int i;
+    sample accum;
+
+    for (i = 1, accum = operands[0]; i < operandCt; ++i) {
+        accum ^= operands[i];
+    }
+    return accum;
 }
 
 
 
 static sample
 applyUnNormalizedFunction(enum function const function,
-                          sample        const leftArg,
-                          sample        const rightArg,
+                          sample        const operands[],
+                          unsigned int  const operandCt,
                           sample        const maxval) {
 /*----------------------------------------------------------------------------
-   Apply dyadic function 'function' to the arguments 'leftArg' and
-   'rightArg', assuming both are based on the same maxval 'maxval'.
-   Return a value which is also a fraction of 'maxval'.
+   Apply function 'function' to the arguments operands[] (there are
+   'operandCt' of them), assuming both are based on the same maxval
+   'maxval'.  Return a value which is also a fraction of 'maxval'.
 
-   Exception: for the shift operations, 'rightArg' is not based on any
+   Exception: for the shift operations, operands[1] is not based on any
    maxval.  It is an absolute bit count.
+
+   We assume 'operandCount' is sensible for 'function'.  E.g. if
+   'function' is FN_DIFFERENCE, 'operandCt' is 2.
+
+   For a function that has a concept of left and right argument,
+   operands[0] is the left and operands[1] is the right.
 -----------------------------------------------------------------------------*/
     sample result;
 
     switch (function) {
     case FN_ADD:
-        result = MIN(maxval, leftArg + rightArg);
+        result = sampleSum(operands, operandCt, maxval);
         break;
     case FN_SUBTRACT:
-        result = MAX(0, (int)leftArg - (int)rightArg);
+        result = MAX(0, (int)operands[0] - (int)operands[1]);
         break;
     case FN_DIFFERENCE:
-        result = leftArg > rightArg ? leftArg - rightArg : rightArg - leftArg;
+        result = operands[0] > operands[1] ?
+            operands[0] - operands[1] : operands[1] - operands[0];
         break;
     case FN_MINIMUM:
-        result = MIN(leftArg, rightArg);
+        result = sampleMin(operands, operandCt);
         break;
     case FN_MAXIMUM:
-        result = MAX(leftArg, rightArg);
+        result = sampleMax(operands, operandCt);
         break;
     case FN_MEAN:
-        result = (leftArg + rightArg + 1) / 2;
+        result = sampleMean(operands, operandCt);
         break;
     case FN_COMPARE:
-        result = leftArg > rightArg ? 2 : leftArg < rightArg ? 0 : 1;
+        result = operands[0] > operands[1] ?
+            2 : operands[0] < operands[1] ? 0 : 1;
         break;
     case FN_MULTIPLY:
-        result = (leftArg * rightArg + maxval/2) / maxval;
+        result = sampleProduct(operands, operandCt, maxval);
         break;
     case FN_DIVIDE:
-        result = (rightArg > leftArg) ?
-            (leftArg * maxval + rightArg/2) / rightArg : maxval;
+        result = (operands[1] > operands[0]) ?
+            ROUNDDIV(operands[0] * maxval, operands[1]) : maxval;
         break;
 
     case FN_AND:
-        result = leftArg & rightArg;
+        result = sampleAnd(operands, operandCt);
         break;
     case FN_OR:
-        result = leftArg | rightArg;
+        result = sampleOr(operands, operandCt);
         break;
     case FN_NAND:
-        result = ~(leftArg & rightArg) & maxval;
+        result = sampleNand(operands, operandCt, maxval);
         break;
     case FN_NOR:
-        result = ~(leftArg | rightArg) & maxval;
+        result = sampleNor(operands, operandCt, maxval);
         break;
     case FN_XOR:
-        result = leftArg ^ rightArg;
+        result = sampleXor(operands, operandCt);
         break;
     case FN_SHIFTLEFT:
-        result = (leftArg << rightArg) & maxval;
+        result = (operands[0] << operands[1]) & maxval;
         break;
     case FN_SHIFTRIGHT:
-        result = leftArg >> rightArg;
+        result = operands[0] >> operands[1];
         break;
     default:
         pm_error("Internal error.  applyUnNormalizedFunction() called "
@@ -400,18 +701,22 @@ doUnNormalizedArith(struct pam *  const inpam1P,
    maxval to do the computation without time-consuming normalization of
    sample values.
 -----------------------------------------------------------------------------*/
+    unsigned int const operandCt = 2;
     sample const maxval = outpamP->maxval;
 
     tuple * tuplerow1;
     tuple * tuplerow2;
     tuple * tuplerowOut;
     unsigned int row;
+    sample * operands;
 
     /* Input conditions: */
     assert(inpam1P->maxval == maxval);
     assert(inpam2P->maxval == maxval);
     assert(outpamP->maxval == maxval);
 
+    MALLOCARRAY_NOFAIL(operands, operandCt);
+
     tuplerow1   = pnm_allocpamrow(inpam1P);
     tuplerow2   = pnm_allocpamrow(inpam2P);
     tuplerowOut = pnm_allocpamrow(outpamP);
@@ -428,10 +733,11 @@ doUnNormalizedArith(struct pam *  const inpam1P,
                 unsigned int const plane1 = MIN(outplane, inpam1P->depth-1);
                 unsigned int const plane2 = MIN(outplane, inpam2P->depth-1);
 
+                operands[0] = tuplerow1[col][plane1];
+                operands[1] = tuplerow2[col][plane2];
+
                 tuplerowOut[col][outplane] = 
-                    applyUnNormalizedFunction(function, 
-                                              tuplerow1[col][plane1], 
-                                              tuplerow2[col][plane2],
+                    applyUnNormalizedFunction(function, operands, operandCt,
                                               maxval);
 
                 assert(tuplerowOut[col][outplane] >= 0);
@@ -444,12 +750,14 @@ doUnNormalizedArith(struct pam *  const inpam1P,
     pnm_freepamrow(tuplerow1);
     pnm_freepamrow(tuplerow2);
     pnm_freepamrow(tuplerowOut);
+
+    free(operands);
 }
 
 
 
 int
-main(int argc, char *argv[]) {
+main(int argc, const char *argv[]) {
 
     struct cmdlineInfo cmdline;
     struct pam inpam1;
@@ -458,12 +766,17 @@ main(int argc, char *argv[]) {
     FILE * if1P;
     FILE * if2P;
     
-    pnm_init(&argc, argv);
+    pm_proginit(&argc, argv);
 
     parseCommandLine(argc, argv, &cmdline);
 
-    if1P = pm_openr(cmdline.input1Filespec);
-    if2P = pm_openr(cmdline.input2Filespec);
+    if (cmdline.operandCt != 2)
+        /* Code for > 2 operands not written yet */
+        pm_error("You specified %u operands.  We understand only 2.",
+                 cmdline.operandCt);
+
+    if1P = pm_openr(cmdline.operandFileNames[0]);
+    if2P = pm_openr(cmdline.operandFileNames[1]);
 
     pnm_readpaminit(if1P, &inpam1, PAM_STRUCT_SIZE(tuple_type));
     pnm_readpaminit(if2P, &inpam2, PAM_STRUCT_SIZE(tuple_type));
diff --git a/other/pambayer.c b/other/pambayer.c
index 9c58a3f4..f8ce0db8 100644
--- a/other/pambayer.c
+++ b/other/pambayer.c
@@ -25,6 +25,7 @@
 #include <unistd.h>
 #include <stdio.h>
 
+#include "pm_c_util.h"
 #include "pam.h"
 #include "shhopt.h"
 #include "mallocvar.h"
diff --git a/other/pamchannel.c b/other/pamchannel.c
index ac7bae65..64ab728b 100644
--- a/other/pamchannel.c
+++ b/other/pamchannel.c
@@ -13,6 +13,7 @@
 
 #include <string.h>
 
+#include "pm_c_util.h"
 #include "pam.h"
 #include "shhopt.h"
 #include "mallocvar.h"
diff --git a/other/pamdepth.c b/other/pamdepth.c
index 0c4490ed..ee59a408 100644
--- a/other/pamdepth.c
+++ b/other/pamdepth.c
@@ -11,8 +11,9 @@
 =============================================================================*/
 #include <assert.h>
 
-#include "shhopt.h"
+#include "pm_c_util.h"
 #include "mallocvar.h"
+#include "shhopt.h"
 #include "pam.h"
 
 struct cmdlineInfo {
@@ -89,7 +90,7 @@ createSampleMap(sample   const oldMaxval,
     MALLOCARRAY_NOFAIL(sampleMap, oldMaxval+1);
 
     for (i = 0; i <= oldMaxval; ++i)
-        sampleMap[i] = (i * newMaxval + oldMaxval / 2) / oldMaxval;
+        sampleMap[i] = ROUNDDIV(i * newMaxval, oldMaxval);
 
     *sampleMapP = sampleMap;
 }
diff --git a/other/pamendian.c b/other/pamendian.c
index 16e1fe56..d82ebd38 100644
--- a/other/pamendian.c
+++ b/other/pamendian.c
@@ -1,5 +1,5 @@
 /******************************************************************************
-                              pnmendian
+                              pamendian
 *******************************************************************************
 
   Reverse the endianness of multi-byte samples in a Netpbm stream.
diff --git a/other/pamfixtrunc.c b/other/pamfixtrunc.c
new file mode 100644
index 00000000..6d71406f
--- /dev/null
+++ b/other/pamfixtrunc.c
@@ -0,0 +1,176 @@
+/*============================================================================
+                             pamfixtrunc
+==============================================================================
+  Fix a Netpbm image that has been truncated, e.g. by I/O error.
+
+  By Bryan Henderson, January 2007.
+
+  Contributed to the public domain by its author.
+
+============================================================================*/
+
+#include <setjmp.h>
+
+#include "pm_c_util.h"
+#include "pam.h"
+#include "shhopt.h"
+#include "mallocvar.h"
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * inputFilespec;  /* Filespec of input file */
+    unsigned int verbose;
+};
+
+
+
+static void
+parseCommandLine(int argc, char ** const argv,
+                 struct cmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   Note that the file spec array we return is stored in the storage that
+   was passed to us as the argv array.
+-----------------------------------------------------------------------------*/
+    optEntry * option_def;
+
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    MALLOCARRAY(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+
+    OPTENT3(0, "verbose",   OPT_FLAG,    NULL, &cmdlineP->verbose,       0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We don't parms that are negative numbers */
+
+    optParseOptions3(&argc, argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+    if (argc-1 == 0) 
+        cmdlineP->inputFilespec = "-";
+    else if (argc-1 != 1)
+        pm_error("Program takes zero or one argument (filename).  You "
+                 "specified %d", argc-1);
+    else
+        cmdlineP->inputFilespec = argv[1];
+}
+
+
+
+static unsigned int readErrRow;
+static bool readErrVerbose;
+
+static pm_usererrormsgfn discardMsg;
+
+static void
+discardMsg(const char * const msg) {
+    if (readErrVerbose)
+        pm_message("Error reading row %u: %s", readErrRow, msg);
+}
+
+
+
+static void
+countRows(const struct pam * const inpamP,
+          bool               const verbose,
+          unsigned int *     const goodRowCountP) {
+
+    tuple * tuplerow;
+    unsigned int row;
+    jmp_buf jmpbuf;
+    int rc;
+    unsigned int goodRowCount;
+    
+    tuplerow = pnm_allocpamrow(inpamP);
+
+    pm_setusererrormsgfn(discardMsg);
+
+    rc = setjmp(jmpbuf);
+    if (rc == 0) {
+        pm_setjmpbuf(&jmpbuf);
+
+        readErrVerbose = verbose;
+        goodRowCount = 0;  /* initial value */
+        for (row = 0; row < inpamP->height; ++row) {
+            readErrRow = row;
+            pnm_readpamrow(inpamP, tuplerow);
+            /* The above does not return if it can't read the next row from
+               the file.  Instead, it longjmps out of this loop.
+            */
+            ++goodRowCount;
+        }
+    }
+    *goodRowCountP = goodRowCount;
+
+    pnm_freepamrow(tuplerow);
+}
+
+
+
+static void
+copyGoodRows(const struct pam * const inpamP,
+             FILE *             const ofP,
+             unsigned int       const goodRowCount) {
+
+    struct pam outpam;
+    tuple * tuplerow;
+    unsigned int row;
+
+    outpam = *inpamP;  /* initial value */
+
+    outpam.file = ofP;
+    outpam.height = goodRowCount;
+
+    tuplerow = pnm_allocpamrow(inpamP);
+
+    pnm_writepaminit(&outpam);
+
+    for (row = 0; row < outpam.height; ++row) {
+        pnm_readpamrow(inpamP, tuplerow);
+        pnm_writepamrow(&outpam, tuplerow);
+    }
+    
+    pnm_freepamrow(tuplerow);
+}
+
+
+
+int
+main(int argc, char * argv[]) {
+    struct cmdlineInfo cmdline;
+    struct pam inpam;
+    FILE * ifP;
+    pm_filepos rasterPos;
+    unsigned int goodRowCount;
+
+    pnm_init(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr_seekable(cmdline.inputFilespec);
+
+    pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type));
+
+    pm_tell2(ifP, &rasterPos, sizeof(rasterPos));
+
+    countRows(&inpam, cmdline.verbose, &goodRowCount);
+
+    pm_message("Copying %u good rows; %u bottom rows missing",
+               goodRowCount, inpam.height - goodRowCount);
+    
+    pm_seek2(ifP, &rasterPos, sizeof(rasterPos));
+
+    copyGoodRows(&inpam, stdout, goodRowCount);
+
+    pm_close(inpam.file);
+    
+    return 0;
+}
+
+
diff --git a/other/pamlookup.c b/other/pamlookup.c
index 2651d596..3b7a7f59 100644
--- a/other/pamlookup.c
+++ b/other/pamlookup.c
@@ -13,6 +13,7 @@
 
 ============================================================================*/
 
+#include "pm_c_util.h"
 #include "pam.h"
 #include "shhopt.h"
 #include "pm_system.h"
diff --git a/other/pampick.c b/other/pampick.c
index 5502a3cb..63f32968 100644
--- a/other/pampick.c
+++ b/other/pampick.c
@@ -12,6 +12,7 @@
 #include <string.h>
 #include <stdio.h>
 
+#include "pm_c_util.h"
 #include "pam.h"
 #include "shhopt.h"
 #include "nstring.h"
diff --git a/other/pamsplit.c b/other/pamsplit.c
index b9ff6247..a03353d7 100644
--- a/other/pamsplit.c
+++ b/other/pamsplit.c
@@ -14,6 +14,8 @@
 
 #include <string.h>
 #include <stdio.h>
+
+#include "pm_c_util.h"
 #include "pam.h"
 #include "shhopt.h"
 #include "nstring.h"
diff --git a/other/pamstack.c b/other/pamstack.c
index d9691f74..d826cf1f 100644
--- a/other/pamstack.c
+++ b/other/pamstack.c
@@ -14,6 +14,7 @@
 
 #include <string.h>
 
+#include "pm_c_util.h"
 #include "mallocvar.h"
 #include "nstring.h"
 #include "shhopt.h"
diff --git a/other/pamsummcol.c b/other/pamsummcol.c
index c2c3e46b..c31a9940 100644
--- a/other/pamsummcol.c
+++ b/other/pamsummcol.c
@@ -10,6 +10,7 @@
 
 ******************************************************************************/
 
+#include "pm_c_util.h"
 #include "pam.h"
 #include "shhopt.h"
 #include "mallocvar.h"
diff --git a/other/pamx/Makefile b/other/pamx/Makefile
index baff506d..a40ea3a6 100644
--- a/other/pamx/Makefile
+++ b/other/pamx/Makefile
@@ -5,11 +5,12 @@ endif
 SUBDIR = other/pamx
 VPATH=.:$(SRCDIR)/$(SUBDIR)
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
+EXTERN_INCLUDE =
 ifneq ($(X11LIB),NONE)
   ifneq ($(X11HDR_DIR),)
-    INCLUDES += -I$(X11HDR_DIR)
+    EXTERN_INCLUDES += -I$(X11HDR_DIR)
   endif
 endif
 
@@ -36,7 +37,7 @@ MERGEBINARIES = $(BINARIES)
 
 all: $(BINARIES)
 
-include $(SRCDIR)/Makefile.common
+include $(SRCDIR)/common.mk
 
 pamx: $(PAMX_OBJECTS) $(NETPBMLIB) $(LIBOPT)
 	$(LD) -o $@ $(PAMX_OBJECTS) \
diff --git a/other/pamx/Makefile2 b/other/pamx/Makefile2
deleted file mode 100644
index f69e103f..00000000
--- a/other/pamx/Makefile2
+++ /dev/null
@@ -1,51 +0,0 @@
-# C compiler to use, including special flags.
-CC=gcc
-
-WARNINGS = -Wall -Wmissing-declarations -Wundef -Wimplicit -Wwrite-strings \
-	-Winline \
-	-Wstrict-prototypes -Wmissing-prototypes \
-	-Werror
-
-CFLAGS = $(WARNINGS) -fno-common -g
-INCLUDES = -I /home/bryanh/netpbm/other/importinc
-
-# X11 include and library information.
-X11_LIB_DIR=-L/subsysx/X11R6/lib
-X11_LIB_NAME=-lX11
-NETPBMLIB = /home/bryanh/netpbm/lib/libnetpbm.so
-
-LIBS=$(X11_LIB_DIR) $(X11_LIB_NAME) -lm
-
-default: pamx
-
-# files for the image library
-IMAGE_SRCS= image.c
-IMAGE_OBJS= ${IMAGE_SRCS:.c=.o}
-
-# files for the image processing library
-PROCESS_HDRS=
-# no image processing.
-PROCESS_SRCS= fill.c
-PROCESS_OBJS= ${PROCESS_SRCS:.c=.o}
-
-X_SRCS= send.c window.c pamx.c
-X_OBJS= ${X_SRCS:.c=.o}
-
-OBJS= $(IMAGE_OBJS) $(PROCESS_OBJS) $(X_OBJS) $(NETPBMLIB)
-
-.c.o: $*.c
-	$(CC) -c $(CFLAGS) $(INCLUDES) $*.c $(CADD)
-
-pamx: $(OBJS) $(OPTIONAL_LIBS)
-	$(CC) -o $@ $(OBJS) $(OPTIONAL_LIBS) $(LIBS)
-
-clean::
-	rm -f *.o pamx
-
-dep:
-	$(CC) -MM -MG $(INCLUDES) *.c >Makefile.depend
-
-include Makefile.depend
-
-Makefile.depend:
-	>$@
diff --git a/other/pamx/image.c b/other/pamx/image.c
index 892a9768..0e719438 100644
--- a/other/pamx/image.c
+++ b/other/pamx/image.c
@@ -237,95 +237,3 @@ freeImage(Image * const imageP) {
 
     free(imageP);
 }
-
-
-
-
-static void
-fillRow1(struct pam *     const pamP,
-         tuple *          const tuplerow,
-         unsigned char ** const pP) {
-
-    unsigned int col;
-    
-    for (col = 0; col < pamP->width; ++col) {
-        unsigned int plane;
-        for (plane = 0; plane < pamP->depth; ++plane)
-            *(*pP)++ =
-                pnm_scalesample(tuplerow[col][0], pamP->maxval, 255);
-    }
-}
-
-
-
-static void
-fillRow3(struct pam *     const pamP,
-         tuple *          const tuplerow,
-         unsigned char ** const pP) {
-
-    unsigned int col;
-    
-    for (col = 0; col < pamP->width; ++col) {
-        unsigned int plane;
-        for (plane = 0; plane < pamP->depth; ++plane)
-            *(*pP)++ =
-                pnm_scalesample(tuplerow[col][plane], pamP->maxval, 255);
-    }
-}
-
-
-
-Image *
-pbmLoad(const char * const fullname,
-        const char * const name,
-        bool         const verbose) {
-
-    FILE * ifP;
-    struct pam pam;
-    Image * imageP;
-    unsigned int row;
-    const char * filename;
-    tuple * tuplerow;
-    unsigned char * p;
-    enum {DEPTH_1, DEPTH_3} depth;
-
-    if (STREQ(fullname, "stdin"))
-        filename = "-";
-    else
-        filename = fullname;
-
-    ifP = pm_openr(filename);
-
-    pnm_readpaminit(ifP, &pam, PAM_STRUCT_SIZE(tuple_type));
-
-    if (strncmp(pam.tuple_type, "RGB", 3) == 0) {
-        depth = DEPTH_3;
-        if (pam.depth < 3)
-            pm_error("Invalid depth %u for RGB tuple type.", pam.depth);
-    } else
-        depth = DEPTH_1;
-
-    imageP = newTrueImage(pam.width, pam.height);
-
-    p = &imageP->data[0];  /* initial value */
-
-    tuplerow = pnm_allocpamrow(&pam);
-
-    for (row = 0; row < pam.height; ++row) {
-        pnm_readpamrow(&pam, tuplerow);
-        
-        switch (depth) {
-        case DEPTH_3:
-            fillRow3(&pam, tuplerow, &p);
-            break;
-        case DEPTH_1:
-            fillRow1(&pam, tuplerow, &p);
-            break;
-        }
-    }
-    pnm_freepamrow(tuplerow);
-    
-    pm_close(ifP);
-
-    return imageP;
-}
diff --git a/other/pamx/image.h b/other/pamx/image.h
index ea597b2e..5b352a28 100644
--- a/other/pamx/image.h
+++ b/other/pamx/image.h
@@ -81,9 +81,4 @@ colorIntensity(unsigned int const red,
             BlueIntensity[blu / 256]);
 }
 
-Image *
-pbmLoad(const char * const fullname,
-        const char * const name,
-        bool         const verbose);
-
 #endif
diff --git a/other/pamx/pamx.c b/other/pamx/pamx.c
index 5fd525b7..130cc64c 100644
--- a/other/pamx/pamx.c
+++ b/other/pamx/pamx.c
@@ -9,6 +9,7 @@
 #include <errno.h>
 #include <string.h>
 
+#include "pm_c_util.h"
 #include "pam.h"
 #include "shhopt.h"
 #include "mallocvar.h"
@@ -176,35 +177,32 @@ errorHandler(Display *     const disp,
 
 
 
-static void
-fillRow1(struct pam *     const pamP,
-         tuple *          const tuplerow,
-         unsigned char ** const pP) {
-
-    unsigned int col;
-    
-    for (col = 0; col < pamP->width; ++col) {
-        unsigned int plane;
-        for (plane = 0; plane < 3; ++plane)
-            *(*pP)++ =
-                pnm_scalesample(tuplerow[col][0], pamP->maxval, 255);
-    }
-}
-
-
+enum usableDepth {DEPTH_1, DEPTH_3};
 
 static void
-fillRow3(struct pam *     const pamP,
-         tuple *          const tuplerow,
-         unsigned char ** const pP) {
+fillRow(struct pam *     const pamP,
+        tuple *          const tuplerow,
+        enum usableDepth const depth,
+        unsigned char ** const pP) {
+/*----------------------------------------------------------------------------
+   Add one row to the 24-bit truecolor image data at *pP, and advance
+   *pP just past that row.
 
+   Use either the first plane or the first 3 planes of tuplerow[]
+   for its contents, according to 'depth'.
+-----------------------------------------------------------------------------*/
     unsigned int col;
     
     for (col = 0; col < pamP->width; ++col) {
+        /* Truecolor image data has 3 bytes per pixel, one each for
+           red, green, and blue.
+        */
         unsigned int plane;
-        for (plane = 0; plane < pamP->depth; ++plane)
+        for (plane = 0; plane < 3; ++plane) {
+            unsigned int const tuplePlane = depth == DEPTH_3 ? plane : 0;
             *(*pP)++ =
-                pnm_scalesample(tuplerow[col][plane], pamP->maxval, 255);
+                pnm_scalesample(tuplerow[col][tuplePlane], pamP->maxval, 255);
+        }
     }
 }
 
@@ -219,7 +217,7 @@ loadPamImage(FILE *   const ifP,
     unsigned int row;
     tuple * tuplerow;
     unsigned char * p;
-    enum {DEPTH_1, DEPTH_3} depth;
+    enum usableDepth depth;
 
     pnm_readpaminit(ifP, &pam, PAM_STRUCT_SIZE(tuple_type));
 
@@ -239,12 +237,17 @@ loadPamImage(FILE *   const ifP,
     for (row = 0; row < pam.height; ++row) {
         pnm_readpamrow(&pam, tuplerow);
         
+        /* This semantically wasteful code allows a dumb compiler
+           optimizer to recognize that the depth is constant and
+           therefore not generate code that checks the depth every
+           time it processes a sample.
+        */
         switch (depth) {
         case DEPTH_3:
-            fillRow3(&pam, tuplerow, &p);
+            fillRow(&pam, tuplerow, DEPTH_3, &p);
             break;
         case DEPTH_1:
-            fillRow1(&pam, tuplerow, &p);
+            fillRow(&pam, tuplerow, DEPTH_1, &p);
             break;
         }
     }
@@ -297,7 +300,7 @@ determineTitle(struct cmdlineInfo const cmdline,
     if (cmdline.title)
         title = strdup(cmdline.title);
     else {
-        if (STREQ(cmdline.inputFileName, "-"))
+        if (streq(cmdline.inputFileName, "-"))
             title = NULL;
         else {
             title = pm_basename(cmdline.inputFileName);
diff --git a/other/pamx/window.c b/other/pamx/window.c
index 1a6e510b..2eb48241 100644
--- a/other/pamx/window.c
+++ b/other/pamx/window.c
@@ -6,6 +6,8 @@
    See COPYRIGHT file for copyright information.
 */
 
+#define _BSD_SOURCE    /* Make sure strcaseeq() is in nstring.h */
+
 #include <assert.h>
 #include <ctype.h>
 #include <signal.h>
@@ -555,7 +557,7 @@ visualClassFromName(const char * const name) {
     bool found;
     
     for (a = 0, found = FALSE; VisualClassName[a].name; ++a) {
-        if (STRCASEEQ(VisualClassName[a].name, name)) {
+        if (strcaseeq(VisualClassName[a].name, name)) {
             /* Check for uniqueness.  We special-case StaticGray
                because we have two spellings but they are unique if
                we find either.
diff --git a/other/pnmcolormap.c b/other/pnmcolormap.c
index 1be54ef8..42b03063 100644
--- a/other/pnmcolormap.c
+++ b/other/pnmcolormap.c
@@ -26,11 +26,12 @@
 #include <math.h>
 
 #include "pm_config.h"
-#include "pam.h"
-#include "pammap.h"
-#include "shhopt.h"
+#include "pm_c_util.h"
 #include "mallocvar.h"
 #include "nstring.h"
+#include "shhopt.h"
+#include "pam.h"
+#include "pammap.h"
 
 enum methodForLargest {LARGE_NORM, LARGE_LUM};
 
@@ -380,7 +381,7 @@ averageColors(int          const boxStart,
         for (i = 0; i < boxSize; ++i) 
             sum += colorfreqtable.table[boxStart+i]->tuple[plane];
 
-        newTuple[plane] = sum / boxSize;
+        newTuple[plane] = ROUNDDIV(sum, boxSize);
     }
 }
 
@@ -414,7 +415,7 @@ averagePixels(int          const boxStart,
             sum += colorfreqtable.table[boxStart+i]->tuple[plane]
                 * colorfreqtable.table[boxStart+i]->value;
 
-        newTuple[plane] = sum / n;
+        newTuple[plane] = ROUNDDIV(sum, n);
     }
 }
 
@@ -631,7 +632,7 @@ validateCompatibleImage(struct pam * const inpamP,
     if (inpamP->format != firstPamP->format)
         pm_error("Image %u format (%d) is not the same as Image 0 (%d)",
                  imageSeq, inpamP->format, firstPamP->format);
-    if (!STREQ(inpamP->tuple_type, firstPamP->tuple_type))
+    if (!streq(inpamP->tuple_type, firstPamP->tuple_type))
         pm_error("Image %u tuple type (%s) is not the same as Image 0 (%s)",
                  imageSeq, inpamP->tuple_type, firstPamP->tuple_type);
 }
diff --git a/other/ppmsvgalib.c b/other/ppmsvgalib.c
index 67cc2b1a..5bcabdc1 100644
--- a/other/ppmsvgalib.c
+++ b/other/ppmsvgalib.c
@@ -16,6 +16,8 @@
 #include <errno.h>
 #include <signal.h>
 #include <unistd.h>
+
+#include "pm_c_util.h"
 #include "ppm.h"
 #include "shhopt.h"
 
@@ -249,7 +251,7 @@ main(int argc, char *argv[]) {
     int format;
     int rc;
 
-    ppm_init( &argc, argv );
+    ppm_init(&argc, argv);
 
     rc = vga_init();         /* Initialize. */
     if (rc < 0)
diff --git a/pm_config.in.h b/pm_config.in.h
index 1956f5d3..51a7b966 100644
--- a/pm_config.in.h
+++ b/pm_config.in.h
@@ -58,19 +58,12 @@
 ** path here.  This is used by PPM to parse color names into rgb values.
 ** If you don't have such a file, comment this out and use the alternative
 ** hex and decimal forms to specify colors (see ppm/pgmtoppm.1 for details).  */
-/* There was some evidence before Netpbm 9.1 that the rgb database macros
-   might be already set right now.  I couldn't figure out how, so I changed
-   their meanings and they are now set unconditionally.  -Bryan 00.05.03.
-*/
-#ifdef VMS
-#define RGB_DB1 "PBMplus_Dir:RGB.TXT"
-#define RGB_DB2 "PBMplus_Dir:RGB.TXT"
-#define RGB_DB3 "PBMplus_Dir:RGB.TXT"
-#else
-#define RGB_DB1 "/usr/lib/X11/rgb.txt"
-#define RGB_DB2 "/usr/share/X11/rgb.txt"
-#define RGB_DB3 "/usr/X11R6/lib/X11/rgb.txt"
-#endif
+
+#define RGB_DB_PATH \
+"/usr/share/netpbm/rgb.txt:" \
+"/usr/lib/X11/rgb.txt:" \
+"/usr/share/X11/rgb.txt:" \
+"/usr/X11R6/lib/X11/rgb.txt"
 
 /* CONFIGURE: This is the name of an environment variable that tells
 ** where the color names database is.  If the environment variable isn't
@@ -111,41 +104,6 @@ extern int rand();
 
 #endif /*SYSV or Amiga*/
 
-/* We should change all of Netpbm to use uint32_t instead of uint32n,
-   because we now have a strategy for ensuring that uint32_t is defined.
-   But we're going to wait a while in case our uint32_t strategy doesn't
-   work.  04.08.24.
-*/
-typedef uint32_t uint32n;
-typedef int32_t int32n;
-
-#include <fcntl.h>
-#include <time.h>
-#include <stdlib.h>
-#include <unistd.h>
-/* 
-   Before Netpbm 9.0, atoi() and exit() were declared for everybody
-   except MSDOS and Amiga, and time() and write() were declared for
-   everybody except MSDOS, Amiga, and __osf__.  fcntl.h, time.h, and
-   stlib.h were included for MSDOS and Amiga, and unistd.h was included
-   for everyone except VMS, MSDOS, and Amiga.  With the netbsd patches,
-   atoi(), exit(), time(), and write() were not declared for __NetBSD__.
-
-   We're hoping that all current systems have the standard header
-   files, and will reinstate some of these explicit declarations if we
-   hear otherwise.  
-
-   If it turns out to be this easy, we should just move these inclusions
-   to the source files that actually need them.
-   
-   -Bryan 2000.04.13
-
-extern int atoi();
-extern void exit();
-extern long time();
-extern int write(); 
-*/
-
 /* CONFIGURE: On most BSD systems, malloc() gets declared in stdlib.h, on
 ** system V, it gets declared in malloc.h. On some systems, malloc.h
 ** doesn't declare these, so we have to do it here. On other systems,
@@ -160,50 +118,37 @@ extern int write();
 #if (defined(SYSV) && !defined(VMS))
 #include <malloc.h>
 #endif
-/* extern char* malloc(); */
-/* extern char* realloc(); */
-/* extern char* calloc(); */
-
-/* CONFIGURE: Some systems don't have vfprintf(), which we need for the
-** error-reporting routines.  If you compile and get a link error about
-** this routine, uncomment the first define, which gives you a vfprintf
-** that uses the theoretically non-portable but fairly common routine
-** _doprnt().  If you then get a link error about _doprnt, or
-** message-printing doesn't look like it's working, try the second
-** define instead.
-*/
-/* #define NEED_VFPRINTF1 */
-/* #define NEED_VFPRINTF2 */
-
-/* CONFIGURE: Some systems don't have strstr(), which some routines need.
-** If you compile and get a link error about this routine, uncomment the
-** define, which gives you a strstr.
-*/
-/* #define NEED_STRSTR */
-
-/* CONFIGURE: Set this option if your compiler uses strerror(errno)
-** instead of sys_errlist[errno] for error messages.
-*/
-#define A_STRERROR
 
 /* CONFIGURE: If your system has the setmode() function, set HAVE_SETMODE.
 ** If you do, and also the O_BINARY file mode, pm_init() will set the mode
 ** of stdin and stdout to binary for all Netpbm programs.
 ** You need this with Cygwin (Windows).
 */
-#ifdef __CYGWIN__
+
+#ifdef _WIN32
+#define MSVCRT 1
+#else
+#define MSVCRT 0
+#endif
+
+#if MSVCRT || defined(__CYGWIN__) || defined(DJGPP)
 #define HAVE_SETMODE
 #endif
 
 /* #define HAVE_SETMODE */
 
+#if (defined(__GLIBC__) || defined(__GNU_LIBRARY__) || defined(__APPLE__))
+  #define HAVE_VASPRINTF 1
+#else
+  #define HAVE_VASPRINTF 0
+#endif
+
 #ifdef __amigaos__
 #include <clib/exec_protos.h>
 #define getpid() ((pid_t)FindTask(NULL))
 #endif
 
 #ifdef DJGPP
-#define HAVE_SETMODE
 #define lstat stat
 #endif /* DJGPP */
 
@@ -240,6 +185,60 @@ extern int write();
   #endif
 #endif
 
+/* CONFIGURE: GNUC extensions are used in performance critical places
+   when available.  Test whether they exist.
+
+   Turn off by defining NO_GCC_BUILTINS.
+
+   Note that though these influence the code produced, the compiler
+   setting ultimately decides what operands are used.  If you
+   want a generic build, check the manual and adjust CFLAGS in
+   config.mk accordingly.
+
+   For example, if you want binaries that run on all Intel x86-32
+   family CPUs back to 80386, adding "-march=i386" to CFLAGS in
+   config.mk is much better than setting NO_GCC_BUILTINS to 1.
+   If you want to be extra sure use:
+   "-march=i386 -mno-mmx -mno-sse -DNO_GCC_BUILTINS"
+*/
+
+#if defined(__GNUC__) && !defined(NO_GCC_BUILTINS)
+  #define GCCVERSION __GNUC__*100 + __GNUC_MINOR__
+#else
+  #define GCCVERSION 0
+#endif
+
+#ifndef HAVE_GCC_MMXSSE
+#if GCCVERSION >=301 && defined(__MMX__) && defined(__SSE__)
+  #define HAVE_GCC_MMXSSE 1
+  /* Use GCC builtins to directly access MMX/SSE features */ 
+#else
+  #define HAVE_GCC_MMXSSE 0
+#endif
+#endif
+
+#ifndef HAVE_GCC_BITCOUNT
+#if GCCVERSION >=304
+  #define HAVE_GCC_BITCOUNT 1
+  /* Use __builtin_clz(),  __builtin_ctz() (and variants for long)
+     to count leading/trailing 0s in int (and long). */
+#else
+  #define HAVE_GCC_BITCOUNT 0
+#endif
+#endif
+
+#ifndef HAVE_GCC_BSWAP
+#if GCCVERSION >=403
+  #define HAVE_GCC_BSWAP 1
+  /* Use __builtin_bswap32(), __builtin_bswap64() for endian conversion.
+     Available from GCC v 4.3 onward.
+     NOTE: On intel CPUs this may produce the bswap operand which is not
+     available on 80386. */
+#else
+  #define HAVE_GCC_BSWAP 0
+#endif
+#endif
+
 
 
 /* CONFIGURE: Some systems seem to need more than standard program linkage
@@ -263,6 +262,8 @@ extern int write();
 # endif
 #endif
 
+#include <unistd.h>  /* Get _LFS_LARGEFILE defined */
+#include <sys/types.h>
 /* In GNU, _LFS_LARGEFILE means the "off_t" functions (ftello, etc.) are
    available.  In AIX, _AIXVERSION_430 means it's AIX Version 4.3.0 or
    better, which seems to mean the "off_t" functions are available.
@@ -298,3 +299,10 @@ typedef long int pm_filepos;
 
 typedef int qsort_comparison_fn(const void *, const void *);
     /* A compare function to pass to <stdlib.h>'s qsort() */
+
+#if defined(WIN32) && !defined(__CYGWIN__)
+  #define pm_mkdir(dir, perm) _mkdir(dir)
+#else
+  #define pm_mkdir(dir, perm) mkdir(dir, perm) 
+#endif
+
diff --git a/srcdir.mk b/srcdir.mk
new file mode 100644
index 00000000..484bb420
--- /dev/null
+++ b/srcdir.mk
@@ -0,0 +1,14 @@
+# This is the version of srcdir.mk that gets used when you build in
+# the source tree.  In contrast, when you build in a separate build tree,
+# you use a version of srcdir.mk that Configure created, which sets SRCDIR
+# to the location of the source tree.
+
+# This is designed to be included by the top level make file, with
+# SRCDIR being passed to all the submakes.  That means that when you
+# build in a separate build tree, you must build from the top; you
+# can't go into a subdirectory and type "make".  When you build in the
+# _source_ tree, local makes work because every local make file checks
+# whether SRCDIR is set, and if it isn't, sets it with the assumption
+# that the build directory and source directory are the same.
+
+SRCDIR = $(CURDIR)
diff --git a/urt/Makefile b/urt/Makefile
index 17fd534b..57543b0c 100644
--- a/urt/Makefile
+++ b/urt/Makefile
@@ -5,7 +5,7 @@ endif
 SUBDIR = urt
 VPATH=.:$(SRCDIR)/$(SUBDIR)
 
-include $(BUILDDIR)/Makefile.config
+include $(BUILDDIR)/config.mk
 
 LIBOBJECTS = Runput.o cmd_name.o \
 	rle_addhist.o rle_error.o rle_getcom.o rle_getrow.o rle_getskip.o \
@@ -24,10 +24,11 @@ librle.a: $(LIBOBJECTS)
 
 # Rule for objects.
 $(LIBOBJECTS): %.o: %.c importinc
-	$(CC) -c $(CFLAGS) -I importinc -o $@ $< $(CFLAGS_PERSONAL) $(CADD)
+	$(CC) -c -I importinc -o $@ \
+	  $< $(CPPFLAGS) $(CFLAGS) $(CFLAGS_PERSONAL) $(CADD)
 
 BINARIES =
 SCRIPTS =
 
 OMIT_URT_RULE = 1
-include $(SRCDIR)/Makefile.common
+include $(SRCDIR)/common.mk
diff --git a/urt/rle_open_f.c b/urt/rle_open_f.c
index f2eae4da..07dbea01 100644
--- a/urt/rle_open_f.c
+++ b/urt/rle_open_f.c
@@ -1,10 +1,10 @@
 /* 
  * rle_open_f.c - Open a file with defaults.
  * 
- * Author : 	Jerry Winters 
- * 		EECS Dept.
- * 		University of Michigan
- * Date:	11/14/89
+ * Author :     Jerry Winters 
+ *      EECS Dept.
+ *      University of Michigan
+ * Date:    11/14/89
  * Copyright (c) 1990, University of Michigan
  */
 
@@ -15,19 +15,24 @@
 #include <unistd.h>
 #include <fcntl.h>
 
+#include "pm_c_util.h"
+#include "nstring.h"
 #include "rle.h"
 
+
+
+#define MAX_CHILDREN 100
+    /* Maximum number of children we track; any more than this remain
+       zombies.
+    */
+
+
 #ifndef NO_OPEN_PIPES
-/* Need to have a SIGCLD signal catcher. */
+/* Need to have a SIGCHLD signal catcher. */
 #include <signal.h>
 #include <sys/wait.h>
 #include <errno.h>
 
-/* Count outstanding children.  Assume no more than 100 possible. */
-#define MAX_CHILDREN 100
-static int catching_children = 0;
-static int pids[MAX_CHILDREN];
-
 
 
 static FILE *
@@ -43,55 +48,55 @@ my_popen(const char * const cmd,
     /* Check args. */
     if ( *mode != 'r' && *mode != 'w' )
     {
-	errno = EINVAL;
-	return NULL;
+        errno = EINVAL;
+        return NULL;
     }
 
     if ( pipe(pipefd) < 0 )
-	return NULL;
+        return NULL;
     
     /* Flush known files. */
     fflush(stdout);
     fflush(stderr);
     if ( (thepid = fork()) < 0 )
     {
-	close(pipefd[0]);
-	close(pipefd[1]);
-	return NULL;
+        close(pipefd[0]);
+        close(pipefd[1]);
+        return NULL;
     }
     else if (thepid == 0) {
-	/* In child. */
-	/* Rearrange file descriptors. */
-	if ( *mode == 'r' )
-	{
-	    /* Parent reads from pipe, so reset stdout. */
-	    close(1);
-	    dup2(pipefd[1],1);
-	} else {
-	    /* Parent writing to pipe. */
-	    close(0);
-	    dup2(pipefd[0],0);
-	}
-	/* Close anything above fd 2. (64 is an arbitrary magic number). */
-	for ( i = 3; i < 64; i++ )
-	    close(i);
-
-	/* Finally, invoke the program. */
-	if ( execl("/bin/sh", "sh", "-c", cmd, NULL) < 0 )
-	    exit(127);
-	/* NOTREACHED */
-    }	
+        /* In child. */
+        /* Rearrange file descriptors. */
+        if ( *mode == 'r' )
+        {
+            /* Parent reads from pipe, so reset stdout. */
+            close(1);
+            dup2(pipefd[1],1);
+        } else {
+            /* Parent writing to pipe. */
+            close(0);
+            dup2(pipefd[0],0);
+        }
+        /* Close anything above fd 2. (64 is an arbitrary magic number). */
+        for ( i = 3; i < 64; i++ )
+            close(i);
+
+        /* Finally, invoke the program. */
+        if ( execl("/bin/sh", "sh", "-c", cmd, NULL) < 0 )
+            exit(127);
+        /* NOTREACHED */
+    }   
 
     /* Close file descriptors, and gen up a FILE ptr */
     if ( *mode == 'r' )
     {
-	/* Parent reads from pipe. */
-	close(pipefd[1]);
-	retfile = fdopen( pipefd[0], mode );
+        /* Parent reads from pipe. */
+        close(pipefd[1]);
+        retfile = fdopen( pipefd[0], mode );
     } else {
-	/* Parent writing to pipe. */
-	close(pipefd[0]);
-	retfile = fdopen( pipefd[1], mode );
+        /* Parent writing to pipe. */
+        close(pipefd[0]);
+        retfile = fdopen( pipefd[1], mode );
     }
 
     /* Return the PID. */
@@ -99,8 +104,128 @@ my_popen(const char * const cmd,
 
     return retfile;
 }
+
+
+
+static void
+reapChildren(int *   const catchingChildrenP,
+             pid_t * const pids) {
+
+    /* Check for dead children. */
+
+    if (*catchingChildrenP > 0) {
+        unsigned int i;
+
+        /* Check all children to see if any are dead, reap them if so. */
+        for (i = 0; i < *catchingChildrenP; ++i) {
+            /* The assumption here is that if it's dead, the kill
+             * will fail, but, because we haven't waited for
+             * it yet, it's a zombie.
+             */
+            if (kill(pids[i], 0) < 0) {
+                int opid = pids[i], pid = 0;
+                /* Wait for processes & delete them from the list,
+                 * until we get the one we know is dead.
+                 * When removing one earlier in the list than
+                 * the one we found, decrement our loop index.
+                 */
+                while (pid != opid) {
+                    unsigned int j;
+                    pid = wait(NULL);
+                    for (j = 0;
+                         j < *catchingChildrenP && pids[j] != pid;
+                         ++j)
+                        ;
+                    if (pid < 0)
+                        break;
+                    if (j < *catchingChildrenP) {
+                        if (i >= j)
+                            --i;
+                        for (++j; j < *catchingChildrenP; ++j)
+                            pids[j-1] = pids[j];
+                        --*catchingChildrenP;
+                    }
+                }
+            }
+        }
+    }
+}
 #endif  /* !NO_OPEN_PIPES */
 
+
+
+static void
+dealWithSubprocess(const char *  const file_name,
+                   const char *  const mode,
+                   int *         const catchingChildrenP,
+                   pid_t *       const pids,
+                   FILE **       const fpP,
+                   bool *        const noSubprocessP,
+                   const char ** const errorP) {
+
+#ifdef NO_OPEN_PIPES
+    *noSubprocessP = TRUE;
+#else
+    const char *cp;
+
+    reapChildren(catchingChildrenP, pids);
+
+    /*  Real file, not stdin or stdout.  If name ends in ".Z",
+     *  pipe from/to un/compress (depending on r/w mode).
+     *  
+     *  If it starts with "|", popen that command.
+     */
+        
+    cp = file_name + strlen(file_name) - 2;
+    /* Pipe case. */
+    if (file_name[0] == '|') {
+        pid_t thepid;     /* PID from my_popen */
+
+        *noSubprocessP = FALSE;
+
+        *fpP = my_popen(file_name + 1, mode, &thepid);
+        if (*fpP == NULL)
+            *errorP = "%s: can't invoke <<%s>> for %s: ";
+        else {
+            /* One more child to catch, eventually. */
+            if (*catchingChildrenP < MAX_CHILDREN)
+                pids[(*catchingChildrenP)++] = thepid;
+        }
+    } else if (cp > file_name && *cp == '.' && *(cp + 1) == 'Z' ) {
+        /* Compress case. */
+        pid_t thepid;     /* PID from my_popen. */
+        const char * command;
+
+        *noSubprocessP = FALSE;
+        
+        if (*mode == 'w')
+            asprintfN(&command, "compress > %s", file_name);
+        else if (*mode == 'a')
+            asprintfN(&command, "compress >> %s", file_name);
+        else
+            asprintfN(&command, "compress -d < %s", file_name);
+        
+        *fpP = my_popen(command, mode, &thepid);
+
+        if (*fpP == NULL)
+            *errorP = "%s: can't invoke 'compress' program, "
+                "trying to open %s for %s";
+        else {
+            /* One more child to catch, eventually. */
+            if (*catchingChildrenP < MAX_CHILDREN)
+                pids[(*catchingChildrenP)++] = thepid;
+        }
+        strfree(command);
+    } else {
+        *noSubprocessP = TRUE;
+        *errorP = NULL;
+    }
+#endif
+}
+
+
+
+
 /* 
  *  Purpose : Open a file for input or ouput as controlled by the mode
  *  parameter.  If no file name is specified (ie. file_name is null) then
@@ -113,10 +238,10 @@ my_popen(const char * const cmd,
  *
  *  parameters
  *   input:
- *     prog_name: 	name of the calling program.
- *     file_name : 	name of the file to open
- *     mode : 		either "r" for read or input file or "w" for write or
- *            		output file
+ *     prog_name:   name of the calling program.
+ *     file_name :  name of the file to open
+ *     mode :       either "r" for read or input file or "w" for write or
+ *                  output file
  *
  *   output:
  *     a file pointer
@@ -127,150 +252,63 @@ rle_open_f_noexit(const char * const prog_name,
                   const char * const file_name, 
                   const char * const mode ) {
 
-    FILE *fp;
-    void perror();
-    CONST_DECL char *err_str;
-    const char *cp;
-    char *combuf;
+    FILE * retval;
+    FILE * fp;
+    const char * err_str;
+    int catching_children;
+    pid_t pids[MAX_CHILDREN];
 
-    if ( *mode == 'w' || *mode == 'a' )
+    catching_children = 0;
+
+    if (*mode == 'w' || *mode == 'a')
         fp = stdout;     /* Set the default value */
     else
         fp = stdin;
     
-    if ( file_name != NULL && strcmp( file_name, "-" ) != 0 )
-    {
-#ifndef NO_OPEN_PIPES
-        /* Check for dead children. */
-        if ( catching_children > 0 )
-        {
-            int i, j;
-
-            /* Check all children to see if any are dead, reap them if so. */
-            for ( i = 0; i < catching_children; i++ )
-            {
-                /* The assumption here is that if it's dead, the kill
-                 * will fail, but, because we haven't waited for
-                 * it yet, it's a zombie.
-                 */
-                if (kill(pids[i], 0) < 0) {
-                    int opid = pids[i], pid = 0;
-                    /* Wait for processes & delete them from the list,
-                     * until we get the one we know is dead.
-                     * When removing one earlier in the list than
-                     * the one we found, decrement our loop index.
-                     */
-                    while (pid != opid) {
-                        pid = wait( NULL );
-                        for ( j = 0;
-                              j < catching_children && pids[j] != pid;
-                              j++ )
-                            ;
-                        if ( pid < 0 )
-                            break;
-                        if ( j < catching_children ) {
-                            if ( i >= j )
-                                i--;
-                            for ( j++; j < catching_children; j++ )
-                                pids[j-1] = pids[j];
-                            catching_children--;
-                        }
-                    }
-                }
-            }
-        }
-
-        /*  Real file, not stdin or stdout.  If name ends in ".Z",
-         *  pipe from/to un/compress (depending on r/w mode).
-         *  
-         *  If it starts with "|", popen that command.
-         */
-	    
-        cp = file_name + strlen( (char*) file_name ) - 2;
-        /* Pipe case. */
-        if ( *file_name == '|' )
-        {
-            int thepid;		/* PID from my_popen */
-            if ( (fp = my_popen( file_name + 1, mode, &thepid )) == NULL )
-            {
-                err_str = "%s: can't invoke <<%s>> for %s: ";
-                goto err;
-            }
-            /* One more child to catch, eventually. */
-            if (catching_children < MAX_CHILDREN) {
-                pids[catching_children++] = thepid;
-            }
-        }
-
-        /* Compress case. */
-        else if ( cp > file_name && *cp == '.' && *(cp + 1) == 'Z' )
-        {
-            int thepid;		/* PID from my_popen. */
-            combuf = (char *)malloc( 20 + strlen( file_name ) );
-            if ( combuf == NULL )
-            {
-                err_str = "%s: out of memory opening (compressed) %s for %s";
-                goto err;
-            }
-
-            if ( *mode == 'w' )
-                sprintf( combuf, "compress > %s", file_name );
-            else if ( *mode == 'a' )
-                sprintf( combuf, "compress >> %s", file_name );
-            else
-                sprintf( combuf, "compress -d < %s", file_name );
-
-            fp = my_popen( combuf, mode, &thepid );
-            free( combuf );
-
-            if ( fp == NULL )
-            {
-                err_str =
-                    "%s: can't invoke 'compress' program, "
-                    "trying to open %s for %s";
-                goto err;
-            }
-            /* One more child to catch, eventually. */
-            if (catching_children < MAX_CHILDREN) {
-                pids[catching_children++] = thepid;
-            }
-        }
-        else
-#endif /* !NO_OPEN_PIPES */
-        {
-            /* Ordinary, boring file case. */
-            /* In the original code, the code to add the "b" was
-               conditionally included only if the macro STDIO_NEEDS_BINARY was
-               defined.  But for Netpbm, there is no need make a distinction;
-               we always add the "b".  -BJH 2000.07.20.  
-            */
-            char mode_string[32];	/* Should be enough. */
-
-            /* Concatenate a 'b' onto the mode. */
-            mode_string[0] = mode[0];
-            mode_string[1] = 'b';
-            strcpy( mode_string + 2, mode + 1 );
+    if (file_name != NULL && !streq(file_name, "-")) {
+        bool noSubprocess;
+        dealWithSubprocess(file_name, mode, &catching_children, pids,
+                           &fp, &noSubprocess, &err_str);
+        
+        if (!err_str) {
+            if (noSubprocess) {
+                /* Ordinary, boring file case. */
+                /* In the original code, the code to add the "b" was
+                   conditionally included only if the macro
+                   STDIO_NEEDS_BINARY was defined.  But for Netpbm,
+                   there is no need make a distinction; we always add
+                   the "b".  -BJH 2000.07.20.
+                */
+                char mode_string[32];   /* Should be enough. */
+
+                /* Concatenate a 'b' onto the mode. */
+                mode_string[0] = mode[0];
+                mode_string[1] = 'b';
+                strcpy( mode_string + 2, mode + 1 );
         
-            if ( (fp = fopen(file_name, mode_string)) == NULL )
-            {
-                err_str = "%s: can't open %s for %s: ";
-                goto err;
+                fp = fopen(file_name, mode_string);
+                if (fp == NULL )
+                    err_str = "%s: can't open %s for %s: ";
             }
         }
-    }
-
-    return fp;
+    } else
+        err_str = NULL;
+
+    if (err_str) {
+        fprintf(stderr, err_str,
+                prog_name, file_name,
+                (*mode == 'w') ? "output" :
+                (*mode == 'a') ? "append" :
+                "input" );
+        fprintf(stderr, "errno = %d (%s)\n", errno, strerror(errno));
+        retval = NULL;
+    } else
+        retval = fp;
+
+    return retval;
+}
 
-err:
-	fprintf( stderr, err_str,
-             prog_name, file_name,
-             (*mode == 'w') ? "output" :
-             (*mode == 'a') ? "append" :
-             "input" );
-	perror( "" );
-	return NULL;
 
-}
 
 FILE *
 rle_open_f(const char * prog_name, const char * file_name, const char * mode)
@@ -278,7 +316,7 @@ rle_open_f(const char * prog_name, const char * file_name, const char * mode)
     FILE *fp;
 
     if ( (fp = rle_open_f_noexit( prog_name, file_name, mode )) == NULL )
-	exit( -1 );
+        exit( -1 );
 
     return fp;
 }
@@ -290,21 +328,21 @@ rle_open_f(const char * prog_name, const char * file_name, const char * mode)
  * Close a file opened by rle_open_f.  If the file is stdin or stdout,
  * it will not be closed.
  * Inputs:
- * 	fd:	File to close.
+ *  fd: File to close.
  * Outputs:
- * 	None.
+ *  None.
  * Assumptions:
- * 	fd is open.
+ *  fd is open.
  * Algorithm:
- * 	If fd is NULL, just return.
- * 	If fd is stdin or stdout, don't close it.  Otherwise, call fclose.
+ *  If fd is NULL, just return.
+ *  If fd is stdin or stdout, don't close it.  Otherwise, call fclose.
  */
 void
 rle_close_f( fd )
-FILE *fd;
+    FILE *fd;
 {
     if ( fd == NULL || fd == stdin || fd == stdout )
-	return;
+        return;
     else
-	fclose( fd );
+        fclose( fd );
 }
diff --git a/urt/scanargs.c b/urt/scanargs.c
index 416f2380..b91f3e37 100644
--- a/urt/scanargs.c
+++ b/urt/scanargs.c
@@ -49,9 +49,9 @@
 #include <stdarg.h>
 #endif
 
+#include "pm_c_util.h"
 #include "nstring.h"
 
-typedef char bool;
 /* 
  * An explicit assumption is made in this code that all pointers look
  * alike, except possible char * pointers.
diff --git a/version.mk b/version.mk
new file mode 100644
index 00000000..d17e51f8
--- /dev/null
+++ b/version.mk
@@ -0,0 +1,3 @@
+NETPBM_MAJOR_RELEASE = 10
+NETPBM_MINOR_RELEASE = 47
+NETPBM_POINT_RELEASE = 59