about summary refs log tree commit diff
path: root/converter
diff options
context:
space:
mode:
Diffstat (limited to 'converter')
-rwxr-xr-xconverter/other/anytopnm2
-rw-r--r--converter/other/giftopnm.c35
-rw-r--r--converter/other/pngtopam.c14
-rw-r--r--converter/other/pnmtopalm/pnmtopalm.c6
-rw-r--r--converter/other/pnmtops.c16
-rw-r--r--converter/pgm/fstopgm.c235
-rw-r--r--converter/ppm/ilbmtoppm.c293
-rw-r--r--converter/ppm/picttoppm.c32
-rw-r--r--converter/ppm/ppmtoarbtxt.c1525
-rw-r--r--converter/ppm/ppmtobmp.c14
10 files changed, 1440 insertions, 732 deletions
diff --git a/converter/other/anytopnm b/converter/other/anytopnm
index acf88136..f3a00793 100755
--- a/converter/other/anytopnm
+++ b/converter/other/anytopnm
@@ -379,7 +379,7 @@ case "$2" in
         ;;
 
     gif )
-        giftopnm "$file"
+        giftopnm -image=all "$file"
         ;;
 
     tiff )
diff --git a/converter/other/giftopnm.c b/converter/other/giftopnm.c
index 9a543532..9f4bc8a1 100644
--- a/converter/other/giftopnm.c
+++ b/converter/other/giftopnm.c
@@ -1674,6 +1674,28 @@ readImageData(FILE *       const ifP,
 
 
 static void
+warnUserNotSquare(unsigned int const aspectRatio) {
+
+    const char * baseMsg =
+        "warning - input pixels are not square, "
+        "but we are rendering them as square pixels "
+        "in the output";
+
+    if (pm_have_float_format()) {
+        float const r = ((float)aspectRatio + 15.0 ) / 64.0;
+
+        pm_message("%s.  To fix the output, run it through "
+                   "'pamscale -%cscale %g'",
+                   baseMsg,
+                   r < 1.0 ? 'x' : 'y',
+                   r < 1.0 ? 1.0 / r : r );
+    } else
+        pm_message("%s", baseMsg);
+}
+
+
+
+static void
 readGifHeader(FILE *             const gifFileP,
               struct gifScreen * const gifScreenP) {
 /*----------------------------------------------------------------------------
@@ -1734,17 +1756,8 @@ readGifHeader(FILE *             const gifFileP,
         }
     }
     
-    if (gifScreenP->AspectRatio != 0 && gifScreenP->AspectRatio != 49) {
-        float   r;
-        r = ( (float) gifScreenP->AspectRatio + 15.0 ) / 64.0;
-        pm_message("warning - input pixels are not square, "
-                   "but we are rendering them as square pixels "
-                   "in the output.  "
-                   "To fix the output, run it through "
-                   "'pnmscale -%cscale %g'",
-                   r < 1.0 ? 'x' : 'y',
-                   r < 1.0 ? 1.0 / r : r );
-    }
+    if (gifScreenP->AspectRatio != 0 && gifScreenP->AspectRatio != 49)
+        warnUserNotSquare(gifScreenP->AspectRatio);
 }
 
 
diff --git a/converter/other/pngtopam.c b/converter/other/pngtopam.c
index e6e68587..59b29f5f 100644
--- a/converter/other/pngtopam.c
+++ b/converter/other/pngtopam.c
@@ -1243,10 +1243,16 @@ warnNonsquarePixels(struct pngx * const pngxP,
             (float)pngx_xPixelsPerMeter(pngxP) / pngx_yPixelsPerMeter(pngxP);
 
         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 );
+            const char * const baseMsg = "warning - non-square pixels";
+
+            if (pm_have_float_format())
+                pm_message("%s; to fix do a 'pamscale -%cscale %g'",
+                           baseMsg,
+                           r < 1.0 ? 'x' : 'y',
+                           r < 1.0 ? 1.0 / r : r);
+            else
+                pm_message("%s", baseMsg);
+
             *errorLevelP = PNMTOPNG_WARNING_LEVEL;
         }
     }
diff --git a/converter/other/pnmtopalm/pnmtopalm.c b/converter/other/pnmtopalm/pnmtopalm.c
index 67a17e54..7740a3ac 100644
--- a/converter/other/pnmtopalm/pnmtopalm.c
+++ b/converter/other/pnmtopalm/pnmtopalm.c
@@ -33,8 +33,8 @@ struct cmdline_info {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    const char *inputFilespec;  /* Filespecs of input files */
-    char *transparent;          /* -transparent value.  Null if unspec */
+    const char * inputFilespec;  /* Filespecs of input files */
+    const char * transparent;    /* -transparent value.  Null if unspec */
     unsigned int depth;         /* -depth value.  0 if unspec */
     unsigned int maxdepth;      /* -maxdepth value.  0 if unspec */
     enum compressionType compression;
@@ -289,7 +289,7 @@ formatName(int const format) {
         
 
 static void
-findTransparentColor(char *         const colorSpec, 
+findTransparentColor(const char *   const colorSpec, 
                      pixval         const newMaxval,
                      bool           const directColor, 
                      pixval         const maxval, 
diff --git a/converter/other/pnmtops.c b/converter/other/pnmtops.c
index 316b7626..cf6b2873 100644
--- a/converter/other/pnmtops.c
+++ b/converter/other/pnmtops.c
@@ -1125,6 +1125,19 @@ validateComputableBoundingBox(float const scols,
 
 
 static void
+warnUserRescaling(float const scale) {
+
+    const char * const baseMsg = "warning, image too large for page";
+
+    if (pm_have_float_format())
+        pm_message("%s; rescaling to %g", baseMsg, scale);
+    else
+        pm_message("%s; rescaling", baseMsg);
+}
+
+
+
+static void
 computeImagePosition(int     const dpiX, 
                      int     const dpiY, 
                      int     const icols, 
@@ -1231,8 +1244,7 @@ computeImagePosition(int     const dpiX,
         *srowsP = scale * rows * pixfacY;
     
         if (scale != requestedScale)
-            pm_message("warning, image too large for page, rescaling to %g", 
-                       scale );
+            warnUserRescaling(scale);
 
         /* Before May 2001, Pnmtops enforced a 5% margin around the page.
            If the image would be too big to leave a 5% margin, Pnmtops would
diff --git a/converter/pgm/fstopgm.c b/converter/pgm/fstopgm.c
index 2c636f41..1f574604 100644
--- a/converter/pgm/fstopgm.c
+++ b/converter/pgm/fstopgm.c
@@ -12,127 +12,144 @@
 
 #include <string.h>
 
+#include "pm.h"
 #include "pgm.h"
 
-static int gethexit ARGS(( FILE* ifp ));
+
+
+static int
+gethexit(FILE * const ifP) {
+
+    for ( ; ; ) {
+        unsigned int const i = getc(ifP);
+
+        if (i == EOF)
+            pm_error("EOF / read error");
+        else {
+            char const c = (char) i;
+            if (c >= '0' && c <= '9')
+                return c - '0';
+            else if (c >= 'A' && c <= 'F')
+                return c - 'A' + 10;
+            else if (c >= 'a' && c <= 'f')
+                return c - 'a' + 10;
+            else {
+                /* Ignore - whitespace. */
+            }
+        }
+    }
+}
+
+
+
+static void
+warnNonsquarePixels(unsigned int const cols,
+                    unsigned int const xcols,
+                    unsigned int const rows,
+                    unsigned int const xrows) {
+    
+    const char * const baseMsg = "warning, non-square pixels";
+
+    if (pm_have_float_format()) {
+        float const rowratio = (float) xrows / (float) rows;
+        float const colratio = (float) xcols / (float) cols;
+
+        pm_message("%s; to fix do a 'pamscale -%cscale %g'",
+                   baseMsg,
+                   rowratio > colratio ? 'y' : 'x',
+                   rowratio > colratio ? 
+                   rowratio / colratio : colratio / rowratio);
+    } else
+        pm_message("%s", baseMsg);
+}
+
+
 
 int
-main( argc, argv )
-int argc;
-char *argv[];
-    {
-    FILE *ifp;
-    register gray **grays, *gP;
-    int argn, row;
-    register int col;
-    int maxval;
-    int rows = 0, cols = 0, depth = 0, xrows = 0, xcols = 0, xdepth = 0;
+main(int argc, const char ** argv) {
+
+    FILE * ifP;
+    gray ** grays;
+    int argn;
+    int row;
+    gray maxval;
+    int rows, cols, depth;
+    int xrows, xcols, xdepth;
 #define STRSIZE 1000
-    char buf[STRSIZE], firstname[STRSIZE], lastname[STRSIZE], email[STRSIZE];
 
+    pm_proginit(&argc, argv);
 
-    pgm_init( &argc, argv );
+    rows = 0;
+    cols = 0;
+    depth = 0;
+
+    xrows = 0;
+    xcols = 0;
+    xdepth = 0;
 
     argn = 1;
 
-    if ( argn < argc )
-	{
-	ifp = pm_openr( argv[argn] );
-	argn++;
-	}
-    else
-	ifp = stdin;
+    if (argn < argc) {
+        ifP = pm_openr(argv[argn]);
+        argn++;
+    } else
+        ifP = stdin;
 
-    if ( argn != argc )
-	pm_usage( "[fsfile]" );
+    if (argn != argc)
+        pm_error("Too many arguments.  The only argument is the file name");
 
     /* Read the FaceSaver(tm) header. */
-    for ( ; ; )
-	{
-	if ( fgets( buf, STRSIZE, ifp ) == (char *) 0 )
-	    pm_error( "error reading header" );
-
-	/* Blank line ends header. */
-	if ( strlen( buf ) == 1 )
-	    break;
-
-	if ( sscanf( buf, "FirstName: %[^\n]", firstname ) == 1 )
-	    ;
-	else if ( sscanf( buf, "LastName: %[^\n]", lastname ) == 1 )
-	    ;
-	else if ( sscanf( buf, "E-mail: %[^\n]", email ) == 1 )
-	    ;
-	else if ( sscanf( buf, "PicData: %d %d %d\n",
-			  &cols, &rows, &depth ) == 3 )
-	    {
-	    if ( depth != 8 )
-		pm_error(
-		    "can't handle 'PicData' depth other than 8" );
-	    }
-	else if ( sscanf( buf, "Image: %d %d %d\n",
-			  &xcols, &xrows, &xdepth ) == 3 )
-	    {
-	    if ( xdepth != 8 )
-		pm_error(
-		    "can't handle 'Image' depth other than 8" );
-	    }
-	}
-    if ( cols <= 0 || rows <= 0 )
-	pm_error( "invalid header" );
-    maxval = pm_bitstomaxval( depth );
-    if ( maxval > PGM_OVERALLMAXVAL )
-	pm_error( "depth %d is too large.  Our maximum is %d",
-              maxval, PGM_OVERALLMAXVAL);
-    if ( xcols != 0 && xrows != 0 && ( xcols != cols || xrows != rows ) )
-	{
-	float rowratio, colratio;
-
-	rowratio = (float) xrows / (float) rows;
-	colratio = (float) xcols / (float) cols;
-	pm_message(
-	    "warning, non-square pixels; to fix do a 'pamscale -%cscale %g'",
-	    rowratio > colratio ? 'y' : 'x',
-	    rowratio > colratio ? rowratio / colratio : colratio / rowratio );
-	}
-
-    /* Now read the hex bits. */
-    grays = pgm_allocarray( cols, rows );
-    for ( row = rows - 1; row >= 0; row--)
-	{
-	for ( col = 0, gP = grays[row]; col < cols; col++, gP++ )
-	    {
-	    *gP = gethexit( ifp ) << 4;
-	    *gP += gethexit( ifp );
-	    }
-	}
-    pm_close( ifp );
-
-    /* And write out the graymap. */
-    pgm_writepgm( stdout, grays, cols, rows, (gray) maxval, 0 );
-    pm_close( stdout );
-
-    exit( 0 );
+    for ( ; ; ) {
+        char buf[STRSIZE];
+        char firstname[STRSIZE];
+        char lastname[STRSIZE];
+        char email[STRSIZE];
+
+        char * const rc = fgets(buf, STRSIZE, ifP);
+
+        if (rc  == NULL)
+            pm_error("error reading header");
+
+        /* Blank line ends header. */
+        if (strlen(buf) == 1)
+            break;
+
+        if (sscanf(buf, "FirstName: %[^\n]", firstname) == 1);
+        else if (sscanf(buf, "LastName: %[^\n]", lastname) == 1);
+        else if (sscanf(buf, "E-mail: %[^\n]", email ) == 1);
+        else if (sscanf(buf, "PicData: %d %d %d\n",
+                        &cols, &rows, &depth ) == 3) {
+            if (depth != 8)
+                pm_error("can't handle 'PicData' depth other than 8");
+        } else if (sscanf(buf, "Image: %d %d %d\n",
+                          &xcols, &xrows, &xdepth ) == 3) {
+            if (xdepth != 8)
+                pm_error("can't handle 'Image' depth other than 8");
+        }
     }
-
-static int
-gethexit( ifp )
-FILE *ifp;
-    {
-    register int i;
-    register char c;
-
-    for ( ; ; )
-	{
-	i = getc( ifp );
-	if ( i == EOF )
-	    pm_error( "EOF / read error" );
-	c = (char) i;
-	if ( c >= '0' && c <= '9' )
-	    return c - '0';
-	else if ( c >= 'A' && c <= 'F' )
-	    return c - 'A' + 10;
-	else if ( c >= 'a' && c <= 'f' )
-	    return c - 'a' + 10;
-	/* Else ignore - whitespace. */
-	}
+    if (cols <= 0 || rows <= 0)
+        pm_error("invalid header");
+    maxval = pm_bitstomaxval(depth);
+    if (maxval > PGM_OVERALLMAXVAL)
+        pm_error("depth %d is too large.  Our maximum is %d",
+                 maxval, PGM_OVERALLMAXVAL);
+    if (xcols != 0 && xrows != 0 && (xcols != cols || xrows != rows))
+        warnNonsquarePixels(cols, xcols, rows, xrows);
+
+    /* Read the hex bits. */
+    grays = pgm_allocarray(cols, rows);
+    for (row = rows - 1; row >= 0; --row) {
+        unsigned int col;
+        for (col = 0; col < cols; ++col) {
+            grays[row][col] =  gethexit(ifP) << 4;
+            grays[row][col] += gethexit(ifP);
+        }
     }
+    pm_close(ifP);
+
+    pgm_writepgm(stdout, grays, cols, rows, maxval, 0);
+    pm_close(stdout);
+
+    return 0;
+}
+
diff --git a/converter/ppm/ilbmtoppm.c b/converter/ppm/ilbmtoppm.c
index a898d705..662be0b5 100644
--- a/converter/ppm/ilbmtoppm.c
+++ b/converter/ppm/ilbmtoppm.c
@@ -89,7 +89,7 @@ static unsigned char *ilbmrow;
 static pixel *pixelrow;
 static FILE *maskfile = NULL;
 static bit *maskrow = NULL;
-static short wrotemask = 0;
+static bool wrotemask;
 static IFF_ID typeid;       /* ID_ILBM, ID_RGBN, ID_RGB8 */
 
 static char *transpName = NULL;  /* -transparent option value */
@@ -191,8 +191,8 @@ read_bytes(FILE *          const ifP,
 
 
 static unsigned char
-get_byte(ifp, iffid, counter)
-    FILE* ifp;
+get_byte(ifP, iffid, counter)
+    FILE* ifP;
     IFF_ID iffid;
     long *counter;
 {
@@ -203,9 +203,9 @@ get_byte(ifp, iffid, counter)
             pm_error("insufficient data in %s chunk", ID2string(iffid));
         --(*counter);
     }
-    i = getc(ifp);
+    i = getc(ifP);
     if( i == EOF )
-        readerr(ifp, iffid);
+        readerr(ifP, iffid);
 
     return (unsigned char) i;
 }
@@ -310,7 +310,7 @@ display_chunk(FILE *        const ifP,
 
 
 static void
-read_cmap(FILE *     const ifp,
+read_cmap(FILE *     const ifP,
           IFF_ID     const iffid,
           long       const chunksize,
           ColorMap * const cmap) {
@@ -320,7 +320,7 @@ read_cmap(FILE *     const ifp,
     colors = chunksize / 3;
     if( colors == 0 ) {
         pm_error("warning - empty %s colormap", ID2string(iffid));
-        skip_chunk(ifp, iffid, chunksize);
+        skip_chunk(ifP, iffid, chunksize);
     } else {
         unsigned int i;
         if( cmap->color )               /* prefer CMAP-chunk over CMYK-chunk */
@@ -330,30 +330,30 @@ read_cmap(FILE *     const ifp,
         
         for( i = 0; i < colors; ++i ) {
             int r, g, b;
-            r = get_byte(ifp, iffid, &chunksize);
-            g = get_byte(ifp, iffid, &chunksize);
-            b = get_byte(ifp, iffid, &chunksize);
+            r = get_byte(ifP, iffid, &chunksize);
+            g = get_byte(ifP, iffid, &chunksize);
+            b = get_byte(ifP, iffid, &chunksize);
             PPM_ASSIGN(cmap->color[i], r, g, b);
         }
-        chunk_end(ifp, iffid, chunksize);
+        chunk_end(ifP, iffid, chunksize);
     }
 }
 
 
 
 static void
-read_cmyk(FILE *     const ifp,
+read_cmyk(FILE *     const ifP,
           IFF_ID     const iffid,
           long       const chunksize,
           ColorMap * const cmap) {
 
     if( HAS_COLORMAP(cmap) ) {      /* prefer RGB color map */
-        skip_chunk(ifp, iffid, chunksize);
+        skip_chunk(ifP, iffid, chunksize);
     } else {
         long const colors = chunksize/4;
         if( colors == 0 ) {
             pm_error("warning - empty %s colormap", ID2string(iffid));
-            skip_chunk(ifp, iffid, chunksize);
+            skip_chunk(ifP, iffid, chunksize);
         } else {
             unsigned int i;
             cmap->color = ppm_allocrow(colors);
@@ -361,10 +361,10 @@ read_cmyk(FILE *     const ifp,
             
             for( i = 0; i < colors; ++i ) {
                 int c, m, y, k;
-                c = get_byte(ifp, iffid, &chunksize);
-                m = get_byte(ifp, iffid, &chunksize);
-                y = get_byte(ifp, iffid, &chunksize);
-                k = get_byte(ifp, iffid, &chunksize);
+                c = get_byte(ifP, iffid, &chunksize);
+                m = get_byte(ifP, iffid, &chunksize);
+                y = get_byte(ifP, iffid, &chunksize);
+                k = get_byte(ifP, iffid, &chunksize);
 
                 {
                     pixval const red = 
@@ -380,7 +380,7 @@ read_cmyk(FILE *     const ifp,
                     PPM_ASSIGN(cmap->color[i], red, green, blue);
                 }
             }
-            chunk_end(ifp, iffid, chunksize);
+            chunk_end(ifP, iffid, chunksize);
         }
     }
 }
@@ -388,7 +388,7 @@ read_cmyk(FILE *     const ifp,
 
 
 static void
-read_clut(FILE *        const ifp,
+read_clut(FILE *        const ifP,
           IFF_ID        const iffid,
           unsigned long const chunksize,
           ColorMap *    const cmap) {
@@ -396,19 +396,19 @@ read_clut(FILE *        const ifp,
     if (chunksize != CLUTSize) {
         pm_message("invalid size for %s chunk - skipping it", 
                    ID2string(iffid));
-        skip_chunk(ifp, iffid, chunksize);
+        skip_chunk(ifP, iffid, chunksize);
     } else {
         long type;
         unsigned char * lut;
         unsigned long remainingChunksize;
         unsigned int i;
 
-        type = get_big_long(ifp, iffid, &remainingChunksize);
-        get_big_long(ifp, iffid, &remainingChunksize); /* skip reserved fld */
+        type = get_big_long(ifP, iffid, &remainingChunksize);
+        get_big_long(ifP, iffid, &remainingChunksize); /* skip reserved fld */
 
         MALLOCARRAY_NOFAIL(lut, 256);
         for( i = 0; i < 256; ++i )
-            lut[i] = get_byte(ifp, iffid, &remainingChunksize);
+            lut[i] = get_byte(ifP, iffid, &remainingChunksize);
 
         switch( type ) {
         case CLUT_MONO:
@@ -433,6 +433,27 @@ read_clut(FILE *        const ifp,
 
 
 
+static void
+warnNonsquarePixels(uint8_t const xAspect,
+                    uint8_t const yAspect) {
+
+    if (xAspect != yAspect) {
+        const char * const baseMsg = "warning - non-square pixels";
+
+        if (pm_have_float_format())
+            pm_message("%s; to fix do a 'pamscale -%cscale %g'",
+                       baseMsg,
+                       xAspect > yAspect ? 'x' : 'y',
+                       xAspect > yAspect ? 
+                       (float)xAspect/yAspect : 
+                       (float)yAspect/xAspect);
+        else
+            pm_message("%s", baseMsg);
+    }
+}
+
+
+
 static BitMapHeader *
 read_bmhd(FILE *        const ifP,
           IFF_ID        const iffid,
@@ -440,7 +461,7 @@ read_bmhd(FILE *        const ifP,
 
     BitMapHeader * bmhdP;
 
-    if( chunksize != BitMapHeaderSize ) {
+    if (chunksize != BitMapHeaderSize) {
         pm_message("invalid size for %s chunk - skipping it", 
                    ID2string(iffid));
         skip_chunk(ifP, iffid, chunksize);
@@ -467,24 +488,24 @@ read_bmhd(FILE *        const ifP,
         bmhdP->pageWidth  = get_big_short(ifP, iffid, &remainingChunksize);
         bmhdP->pageHeight = get_big_short(ifP, iffid, &remainingChunksize);
 
-        if( verbose ) {
-            if( typeid == ID_ILBM )
+        if (verbose) {
+            if (typeid == ID_ILBM)
                 pm_message("dimensions: %dx%d, %d planes", 
                            bmhdP->w, bmhdP->h, bmhdP->nPlanes);
             else
                 pm_message("dimensions: %dx%d", bmhdP->w, bmhdP->h);
 
-            if( typeid == ID_ILBM || typeid == ID_PBM ) {
+            if (typeid == ID_ILBM || typeid == ID_PBM) {
                 pm_message("compression: %s",
                            bmhdP->compression <= cmpMAXKNOWN ?
                            cmpNAME[bmhdP->compression] : "unknown");
 
-                switch( bmhdP->masking ) {
+                switch(bmhdP->masking) {
                 case mskNone:
                     break;
                 case mskHasMask:
                 case mskHasTransparentColor:
-                    if( !maskfile )
+                    if (!maskfile)
                         pm_message("use '-maskfile <filename>' "
                                    "to generate a PBM mask file from %s", 
                                    mskNAME[bmhdP->masking]);
@@ -498,27 +519,20 @@ read_bmhd(FILE *        const ifP,
                 }
             }
             else    /* RGBN/RGB8 */
-                if( !maskfile )
+                if (!maskfile)
                     pm_message("use '-maskfile <filename>' "
                                "to generate a PBM mask file "
                                "from genlock bits");
         }
 
         /* fix aspect ratio */
-        if( bmhdP->xAspect == 0 || bmhdP->yAspect == 0 ) {
+        if (bmhdP->xAspect == 0 || bmhdP->yAspect == 0) {
             pm_message("warning - illegal aspect ratio %d:%d, using 1:1", 
                        bmhdP->xAspect, bmhdP->yAspect);
             bmhdP->xAspect = bmhdP->yAspect = 1;
         }
 
-        if( bmhdP->xAspect != bmhdP->yAspect ) {
-            pm_message("warning - non-square pixels; "
-                       "to fix do a 'pnmscale -%cscale %g'",
-                       bmhdP->xAspect > bmhdP->yAspect ? 'x' : 'y',
-                       bmhdP->xAspect > bmhdP->yAspect ? 
-                       (float)(bmhdP->xAspect)/bmhdP->yAspect : 
-                       (float)(bmhdP->yAspect)/bmhdP->xAspect);
-        }
+        warnNonsquarePixels(bmhdP->xAspect, bmhdP->yAspect);
     }
     return bmhdP;
 }
@@ -627,42 +641,42 @@ decode_mask(FILE *          const ifP,
     unsigned char *ilp;
 
     cols = bmhdP->w;
-    switch( bmhdP->masking ) {
+    switch (bmhdP->masking) {
     case mskNone:
         break;
     case mskHasMask:        /* mask plane */
         read_ilbm_plane(ifP, remainingChunksizeP, RowBytes(cols), 
                         bmhdP->compression);
-        if( maskfile ) {
+        if (maskfile) {
             ilp = ilbmrow;
             cbit = 7;
-            for( col = 0; col < cols; col++, cbit-- ) {
-                if( cbit < 0 ) {
+            for (col = 0; col < cols; ++col, --cbit) {
+                if (cbit < 0) {
                     cbit = 7;
-                    ilp++;
+                    ++ilp;
                 }
-                if( *ilp & bit_mask[cbit] )
+                if (*ilp & bit_mask[cbit])
                     maskrow[col] = PBM_BLACK;
                 else
                     maskrow[col] = PBM_WHITE;
             }
             pbm_writepbmrow(maskfile, maskrow, cols, 0);
-            wrotemask = 1;
+            wrotemask = true;
         }
         break;
     case mskHasTransparentColor:
-        if( !chunkyrow )
+        if (!chunkyrow)
             pm_error("decode_mask(): chunkyrow == NULL - can't happen");
         
-        if( maskfile ) {
-            for( col = 0; col < cols; col++ ) {
-                if( chunkyrow[col] == bmhdP->transparentColor )
+        if (maskfile) {
+            for (col = 0; col < cols; ++col) {
+                if (chunkyrow[col] == bmhdP->transparentColor)
                     maskrow[col] = PBM_WHITE;
                 else
                     maskrow[col] = PBM_BLACK;
             }
             pbm_writepbmrow(maskfile, maskrow, cols, 0);
-                wrotemask = 1;
+                wrotemask = true;
         }
         break;
     case mskLasso:
@@ -1031,7 +1045,7 @@ get_color(cmap, idx, red, green, blue)
  ****************************************************************************/
 
 static void
-std_to_ppm(FILE *         const ifp, 
+std_to_ppm(FILE *         const ifP, 
            long           const chunksize, 
            BitMapHeader * const bmhdP, 
            ColorMap *     const cmap, 
@@ -1040,7 +1054,7 @@ std_to_ppm(FILE *         const ifp,
 
 
 static void
-ham_to_ppm(FILE *         const ifp, 
+ham_to_ppm(FILE *         const ifP, 
            long           const chunksize, 
            BitMapHeader * const bmhdP, 
            ColorMap *     const cmap, 
@@ -1063,7 +1077,7 @@ ham_to_ppm(FILE *         const ifp,
 
         pm_message("%d-plane HAM?? - interpreting image as a normal ILBM", 
                    bmhdP->nPlanes);
-        std_to_ppm(ifp, chunksize, bmhdP, cmap, assumed_viewportmodes);
+        std_to_ppm(ifP, chunksize, bmhdP, cmap, assumed_viewportmodes);
         return;
     } else {
         unsigned long remainingChunksize;
@@ -1107,8 +1121,8 @@ ham_to_ppm(FILE *         const ifp,
             if( HAS_MULTIPALETTE(cmap) )
                 multi_update(cmap, row);
 
-            decode_row(ifp, &remainingChunksize, rawrow, bmhdP->nPlanes, bmhdP);
-            decode_mask(ifp, &remainingChunksize, rawrow, bmhdP);
+            decode_row(ifP, &remainingChunksize, rawrow, bmhdP->nPlanes, bmhdP);
+            decode_mask(ifP, &remainingChunksize, rawrow, bmhdP);
 
             r = g = b = 0;
             for( col = 0; col < cols; col++ ) {
@@ -1142,7 +1156,7 @@ ham_to_ppm(FILE *         const ifp,
             }
             ppm_writeppmrow(stdout, pixelrow, cols, MAXCOLVAL, 0);
         }
-        chunk_end(ifp, ID_BODY, remainingChunksize);
+        chunk_end(ifP, ID_BODY, remainingChunksize);
     }
 }
 
@@ -1469,46 +1483,54 @@ rgbn_to_ppm(FILE *         const ifP,
     unsigned int const rows = bmhdP->h;
     unsigned int const cols = bmhdP->w;
 
-    int row, col, count, genlock, tries;
-    pixval r, g, b, maxval;
+    unsigned int row;
+    unsigned int count;
+    pixval maxval;
     unsigned long remainingChunksize;
     pixel * transpColorP;
 
     pm_message("input is a %d-bit RGB image", (typeid == ID_RGB8 ? 8 : 4));
 
-    if( bmhdP->compression != 4 )
+    if (bmhdP->compression != 4)
         pm_error("invalid compression mode for %s: %d (must be 4)", 
                  ID2string(typeid), bmhdP->compression);
-
-    switch( typeid ) {
-        case ID_RGBN:
-            if( bmhdP->nPlanes != 13 )
-                pm_error("invalid number of planes for %s: %d (must be 13)", 
-                         ID2string(typeid), bmhdP->nPlanes);
-            maxval = lut_maxval(cmap, 15);
-            break;
-        case ID_RGB8:
-            if( bmhdP->nPlanes != 25 )
-                pm_error("invalid number of planes for %s: %d (must be 25)", 
-                         ID2string(typeid), bmhdP->nPlanes);
-            maxval = 255;
-            break;
-        default:
-            pm_error("rgbn_to_ppm(): invalid IFF ID %s - can't happen", 
-                     ID2string(typeid));
+    
+    switch (typeid) {
+    case ID_RGBN:
+        if (bmhdP->nPlanes != 13)
+            pm_error("invalid number of planes for %s: %d (must be 13)", 
+                     ID2string(typeid), bmhdP->nPlanes);
+        maxval = lut_maxval(cmap, 15);
+        break;
+    case ID_RGB8:
+        if (bmhdP->nPlanes != 25)
+            pm_error("invalid number of planes for %s: %d (must be 25)", 
+                     ID2string(typeid), bmhdP->nPlanes);
+        maxval = 255;
+        break;
+    default:
+        pm_error("rgbn_to_ppm(): invalid IFF ID %s - can't happen", 
+                 ID2string(typeid));
     }
 
     transpColorP = transpColor(bmhdP, cmap, transpName, maxval);
 
     ppm_writeppminit(stdout, cols, rows, maxval, 0);
 
-    remainingChunksize = chunksize;  /* initial value */
-    count = 0;
-    for( row = 0; row < rows; row++ ) {
-        for( col = 0; col < cols; col++ ) {
+    for (row = 0, count = 0, remainingChunksize = chunksize;
+         row < rows;
+         ++row) {
+
+        unsigned int col;
+
+        for (col = 0; col < cols; ++col) {
+            unsigned int tries;
+            unsigned int genlock;
+            pixval r, g, b;
+
             tries = 0;
-            while( !count ) {
-                if( typeid == ID_RGB8 ) {
+            while (count == 0) {
+                if (typeid == ID_RGB8) {
                     r = lookup_red(cmap,   get_byte(ifP, ID_BODY, 
                                                     &remainingChunksize));
                     g = lookup_green(cmap, get_byte(ifP, ID_BODY,
@@ -1518,47 +1540,46 @@ rgbn_to_ppm(FILE *         const ifP,
                     count = get_byte(ifP, ID_BODY, &remainingChunksize);
                     genlock = count & 0x80;
                     count &= 0x7f;
-                }
-                else {
-                    int word;
-                    word = get_big_short(ifP, ID_BODY, &remainingChunksize);
+                } else {
+                    unsigned int const word =
+                        get_big_short(ifP, ID_BODY, &remainingChunksize);
                     r = lookup_red(cmap, (word & 0xf000) >> 12);
                     g = lookup_green(cmap, (word & 0x0f00) >> 8);
                     b = lookup_blue(cmap, (word & 0x00f0) >> 4);
                     genlock = word & 0x0008;
                     count = word & 0x0007;
                 }
-                if( !count ) {
+                if (!count) {
                     count = get_byte(ifP, ID_BODY, &remainingChunksize);
-                    if( !count )
+                    if (count == 0)
                         count =
                             get_big_short(ifP, ID_BODY, &remainingChunksize);
-                        if( !count )
-                            ++tries;
+                    if (count == 0)
+                        ++tries;
                 }
             }
-            if( tries ) {
-                pm_message("warning - repeat count 0 at col %d row %d: "
-                           "skipped %d RGB entr%s",
+            if (tries > 0) {
+                pm_message("warning - repeat count 0 at col %u row %u: "
+                           "skipped %u RGB entr%s",
                             col, row, tries, (tries == 1 ? "y" : "ies"));
             }
-            if( maskfile ) {
+            if (maskfile) {
                 /* genlock bit set -> transparent */
-                if( genlock )
+                if (genlock)
                     maskrow[col] = PBM_WHITE;
                 else
                     maskrow[col] = PBM_BLACK;
             }
-            if( transpColorP && maskrow && maskrow[col] == PBM_WHITE )
+            if (transpColorP && maskrow && maskrow[col] == PBM_WHITE)
                 pixelrow[col] = *transpColorP;
             else
                 PPM_ASSIGN(pixelrow[col], r, g, b);
             --count;
         }
         ppm_writeppmrow(stdout, pixelrow, cols, maxval, 0);
-        if( maskfile ) {
+        if (maskfile) {
             pbm_writepbmrow(maskfile, maskrow, cols, 0);
-            wrotemask = 1;
+            wrotemask = true;
         }
     }
     chunk_end(ifP, ID_BODY, remainingChunksize);
@@ -1947,13 +1968,13 @@ PCHG_ConvertBig(PCHGHeader *    const PCHG,
 
 
 static void
-read_pchg(FILE *     const ifp,
+read_pchg(FILE *     const ifP,
           IFF_ID     const iffid,
           long       const chunksize,
           ColorMap * const cmap) {
 
     if( cmap->mp_type >= MP_TYPE_PCHG ) {
-        skip_chunk(ifp, iffid, chunksize);
+        skip_chunk(ifP, iffid, chunksize);
     } else {
         PCHGHeader      PCHG;
         unsigned char   *data;
@@ -1967,15 +1988,15 @@ read_pchg(FILE *     const ifp,
 
         remainingChunksize = chunksize;  /* initial value */
 
-        PCHG.Compression = get_big_short(ifp, iffid, &remainingChunksize);
-        PCHG.Flags       = get_big_short(ifp, iffid, &remainingChunksize);
-        PCHG.StartLine   = get_big_short(ifp, iffid, &remainingChunksize);
-        PCHG.LineCount   = get_big_short(ifp, iffid, &remainingChunksize);
-        PCHG.ChangedLines= get_big_short(ifp, iffid, &remainingChunksize);
-        PCHG.MinReg      = get_big_short(ifp, iffid, &remainingChunksize);
-        PCHG.MaxReg      = get_big_short(ifp, iffid, &remainingChunksize);
-        PCHG.MaxChanges  = get_big_short(ifp, iffid, &remainingChunksize);
-        PCHG.TotalChanges= get_big_long(ifp, iffid, &remainingChunksize);
+        PCHG.Compression = get_big_short(ifP, iffid, &remainingChunksize);
+        PCHG.Flags       = get_big_short(ifP, iffid, &remainingChunksize);
+        PCHG.StartLine   = get_big_short(ifP, iffid, &remainingChunksize);
+        PCHG.LineCount   = get_big_short(ifP, iffid, &remainingChunksize);
+        PCHG.ChangedLines= get_big_short(ifP, iffid, &remainingChunksize);
+        PCHG.MinReg      = get_big_short(ifP, iffid, &remainingChunksize);
+        PCHG.MaxReg      = get_big_short(ifP, iffid, &remainingChunksize);
+        PCHG.MaxChanges  = get_big_short(ifP, iffid, &remainingChunksize);
+        PCHG.TotalChanges= get_big_long(ifP, iffid, &remainingChunksize);
 
 #ifdef DEBUG
         pm_message("PCHG StartLine   : %d", PCHG.StartLine);
@@ -1990,17 +2011,17 @@ read_pchg(FILE *     const ifp,
             long treesize, compsize;
 
             CompHdr.CompInfoSize     =
-                get_big_long(ifp, iffid, &remainingChunksize);
+                get_big_long(ifP, iffid, &remainingChunksize);
             CompHdr.OriginalDataSize =
-                get_big_long(ifp, iffid, &remainingChunksize);
+                get_big_long(ifP, iffid, &remainingChunksize);
 
             treesize = CompHdr.CompInfoSize;
             MALLOCARRAY_NOFAIL(comptree, treesize);
-            read_bytes(ifp, treesize, comptree, iffid, &remainingChunksize);
+            read_bytes(ifP, treesize, comptree, iffid, &remainingChunksize);
 
             compsize = remainingChunksize;
             MALLOCARRAY_NOFAIL(compdata, compsize);
-            read_bytes(ifp, compsize, compdata, iffid, &remainingChunksize);
+            read_bytes(ifP, compsize, compdata, iffid, &remainingChunksize);
 
             datasize = CompHdr.OriginalDataSize;
             MALLOCARRAY_NOFAIL(data, datasize);
@@ -2015,7 +2036,7 @@ read_pchg(FILE *     const ifp,
 #endif
             datasize = remainingChunksize;
             MALLOCARRAY_NOFAIL(data, datasize);
-            read_bytes(ifp, datasize, data, iffid, &remainingChunksize);
+            read_bytes(ifP, datasize, data, iffid, &remainingChunksize);
         }
 
         if( PCHG.Flags & PCHGF_USE_ALPHA )
@@ -2054,7 +2075,7 @@ read_pchg(FILE *     const ifp,
                          ID2string(iffid));
         }
         free(data);
-        chunk_end(ifp, iffid, remainingChunksize);
+        chunk_end(ifP, iffid, remainingChunksize);
     }
 }
 
@@ -2079,7 +2100,7 @@ ignored_iffid(IFF_ID       const iffid,
 
 
 static void 
-process_body( FILE *          const ifp,
+process_body( FILE *          const ifP,
               long            const chunksize,
               BitMapHeader *  const bmhdP,
               ColorMap *      const cmap,
@@ -2089,19 +2110,19 @@ process_body( FILE *          const ifp,
               DirectColor *   const dcol,
               int *           const viewportmodesP) {
     
-    if( bmhdP == NULL )
+    if (bmhdP == NULL)
         pm_error("%s chunk without %s chunk", 
                  ID2string(ID_BODY), ID2string(ID_BMHD));
 
     prepareCmap(bmhdP, cmap);
 
     pixelrow = ppm_allocrow(bmhdP->w);
-    if( maskfile ) {
+    if (maskfile) {
         maskrow = pbm_allocrow(bmhdP->w);
         pbm_writepbminit(maskfile, bmhdP->w, bmhdP->h, 0);
     }
 
-    if( typeid == ID_ILBM ) {
+    if (typeid == ID_ILBM) {
         int isdeep;
 
         MALLOCARRAY_NOFAIL(ilbmrow, RowBytes(bmhdP->w));
@@ -2115,27 +2136,27 @@ process_body( FILE *          const ifp,
         } else
             isdeep = isdeepopt;
         
-        if( isdeep > 0 )
-            deep_to_ppm(ifp, chunksize, bmhdP, cmap);
-        else if( dcol )
-            dcol_to_ppm(ifp, chunksize, bmhdP, cmap, dcol);
-        else if( bmhdP->nPlanes > 8 ) {
-            if( bmhdP->nPlanes <= 16 && HAS_COLORMAP(cmap) )
-                std_to_ppm(ifp, chunksize, bmhdP, cmap, *viewportmodesP);
-            else if( isdeep >= 0 && (bmhdP->nPlanes % 3 == 0) )
-                deep_to_ppm(ifp, chunksize, bmhdP, cmap);
-            else if( bmhdP->nPlanes <= 16 )   
+        if (isdeep > 0)
+            deep_to_ppm(ifP, chunksize, bmhdP, cmap);
+        else if (dcol)
+            dcol_to_ppm(ifP, chunksize, bmhdP, cmap, dcol);
+        else if (bmhdP->nPlanes > 8) {
+            if (bmhdP->nPlanes <= 16 && HAS_COLORMAP(cmap))
+                std_to_ppm(ifP, chunksize, bmhdP, cmap, *viewportmodesP);
+            else if (isdeep >= 0 && (bmhdP->nPlanes % 3 == 0))
+                deep_to_ppm(ifP, chunksize, bmhdP, cmap);
+            else if (bmhdP->nPlanes <= 16)
                 /* will be interpreted as grayscale */
-                std_to_ppm(ifp, chunksize, bmhdP, cmap, *viewportmodesP);
+                std_to_ppm(ifP, chunksize, bmhdP, cmap, *viewportmodesP);
             else
                 pm_error("don't know how to interpret %d-plane image", 
                          bmhdP->nPlanes);
         } else
-            std_to_ppm(ifp, chunksize, bmhdP, cmap, *viewportmodesP);
+            std_to_ppm(ifP, chunksize, bmhdP, cmap, *viewportmodesP);
     } else if( typeid == ID_PBM )
-        ipbm_to_ppm(ifp, chunksize, bmhdP, cmap, *viewportmodesP);
+        ipbm_to_ppm(ifP, chunksize, bmhdP, cmap, *viewportmodesP);
     else   /* RGBN or RGB8 */
-        rgbn_to_ppm(ifp, chunksize, bmhdP, cmap);
+        rgbn_to_ppm(ifP, chunksize, bmhdP, cmap);
 }
 
 
@@ -2407,6 +2428,8 @@ main(int argc, char *argv[]) {
     if( argn != argc )
         pm_usage(usage);
 
+    wrotemask = false;  /* initial value */
+
     /* Read in the ILBM file. */
 
     firstIffid = get_big_long(ifP, ID_FORM, NULL);
diff --git a/converter/ppm/picttoppm.c b/converter/ppm/picttoppm.c
index 43b8d1ef..828d5270 100644
--- a/converter/ppm/picttoppm.c
+++ b/converter/ppm/picttoppm.c
@@ -1200,7 +1200,7 @@ doDiffSize(struct Rect       const clipsrc,
 
     unsigned int const dstadd = dstwid - xsize;
 
-    FILE * pnmscalePipeP;
+    FILE * pamscalePipeP;
     const char * command;
     FILE * scaled;
     int cols, rows, format;
@@ -1221,19 +1221,19 @@ doDiffSize(struct Rect       const clipsrc,
 
     pm_close(tempFileP);
 
-    pm_asprintf(&command, "pnmscale -xsize %d -ysize %d > %s",
+    pm_asprintf(&command, "pamscale -xsize %d -ysize %d > %s",
                 rectwidth(&clipdst), rectheight(&clipdst), tempFilename);
 
     pm_message("running command '%s'", command);
 
-    pnmscalePipeP = popen(command, "w");
-    if (pnmscalePipeP == NULL)
+    pamscalePipeP = popen(command, "w");
+    if (pamscalePipeP == NULL)
         pm_error("cannot execute command '%s'  popen() errno = %s (%d)",
                  command, strerror(errno), errno);
 
     pm_strfree(command);
 
-    fprintf(pnmscalePipeP, "P6\n%d %d\n%d\n",
+    fprintf(pamscalePipeP, "P6\n%d %d\n%d\n",
             rectwidth(&clipsrc), rectheight(&clipsrc), PPM_MAXMAXVAL);
 
     switch (pixSize) {
@@ -1245,9 +1245,9 @@ doDiffSize(struct Rect       const clipsrc,
             for (colNumber = 0; colNumber < xsize; ++colNumber) {
                 unsigned int const colorIndex = row[colNumber];
                 struct RGBColor * const ct = &color_map[colorIndex];
-                fputc(redepth(ct->red, 65535L), pnmscalePipeP);
-                fputc(redepth(ct->grn, 65535L), pnmscalePipeP);
-                fputc(redepth(ct->blu, 65535L), pnmscalePipeP);
+                fputc(redepth(ct->red, 65535L), pamscalePipeP);
+                fputc(redepth(ct->grn, 65535L), pamscalePipeP);
+                fputc(redepth(ct->blu, 65535L), pamscalePipeP);
             }
         }
     }
@@ -1259,9 +1259,9 @@ doDiffSize(struct Rect       const clipsrc,
             unsigned int colNumber;
             for (colNumber = 0; colNumber < xsize; ++colNumber) {
                 struct RGBColor const color = decode16(&row[colNumber * 2]);
-                fputc(redepth(color.red, 32), pnmscalePipeP);
-                fputc(redepth(color.grn, 32), pnmscalePipeP);
-                fputc(redepth(color.blu, 32), pnmscalePipeP);
+                fputc(redepth(color.red, 32), pamscalePipeP);
+                fputc(redepth(color.grn, 32), pamscalePipeP);
+                fputc(redepth(color.blu, 32), pamscalePipeP);
             }
         }
     }
@@ -1278,17 +1278,17 @@ doDiffSize(struct Rect       const clipsrc,
 
             unsigned int colNumber;
             for (colNumber = 0; colNumber < xsize; ++colNumber) {
-                fputc(redepth(redPlane[colNumber], 256), pnmscalePipeP);
-                fputc(redepth(grnPlane[colNumber], 256), pnmscalePipeP);
-                fputc(redepth(bluPlane[colNumber], 256), pnmscalePipeP);
+                fputc(redepth(redPlane[colNumber], 256), pamscalePipeP);
+                fputc(redepth(grnPlane[colNumber], 256), pamscalePipeP);
+                fputc(redepth(bluPlane[colNumber], 256), pamscalePipeP);
             }
         }
     }
     break;
     }
 
-    if (pclose(pnmscalePipeP))
-        pm_error("pnmscale failed.  pclose() returned Errno %s (%d)",
+    if (pclose(pamscalePipeP))
+        pm_error("pamscale failed.  pclose() returned Errno %s (%d)",
                  strerror(errno), errno);
 
     ppm_readppminit(scaled = pm_openr(tempFilename), &cols, &rows,
diff --git a/converter/ppm/ppmtoarbtxt.c b/converter/ppm/ppmtoarbtxt.c
index df859c84..569f5ea2 100644
--- a/converter/ppm/ppmtoarbtxt.c
+++ b/converter/ppm/ppmtoarbtxt.c
@@ -1,4 +1,4 @@
-/* ppmtoarbtxt.c - convert portable pixmap to cleartext
+/* ppmtoarbtxt.c - convert PPM to a custom text-based format
 **
 ** Renamed from ppmtotxt.c by Bryan Henderson in January 2003.
 **
@@ -12,51 +12,150 @@
 ** implied warranty.
 */
 
+#include <assert.h>
 #include <string.h>
+#ifdef __GLIBC__
+  #include <printf.h>  /* Necessary for parse_printf_format() */
+#endif
 
-#include "ppm.h"
 #include "mallocvar.h"
 #include "nstring.h"
+#include "shhopt.h"
+#include "ppm.h"
+
+/* HAVE_PARSE_PRINTF_FORMAT means the system library has
+   parse_printf_format(), declared in <printf.h>.  This essentially means
+   systems with GNU libc.
+*/
+
+#ifndef HAVE_PARSE_PRINTF_FORMAT
+  #ifdef PA_FLAG_MASK                   /* Defined in printf.h */
+    #define HAVE_PARSE_PRINTF_FORMAT 1
+  #else
+    #define HAVE_PARSE_PRINTF_FORMAT 0
+  #endif
+#endif
+
+
+
+struct CmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * inputFileName;
+    const char * bodySklFileName;
+    const char * hd;
+    const char * tl;
+    unsigned int debug;
+};
+
+
+
+static void
+parseCommandLine(int argc, const char ** argv,
+                 struct CmdlineInfo * const cmdlineP) {
+/*----------------------------------------------------------------------------
+   Note that many of the strings that this function returns in the
+   *cmdline_p structure are actually in the supplied argv array.  And
+   sometimes, one of these strings is actually just a suffix of an entry
+   in argv!
+-----------------------------------------------------------------------------*/
+    optEntry * option_def;
+        /* Instructions to OptParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int hdSpec, tlSpec;
+
+    unsigned int option_def_index;
+    
+    MALLOCARRAY(option_def, 100);
+
+    option_def_index = 0;   /* incremented by OPTENTRY */
+    OPTENT3(0,   "hd",   OPT_STRING, &cmdlineP->hd, 
+            &hdSpec,             0);
+    OPTENT3(0,   "tl",   OPT_STRING, &cmdlineP->tl,
+            &tlSpec,             0);
+    OPTENT3(0,   "debug", OPT_FLAG, NULL,
+            &cmdlineP->debug,      0);
+
+    opt.opt_table = option_def;
+    opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
+    opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
+
+    pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
+    free(option_def);
+
+    if (!hdSpec)
+        cmdlineP->hd = NULL;
+
+    if (!tlSpec)
+        cmdlineP->tl = NULL;
+
+    if (argc-1 < 1)
+        pm_error("You must specify the body skeleton file name as an "
+                 "argument");
+    else {
+        cmdlineP->bodySklFileName = strdup(argv[1]);
+
+        if (argc-1 < 2)
+            cmdlineP->inputFileName = strdup("-");  /* he wants stdin */
+        else {
+            cmdlineP->inputFileName = strdup(argv[2]);
+            if (argc-1 > 2)
+                pm_error("Too many arguments.  The only possible arguments "
+                         "are the body skeleton file name and input image "
+                         "file name");
+        }
+    }
+}
+
+
+
 
 typedef enum {
 /* The types of object we handle */
     BDATA, IRED, IGREEN, IBLUE, ILUM, FRED, FGREEN, FBLUE, FLUM,
     WIDTH, HEIGHT, POSX, POSY
-} SKL_OBJ_TYP;
+} SkeletonObjectType;
 
 typedef enum {
     OBJTYP_ICOLOR, OBJTYP_FCOLOR, OBJTYP_INT, OBJTYP_BDATA
-} SKL_OBJ_CLASS;
+} SkeletonObjectClass;
 
 /* Maximum size for a format string ("%d" etc.) */
+/* Add one to this size for the terminating '\0'. */
 #define MAXFORMAT 16
 
+typedef union {
 /* The data we keep for each object */
-typedef union
- {
-  struct BNDAT { char *bdat;   /* Binary data (text with newlines etc.) */
-                 int ndat;
-               } bin_data;
-
-  struct ICDAT { char icformat[MAXFORMAT];  /* Integer colors */
-                 int icolmin, icolmax;
-               } icol_data;
-
-  struct FCDAT { char fcformat[MAXFORMAT];  /* Float colors */
-                 double fcolmin, fcolmax;
-               } fcol_data;
-
-  struct IDAT  { char iformat[MAXFORMAT];   /* Integer data */
-               } i_data;
- } SKL_OBJ_DATA;
+    struct Bndat {
+        char * bdat;   /* Binary data (text with newlines etc.) */
+        unsigned int ndat;
+    } binData;
+    
+    struct Icdat {
+        char icformat[MAXFORMAT+1];  /* Integer colors */
+        unsigned int icolmin, icolmax;
+    } icolData;
+
+    struct Fcdat {
+        char fcformat[MAXFORMAT+1];  /* Float colors */
+        double fcolmin, fcolmax;
+    } fcolData;
+    
+    struct Idat {
+        char iformat[MAXFORMAT+1];   /* Integer data */
+    } iData;
+} SkeletonObjectData;
 
 
 /* Each object has a type and some data */
-typedef struct
- { 
-   SKL_OBJ_TYP otyp;
-   SKL_OBJ_DATA odata;
- } SKL_OBJ;
+typedef struct { 
+    SkeletonObjectType objType;
+    SkeletonObjectData odata;
+} SkeletonObject;
+
 
 
 #define MAX_SKL_HEAD_OBJ 64
@@ -66,199 +165,164 @@ typedef struct
 #define MAX_OBJ_BUF 80
 
 
-static void write_txt (fout, nobj, obj, width, height, x, y, red, green, blue)
-FILE *fout;
-int nobj;
-SKL_OBJ *obj[];
-int width, height, x, y;
-double red, green, blue;
-
-{register int count;
-
-#define WRITE_BNDAT(fd,theobj) \
- {struct BNDAT *bdata = &((theobj)->odata.bin_data); \
-       fwrite (bdata->bdat,bdata->ndat,1,fd); }
-
-#define WRITE_ICOL(fd,theobj,thecol) \
- {struct ICDAT *icdata = &((theobj)->odata.icol_data); \
-  fprintf (fd,icdata->icformat,(int)(icdata->icolmin \
-                + (icdata->icolmax - icdata->icolmin)*(thecol))); }
-
-#define WRITE_FCOL(fd,theobj,thecol) \
- {struct FCDAT *fcdata = &((theobj)->odata.fcol_data); \
-  fprintf (fd,fcdata->fcformat,(double)(fcdata->fcolmin \
-                + (fcdata->fcolmax - fcdata->fcolmin)*(thecol))); }
-
-#define WRITE_IDAT(fd,theobj,thedat) \
- {struct IDAT *idata = &((theobj)->odata.i_data); \
-  fprintf (fd,idata->iformat,thedat); }
-
- for (count = 0; count < nobj; count++)
- {
-   switch (obj[count]->otyp)
-   {
-     case BDATA:
-       WRITE_BNDAT (fout,obj[count]);
-       break;
-     case IRED:
-       WRITE_ICOL (fout,obj[count],red);
-       break;
-     case IGREEN:
-       WRITE_ICOL (fout,obj[count],green);
-       break;
-     case IBLUE:
-       WRITE_ICOL (fout,obj[count],blue);
-       break;
-     case ILUM:
-       WRITE_ICOL (fout,obj[count],0.299*red+0.587*green+0.114*blue);
-       break;
-     case FRED:
-       WRITE_FCOL (fout,obj[count],red);
-       break;
-     case FGREEN:
-       WRITE_FCOL (fout,obj[count],green);
-       break;
-     case FBLUE:
-       WRITE_FCOL (fout,obj[count],blue);
-       break;
-     case FLUM:
-       WRITE_FCOL (fout,obj[count],0.299*red+0.587*green+0.114*blue);
-       break;
-     case WIDTH:
-       WRITE_IDAT (fout,obj[count],width);
-       break;
-     case HEIGHT:
-       WRITE_IDAT (fout,obj[count],height);
-       break;
-     case POSX:
-       WRITE_IDAT (fout,obj[count],x);
-       break;
-     case POSY:
-       WRITE_IDAT (fout,obj[count],y);
-       break;
-   }
- }
-}
 
+static void
+dumpSkeleton(SkeletonObject ** const skeletonPList,
+             unsigned int      const nSkeleton) {
 
-static SKL_OBJ *
-save_bin_data(int    const ndat, 
-              char * const bdat) {
+    unsigned int i;
 
-    /* Save binary data in Object */
+    pm_message("%u objects", nSkeleton);
 
-    SKL_OBJ *obj;
+    for (i = 0; i < nSkeleton; ++i) {
+        SkeletonObject * const skeletonP = skeletonPList[i];
 
-    obj = (SKL_OBJ *)malloc (sizeof (SKL_OBJ) + ndat);
-    if (obj != NULL)
-    {
-        obj->otyp = BDATA;
-        obj->odata.bin_data.ndat = ndat;
-        obj->odata.bin_data.bdat = ((char *)(obj))+sizeof (SKL_OBJ);
-        memcpy (obj->odata.bin_data.bdat,bdat,ndat);
+        pm_message("  Object: Type %u", skeletonP->objType);
     }
-    return (obj);
 }
 
 
 
-static SKL_OBJ *
-save_icol_data(SKL_OBJ_TYP  const ctyp,
-               const char * const format,
-               int          const icolmin,
-               int          const icolmax) {
-/* Save integer color data in Object */
-
-    SKL_OBJ * objP;
-
-    MALLOCVAR(objP);
-
-    if (objP) {
-        objP->otyp = ctyp;
-        strcpy(objP->odata.icol_data.icformat, format);
-        objP->odata.icol_data.icolmin = icolmin;
-        objP->odata.icol_data.icolmax = icolmax;
-    }
-    return objP;
+static void
+dumpAllSkeleton(SkeletonObject ** const bodySkeletonPList,
+                unsigned int      const bodyNskl,
+                SkeletonObject ** const headSkeletonPList, 
+                unsigned int      const headNskl,
+                SkeletonObject ** const tailSkeletonPList,
+                unsigned int      const tailNskl) {
+    
+    pm_message("Body skeleton:");
+    dumpSkeleton(bodySkeletonPList, bodyNskl);
+
+    pm_message("Head skeleton:");
+    dumpSkeleton(headSkeletonPList, headNskl);
+
+    pm_message("Tail skeleton:");
+    dumpSkeleton(tailSkeletonPList, tailNskl);
 }
 
 
 
-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 */
-
-    SKL_OBJ * objP;
+static void
+writeBndat(FILE *           const ofP,
+           SkeletonObject * const objectP) {
 
-    MALLOCVAR(objP);
+    struct Bndat * const bdataP = &objectP->odata.binData;
 
-    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;
+    fwrite(bdataP->bdat, bdataP->ndat, 1, ofP);
 }
 
 
 
-static SKL_OBJ *
-save_i_data(SKL_OBJ_TYP  const ctyp,
-            const char * const format) {
+static void
+writeIcol(FILE *           const ofP,
+          SkeletonObject * const objectP,
+          double           const value) {
+
+    struct Icdat * const icdataP = &objectP->odata.icolData;
+    
+    fprintf(ofP, icdataP->icformat,
+            (unsigned int)
+            (icdataP->icolmin
+             + (icdataP->icolmax - icdataP->icolmin) * value));
+}
 
-/* Save universal data in Object */
 
-    SKL_OBJ * objP;
 
-    MALLOCVAR(objP);
-    if (objP) {
-        objP->otyp = ctyp;
-        strcpy(objP->odata.i_data.iformat, format);
-    }
-    return objP;
+static void
+writeFcol(FILE *           const ofP,
+          SkeletonObject * const objectP,
+          double           const value) {
+
+    struct Fcdat * const fcdataP = &objectP->odata.fcolData;
+    
+    fprintf(ofP, fcdataP->fcformat,
+            (double)
+            (fcdataP->fcolmin
+             + (fcdataP->fcolmax - fcdataP->fcolmin) * value));
 }
 
 
 
-static char const escape = '#';
+static void
+writeIdat(FILE *           const ofP,
+          SkeletonObject * const objectP,
+          unsigned int     const value) {
+
+    struct Idat * const idataP = &objectP->odata.iData;
+    
+    fprintf(ofP, idataP->iformat, value);
+}
 
 
 
-static SKL_OBJ_TYP
-interpretObjType(const char * const typstr) {
+static void
+writeText(FILE *            const ofP,
+          unsigned int      const nObj,
+          SkeletonObject ** const obj,
+          unsigned int      const width,
+          unsigned int      const height,
+          unsigned int      const x,
+          unsigned int      const y,
+          double            const red,
+          double            const green,
+          double            const blue) {
+    
+    unsigned int i;
 
-    SKL_OBJ_TYP otyp;
-
-    /* Check for integer colors */
-    if      (streq(typstr, "ired")  ) otyp = IRED;
-    else if (streq(typstr, "igreen")) otyp = IGREEN;
-    else if (streq(typstr, "iblue") ) otyp = IBLUE;
-    else if (streq(typstr, "ilum")  ) otyp = ILUM;
-    /* Check for real colors */
-    else if (streq(typstr, "fred")  ) otyp = FRED;
-    else if (streq(typstr, "fgreen")) otyp = FGREEN;
-    else if (streq(typstr, "fblue") ) otyp = FBLUE;
-    else if (streq(typstr, "flum")  ) otyp = FLUM;
-    /* Check for integer data */
-    else if (streq(typstr, "width") ) otyp = WIDTH;
-    else if (streq(typstr, "height")) otyp = HEIGHT;
-    else if (streq(typstr, "posx")  ) otyp = POSX;
-    else if (streq(typstr, "posy")  ) otyp = POSY;
-    else                              otyp = BDATA;
-
-    return otyp;
+    for (i = 0; i < nObj; ++i) {
+        switch (obj[i]->objType) {
+        case BDATA:
+            writeBndat(ofP, obj[i]);
+            break;
+        case IRED:
+            writeIcol(ofP, obj[i], red);
+            break;
+        case IGREEN:
+            writeIcol(ofP, obj[i], green);
+            break;
+        case IBLUE:
+            writeIcol(ofP, obj[i], blue);
+            break;
+        case ILUM:
+            writeIcol(ofP, obj[i],
+                      PPM_LUMINR*red + PPM_LUMING*green + PPM_LUMINB*blue);
+            break;
+        case FRED:
+            writeFcol(ofP, obj[i], red);
+            break;
+        case FGREEN:
+            writeFcol(ofP, obj[i], green);
+            break;
+        case FBLUE:
+            writeFcol(ofP, obj[i], blue);
+            break;
+        case FLUM:
+            writeFcol(ofP, obj[i],
+                      PPM_LUMINR*red + PPM_LUMING*green + PPM_LUMINB*blue);
+            break;
+        case WIDTH:
+            writeIdat(ofP, obj[i], width);
+            break;
+        case HEIGHT:
+            writeIdat(ofP, obj[i], height);
+            break;
+        case POSX:
+            writeIdat(ofP, obj[i], x);
+            break;
+        case POSY:
+            writeIdat(ofP, obj[i], y);
+            break;
+        }
+    }
 }
 
 
 
-static SKL_OBJ_CLASS
-objClass(SKL_OBJ_TYP const otyp) {
+static SkeletonObjectClass
+objClass(SkeletonObjectType const objType) {
 
-    switch (otyp) {
+    switch (objType) {
     case IRED:
     case IGREEN:
     case IBLUE:
@@ -283,298 +347,871 @@ objClass(SKL_OBJ_TYP const otyp) {
 }
 
 
+/*----------------------------------------------------------------------------
+  Format string validation
+
+  We validate format strings (such as "%f" "%03d") found in the skeleton files
+  for convenience, even though behavior is documented as undefined when the
+  user supplies a bogus format string.  Certain strings, most notably those
+  with "%n", are especially risky; they pose a security threat.
+
+  On systems with Glibc, we check with parse_printf_format().  On other
+  systems we conduct a cursory scan of the characters in the format string,
+  looking for characters that trigger non-numeric conversions, etc.
+
+  Documentation for parse_printf_format() is usually available in texinfo
+  format on GNU/Linux systems.  As of Dec. 2014 there is no official man page.
+  
+  Online documentation is available from:
+  https://
+  www.gnu.org/software/libc/manual/html_node/Parsing-a-Template-String.html
+-----------------------------------------------------------------------------*/
 
+#if HAVE_PARSE_PRINTF_FORMAT
 static void
-addImpostorReplacementSeq(char *         const line,
-                          unsigned int   const startCursor,
-                          const char *   const seqContents,
-                          unsigned int   const seqContentsLen,
-                          unsigned int * const newCursorP) {
+validateParsePrintfFLag(int                const printfConversion,
+                        SkeletonObjectType const ctyp,
+                        const char **      const errorP) {
 /*----------------------------------------------------------------------------
-   Add to line line[], at position 'startCursor', text that looks like a
-   replacement sequence but doesn't have the proper contents (the
-   stuff between the parentheses) to be one.  For example,
+  Assuming 'printfConversion' is the value reported by parse_printf_format()
+  as the type of argument a format string requires, 
+  return an explanation of how it is incompatible with 'ctyp' as
+  *errorP -- return null string if it is compatible.
+-----------------------------------------------------------------------------*/
+    /* We first check for "%n", then the type modifiers, and finally the
+       actual conversion type (char, int, float, double, string or pointer.)
+    */
+    switch (printfConversion & PA_FLAG_MASK) {
+    case PA_FLAG_PTR:  /* This means %n */
+        pm_asprintf(errorP, "Contains a %%n conversion specifier");
+        break;
+
+    case PA_FLAG_SHORT:
+    case PA_FLAG_LONG:
+    case PA_FLAG_LONG_LONG:
+        /* We omit PA_FLAG_LONG_DOUBLE because it is a synonym for
+           PA_FLAG_LONG_LONG: listing both causes compilation errors.
+        */
+        pm_asprintf(errorP, "Invalid type modifier");
+        break;
+
+    default:
+        switch (printfConversion & ~PA_FLAG_MASK) {
+        case PA_CHAR:
+            pm_message("Warning: char type conversion."); 
+        case PA_INT:
+            if(objClass(ctyp) == OBJTYP_ICOLOR ||
+               objClass(ctyp) == OBJTYP_INT )
+                *errorP = NULL;
+            else
+                pm_asprintf(errorP, "Conversion specifier requires a "
+                            "character or integer argument, but it is in "
+                            "a replacement sequence for a different type");
+            break;
+        case PA_DOUBLE:
+            if(objClass(ctyp) == OBJTYP_FCOLOR)
+                *errorP = NULL;
+            else
+                pm_asprintf(errorP, "Conversion specifier requires a "
+                            "double precision floating point argument, "
+                            "but it is in "
+                            "a replacement sequence for a different type");
+            break;
+        case PA_FLOAT:
+        case PA_STRING:    /* %s */
+        case PA_POINTER:   /* %p */
+        default:
+            pm_asprintf(errorP, "Conversion specifier requires an argument of "
+                        "a type that this program never provides for "
+                        "any replacement sequence");
+        }
+    }
+}
+#endif
 
-     "#(fread x)"
 
-   seqContents[] is the contents; 'seqContentsLen' its length.
 
-   Return as *newCursorP where the line[] cursor ends up after adding
-   the sequence.
+static void
+validateFormatWithPpf(const char *       const format,
+                      SkeletonObjectType const ctyp,
+                      const char **      const errorP) {
+/*----------------------------------------------------------------------------
+  Validate format string 'format' for use with a skeleton of type 'ctyp',
+  using the system parse_printf_format() function.
+
+  Return as *errorP an explanation of how it is invalid, or a null string
+  if it is valid.
 -----------------------------------------------------------------------------*/
-    unsigned int cursor;
-    unsigned int i;
+    /* We request parse_printf_format() to report the details of the first
+       8 conversions.  8 because the maximum length of format is 16 means it
+       can have up to 8 conversions: "%d%d%d%d%d%d%d%d".
+
+       Actually this is more than necessary: we are concerned with only the
+       first conversion and whether there it is the only one.
+    */
 
-    cursor = startCursor;
+    int printfConversion[MAXFORMAT/2] = {0, 0, 0, 0, 0, 0, 0, 0};
 
-    line[cursor++] = escape;
-    line[cursor++] = '(';
+    size_t const n =
+        parse_printf_format(format, MAXFORMAT/2, printfConversion);
+
+    switch (n) {
+    case 0:
+        pm_asprintf(errorP, "No transformation found");
+        break;
+
+    case 1:
+        validateParsePrintfFLag(printfConversion[0], ctyp, errorP);
+        break;
+
+    default:
+        pm_asprintf(errorP, "Has %lu extra transformation%s ",
+                    n-1, n-1 > 1 ? "s" : "");
+        break;
+    }
+}
 
-    for (i = 0; i < seqContentsLen; ++i)
-        line[cursor++] = seqContents[i];
 
-    line[cursor++] = ')';
 
-    *newCursorP = cursor;
+static void
+validateFormatOne(char               const typeSpecifier,
+                  bool               const isLastInString,
+                  SkeletonObjectType const ctyp,
+                  bool *             const validatedP,
+                  const char **      const errorP) {
+
+    switch (typeSpecifier) {
+        /* Valid character encountered.  Skip. */
+        /* ' ' (space) is listed here, but should never occur for
+           we use sscanf() to parse the fields.
+        */
+    case ' ': case '-': case '+': case '\'': case '#': case '.':
+    case '0': case '1': case '2': case '3': case '4': case '5':
+    case '6': case '7': case '8': case '9':
+        break;
+        
+    case 'c': case 'C':
+        pm_message("Warning: char type conversion: %%%c.", typeSpecifier);
+    case 'i': case 'd': case 'u': case 'o': case 'x': case 'X':
+        if (!isLastInString)
+            pm_asprintf(errorP, "Extra characters at end");
+        else if(objClass(ctyp) != OBJTYP_ICOLOR &&
+                objClass(ctyp) != OBJTYP_INT )
+            pm_asprintf(errorP, "Conversion type mismatch");
+        else
+            *validatedP = true;
+        break;
+
+    case 'f': case 'F': case 'g': case 'G': case 'a': case 'A':
+        if (!isLastInString)
+            pm_asprintf(errorP, "Extra characters at end");
+        else if(objClass(ctyp) != OBJTYP_FCOLOR)
+            pm_asprintf(errorP, "Conversion type mismatch");
+        else
+            *validatedP = true;
+        break;
+
+    case '\0':
+        pm_asprintf(errorP, "No conversion specified");
+        break;
+    case '%':
+        pm_asprintf(errorP, "No more than one %% is allowed");
+        break;
+    case '$':
+    case '*':
+        pm_asprintf(errorP, "%c is not allowed", typeSpecifier);
+        break;
+    case 'h': case 'l': case 'L': case 'q': case 'j': case 'Z': case 't':
+        pm_asprintf(errorP, "Modifier %c is not allowed in format",
+                    typeSpecifier);
+        break;
+    case 's': case 'S': case 'm': case 'p': case 'n':
+        pm_asprintf(errorP, "Invalid conversion type");
+        break;
+    default:
+        pm_asprintf(errorP, "Abnormal character");
+        break;
+    }
 }
 
 
 
-static int
-read_skeleton(const char *   const filename,
-              unsigned int   const maxskl,
-              unsigned int * const nsklP,
-              SKL_OBJ **     const skl) {
+static void
+validateFormatGen(const char *       const format,
+                  SkeletonObjectType const ctyp,
+                  const char **      const errorP)  {
 /*----------------------------------------------------------------------------
-  Read skeleton file
+  Validate format string 'format' for use with a skeleton of type 'ctyp',
+  without using the system parse_printf_format() function.
+
+  The string must begin with "%" and end with the translation type character
+  ("%d", "%x", "%f", etc.)
+
+  We check only for invalid characters.  Invalid constructs, such as
+  "%00.00.00d" will pass this test.
+
+  Return as *errorP an explanation of how it is invalid, or a null string
+  if it is valid.
 -----------------------------------------------------------------------------*/
-    FILE * sklfile;
-    unsigned int slen;
-    unsigned int objlen;
-    int chr;
-    SKL_OBJ_TYP otyp;
-    char line[MAX_LINE_BUF+MAX_OBJ_BUF+16];
-    char objstr[MAX_OBJ_BUF],typstr[MAX_OBJ_BUF];
-    unsigned int nskl;
-
-#define SAVE_BIN(slen,line) \
-    { if (slen > 0 && (skl[nskl] = save_bin_data(slen,line)) != NULL) ++nskl; \
-      slen = 0; }
-
-    sklfile = pm_openr(filename);
-
-    /* Parse skeleton file */
-    nskl = 0;  /* initial value */
-
-    slen = 0;
-    while ((chr = getc (sklfile)) != EOF) {  /* Up to end of skeleton file */
-        if (nskl >= maxskl)
-            return -1;
-
-        if (slen+1 >= MAX_LINE_BUF) {
-            /* Buffer finished.  Save as binary object */
-            SAVE_BIN(slen,line);
+    if (format[0] != '%')
+        pm_asprintf(errorP, "Does not start with %%");
+    else {
+        unsigned int i;
+        bool validated;
+
+        for (i = 1, validated = false, *errorP = NULL;
+             !validated && !*errorP;
+             ++i) {
+
+            validateFormatOne(format[i], format[i+1] == '\0', ctyp,
+                              &validated, errorP);
         }
+    }
+}
 
-        if (chr != escape) {
-            /* Not a replacement sequence; just a literal character */
-            line[slen++] = chr;
-            continue;
-        }
 
-        chr = getc(sklfile);
-        if (chr == EOF) {
-            /* Not a valid replacement sequence */
-            line[slen++] = escape;
-            break;
-        }
-        if (chr != '(') {
-            /* Not a valid replacement sequence */
-            line[slen++] = escape;
-            line[slen++] = chr;
-            continue;
+
+static void
+validateFormat(const char *       const format,
+               SkeletonObjectType const ctyp) {
+
+    const char * error;
+
+    if (strlen(format) > MAXFORMAT)
+        pm_asprintf(&error, "Too long");
+    else {
+#if HAVE_PARSE_PRINTF_FORMAT
+        if (true)
+            validateFormatWithPpf(format, ctyp, &error);
+        else  /* Silence compiler warning about unused function */
+            validateFormatGen(format, ctyp, &error);
+#else
+        validateFormatGen(format, ctyp, &error);
+#endif
+    }
+
+    if (error)
+        pm_error("Invalid format string '%s'.  %s", format, error);
+}              
+               
+
+
+static SkeletonObject *
+newBinDataObj(unsigned int const nDat, 
+              const char * const bdat) {
+/*----------------------------------------------------------------------------
+  Create a binary data object.
+-----------------------------------------------------------------------------*/
+    SkeletonObject * objectP;
+
+    objectP = malloc(sizeof(*objectP) + nDat);
+
+    if (!objectP)
+        pm_error("Failed to allocate memory for binary data object "
+                 "with %u bytes", nDat);
+
+    objectP->objType = BDATA;
+    objectP->odata.binData.ndat = nDat;
+    objectP->odata.binData.bdat = ((char *)objectP) + sizeof(SkeletonObject);
+    memcpy(objectP->odata.binData.bdat, bdat, nDat);
+
+    return objectP;
+}
+
+
+
+static SkeletonObject *
+newIcolDataObj(SkeletonObjectType const ctyp,
+               const char *       const format,
+               unsigned int       const icolmin,
+               unsigned int       const icolmax) {
+/*----------------------------------------------------------------------------
+  Create integer color data object.
+-----------------------------------------------------------------------------*/
+    SkeletonObject * objectP;
+
+    MALLOCVAR(objectP);
+
+    if (!objectP)
+        pm_error("Failed to allocate memory for an integer color data "
+                 "object");
+
+    objectP->objType = ctyp;
+    validateFormat(format, ctyp);
+    strcpy(objectP->odata.icolData.icformat, format);
+    objectP->odata.icolData.icolmin = icolmin;
+    objectP->odata.icolData.icolmax = icolmax;
+
+    return objectP;
+}
+
+
+
+static SkeletonObject *
+newFcolDataObj(SkeletonObjectType  const ctyp,
+               const char *        const format,
+               double              const fcolmin,
+               double              const fcolmax) {
+/*----------------------------------------------------------------------------
+  Create float color data object.
+-----------------------------------------------------------------------------*/
+    SkeletonObject * objectP;
+
+    MALLOCVAR(objectP);
+
+    if (!objectP)
+        pm_error("Failed to allocate memory for a float color data object");
+
+    objectP->objType = ctyp;
+    validateFormat(format, ctyp);
+    strcpy(objectP->odata.fcolData.fcformat, format);
+    objectP->odata.fcolData.fcolmin = fcolmin;
+    objectP->odata.fcolData.fcolmax = fcolmax;
+
+    return objectP;
+}
+
+
+
+static SkeletonObject *
+newIdataObj(SkeletonObjectType const ctyp,
+            const char *       const format) {
+/*----------------------------------------------------------------------------
+  Create universal data object.
+-----------------------------------------------------------------------------*/
+    SkeletonObject * objectP;
+
+    MALLOCVAR(objectP);
+
+    if (!objectP)
+        pm_error("Failed to allocate memory for a universal data object");
+
+    objectP->objType = ctyp;
+    validateFormat(format, ctyp);
+    strcpy(objectP->odata.iData.iformat, format);
+
+    return objectP;
+}
+
+
+
+static char const escape = '#';
+
+
+
+static SkeletonObjectType
+interpretObjType(const char * const typstr) {
+
+    SkeletonObjectType objType;
+
+    /* handle integer colors */
+    if      (streq(typstr, "ired")  ) objType = IRED;
+    else if (streq(typstr, "igreen")) objType = IGREEN;
+    else if (streq(typstr, "iblue") ) objType = IBLUE;
+    else if (streq(typstr, "ilum")  ) objType = ILUM;
+    /* handle real colors */
+    else if (streq(typstr, "fred")  ) objType = FRED;
+    else if (streq(typstr, "fgreen")) objType = FGREEN;
+    else if (streq(typstr, "fblue") ) objType = FBLUE;
+    else if (streq(typstr, "flum")  ) objType = FLUM;
+    /* handle integer data */
+    else if (streq(typstr, "width") ) objType = WIDTH;
+    else if (streq(typstr, "height")) objType = HEIGHT;
+    else if (streq(typstr, "posx")  ) objType = POSX;
+    else if (streq(typstr, "posy")  ) objType = POSY;
+    else                              objType = BDATA;
+
+    return objType;
+}
+
+
+
+static SkeletonObject *
+newIcSkelFromReplString(const char *       const objstr,
+                        SkeletonObjectType const objType) {
+
+    SkeletonObject * retval;
+    unsigned int icolmin, icolmax;
+    char formstr[MAX_OBJ_BUF];
+    unsigned int nOdata;
+
+    nOdata = sscanf(objstr, "%*s%s%u%u", formstr, &icolmin, &icolmax);
+
+    if (nOdata == 3)
+        retval = newIcolDataObj(objType, formstr, icolmin, icolmax);
+    else if (nOdata == EOF) {
+        /* No arguments specified.  Use defaults */
+        retval = newIcolDataObj(objType, "%u", 0, 255);
+    } else
+        retval = NULL;
+
+    return retval;
+}
+
+
+
+static SkeletonObject *
+newFcSkelFromReplString(const char *       const objstr,
+                        SkeletonObjectType const objType) {
+
+    SkeletonObject * retval;
+    double fcolmin, fcolmax;
+    char formstr[MAX_OBJ_BUF];
+    unsigned int nOdata;
+
+    nOdata = sscanf(objstr, "%*s%s%lf%lf", formstr,
+                    &fcolmin, &fcolmax);
+
+    if (nOdata == 3)
+        retval = newFcolDataObj(objType, formstr, fcolmin, fcolmax);
+    else if (nOdata == EOF) {
+        /* No arguments specified.  Use defaults */
+        retval = newFcolDataObj(objType, "%f", 0.0, 1.0);
+    } else
+        retval = NULL;
+
+    return retval;
+} 
+
+
+
+static SkeletonObject *
+newISkelFromReplString(const char *       const objstr,
+                       SkeletonObjectType const objType) {
+
+    SkeletonObject * retval;
+    char formstr[MAX_OBJ_BUF];
+    unsigned int const nOdata = sscanf(objstr, "%*s%s", formstr);
+    
+    if (nOdata == 1)
+        retval = newIdataObj(objType, formstr);
+    else if (nOdata == EOF) {
+        /* No arguments specified.  Use defaults */
+        retval = newIdataObj(objType, "%u");
+    } else
+        retval = NULL;
+
+    return retval;
+} 
+
+
+
+static SkeletonObject *
+newSkeletonFromReplString(const char * const objstr) {
+/*----------------------------------------------------------------------------
+  Create a skeleton from the replacement string 'objstr' (the stuff
+  between the parentheses in #(...) ).
+
+  Return NULL if it isn't a valid replacement string.
+-----------------------------------------------------------------------------*/
+    /* We use sscanf() to parse the contents of objstr, giving it a format
+       template with the largest number of fields possible plus one extra to
+       pick up and check for the existence of invalid trailing characters.  We
+       read and discard fields beyond the first, if any.  The appropriate
+       new**SkelFromReplString() function determines their contents with a
+       separate call to sscanf().
+    */
+
+    SkeletonObject * retval;
+    char typstr[MAX_OBJ_BUF];
+    SkeletonObjectType objType;
+    int conversionCt;
+    char s1[MAX_OBJ_BUF];    /* Dry read. */
+    char s2[MAX_OBJ_BUF];    /* Extra tailing characters. */
+    float f1, f2;            /* Dry read. */ 
+
+    typstr[0] = '\0';  /* initial value */
+
+    conversionCt = sscanf(objstr, "%s%s%f%f%s", typstr, s1, &f1, &f2, s2);
+    switch (conversionCt) {
+    case 1: case 2: case 4:
+        objType = interpretObjType(typstr);
+      break;
+    default:
+        objType = BDATA;
+    }
+
+    switch (objClass(objType)) {
+    case OBJTYP_ICOLOR:
+        retval = newIcSkelFromReplString(objstr, objType);
+        break;
+    case OBJTYP_FCOLOR:
+        retval = newFcSkelFromReplString(objstr, objType);
+        break;
+    case OBJTYP_INT:
+        retval = newISkelFromReplString(objstr, objType);
+        break;
+    case OBJTYP_BDATA:
+        retval = NULL;
+    }
+    return retval;
+}
+
+
+
+static void
+readThroughCloseParen(FILE * const ifP,
+                      char * const objstr,
+                      size_t const objstrSize,
+                      bool * const unclosedP) {
+/*----------------------------------------------------------------------------
+   Read *ifP up through close parenthesis ( ')' ) into 'objstr', which
+   is of size 'objstrSize'.  Make it a NUL-terminated string.
+
+   Return *unclosedP true iff we run out of file or run out of objstr
+   before we see a close parenthesis.  In this case, return the rest of
+   the file, or as much as fits, in 'objstr', not NUL-terminated.
+-----------------------------------------------------------------------------*/
+    unsigned int i;
+    bool eof;
+    bool gotEscSeq;
+
+    for (i= 0, eof = false, gotEscSeq = false;
+         i < objstrSize - 1 && !gotEscSeq && !eof;
+         ++i) {
+
+        int rc;
+
+        rc = getc(ifP);
+        if (rc == EOF)
+            eof = true;
+        else {
+            char const chr = rc;
+            if (chr == ')') {
+                gotEscSeq = true;
+                objstr[i] = '\0';
+	        } else
+                objstr[i] = chr;
         }
-        /* Read replacement string up through ')'.  Put contents of
-           parentheses in objstr[].
+    }
+    *unclosedP = !gotEscSeq;
+}
+
+
+
+typedef struct {
+    unsigned int      capacity;
+    SkeletonObject ** skeletonPList;
+    unsigned int      nSkeleton;
+} SkeletonBuffer;
+
+
+
+static void
+SkeletonBuffer_init(SkeletonBuffer *  const bufferP,
+                    unsigned int      const capacity,
+                    SkeletonObject ** const skeletonPList) {
+
+    bufferP->capacity      = capacity;
+    bufferP->skeletonPList = skeletonPList;
+    bufferP->nSkeleton     = 0;
+}
+
+
+
+static void
+SkeletonBuffer_add(SkeletonBuffer * const bufferP,
+                   SkeletonObject * const skeletonP) {
+
+    if (bufferP->nSkeleton >= bufferP->capacity)
+        pm_error("Too many skeletons.  Max = %u", bufferP->capacity);
+
+    bufferP->skeletonPList[bufferP->nSkeleton++] = skeletonP;
+}                   
+
+
+
+typedef struct {
+
+    char data[MAX_LINE_BUF + MAX_OBJ_BUF + 16];
+
+    unsigned int length;
+
+    SkeletonBuffer * skeletonBufferP;
+        /* The buffer to which we flush.  Flushing means turning all the
+           characters currently in our buffer into a binary skeleton object
+           here.
         */
-        for (objlen = 0; objlen < sizeof(objstr)-1; ++objlen) {
-            chr = getc(sklfile);
-            if (chr == EOF) break;
-            if (chr == ')') break;
-            objstr[objlen] = chr;
-        }
-        objstr[objlen] = '\0';
-
-        if (chr != ')') {
-            /* Not valid replacement sequence */
-            unsigned int i;
-            line[slen++] = escape;
-            line[slen++] = chr;
-            for (i = 0; i < objlen; ++i)
-                line[slen++] = objstr[i];
-            if (chr == EOF)
-                break;
-            continue;
-        }
 
-        typstr[0] = '\0';           /* Get typ of data */
-        sscanf(objstr, "%s", typstr);
-
-        otyp = interpretObjType(typstr);
-
-        switch (objClass(otyp)) {
-        case OBJTYP_ICOLOR: {
-            int icolmin, icolmax;
-            char formstr[MAX_OBJ_BUF];
-            int n_odata;
-
-            n_odata = sscanf(objstr, "%*s%s%d%d", formstr, &icolmin, &icolmax);
-
-            if (n_odata == 3) {
-                SAVE_BIN(slen, line);
-                skl[nskl] = save_icol_data(otyp, formstr, icolmin, icolmax);
-                if (skl[nskl] != NULL)
-                    ++nskl;
-            } else if (n_odata == EOF) {
-                /* No arguments specified.  Use defaults */
-                SAVE_BIN(slen, line);
-                skl[nskl] = save_icol_data(otyp, "%d", 0, 255);
-                if (skl[nskl] != NULL)
-                    ++nskl;
-            } else
-                addImpostorReplacementSeq(line, slen, objstr, objlen, &slen);
-        } break;
-        case OBJTYP_FCOLOR: {
-            double fcolmin, fcolmax;
-            char formstr[MAX_OBJ_BUF];
-            int n_odata;
-
-            n_odata = sscanf(objstr, "%*s%s%lf%lf", formstr,
-                             &fcolmin, &fcolmax);
-
-            if (n_odata == 3) {
-                SAVE_BIN(slen, line);
-                skl[nskl] = save_fcol_data(otyp, formstr, fcolmin, fcolmax);
-                if (skl[nskl] != NULL)
-                    ++nskl;
-            } else if (n_odata == EOF) {
-                /* No arguments specified.  Use defaults */
-                SAVE_BIN(slen, line);
-                skl[nskl] = save_fcol_data(otyp, "%f", 0.0, 1.0);
-                if (skl[nskl] != NULL)
-                    ++nskl;
-            } else
-                addImpostorReplacementSeq(line, slen, objstr, objlen, &slen);
-        } break;
-
-        case OBJTYP_INT: {
-            char formstr[MAX_OBJ_BUF];
-            int const n_odata = sscanf(objstr, "%*s%s", formstr);
-
-            if (n_odata == 1) {
-                SAVE_BIN(slen, line);
-                skl[nskl] = save_i_data(otyp, formstr);
-                if (skl[nskl] != NULL)
-                    ++nskl;
-            } else if (n_odata == EOF) {
-                /* No arguments specified.  Use defaults */
-                SAVE_BIN(slen, line);
-                skl[nskl] = save_i_data(otyp, "%d");
-                if (skl[nskl] != NULL)
-                    ++nskl;
-            } else
-                addImpostorReplacementSeq(line, slen, objstr, objlen, &slen);
-        } break;
-        case OBJTYP_BDATA:
-            addImpostorReplacementSeq(line, slen, objstr, objlen, &slen);
+} Buffer;
+
+
+
+static void
+Buffer_init(Buffer *         const bufferP,
+            SkeletonBuffer * const skeletonBufferP) {
+
+    bufferP->skeletonBufferP = skeletonBufferP;
+    bufferP->length = 0;
+}
+
+
+
+static void
+Buffer_flush(Buffer * const bufferP) {
+/*----------------------------------------------------------------------------
+   Flush the buffer out to a binary skeleton object.
+-----------------------------------------------------------------------------*/
+    SkeletonBuffer_add(bufferP->skeletonBufferP,
+                       newBinDataObj(bufferP->length, bufferP->data));
+
+    bufferP->length = 0;
+}
+
+
+
+static void
+Buffer_add(Buffer * const bufferP,
+           char     const newChar) {
+
+    if (bufferP->length >= MAX_LINE_BUF)
+        Buffer_flush(bufferP);
+
+    assert(bufferP->length < MAX_LINE_BUF);
+
+    bufferP->data[bufferP->length++] = newChar;
+}
+
+
+
+static void
+Buffer_dropFinalNewline(Buffer * const bufferP) {
+/*----------------------------------------------------------------------------
+   If the last thing in the buffer is a newline, remove it.
+-----------------------------------------------------------------------------*/
+    if (bufferP->length >= 1 && bufferP->data[bufferP->length-1] == '\n') {
+            /* Drop finishing newline character */
+            --bufferP->length;
+    }
+}
+
+
+
+static void
+addImpostorReplacementSeq(Buffer *     const bufferP,
+                          const char * const seqContents) {
+/*----------------------------------------------------------------------------
+  Add to buffer *bufferP something that looks like a replacement sequence but
+  doesn't have the proper contents (the stuff between the parentheses) to be
+  one.  For example,
+
+  "#(fread x)"
+
+  seqContents[] is the contents, NUL-terminated.
+-----------------------------------------------------------------------------*/
+    const char * p;
+
+    Buffer_add(bufferP, escape);
+    Buffer_add(bufferP, '(');
+
+    for (p = &seqContents[0]; *p; ++p)
+        Buffer_add(bufferP, *p);
+
+    Buffer_add(bufferP, ')');
+}
+
+
+
+static void
+readSkeletonFile(const char *      const filename,
+                 unsigned int      const maxskl,
+                 const char **     const errorP,
+                 unsigned int *    const nSkeletonP,
+                 SkeletonObject ** const skeletonPList) {
+/*----------------------------------------------------------------------------
+-----------------------------------------------------------------------------*/
+    FILE * sklfileP;
+    SkeletonBuffer skeletonBuffer;
+        /* A buffer for accumulating skeleton objects */
+    Buffer buffer;
+        /* A buffer for accumulating binary (literal; unsubstituted) data, on
+           its way to becoming a binary skeleton object. 
+        */
+    bool eof;
+    const char * error;
+
+    SkeletonBuffer_init(&skeletonBuffer, maxskl, skeletonPList);
+
+    Buffer_init(&buffer, &skeletonBuffer);
+
+    sklfileP = pm_openr(filename);
+
+    for (eof = false, error = NULL; !eof && !error; ) {
+
+        int rc;
+
+        rc = getc(sklfileP);
+
+        if (rc == EOF)
+            eof = true;
+        else {
+            char const chr = rc;
+
+            if (chr != escape) {
+                /* Not a replacement sequence; just a literal character */
+                Buffer_add(&buffer, chr);
+            } else {
+                int rc;
+                rc = getc(sklfileP);
+                if (rc == EOF) {
+                    /* Not a replacement sequence, just an escape caharacter
+                       at the end of the file.
+                    */
+                    Buffer_add(&buffer, escape);
+                    eof = true;
+                } else {
+                    char const chr = rc;
+
+                    if (chr != '(') {
+                        /* Not a replacement sequence, just a lone escape
+                           character
+                        */
+                        Buffer_add(&buffer, escape);
+                        Buffer_add(&buffer, chr);
+                    } else {
+                        char objstr[MAX_OBJ_BUF];
+                        bool unclosed;
+                        readThroughCloseParen(sklfileP,
+                                              objstr, sizeof(objstr),
+                                              &unclosed);
+                        if (unclosed)
+                            pm_asprintf(&error, "Unclosed parentheses "
+                                        "in #() escape sequence");
+                        else {
+                            SkeletonObject * const skeletonP =
+                                newSkeletonFromReplString(objstr);
+
+                            if (skeletonP) {
+                                Buffer_flush(&buffer);
+                                SkeletonBuffer_add(&skeletonBuffer, skeletonP);
+                            } else
+                                addImpostorReplacementSeq(&buffer, objstr);
+                        }
+                    }
+                }
+            }
         }
-    } /* EOF of skeleton file */
+    }
 
-    if (slen >= 1 && line[slen-1] == '\n')
-        /* Drop finishing newline character */
-        --slen;
+    if (!error) {
+        Buffer_dropFinalNewline(&buffer);
+        Buffer_flush(&buffer);
+    }
+    *errorP = error;
+    *nSkeletonP = skeletonBuffer.nSkeleton;
 
-    SAVE_BIN(slen, line);  /* Save whatever is left */
+    fclose(sklfileP);
+}
 
-    *nsklP = nskl;
 
-    fclose(sklfile);
-    return 0;
+
+static void
+convertIt(FILE *            const ifP,
+          FILE *            const ofP,
+          SkeletonObject ** const bodySkeletonPList,
+          unsigned int      const bodyNskl,
+          SkeletonObject ** const headSkeletonPList, 
+          unsigned int      const headNskl,
+          SkeletonObject ** const tailSkeletonPList,
+          unsigned int      const tailNskl) {
+
+    pixel * pixelrow;
+    pixval maxval;
+    double dmaxval;
+    int rows, cols;
+    int format;
+    unsigned int row;
+
+    ppm_readppminit(ifP, &cols, &rows, &maxval, &format);
+
+    pixelrow = ppm_allocrow(cols);
+
+    dmaxval = (double)maxval;
+
+    /* Write header */
+    writeText(ofP, headNskl, headSkeletonPList, 
+              cols, rows , 0, 0, 0.0, 0.0, 0.0);
+
+    /* Write raster */
+    for (row = 0; row < rows; ++row) {
+        unsigned int col;
+
+        ppm_readppmrow(ifP, pixelrow, cols, maxval, format);
+
+        for (col = 0; col < cols; ++col) {
+            pixel const thisPixel = pixelrow[col];
+
+            writeText(ofP, bodyNskl, bodySkeletonPList,
+                      cols, rows, col, row,
+                      PPM_GETR(thisPixel)/dmaxval,
+                      PPM_GETG(thisPixel)/dmaxval,
+                      PPM_GETB(thisPixel)/dmaxval);
+        }
+    }
+
+    /* Write trailer */
+    writeText(ofP, tailNskl, tailSkeletonPList, 
+              cols, rows, 0, 0, 0.0, 0.0, 0.0);
 }
 
 
 
-int main( argc, argv )
-int argc;
-char* argv[];
-
-{register int col;
- register pixel* xP;
- pixel* pixelrow;
- pixval maxval,red,green,blue;
- double dmaxval;
- int argn, rows, cols, format, row;
- unsigned int head_nskl,body_nskl,tail_nskl;
- SKL_OBJ *head_skl[MAX_SKL_HEAD_OBJ];
- SKL_OBJ *body_skl[MAX_SKL_BODY_OBJ];
- SKL_OBJ *tail_skl[MAX_SKL_TAIL_OBJ];
- FILE *ifp;
- const char *usage = "bodyskl [ -hd headskl ] [ -tl tailskl ] [pnmfile]";
-
- ppm_init( &argc, argv );
-
- argn = 1;
- if (argn == argc)
-   pm_usage( usage );
-                          /* Read body skeleton file */
- if (read_skeleton (argv[argn],sizeof (body_skl)/sizeof (SKL_OBJ *),
-                    &body_nskl,body_skl) < 0)
-   pm_usage ( usage );
- ++argn;
-
- head_nskl = tail_nskl = 0;
-
- while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0')
- {
-   if ( pm_keymatch ( argv[argn], "-hd", 1) && argn+1 < argc )
-   {
-     argn++;           /* Read header skeleton */
-     if (read_skeleton (argv[argn],sizeof (head_skl)/sizeof (SKL_OBJ *),
-                        &head_nskl,head_skl) < 0)
-       pm_usage ( usage );
-   }
-   else if ( pm_keymatch ( argv[argn], "-tl", 1) && argn+1 < argc )
-   {
-     argn++;           /* Read tail skeleton */
-     if (read_skeleton (argv[argn],sizeof (tail_skl)/sizeof (SKL_OBJ *),
-                        &tail_nskl,tail_skl) < 0)
-       pm_usage ( usage );
-   }
-   else
-   {
-     pm_usage ( usage );
-   }
-   argn++;
- }
-
- if ( argn != argc )
- {
-   ifp = pm_openr( argv[argn] );
-   ++argn;
- }
- else 
- {
-   ifp = stdin;
- }
-
- if ( argn != argc )
-   pm_usage( usage );
-
- ppm_readppminit( ifp, &cols, &rows, &maxval, &format );
- pixelrow = ppm_allocrow( cols );
- dmaxval = (double)maxval;
-
- if (head_nskl > 0)    /* Write header */
-   write_txt (stdout,head_nskl,head_skl,cols,rows,0,0,0.0,0.0,0.0);
-
- for ( row = 0; row < rows; ++row )
- {
-   ppm_readppmrow( ifp, pixelrow, cols, maxval, format );
-
-   for ( col = 0, xP = pixelrow; col < cols; ++col, ++xP )
-   {
-     red = PPM_GETR( *xP );
-     green = PPM_GETG( *xP );
-     blue = PPM_GETB( *xP );
-     write_txt (stdout,body_nskl,body_skl,cols,rows,col,row,
-                red/dmaxval,green/dmaxval,blue/dmaxval);
-   }
- }
-
- if (tail_nskl > 0)    /* Write trailer */
-   write_txt (stdout,tail_nskl,tail_skl,cols,rows,0,0,0.0,0.0,0.0);
-
- pm_close( ifp );
-
- exit( 0 );
+int
+main(int           argc,
+     const char ** argv) {
+    
+    struct CmdlineInfo cmdline;
+
+    unsigned int headNskl, bodyNskl, tailNskl;
+    SkeletonObject * headSkeletonPList[MAX_SKL_HEAD_OBJ];
+    SkeletonObject * bodySkeletonPList[MAX_SKL_BODY_OBJ];
+    SkeletonObject * tailSkeletonPList[MAX_SKL_TAIL_OBJ];
+    FILE * ifP;
+    const char * error;
+
+    pm_proginit(&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    ifP = pm_openr(cmdline.inputFileName);
+
+    readSkeletonFile(cmdline.bodySklFileName, ARRAY_SIZE(bodySkeletonPList),
+                     &error, &bodyNskl, bodySkeletonPList);
+    if (error)
+        pm_error("Invalid body skeleton file '%s'.  %s",
+                 cmdline.bodySklFileName, error);
+
+    if (cmdline.hd) {
+        readSkeletonFile(cmdline.hd, ARRAY_SIZE(headSkeletonPList),
+                         &error, &headNskl, headSkeletonPList);
+        if (error)
+            pm_error("Invalid head skeleton file '%s'.  %s",
+                     cmdline.hd, error);
+    } else
+        headNskl = 0;
+
+    if (cmdline.tl) {
+        readSkeletonFile(cmdline.tl, ARRAY_SIZE(tailSkeletonPList),
+                         &error, &tailNskl, tailSkeletonPList);
+        if (error)
+            pm_error("Invalid tail skeleton file '%s'.  %s",
+                     cmdline.tl, error);
+    } else
+        tailNskl = 0;
+
+    if (cmdline.debug)
+        dumpAllSkeleton(bodySkeletonPList, bodyNskl,
+                        headSkeletonPList, headNskl,
+                        tailSkeletonPList, tailNskl);
+
+    convertIt(ifP, stdout,
+              bodySkeletonPList, bodyNskl,
+              headSkeletonPList, headNskl,
+              tailSkeletonPList, tailNskl);
+
+    pm_close(ifP);
+
+    return 0;
 }
+
+
diff --git a/converter/ppm/ppmtobmp.c b/converter/ppm/ppmtobmp.c
index 1fc82ecb..24b1b3e5 100644
--- a/converter/ppm/ppmtobmp.c
+++ b/converter/ppm/ppmtobmp.c
@@ -63,11 +63,11 @@ freeColorMap(const colorMap * const colorMapP) {
 
 
 
-struct cmdlineInfo {
+struct CmdlineInfo {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    char *input_filename;
+    const char * inputFilename;
     int class;  /* C_WIN or C_OS2 */
     unsigned int bppSpec;
     unsigned int bpp;
@@ -77,7 +77,7 @@ struct cmdlineInfo {
 
 static void
 parseCommandLine(int argc, const char ** argv,
-                 struct cmdlineInfo * const cmdlineP) {
+                 struct CmdlineInfo * const cmdlineP) {
 /*----------------------------------------------------------------------------
    Note that many of the strings that this function returns in the
    *cmdline_p structure are actually in the supplied argv array.  And
@@ -131,9 +131,9 @@ parseCommandLine(int argc, const char ** argv,
         cmdlineP->mapfile = NULL;
 
     if (argc - 1 == 0)
-        cmdlineP->input_filename = strdup("-");  /* he wants stdin */
+        cmdlineP->inputFilename = strdup("-");  /* he wants stdin */
     else if (argc - 1 == 1)
-        cmdlineP->input_filename = strdup(argv[1]);
+        cmdlineP->inputFilename = strdup(argv[1]);
     else 
         pm_error("Too many arguments.  The only argument accepted "
                  "is the input file specificaton");
@@ -915,7 +915,7 @@ int
 main(int           argc,
      const char ** argv) {
 
-    struct cmdlineInfo cmdline;
+    struct CmdlineInfo cmdline;
     FILE * ifP;
     int rows;
     int cols;
@@ -926,7 +926,7 @@ main(int           argc,
 
     parseCommandLine(argc, argv, &cmdline);
 
-    ifP = pm_openr(cmdline.input_filename);
+    ifP = pm_openr(cmdline.inputFilename);
     
     ppm_readppminit(ifP, &cols, &rows, &maxval, &ppmFormat);