diff options
41 files changed, 1955 insertions, 895 deletions
diff --git a/analyzer/pnmhistmap.c b/analyzer/pnmhistmap.c index f41b0d51..fc1bbbde 100644 --- a/analyzer/pnmhistmap.c +++ b/analyzer/pnmhistmap.c @@ -426,7 +426,7 @@ ppmHist(FILE * const ifP, clipHistogramAll(hist, histWidth, hmax); vscale = (double) histHeight / hmax; - if (verbose) + if (verbose && pm_have_float_format()) pm_message("Done: height = %u, vertical scale factor = %g", hmax, vscale); @@ -477,7 +477,7 @@ reportScale(unsigned int const histWidth, double const hscale = (float)(histWidth-1) / (range-1); - if (hscale - 1.0 < epsilon && verbose) + if (hscale - 1.0 < epsilon && verbose && pm_have_float_format()) pm_message("Horizontal scale factor: %g", hscale); } diff --git a/buildtools/README.pkg b/buildtools/README.pkg index f098e04b..6761ecf3 100644 --- a/buildtools/README.pkg +++ b/buildtools/README.pkg @@ -145,11 +145,11 @@ entire installation when you don't want it anymore, and to keep multiple versions around. -netpbm.pkgconfig ----------------- +netpbm.pc +--------- -You should create a file named 'netpbm.pkgconfig' out of the template file -'pkgconfig_template' in the package directory, and install netpbm.pkgconfig in +You should create a file named 'netpbm.pc' out of the template file +'pkgconfig_template' in the package directory, and install netpbm.pc in your pkg-config directory (typically /usr/lib/pkgconfig). Programs that want to find out where you installed some part of Netpbm can use a 'pkg-config netpbm ...' command to find out. For example, a make file for a program that @@ -175,4 +175,4 @@ Using netpbm-config, it's possible to have a viable Netpbm installation where netpbm.config is the only file in any default search path. The xxx-config concept (in general, not just Netpbm) has largely been replaced -by the pkg-config concept (see netpbm.pkgconfig above). +by the pkg-config concept (see netpbm.pc above). diff --git a/buildtools/debian/README b/buildtools/debian/README index 1c600c6b..02fae4b5 100644 --- a/buildtools/debian/README +++ b/buildtools/debian/README @@ -30,15 +30,15 @@ To install Netpbm as a Debian package: 3) $ dpkg --install netpbm-sfXXXX.deb - (netpbm-sfXXXX.deb is the file created by 'makedeb', in the current + (netpbm-sfXXXX.deb is the file created by 'make deb', in the current directory). PREREQUSISITES -------------- -The following information was taken from the Wheezy version of Debian, in -January 2014. +The following information was taken from the Wheezy version (Version 7) of +Debian, in January 2014. You don't actually need the current version of any of these. For example, while we list package libjpeg8-dev, the package libjpeg62-dev works fine. 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); diff --git a/doc/HISTORY b/doc/HISTORY index 037b074a..cefdfa35 100644 --- a/doc/HISTORY +++ b/doc/HISTORY @@ -4,21 +4,45 @@ Netpbm. CHANGE HISTORY -------------- -14.12.25 BJH Release 10.68.03 +14.12.26 BJH Release 10.69.00 + + pnmnorm: add -bsingle, -wsingle. + + ppmtoarbtxt: Do some validation of format strings. Thanks + Prophet of the Way <afu@wta.att.ne.jp>. + + pamcrater: Add -verbose. + + ppmtoarbtxt: Fail if a #() escape sequence runs off end of + file or is too long to process; before, the program would + treat the text from # to EOF or where the buffer filled up + as literal text, even ignoring any #() within. + + NetBSD: show actual numbers in messages instead of "f" or + no information, by using NetBSD's vasprintf. + + Make %g in messages display the actual number instead of "g" in + messages where platform doesn't have vasprintf. (But scores of + %f are still left). + + anytopnm: convert all images in a multi-image GIF instead of + just the first. + + Improve "bad magic number" message from pbmXXX, and pgmXXX, and + pnmXXX programs. Fix bogus message from ppmXXX programs when the input is not (per the magic number) a Netpbm image. Introduced after Netpbm 10.26 (January 2005) but before Netpbm 10.35 (August 2006). -14.11.23 BJH Release 10.68.02 + ppmtoarbtxt: Fix some undefined behavior when program limits + are exceeded (i.e. buffer overruns). pambackground: fix bug: segfault or incorrect results in most cases. Thanks Ludolf Holzheid (ludolf.holzheid@gmx.de). Introduced in Netpbm 10.37 (December 2006). -14.09.28 BJH Release 10.68.01 - Windows build: fix universal build failure with "No rule to make ...icon.netpbm.oLINKERISCOMPILER...". Broken in Netpbm 10.67 (June 2014). @@ -48,7 +72,7 @@ CHANGE HISTORY pcdovtoppm: Fix crash due to invalid operator == on some systems. Always broken (pcdovtoppm was new sometime between - Netpbm 9.25 (March 2002) and Netpbm 10.11 (Februrary 2010)). + Netpbm 9.25 (March 2002) and Netpbm 10.11 (October 2002)). Build: change _XOPEN_SOURCE from 600 back to 500 in 7 files. It was changed from 500 to 600 in Subversion revision 1731 in diff --git a/editor/pnmnorm.c b/editor/pnmnorm.c index 70d5641a..131b39d0 100644 --- a/editor/pnmnorm.c +++ b/editor/pnmnorm.c @@ -51,6 +51,8 @@ struct cmdlineInfo { xelval wvalue; unsigned int wpercentSpec; float wpercent; + unsigned int bsingle; + unsigned int wsingle; float middle; unsigned int midvalueSpec; xelval midvalue; @@ -101,6 +103,10 @@ parseCommandLine(int argc, const char ** argv, &cmdlineP->bvalue, &cmdlineP->bvalueSpec, 0); OPTENT3(0, "wvalue", OPT_UINT, &cmdlineP->wvalue, &cmdlineP->wvalueSpec, 0); + OPTENT3(0, "bsingle", OPT_FLAG, + NULL, &cmdlineP->bsingle, 0); + OPTENT3(0, "wsingle", OPT_FLAG, + NULL, &cmdlineP->wsingle, 0); OPTENT3(0, "middle", OPT_FLOAT, &cmdlineP->middle, &middleSpec, 0); OPTENT3(0, "midvalue", OPT_UINT, @@ -150,6 +156,12 @@ parseCommandLine(int argc, const char ** argv, pm_error("You specified a per centage > 100 for bpercent: %f", cmdlineP->bpercent); + if (cmdlineP->bsingle && (cmdlineP->bpercentSpec || cmdlineP->bvalueSpec)) + pm_error("You cannot specify both -bsingle and -bpercent or -bvalue"); + + if (cmdlineP->wsingle && (cmdlineP->wpercentSpec || cmdlineP->wvalueSpec)) + pm_error("You cannot specify both -wsingle and -wpercent or -wvalue"); + if (middleSpec) { if (cmdlineP->middle < 0.0 || cmdlineP->middle > 1.0) pm_error("-middle is a normalized brightness value; it " @@ -250,6 +262,50 @@ buildHistogram(FILE * const ifp, +static xelval +minimumValue(const unsigned int * const hist, + unsigned int const highest) { + + xelval i; + bool foundOne; + + for (i = 0, foundOne = false; !foundOne; ) { + if (hist[i] > 0) + foundOne = true; + else { + if (i == highest) + pm_error("INTERNAL ERROR in '%s'. No pixels", __FUNCTION__); + else + ++i; + } + } + return i; +} + + + +static xelval +maximumValue(const unsigned int * const hist, + unsigned int const highest) { + + xelval i; + bool foundOne; + + for (i = highest, foundOne = false; !foundOne; ) { + if (hist[i] > 0) + foundOne = true; + else { + if (i == 0) + pm_error("INTERNAL ERROR in '%s'. No pixels", __FUNCTION__); + else + --i; + } + } + return i; +} + + + static void computeBottomPercentile(unsigned int hist[], unsigned int const highest, @@ -259,7 +315,7 @@ computeBottomPercentile(unsigned int hist[], /*---------------------------------------------------------------------------- Compute the lowest index of hist[] such that the sum of the hist[] values with that index and lower represent at least 'percent' per cent of - 'n' (which is assumed to be the sum of all the values in hist[], + 'total' (which is assumed to be the sum of all the values in hist[], given to us to save us the time of computing it). -----------------------------------------------------------------------------*/ unsigned int cutoff = total * percent / 100.0; @@ -291,7 +347,7 @@ computeTopPercentile(unsigned int hist[], /*---------------------------------------------------------------------------- Compute the highest index of hist[] such that the sum of the hist[] values with that index and higher represent 'percent' per cent of - 'n' (which is assumed to be the sum of all the values in hist[], + 'total' (which is assumed to be the sum of all the values in hist[], given to us to save us the time of computing it). -----------------------------------------------------------------------------*/ unsigned int cutoff = total * percent / 100.0; @@ -450,7 +506,7 @@ resolvePercentParams(FILE * const ifP, /*---------------------------------------------------------------------------- Figure out the endpoint of the stretch (the value that is to be stretched to black and the one that is to be stretched to white) as requested - by the -bvalue, -bpercent, -wvalue, and -wpercent options. + by the -{b,w}{value,percent,single} options. These values may be invalid because of overlapping, and they may exceed the maximum allowed stretch; Caller must deal with that. @@ -465,7 +521,9 @@ resolvePercentParams(FILE * const ifP, buildHistogram(ifP, cols, rows, maxval, format, hist, cmdline.brightMethod); - if (cmdline.bvalueSpec && !cmdline.bpercentSpec) { + if (cmdline.bsingle) + *bvalueP = minimumValue(hist, maxval); + else if (cmdline.bvalueSpec && !cmdline.bpercentSpec) { *bvalueP = cmdline.bvalue; } else { xelval percentBvalue; @@ -477,7 +535,9 @@ resolvePercentParams(FILE * const ifP, *bvalueP = percentBvalue; } - if (cmdline.wvalueSpec && !cmdline.wpercentSpec) { + if (cmdline.wsingle) + *wvalueP = maximumValue(hist, maxval); + else if (cmdline.wvalueSpec && !cmdline.wpercentSpec) { *wvalueP = cmdline.wvalue; } else { xelval percentWvalue; diff --git a/generator/pamcrater.c b/generator/pamcrater.c index d61ce548..50745501 100644 --- a/generator/pamcrater.c +++ b/generator/pamcrater.c @@ -62,8 +62,10 @@ struct CmdlineInfo { unsigned int width; unsigned int randomseedSpec; unsigned int randomseed; + unsigned int verbose; unsigned int test; unsigned int radius; + int offset; }; @@ -81,7 +83,7 @@ parseCommandLine(int argc, const char ** const argv, optStruct3 opt; unsigned int option_def_index; - unsigned int numberSpec, heightSpec, widthSpec, radiusSpec; + unsigned int numberSpec, heightSpec, widthSpec, radiusSpec, offsetSpec; MALLOCARRAY_NOFAIL(option_def, 100); @@ -94,10 +96,14 @@ parseCommandLine(int argc, const char ** const argv, &widthSpec, 0); OPTENT3(0, "randomseed", OPT_UINT, &cmdlineP->randomseed, &cmdlineP->randomseedSpec, 0); + OPTENT3(0, "verbose", OPT_FLAG, NULL, + &cmdlineP->verbose, 0); OPTENT3(0, "test", OPT_FLAG, NULL, &cmdlineP->test, 0); OPTENT3(0, "radius", OPT_UINT, &cmdlineP->radius, &radiusSpec, 0); + OPTENT3(0, "offset", OPT_INT, &cmdlineP->offset, + &offsetSpec, 0); opt.opt_table = option_def; opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ @@ -122,6 +128,9 @@ parseCommandLine(int argc, const char ** const argv, if (cmdlineP->width == 0) pm_error("-width must be positive"); + if (!offsetSpec) + cmdlineP->offset=0; + if (cmdlineP->test) { if (!radiusSpec) pm_error("With -test, you must specify -radius"); @@ -139,6 +148,9 @@ parseCommandLine(int argc, const char ** const argv, if (radiusSpec) pm_error("-radius is meaningful only with -test"); + if (offsetSpec) + pm_error("-offset is meaningful only with -test"); + if (!numberSpec) cmdlineP->number = 50000; @@ -149,9 +161,6 @@ parseCommandLine(int argc, const char ** const argv, } -/* Definitions for obtaining random numbers. */ - -/* Display parameters */ static double const arand = 32767.0; /* Random number parameters */ static double const CdepthPower = 1.5; /* Crater depth power factor */ @@ -161,7 +170,9 @@ static double const DepthBias2 = 0.5; /* Square of depth bias */ static double const cast(double const high) { - +/*---------------------------------------------------------------------------- + A random number in the range [0, 'high']. +-----------------------------------------------------------------------------*/ return high * ((rand() & 0x7FFF) / arand); } @@ -196,8 +207,15 @@ terrainModP(struct pam * const pamP, tuple ** const terrain, int const x, int const y) { +/*---------------------------------------------------------------------------- + A pointer to the sample in 'terrain' of an image described by *pamP that is + at Column 'x' of Row 'y', but modulus the image size. - return &terrain[mod(y, pamP->height)][mod(x, pamP->width)][0]; + So e.g. if the image is 10 x 10 and 'x' and 'y' are both 12, our value + would be a pointer to the sample at Column 2 or Row 2. If they are both + -1, we would point to Column 9, Row 9. +-----------------------------------------------------------------------------*/ + return &terrain[mod(x, pamP->height)][mod(y, pamP->width)][0]; } @@ -208,25 +226,49 @@ terrainMod(struct pam * const pamP, tuple ** const terrain, int const x, int const y) { +/*---------------------------------------------------------------------------- + The value of the sample in 'terrain' of an image described by *pamP that is + at Column 'x' of Row 'y', but modulus the image size. + So e.g. if the image is 10 x 10 and 'x' and 'y' are both 12, our value + would be the value of the sample at Column 2 or Row 2. If they are both + -1, we would return Column 9, Row 9. +-----------------------------------------------------------------------------*/ return *terrainModP(pamP, terrain, x, y); } static void +setElev(struct pam * const pamP, + tuple ** const terrain, + int const cx, + int const cy, + unsigned int const elevation) { + + *terrainModP(pamP, terrain, cx, cy) = MIN(pamP->maxval, elevation); +} + + + +static void smallCrater(struct pam * const pamP, tuple ** const terrain, int const cx, int const cy, - double const g) { + double const radius) { /*---------------------------------------------------------------------------- Generate a crater with a special method for tiny craters. + + Center the crater at Column 'cx', Row 'cy'; wrap as necessary to get them + on the canvas. These might even be negative. -----------------------------------------------------------------------------*/ int y; unsigned int amptot; unsigned int npatch; + assert(radius < 3); + /* Set pixel to the average of its Moore neighborhood. */ for (y = cy - 1, amptot = 0, npatch = 0; y <= cy + 1; ++y) { @@ -238,12 +280,48 @@ smallCrater(struct pam * const pamP, } { unsigned int const axelev = amptot / npatch; + /* The mean elevation of the Moore neighborhood (9 pixels + centered on the crater location). + */ /* Perturb the mean elevation by a small random factor. */ - int const x = g >= 1 ? ((rand() >> 8) & 3) - 1 : 0; - *terrainModP(pamP, terrain, cx, cy) = axelev + x; + int const x = radius >= 1 ? ((rand() >> 8) & 0x3) - 1 : 0; + + assert(axelev > 0); + + setElev(pamP, terrain, cx, cy, axelev + x); + } +} + + + +static unsigned int +meanElev(struct pam * const pamP, + tuple ** const terrain, + int const cx, + int const cy, + double const radius) { +/*---------------------------------------------------------------------------- + The mean elevation in 'terrain', which is described by *pamP, within + 'radius' pixels vertically and horizontally of (cx, cy). + + We assume the area is a fraction the whole 'terrain'. +-----------------------------------------------------------------------------*/ + unsigned int amptot; + unsigned int npatch; + int y; + + for (y = cy - radius, amptot = 0, npatch = 0; y <= cy + radius; ++y) { + int x; + for (x = cx - radius; x <= cx + radius; ++x) { + amptot += terrainMod(pamP, terrain, x, y); + ++npatch; + } } + assert(npatch > 0); + + return amptot / npatch; } @@ -258,39 +336,24 @@ normalCrater(struct pam * const pamP, Generate a regular (not tiny) crater. Generate an impact feature of the correct size and shape. ------------------------------------------------------------------------------*/ +----------------------------------------------------------------------------*/ int const impactRadius = (int) MAX(2, (radius / 3)); int const craterRadius = (int) radius; double const rollmin = 0.9; int y; - unsigned int amptot, axelev; - unsigned int npatch; - /* Determine mean elevation around the impact area. - We assume the impact area is a fraction of the total crater size. */ - - for (y = cy - impactRadius, amptot = 0, npatch = 0; - y <= cy + impactRadius; - ++y) { - int x; - for (x = cx - impactRadius; x <= cx + impactRadius; ++x) { - amptot += terrainMod(pamP, terrain, x, y); - ++npatch; - } - } - assert(npatch > 0); - axelev = amptot / npatch; + unsigned int const axelev = meanElev(pamP, terrain, cx, cy, impactRadius); + /* The mean elevation of the impact area, before impact */ for (y = cy - craterRadius; y <= cy + craterRadius; ++y) { - int const dysq = (cy - y) * (cy - y); + int const dysq = SQR(cy - y); int x; for (x = cx - craterRadius; x <= cx + craterRadius; ++x) { - int const dxsq = (cx - x) * (cx - x); - double const cd = (dxsq + dysq) / - (double) (craterRadius * craterRadius); + int const dxsq = SQR(cx - x); + double const cd = (dxsq + dysq) / (double) SQR(craterRadius); double const cd2 = cd * 2.25; double const tcz = sqrt(DepthBias2) - sqrt(fabs(1 - cd2)); double cz; @@ -312,7 +375,7 @@ normalCrater(struct pam * const pamP, (terrainMod(pamP, terrain, x, y) + cz) * roll; av = MAX(1000, MIN(64000, av)); - *terrainModP(pamP, terrain, x, y) = av; + setElev(pamP, terrain, x, y, av); } } } @@ -329,7 +392,12 @@ plopCrater(struct pam * const pamP, tuple ** const terrain, int const cx, int const cy, - double const radius) { + double const radius, + bool const verbose) { + + if (verbose && pm_have_float_format()) + pm_message("Plopping crater at (%4d, %4d) with radius %g", + cx, cy, radius); if (radius < 3) smallCrater (pamP, terrain, cx, cy, radius); @@ -340,40 +408,58 @@ plopCrater(struct pam * const pamP, static void +initCanvas(unsigned int const width, + unsigned int const height, + struct pam * const pamP, + tuple *** const terrainP) { +/*---------------------------------------------------------------------------- + Initialize the output image to a flat area of middle elevation. +-----------------------------------------------------------------------------*/ + tuple ** terrain; /* elevation array */ + unsigned int row; + + pamP->size = sizeof(*pamP); + pamP->len = PAM_STRUCT_SIZE(tuple_type); + pamP->file = stdout; + pamP->format = PAM_FORMAT; + pamP->height = height; + pamP->width = width; + pamP->depth = 1; + pamP->maxval = 65535; + pamP->bytes_per_sample = 2; + STRSCPY(pamP->tuple_type, "elevation"); + + terrain = pnm_allocpamarray(pamP); + + for (row = 0; row < pamP->height; ++row) { + unsigned int col; + for (col = 0; col < pamP->width; ++col) + terrain[row][col][0] = pamP->maxval / 2; + } + *terrainP = terrain; +} + + + +static void genCraters(struct CmdlineInfo const cmdline) { /*---------------------------------------------------------------------------- Generate cratered terrain -----------------------------------------------------------------------------*/ tuple ** terrain; /* elevation array */ - unsigned int row; struct pam pam; /* Allocate the elevation array and initialize it to mean surface elevation. */ - pam.size = sizeof(pam); - pam.len = PAM_STRUCT_SIZE(tuple_type); - pam.file = stdout; - pam.format = PAM_FORMAT; - pam.height = cmdline.height; - pam.width = cmdline.width; - pam.depth = 1; - pam.maxval = 65535; - pam.bytes_per_sample = 2; - STRSCPY(pam.tuple_type, "elevation"); - - terrain = pnm_allocpamarray(&pam); + initCanvas(cmdline.width, cmdline.height, &pam, &terrain); - for (row = 0; row < pam.height; ++row) { - unsigned int col; - for (col = 0; col < pam.width; ++col) - terrain[row][col][0] = pam.maxval / 2; - } - if (cmdline.test) plopCrater(&pam, terrain, - pam.width/2, pam.height/2, (double) cmdline.radius); + pam.width/2 + cmdline.offset, + pam.height/2 + cmdline.offset, + (double) cmdline.radius, cmdline.verbose); else { unsigned int const ncraters = cmdline.number; /* num of craters */ unsigned int l; @@ -391,7 +477,7 @@ genCraters(struct CmdlineInfo const cmdline) { */ double const radius = sqrt(1 / (M_PI * (1 - cast(0.9999)))); - plopCrater(&pam, terrain, cx, cy, radius); + plopCrater(&pam, terrain, cx, cy, radius, cmdline.verbose); if (((l + 1) % 100000) == 0) pm_message("%u craters generated of %u (%u%% done)", diff --git a/generator/ppmrough.c b/generator/ppmrough.c index 5d5bdeda..e749c9c2 100644 --- a/generator/ppmrough.c +++ b/generator/ppmrough.c @@ -15,6 +15,7 @@ #include <sys/time.h> #include "pm_c_util.h" +#include "mallocvar.h" #include "shhopt.h" #include "ppm.h" @@ -38,14 +39,16 @@ struct CmdlineInfo { static void parseCommandLine(int argc, const char ** argv, - struct CmdlineInfo * const cmdlineP) -{ - optEntry *option_def = malloc(100*sizeof(optEntry)); - /* Instructions to OptParseOptions2 on how to parse our options. */ + struct CmdlineInfo * const cmdlineP) { + + optEntry * option_def; + /* Instructions to OptParseOptions2 on how to parse our options. */ optStruct3 opt; unsigned int option_def_index; + MALLOCARRAY(option_def, 100); + option_def_index = 0; /* incremented by OPTENTRY */ OPTENT3(0, "width", OPT_UINT, &cmdlineP->width, NULL, 0); OPTENT3(0, "height", OPT_UINT, &cmdlineP->height, NULL, 0); diff --git a/lib/libpbm2.c b/lib/libpbm2.c index a8e4b0f6..a77fa0ae 100644 --- a/lib/libpbm2.c +++ b/lib/libpbm2.c @@ -87,10 +87,13 @@ pbm_readpbminit(FILE * const ifP, int * const rowsP, int * const formatP) { - *formatP = pm_readmagicnumber(ifP); + int realFormat; - switch (PAM_FORMAT_TYPE(*formatP)) { + realFormat = pm_readmagicnumber(ifP); + + switch (PAM_FORMAT_TYPE(realFormat)) { case PBM_TYPE: + *formatP = realFormat; pbm_readpbminitrest(ifP, colsP, rowsP); break; @@ -110,7 +113,8 @@ pbm_readpbminit(FILE * const ifP, "to PBM with 'pamtopnm'"); break; default: - pm_error("bad magic number - not a Netpbm file"); + pm_error("bad magic number 0x%x - not a PPM, PGM, PBM, or PAM file", + realFormat); } validateComputableSize(*colsP, *rowsP); } diff --git a/lib/libpgm1.c b/lib/libpgm1.c index 57344c16..de5aedde 100644 --- a/lib/libpgm1.c +++ b/lib/libpgm1.c @@ -167,7 +167,8 @@ pgm_readpgminit(FILE * const fileP, break; default: - pm_error("bad magic number - not a Netpbm file"); + pm_error("bad magic number 0x%x - not a PPM, PGM, PBM, or PAM file", + realFormat); } validateComputableSize(*colsP, *rowsP); } diff --git a/lib/libpm.c b/lib/libpm.c index 228f42c6..fa8ae4db 100644 --- a/lib/libpm.c +++ b/lib/libpm.c @@ -278,6 +278,19 @@ pm_error(const char format[], ...) { +bool +pm_have_float_format(void) { +/*---------------------------------------------------------------------------- + Return true iff %f, %e, and %g work in format strings for pm_message, etc. + + Where they don't "work," that means the specifier just appears itself in + the formatted strings, e.g. "the number is g". +-----------------------------------------------------------------------------*/ + return pm_vasprintf_knows_float(); +} + + + static void * mallocz(size_t const size) { diff --git a/lib/libpnm1.c b/lib/libpnm1.c index 29fee13a..045b7afc 100644 --- a/lib/libpnm1.c +++ b/lib/libpnm1.c @@ -129,7 +129,8 @@ pnm_readpnminit(FILE * const fileP, break; default: - pm_error("bad magic number - not a ppm, pgm, or pbm file"); + pm_error("bad magic number 0x%x - not a PPM, PGM, PBM, or PAM file", + realFormat); } validateComputableSize(*colsP, *rowsP); } diff --git a/lib/pm.h b/lib/pm.h index 72ecc919..a24ea7bb 100644 --- a/lib/pm.h +++ b/lib/pm.h @@ -23,6 +23,8 @@ #include <sys/stat.h> #include <fcntl.h> +#include "pm_c_util.h" + #ifdef __cplusplus extern "C" { #endif @@ -219,6 +221,9 @@ pm_errormsg(const char format[], ...); void PM_GNU_PRINTF_ATTR(1,2) pm_error (const char reason[], ...); +bool +pm_have_float_format(void); + /* Obsolete - use shhopt and user's manual instead */ void pm_usage (const char usage[]); diff --git a/lib/util/matrix.c b/lib/util/matrix.c index 5101f2c3..e9456e93 100644 --- a/lib/util/matrix.c +++ b/lib/util/matrix.c @@ -106,10 +106,14 @@ findLargestIthCoeff(unsigned int const n, maxSoFar = thisA; } } - if (maxSoFar < epsilon) - pm_asprintf(errorP, "Matrix equation has no unique solution. " - "(debug: coeff %u %e < %e)", i, maxSoFar, epsilon); - else { + if (maxSoFar < epsilon) { + const char * const baseMsg = "Matrix equation has no unique solution"; + if (pm_have_float_format()) + pm_asprintf(errorP, "%s. (debug: coeff %u %e < %e)", + baseMsg, i, maxSoFar, epsilon); + else + pm_asprintf(errorP, "%s", baseMsg); + } else { *istarP = maxIdx; *errorP = NULL; } diff --git a/lib/util/nstring.c b/lib/util/nstring.c index 1fe66a27..74618422 100644 --- a/lib/util/nstring.c +++ b/lib/util/nstring.c @@ -221,8 +221,8 @@ pm_vsnprintf(char * const str, const char *q = strchr(p + 1,'%'); size_t n = !q ? strlen(p) : (q - p); if (str_l < str_m) { - size_t avail = str_m - str_l; - fast_memcpy(str + str_l, p, (n > avail ? avail : n)); + size_t const avail = str_m - str_l; + fast_memcpy(str + str_l, p, (MIN(n, avail))); } p += n; str_l += n; } else { @@ -231,7 +231,6 @@ pm_vsnprintf(char * const str, size_t precision = 0; bool precision_specified; bool justify_left; - bool zero_padding; bool alternate_form; bool force_sign; bool space_for_positive; @@ -252,16 +251,18 @@ pm_vsnprintf(char * const str, argument for the c conversion is unsigned. */ - size_t number_of_zeros_to_pad = 0; + bool zero_padding; + + size_t number_of_zeros_to_pad; /* number of zeros to be inserted for numeric conversions as required by the precision or minimal field width */ - size_t zero_padding_insertion_ind = 0; + size_t zero_padding_insertion_ind; /* index into tmp where zero padding is to be inserted */ - char fmt_spec = '\0'; + char fmt_spec; /* current conversion specifier character */ str_arg = credits; @@ -272,10 +273,14 @@ pm_vsnprintf(char * const str, /* parse flags */ justify_left = false; /* initial value */ - zero_padding = false; /* initial value */ alternate_form = false; /* initial value */ force_sign = false; /* initial value */ space_for_positive = false; /* initial value */ + zero_padding = false; /* initial value */ + number_of_zeros_to_pad = 0; /* initial value */ + zero_padding_insertion_ind = 0; /* initial value */ + fmt_spec = '\0'; /* initial value */ + while (*p == '0' || *p == '-' || *p == '+' || *p == ' ' || *p == '#' || *p == '\'') { switch (*p) { @@ -408,9 +413,7 @@ pm_vsnprintf(char * const str, else { /* memchr on HP does not like n > 2^31 !!! */ const char * q = - memchr(str_arg, '\0', - precision <= 0x7fffffff ? - precision : 0x7fffffff); + memchr(str_arg, '\0', MIN(precision, 0x7fffffff)); str_arg_l = !q ? precision : (q-str_arg); } break; @@ -570,9 +573,9 @@ pm_vsnprintf(char * const str, */ if (zero_padding_insertion_ind < str_arg_l && tmp[zero_padding_insertion_ind] == '-') { - zero_padding_insertion_ind++; + zero_padding_insertion_ind += 1; } - if (zero_padding_insertion_ind+1 < str_arg_l && + if (zero_padding_insertion_ind + 1 < str_arg_l && tmp[zero_padding_insertion_ind] == '0' && (tmp[zero_padding_insertion_ind+1] == 'x' || tmp[zero_padding_insertion_ind+1] == 'X') ) { @@ -580,7 +583,7 @@ pm_vsnprintf(char * const str, } } { - size_t num_of_digits = + size_t const num_of_digits = str_arg_l - zero_padding_insertion_ind; if (alternate_form && fmt_spec == 'o' /* unless zero is already the first character */ @@ -606,9 +609,10 @@ pm_vsnprintf(char * const str, } /* zero padding to specified minimal field width? */ if (!justify_left && zero_padding) { - int n = + int const n = min_field_width - (str_arg_l+number_of_zeros_to_pad); - if (n > 0) number_of_zeros_to_pad += n; + if (n > 0) + number_of_zeros_to_pad += n; } } break; case 'f': { @@ -654,12 +658,12 @@ pm_vsnprintf(char * const str, if (!justify_left) { /* left padding with blank or zero */ - int n = min_field_width - (str_arg_l+number_of_zeros_to_pad); + int n = min_field_width - (str_arg_l + number_of_zeros_to_pad); if (n > 0) { if (str_l < str_m) { - size_t avail = str_m-str_l; - fast_memset(str+str_l, (zero_padding ? '0' : ' '), - (n > avail ? avail : n)); + size_t const avail = str_m - str_l; + fast_memset(str + str_l, (zero_padding ? '0' : ' '), + (MIN(n, avail))); } str_l += n; } @@ -673,27 +677,31 @@ pm_vsnprintf(char * const str, */ zero_padding_insertion_ind = 0; } else { - /* insert first part of numerics (sign or '0x') before - zero padding - */ - int n = zero_padding_insertion_ind; - if (n > 0) { - if (str_l < str_m) { - size_t avail = str_m-str_l; - fast_memcpy(str+str_l, str_arg, (n>avail?avail:n)); + { + /* insert first part of numerics (sign or '0x') before + zero padding + */ + int const n = zero_padding_insertion_ind; + if (n > 0) { + if (str_l < str_m) { + size_t const avail = str_m - str_l; + fast_memcpy(str + str_l, str_arg, (MIN(n, avail))); + } + str_l += n; } - str_l += n; } - /* insert zero padding as requested by the precision - or min field width - */ - n = number_of_zeros_to_pad; - if (n > 0) { - if (str_l < str_m) { - size_t avail = str_m - str_l; - fast_memset(str + str_l, '0', (n > avail ? avail : n)); + { + /* insert zero padding as requested by the precision + or min field width + */ + int const n = number_of_zeros_to_pad; + if (n > 0) { + if (str_l < str_m) { + size_t const avail = str_m - str_l; + fast_memset(str + str_l, '0', (MIN(n, avail))); + } + str_l += n; } - str_l += n; } } /* insert formatted string (or as-is conversion specifier @@ -706,7 +714,7 @@ pm_vsnprintf(char * const str, size_t const avail = str_m-str_l; fast_memcpy(str + str_l, str_arg + zero_padding_insertion_ind, - (n > avail ? avail : n)); + MIN(n, avail)); } str_l += n; } @@ -714,11 +722,12 @@ pm_vsnprintf(char * const str, /* insert right padding */ if (justify_left) { /* right blank padding to the field width */ - int n = min_field_width - (str_arg_l+number_of_zeros_to_pad); + int const n = + min_field_width - (str_arg_l + number_of_zeros_to_pad); if (n > 0) { if (str_l < str_m) { - size_t avail = str_m-str_l; - fast_memset(str+str_l, ' ', (n>avail?avail:n)); + size_t const avail = str_m - str_l; + fast_memset(str+str_l, ' ', (MIN(n, avail))); } str_l += n; } @@ -730,7 +739,7 @@ pm_vsnprintf(char * const str, of overwriting the last character (shouldn't happen, but just in case) */ - str[str_l <= str_m-1 ? str_l : str_m-1] = '\0'; + str[MIN(str_l, str_m - 1)] = '\0'; } *sizeP = str_l; } diff --git a/lib/util/nstring.h b/lib/util/nstring.h index 28adda94..7238a76e 100644 --- a/lib/util/nstring.h +++ b/lib/util/nstring.h @@ -175,6 +175,9 @@ pm_vasprintf(const char ** const resultP, const char * const format, va_list args); +bool +pm_vasprintf_knows_float(void); + void pm_strfree(const char * const string); diff --git a/lib/util/vasprintf.c b/lib/util/vasprintf.c index e38252fa..a947f763 100644 --- a/lib/util/vasprintf.c +++ b/lib/util/vasprintf.c @@ -6,6 +6,8 @@ #include <string.h> #include "pm_config.h" +#include "pm_c_util.h" + #include "nstring.h" @@ -38,6 +40,12 @@ pm_vasprintf(const char ** const resultP, So instead, we just allocate 4K and truncate or waste as necessary. + + Note that we don't recognize the floating point specifiers (%f, %e, %g) + - we render them as 'f', 'e', and 'g'. It would be too much work to + make this code handle those, just for the few systems on which it runs. + Instead, we have pm_vasprintf_knows_float(), and any caller that cares + enough can avoid using these specifiers where they don't work. */ size_t const allocSize = 4096; result = malloc(allocSize); @@ -56,3 +64,14 @@ pm_vasprintf(const char ** const resultP, } #endif } + + + +bool +pm_vasprintf_knows_float(void) { +#if HAVE_VASPRINTF + return true; +#else + return false; +#endif +} diff --git a/pm_config.in.h b/pm_config.in.h index ed1ebb89..1e54f06e 100644 --- a/pm_config.in.h +++ b/pm_config.in.h @@ -139,7 +139,7 @@ /* #define HAVE_SETMODE */ -#if (defined(__GLIBC__) || defined(__GNU_LIBRARY__) || defined(__APPLE__)) +#if (defined(__GLIBC__) || defined(__GNU_LIBRARY__) || defined(__APPLE__)) || defined(__NetBSD__) #define HAVE_VASPRINTF 1 #else #define HAVE_VASPRINTF 0 diff --git a/test/Test-Order b/test/Test-Order index cc79570e..7467ba0b 100644 --- a/test/Test-Order +++ b/test/Test-Order @@ -55,6 +55,8 @@ pamflip2.test pamenlarge.test pnminvert.test pamchannel.test +ppmchange.test +pambackground.test pbmpscale.test pnmremap1.test diff --git a/test/gif-roundtrip.test b/test/gif-roundtrip.test index cb62bfb1..8d1a9bbe 100755 --- a/test/gif-roundtrip.test +++ b/test/gif-roundtrip.test @@ -27,7 +27,7 @@ test_grn=${tmpdir}/testimg.grn test_blu=${tmpdir}/testimg.blu out_red=${tmpdir}/out.red out_grn=${tmpdir}/out.grn -out_blu=${tmpdir}/out.blu +#out_blu=${tmpdir}/out.blu pamtogif ${test_red} | giftopnm > ${out_red} && pamtogif ${test_grn} | giftopnm > ${out_grn} && @@ -35,8 +35,7 @@ pamtogif ${test_blu} | giftopnm | \ rgb3toppm ${out_red} ${out_grn} - | \ cksum -rm ${test_ppm} ${test_grn} ${test_blu} \ - ${out_red} ${out_grn} ${out_blu} +rm ${test_ppm} ${test_grn} ${test_blu} ${out_red} ${out_grn} # Test 2. Should produce 1571496937 33838 diff --git a/test/pambackground.ok b/test/pambackground.ok new file mode 100644 index 00000000..acb69bfa --- /dev/null +++ b/test/pambackground.ok @@ -0,0 +1,3 @@ +2155020792 451 +2514682516 33871 +2667595257 17328 diff --git a/test/pambackground.test b/test/pambackground.test new file mode 100755 index 00000000..58526fdd --- /dev/null +++ b/test/pambackground.test @@ -0,0 +1,42 @@ +#! /bin/bash +# This script tests: pambackground +# Also requires: pamgradient pamseq pbmmake pnmmargin pnmremap pnmtile + + alias pambackground="${PBM_TESTPREFIX}pambackground" + alias pamgradient="${PBM_BINPREFIX}pamgradient" + alias pamseq="${PBM_BINPREFIX}pamseq" + alias pbmmake="${PBM_BINPREFIX}pbmmake" + alias pnmmargin="${PBM_BINPREFIX}pnmmargin" + alias pnmremap="${PBM_BINPREFIX}pnmremap" + alias pnmtile="${PBM_BINPREFIX}pnmtile" + shopt -s expand_aliases + +tmpdir=${tmpdir:-/tmp} + +# Test 1. +# Should produce: 2155020792 451 +pbmmake -g 23 11 | pnmmargin -black 2 | pambackground | cksum + + +# Test 2. +# Should produce: 2514682516 33871 +ibmttl_pam=${tmpdir}/ibmttl.pam + +pamseq 3 1 > ${ibmttl_pam} && \ +pnmremap -quiet -mapfile ${ibmttl_pam} testimg.ppm | pambackground | cksum +rm ${ibmttl_pam} + + +# Test 3. +# Should produce: 2667595257 17328 +pamgradient rgb:01/01/01 rgb:ff/7f/00 rgb:00/ff/7f rgb:fe/fe/fe 10 10 | \ +pnmmargin -white 2 | pnmtile 144 120 | pambackground | cksum + + +# pamgradient rgb:01/01/01 rgb:ff/7f/00 rgb:00/ff/7f rgb:fe/fe/fe 10 10 | \ +# pnmmargin -white 2 | pnmtile 144 120 | cksum +# should produce: 3147800256 51855 +# Above input image is a "wall" with 12x10 "windows". + +# pnmremap -mapfile ${ibmttl_pam} testimg.ppm | cksum +# should produce : 452672747 101517 diff --git a/test/pamcrater.test b/test/pamcrater.test index 1d182208..cc937731 100755 --- a/test/pamcrater.test +++ b/test/pamcrater.test @@ -1,13 +1,21 @@ #! /bin/bash # This script tests: pamcrater pamshadedrelief -# Also requires: pamslice pamvalidate +# Also requires: pamstack pamvalidate pamcut pamflip alias pamcrater="${PBM_TESTPREFIX}pamcrater" - alias pamslice="${PBM_TESTPREFIX}pamshadedrelief" - alias pamslice="${PBM_BINPREFIX}pamslice" - alias pamvalidate="${PBM_TESTPREFIX}pamvalidate" + alias pamshadedrelief="${PBM_TESTPREFIX}pamshadedrelief" + alias pamstack="${PBM_BINPREFIX}pamstack" + alias pamvalidate="${PBM_BINPREFIX}pamvalidate" + alias pamcut="${PBM_BINPREFIX}pamcut" + alias pamflip="${PBM_BINPREFIX}pamflip" + shopt -s expand_aliases +# We use the undocumented --test and --radius options of pamcrater. +# pamcrater --test --radius=N +# The above draws a single crater of radius N. +# The resulting image should be symmetric. + tmpdir=${tmpdir:-/tmp} test_pam=${tmpdir}/test.pam @@ -29,11 +37,11 @@ pamstack ${test10_pam} ${test50_pam} ${test100_pam} ${test150_pam} | for i in 1 10 70 do - ( pamslice -row=$((128 + $i)) ${test_pam} | cksum && - pamslice -row=$((128 - $i)) ${test_pam} | cksum && - pamslice -col=$((128 + $i)) ${test_pam} | cksum && - pamslice -col=$((128 - $i)) ${test_pam} | cksum - ) | uniq -c | awk '{print $1}' + ( pamcut -top=$((128 + $i)) -height=1 ${test_pam} | cksum && + pamcut -top=$((128 - $i)) -height=1 ${test_pam} | cksum && + pamcut -left=$((128 + $i)) -width=1 ${test_pam} | pamflip -xy | cksum && + pamcut -left=$((128 - $i)) -width=1 ${test_pam} | pamflip -xy | cksum + ) | uniq -c | awk '{print $1}' done rm ${test_pam} ${test10_pam} ${test50_pam} @@ -42,12 +50,12 @@ rm ${test_pam} ${test10_pam} ${test50_pam} pamshadedrelief ${test100_pam} > ${testshaded_pam} -( pamslice -row=$((128 + 12)) ${testshaded_pam} | cksum && - pamslice -row=$((128 - 12)) ${testshaded_pam} | cksum && - pamslice -row=$((128 + 31)) ${testshaded_pam} | cksum && - pamslice -row=$((128 - 31)) ${testshaded_pam} | cksum && - pamslice -row=$((128 + 99)) ${testshaded_pam} | cksum && - pamslice -row=$((128 - 99)) ${testshaded_pam} | cksum +( pamcut -top=$((128 + 12)) -height=1 ${testshaded_pam} | cksum && + pamcut -top=$((128 - 12)) -height=1 ${testshaded_pam} | cksum && + pamcut -top=$((128 + 31)) -height=1 ${testshaded_pam} | cksum && + pamcut -top=$((128 - 31)) -height=1 ${testshaded_pam} | cksum && + pamcut -top=$((128 + 99)) -height=1 ${testshaded_pam} | cksum && + pamcut -top=$((128 - 99)) -height=1 ${testshaded_pam} | cksum ) | uniq -c | awk '{print $1}' rm ${testshaded_pam} ${test100_pam} diff --git a/test/pamenlarge.ok b/test/pamenlarge.ok index 055262c9..a2408871 100644 --- a/test/pamenlarge.ok +++ b/test/pamenlarge.ok @@ -1,4 +1,4 @@ 3424505894 913236 3763267672 304422 3342398172 297 -237488670 3133413 +398497872 6806 diff --git a/test/pamenlarge.test b/test/pamenlarge.test index 8f9e3c70..ced156dd 100755 --- a/test/pamenlarge.test +++ b/test/pamenlarge.test @@ -1,8 +1,11 @@ #! /bin/bash # This script tests: pamenlarge -# Also requires: pamchannel +# Also requires: pamchannel pamseq pamtopnm alias pamenlarge="${PBM_TESTPREFIX}pamenlarge" + alias pamchannel="${PBM_TESTPREFIX}pamchannel" + alias pamseq="${PBM_TESTPREFIX}pamseq" + alias pamchannel="${PBM_TESTPREFIX}pamchannel" shopt -s expand_aliases # Test 1. Should print 3424505894 913236 @@ -12,5 +15,5 @@ pamchannel -infile=testimg.ppm -tupletype="GRAYSCALE" 0 | pamtopnm | \ pamenlarge 3 | cksum # Test 3. Should print 3342398172 297 pamenlarge 3 testgrid.pbm | cksum -# Test 4. Should print 237488670 3133413 -pamenlarge 3 -plain testimg.ppm | cksum +# Test 4. Should print 398497872 6806 +pamseq 3 4 | pamtopnm -assume | pamenlarge 3 -plain | cksum diff --git a/test/png-roundtrip.test b/test/png-roundtrip.test index 223103c8..dc05420a 100755 --- a/test/png-roundtrip.test +++ b/test/png-roundtrip.test @@ -6,8 +6,6 @@ alias pnmtopng="${PBM_TESTPREFIX}pnmtopng" shopt -s expand_aliases -## Fails because .ok not set yet. - # Test 1. Should print 1926073387 101484 18 times for flags in "" -interlace \ -gamma=.45 \ diff --git a/test/ppmchange-roundtrip.test b/test/ppmchange-roundtrip.test index a357870e..071c0b93 100755 --- a/test/ppmchange-roundtrip.test +++ b/test/ppmchange-roundtrip.test @@ -13,5 +13,5 @@ pnminvert | ppmtopgm | \ pgmtopbm -th -val=0.5 | cksum ppmchange black white white black testgrid.pbm | \ -ppmchange black white white black -plain | \ +ppmchange black white white black | \ ppmtopgm | pgmtopbm -th -val=0.5 | cksum diff --git a/test/ppmchange.ok b/test/ppmchange.ok new file mode 100644 index 00000000..130c3c45 --- /dev/null +++ b/test/ppmchange.ok @@ -0,0 +1,4 @@ +22488533 203 +1008787190 613 +3885709071 613 +2101746192 613 diff --git a/test/ppmchange.test b/test/ppmchange.test new file mode 100755 index 00000000..9805be0a --- /dev/null +++ b/test/ppmchange.test @@ -0,0 +1,63 @@ +#! /bin/bash +# This script tests: ppmchange +# Also requires: ppmrainbow pgmramp + + alias ppmchange="${PBM_TESTPREFIX}ppmchange" + alias ppmrainbow="${PBM_BINPREFIX}ppmrainbow" + alias pgmramp="${PBM_BINPREFIX}pgmramp" + shopt -s expand_aliases + +# Failure message +## If this test fails and ppmchange-roundtrip.test succeeds, +## the probably cause is a problem with one of the options of +## ppmchange: -closeness or -remainder. + +tmpdir=${tmpdir:-/tmp} +rainbow_ppm=${tmpdir}/rainbow.ppm + +# Explicit values for intermediate colors: rgb.txt may be defining them +# in unusual ways. + +brown=rgb:67/43/00 +cyan=rgb:00/ff/ff +yellow=rgb:ff/ff/00 +gray=rgb:7f/7f/7f + + +# Test 1. Should print 811868957 60 +pgmramp -lr 8 8 | ppmchange black black white white $gray $gray \ + -close=10 -remainder=blue | cksum + + +# Test 2. Should print 1008787190 613 + +ppmrainbow -tmpdir=$tmpdir -width=200 -height=1 red green blue | \ + tee ${rainbow_ppm} | \ + ppmchange red $brown green $brown blue $brown | cksum + + +# Test 3. Should print 3885709071 613 + +ppmchange red $brown green $cyan blue $yellow \ + -closeness=25 ${rainbow_ppm} | cksum + + +# Test 4. Should print 2101746192 613 + +ppmchange red rgb:64/00/01 rgb:00/ff/00 rgb:00/32/02 blue blue \ + -remainder=black -closeness=25 ${rainbow_ppm} | cksum + +rm ${rainbow_ppm} + + +# cksum ${rainbow_ppm} +# 1983174784 613 rainbow.ppm + +# ppmchange red rgb:64/00/01 rgb:00/ff/00 rgb:00/32/02 blue blue \ +# -remainder=black -closeness=25 ${rainbow_ppm} | \ +# pphist -sort=rgb -noheader +# +# 0 0 0 0 75 +# 0 0 255 29 42 +# 0 50 2 30 42 +# 100 0 1 30 41 diff --git a/test/ppmmix.test b/test/ppmmix.test index 25e35f0e..9a54143f 100755 --- a/test/ppmmix.test +++ b/test/ppmmix.test @@ -19,12 +19,9 @@ tmpdir=${tmpdir:-/tmp} a1_pgm=${tmpdir}/a1.pgm a2_pgm=${tmpdir}/a2.pgm -pbmmake -g 8 8 | \ - pgmtopgm > ${a1_pgm} && -pbmmake -g 2 2 | pamenlarge 4 | \ - pgmtopgm > ${a2_pgm} && -ppmmix 0.75 ${a1_pgm} ${a2_pgm} -plain | \ - ppmtopgm | pamdepth 3 -plain && +pbmmake -g 8 8 | pgmtopgm > ${a1_pgm} && +pbmmake -g 2 2 | pamenlarge 4 | pgmtopgm > ${a2_pgm} && +ppmmix 0.75 ${a1_pgm} ${a2_pgm} | ppmtopgm | pamdepth 3 -plain && rm ${a1_pgm} ${a2_pgm} # Mix image with itself. diff --git a/test/symmetry.test b/test/symmetry.test index 5f09e91e..596b67b2 100755 --- a/test/symmetry.test +++ b/test/symmetry.test @@ -1,10 +1,11 @@ #! /bin/bash -# This script tests: pgmramp pamgauss pgmkernel pbmpscale +# This script tests: pgmramp pamgauss pgmkernel pbmmake pbmpscale # Also requires: pamflip alias pgmramp="${PBM_TESTPREFIX}pgmramp" alias pamgauss="${PBM_TESTPREFIX}pamgauss" alias pgmkernel="${PBM_TESTPREFIX}pgmkernel" + alias pbmmake="${PBM_TESTPREFIX}pbmmake" alias pbmpscale="${PBM_TESTPREFIX}pbmpscale" alias pamflip="${PBM_BINPREFIX}pamflip" shopt -s expand_aliases diff --git a/version.mk b/version.mk index d4fb3e35..08a9e283 100644 --- a/version.mk +++ b/version.mk @@ -1,3 +1,3 @@ NETPBM_MAJOR_RELEASE = 10 -NETPBM_MINOR_RELEASE = 68 -NETPBM_POINT_RELEASE = 3 +NETPBM_MINOR_RELEASE = 69 +NETPBM_POINT_RELEASE = 0 |