diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/libpam.c | 33 | ||||
-rw-r--r-- | lib/libpamwrite.c | 205 | ||||
-rw-r--r-- | lib/libpbm2.c | 4 | ||||
-rw-r--r-- | lib/libpbmfont1.c | 63 | ||||
-rw-r--r-- | lib/libpm.c | 3 | ||||
-rw-r--r-- | lib/libpnm2.c | 61 | ||||
-rw-r--r-- | lib/pam.h | 26 | ||||
-rw-r--r-- | lib/pbm.h | 13 | ||||
-rw-r--r-- | lib/pm.h | 3 | ||||
-rw-r--r-- | lib/pmfileio.c | 112 | ||||
-rw-r--r-- | lib/util/rand.c | 43 | ||||
-rw-r--r-- | lib/util/rand.h | 5 |
12 files changed, 415 insertions, 156 deletions
diff --git a/lib/libpam.c b/lib/libpam.c index 72502749..b24f230e 100644 --- a/lib/libpam.c +++ b/lib/libpam.c @@ -90,7 +90,12 @@ validateComputableSize(struct pam * const pamP) { the size of a tuple row, in bytes, can be represented by an 'int'. Another common operation is adding 1 or 2 to the highest row, column, - or plane number in the image, so we make sure that's possible. + or plane number in the image, so we make sure that's possible. And in + bitmap images, rounding up to multiple of 8 is common, so we provide for + that too. + + Note that it's still the programmer's responsibility to ensure that his + code, using values known to have been validated here, cannot overflow. -----------------------------------------------------------------------------*/ if (pamP->width == 0) pm_error("Width is zero. Image must be at least one pixel wide"); @@ -111,10 +116,10 @@ validateComputableSize(struct pam * const pamP) { if (depth > INT_MAX - 2) pm_error("image depth (%u) too large to be processed", depth); - if (pamP->width > INT_MAX - 2) + if (pamP->width > INT_MAX - 10) pm_error("image width (%u) too large to be processed", pamP->width); - if (pamP->height > INT_MAX - 2) + if (pamP->height > INT_MAX - 10) pm_error("image height (%u) too large to be processed", pamP->height); } @@ -416,6 +421,20 @@ pnm_setpamrow(const struct pam * const pamP, +static void +setSeekableAndRasterPos(struct pam * const pamP) { + + if (pamP->size >= PAM_STRUCT_SIZE(is_seekable)) + pamP->is_seekable = pm_is_seekable(pamP->file); + + if (pamP->size >= PAM_STRUCT_SIZE(raster_pos)) { + if (pamP->is_seekable) + pm_tell2(pamP->file, &pamP->raster_pos, sizeof(pamP->raster_pos)); + } +} + + + #define MAX_LABEL_LENGTH 8 #define MAX_VALUE_LENGTH 255 @@ -945,6 +964,8 @@ pnm_readpaminit(FILE * const file, pamP->plainformat = FALSE; /* See below for complex explanation of why this is FALSE. */ + setSeekableAndRasterPos(pamP); + interpretTupleType(pamP); validateComputableSize(pamP); @@ -1053,8 +1074,6 @@ pnm_writepaminit(struct pam * const pamP) { interpretTupleType(pamP); - pamP->len = MIN(pamP->size, PAM_STRUCT_SIZE(opacity_plane)); - switch (PAM_FORMAT_TYPE(pamP->format)) { case PAM_TYPE: /* See explanation below of why we ignore 'pm_plain_output' here. */ @@ -1113,6 +1132,10 @@ pnm_writepaminit(struct pam * const pamP) { pm_error("Invalid format passed to pnm_writepaminit(): %d", pamP->format); } + + setSeekableAndRasterPos(pamP); + + pamP->len = MIN(pamP->size, PAM_STRUCT_SIZE(raster_pos)); } diff --git a/lib/libpamwrite.c b/lib/libpamwrite.c index 7edc90dc..0e1ff469 100644 --- a/lib/libpamwrite.c +++ b/lib/libpamwrite.c @@ -71,7 +71,7 @@ writePamPlainPbmRow(const struct pam * const pamP, static void writePamPlainRow(const struct pam * const pamP, - const tuple * const tuplerow) { + const tuple * const tuplerow) { int const samplesPerLine = samplesPerPlainLine(pamP->maxval, pamP->depth, 79); @@ -101,17 +101,25 @@ writePamPlainRow(const struct pam * const pamP, static void -formatPbmRow(const struct pam * const pamP, - const tuple * const tuplerow, - unsigned char * const outbuf, - unsigned int * const rowSizeP) { +formatPbm(const struct pam * const pamP, + const tuple * const tuplerow, + unsigned char * const outbuf, + unsigned int const nTuple, + unsigned int * const rowSizeP) { +/*---------------------------------------------------------------------------- + Create the image of 'nTuple' consecutive tuples of a row in the raster of a + raw format PBM image. + Put the image at *outbuf; put the number of bytes of it at *rowSizeP. +-----------------------------------------------------------------------------*/ unsigned char accum; int col; + assert(nTuple <= pamP->width); + accum = 0; /* initial value */ - for (col=0; col < pamP->width; ++col) { + for (col=0; col < nTuple; ++col) { accum |= (tuplerow[col][0] == PAM_PBM_BLACK ? PBM_BLACK : PBM_WHITE) << (7-col%8); @@ -120,12 +128,12 @@ formatPbmRow(const struct pam * const pamP, accum = 0; } } - if (pamP->width % 8 != 0) { - unsigned int const lastByteIndex = pamP->width/8; + if (nTuple % 8 != 0) { + unsigned int const lastByteIndex = nTuple/8; outbuf[lastByteIndex] = accum; *rowSizeP = lastByteIndex + 1; } else - *rowSizeP = pamP->width/8; + *rowSizeP = nTuple/8; } @@ -171,36 +179,40 @@ sampleToBytes4(unsigned char buf[4], static __inline__ void -format1BpsRow(const struct pam * const pamP, - const tuple * const tuplerow, - unsigned char * const outbuf, - unsigned int * const rowSizeP) { +format1Bps(const struct pam * const pamP, + const tuple * const tuplerow, + unsigned char * const outbuf, + unsigned int const nTuple, + unsigned int * const rowSizeP) { /*---------------------------------------------------------------------------- - Create the image of a row in the raster of a raw format Netpbm - image that has one byte per sample (ergo not PBM). + Create the image of 'nTuple' consecutive tuples of a row in the raster of a + raw format Netpbm image that has one byte per sample (ergo not PBM). Put the image at *outbuf; put the number of bytes of it at *rowSizeP. -----------------------------------------------------------------------------*/ int col; unsigned int bufferCursor; + assert(nTuple <= pamP->width); + bufferCursor = 0; /* initial value */ - for (col = 0; col < pamP->width; ++col) { + for (col = 0; col < nTuple; ++col) { unsigned int plane; for (plane=0; plane < pamP->depth; ++plane) outbuf[bufferCursor++] = (unsigned char)tuplerow[col][plane]; } - *rowSizeP = pamP->width * 1 * pamP->depth; + *rowSizeP = nTuple * 1 * pamP->depth; } static __inline__ void -format2BpsRow(const struct pam * const pamP, - const tuple * const tuplerow, - unsigned char * const outbuf, - unsigned int * const rowSizeP) { +format2Bps(const struct pam * const pamP, + const tuple * const tuplerow, + unsigned char * const outbuf, + unsigned int const nTuple, + unsigned int * const rowSizeP) { /*---------------------------------------------------------------------------- Analogous to format1BpsRow(). -----------------------------------------------------------------------------*/ @@ -209,24 +221,27 @@ format2BpsRow(const struct pam * const pamP, int col; unsigned int bufferCursor; + assert(nTuple <= pamP->width); + bufferCursor = 0; /* initial value */ - for (col=0; col < pamP->width; ++col) { + for (col=0; col < nTuple; ++col) { unsigned int plane; for (plane = 0; plane < pamP->depth; ++plane) sampleToBytes2(ob[bufferCursor++], tuplerow[col][plane]); } - *rowSizeP = pamP->width * 2 * pamP->depth; + *rowSizeP = nTuple * 2 * pamP->depth; } static __inline__ void -format3BpsRow(const struct pam * const pamP, - const tuple * const tuplerow, - unsigned char * const outbuf, - unsigned int * const rowSizeP) { +format3Bps(const struct pam * const pamP, + const tuple * const tuplerow, + unsigned char * const outbuf, + unsigned int const nTuple, + unsigned int * const rowSizeP) { /*---------------------------------------------------------------------------- Analogous to format1BpsRow(). -----------------------------------------------------------------------------*/ @@ -235,24 +250,27 @@ format3BpsRow(const struct pam * const pamP, int col; unsigned int bufferCursor; + assert(nTuple <= pamP->width); + bufferCursor = 0; /* initial value */ - for (col=0; col < pamP->width; ++col) { + for (col=0; col < nTuple; ++col) { unsigned int plane; for (plane = 0; plane < pamP->depth; ++plane) sampleToBytes3(ob[bufferCursor++], tuplerow[col][plane]); } - *rowSizeP = pamP->width * 3 * pamP->depth; + *rowSizeP = nTuple * 3 * pamP->depth; } static __inline__ void -format4BpsRow(const struct pam * const pamP, - const tuple * const tuplerow, - unsigned char * const outbuf, - unsigned int * const rowSizeP) { +format4Bps(const struct pam * const pamP, + const tuple * const tuplerow, + unsigned char * const outbuf, + unsigned int const nTuple, + unsigned int * const rowSizeP) { /*---------------------------------------------------------------------------- Analogous to format1BpsRow(). -----------------------------------------------------------------------------*/ @@ -261,41 +279,49 @@ format4BpsRow(const struct pam * const pamP, int col; unsigned int bufferCursor; + assert(nTuple <= pamP->width); + bufferCursor = 0; /* initial value */ - for (col=0; col < pamP->width; ++col) { + for (col=0; col < nTuple; ++col) { unsigned int plane; for (plane = 0; plane < pamP->depth; ++plane) sampleToBytes4(ob[bufferCursor++], tuplerow[col][plane]); } - *rowSizeP = pamP->width * 4 * pamP->depth; + *rowSizeP = nTuple * 4 * pamP->depth; } void -pnm_formatpamrow(const struct pam * const pamP, - const tuple * const tuplerow, - unsigned char * const outbuf, - unsigned int * const rowSizeP) { -/*---------------------------------------------------------------------------- - Create the image of a row in the raster of a raw (not plain) format - Netpbm image, as described by *pamP and tuplerow[]. Put the image - at *outbuf. +pnm_formatpamtuples(const struct pam * const pamP, + const tuple * const tuplerow, + unsigned char * const outbuf, + unsigned int const nTuple, + unsigned int * const rowSizeP) { +/*---------------------------------------------------------------------------- Create the image of 'nTuple' consecutive tuples of a row in the raster of a + raw (not plain) format Netpbm image, as described by *pamP and tuplerow[]. + Put the image at *outbuf. 'outbuf' must be the address of space allocated with pnm_allocrowimage(). - We return as *rowSizeP the number of bytes in the row image. + We return as *rowSizeP the number of bytes in the image. -----------------------------------------------------------------------------*/ + if (nTuple > pamP->width) { + pm_error("pnm_formatpamtuples called to write more tuples (%u) " + "than the width of a row (%u)", + nTuple, pamP->width); + } + if (PAM_FORMAT_TYPE(pamP->format) == PBM_TYPE) - formatPbmRow(pamP, tuplerow, outbuf, rowSizeP); + formatPbm(pamP, tuplerow, outbuf, nTuple, rowSizeP); else { switch(pamP->bytes_per_sample){ - case 1: format1BpsRow(pamP, tuplerow, outbuf, rowSizeP); break; - case 2: format2BpsRow(pamP, tuplerow, outbuf, rowSizeP); break; - case 3: format3BpsRow(pamP, tuplerow, outbuf, rowSizeP); break; - case 4: format4BpsRow(pamP, tuplerow, outbuf, rowSizeP); break; + case 1: format1Bps(pamP, tuplerow, outbuf, nTuple, rowSizeP); break; + case 2: format2Bps(pamP, tuplerow, outbuf, nTuple, rowSizeP); break; + case 3: format3Bps(pamP, tuplerow, outbuf, nTuple, rowSizeP); break; + case 4: format4Bps(pamP, tuplerow, outbuf, nTuple, rowSizeP); break; default: pm_error("invalid bytes per sample passed to " "pnm_formatpamrow(): %u", pamP->bytes_per_sample); @@ -305,6 +331,19 @@ pnm_formatpamrow(const struct pam * const pamP, +void +pnm_formatpamrow(const struct pam * const pamP, + const tuple * const tuplerow, + unsigned char * const outbuf, + unsigned int * const rowSizeP) { +/*---------------------------------------------------------------------------- + Same as 'pnm_formatpamtuples', except formats an entire row. +-----------------------------------------------------------------------------*/ + pnm_formatpamtuples(pamP, tuplerow, outbuf, pamP->width, rowSizeP); +} + + + static void writePamRawRow(const struct pam * const pamP, const tuple * const tuplerow, @@ -398,6 +437,74 @@ pnm_writepamrowmult(const struct pam * const pamP, void +pnm_writepamrowpart(const struct pam * const pamP, + const tuple * const tuplerow, + unsigned int const firstRow, + unsigned int const firstCol, + unsigned int const rowCt, + unsigned int const colCt) { +/*---------------------------------------------------------------------------- + Write part of multiple consecutive rows to the file. + + For each of 'rowCt' consecutive rows starting at 'firstRow', write the + 'colCt' columns starting at 'firstCol'. The tuples to write are those in + 'tuplerow', starting at the beginning of 'tuplerow'. + + Fail if the file is not seekable (or not known to be seekable) or the + output format is not raw (i.e. is plain) or the output format is PBM. +-----------------------------------------------------------------------------*/ + unsigned int const bytesPerTuple = pamP->depth * pamP->bytes_per_sample; + + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; + unsigned int tupleImageSize; + unsigned char * outbuf; /* malloc'ed */ + + if (pamP->len < PAM_STRUCT_SIZE(raster_pos) || !pamP->raster_pos) + pm_error("pnm_writepamrowpart called on nonseekable file"); + + if (PAM_FORMAT_TYPE(pamP->format) == PBM_TYPE) + pm_error("pnm_witepamrowpart called for PBM image"); + + if (pm_plain_output || pamP->plainformat) + pm_error("pnm_writepamrowpart called for plain format image"); + + outbuf = pnm_allocrowimage(pamP); + + pnm_formatpamtuples(pamP, tuplerow, outbuf, colCt, &tupleImageSize); + + if (setjmp(jmpbuf) != 0) { + pnm_freerowimage(outbuf); + pm_setjmpbuf(origJmpbufP); + pm_longjmp(); + } else { + unsigned int row; + + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); + + for (row = firstRow; row < firstRow + rowCt; ++row) { + pm_filepos const firstTuplePos = + pamP->raster_pos + + (row * pamP->width + firstCol) * bytesPerTuple; + size_t bytesWritten; + + pm_seek2(pamP->file, &firstTuplePos, sizeof(firstTuplePos)); + + bytesWritten = fwrite(outbuf, 1, tupleImageSize, pamP->file); + + if (bytesWritten != tupleImageSize) + pm_error("fwrite() failed to write %u image tuples " + "to the file. errno=%d (%s)", + colCt, errno, strerror(errno)); + } + pm_setjmpbuf(origJmpbufP); + } + pnm_freerowimage(outbuf); +} + + + +void pnm_writepam(struct pam * const pamP, tuple ** const tuplearray) { diff --git a/lib/libpbm2.c b/lib/libpbm2.c index a611bec5..2a2e2aac 100644 --- a/lib/libpbm2.c +++ b/lib/libpbm2.c @@ -69,8 +69,8 @@ validateComputableSize(unsigned int const cols, you expect. That failed expectation can be disastrous if you use it to allocate memory. - A common operation is adding 1 or 2 to the highest row or - column number in the image, so we make sure that's possible. + See comments at 'validateComputableSize' in libpam.c for details on + the purpose of these validations. -----------------------------------------------------------------------------*/ if (cols > INT_MAX - 10) pm_error("image width (%u) too large to be processed", cols); diff --git a/lib/libpbmfont1.c b/lib/libpbmfont1.c index fe014150..a76d0e6b 100644 --- a/lib/libpbmfont1.c +++ b/lib/libpbmfont1.c @@ -184,7 +184,7 @@ computeCharacterSize(const bit ** const font, -struct font* +struct font * pbm_dissectfont(const bit ** const fontsheet, unsigned int const frows, unsigned int const fcols) { @@ -222,56 +222,61 @@ pbm_dissectfont(const bit ** const fontsheet, unsigned int charWidth, charHeight; /* Maximum dimensions of glyph itself, inside its cell */ - int row, col, ch, r, c, i; - struct font * fn; + unsigned int row, col; + int ch; + unsigned int i; + struct font * fontP; computeCharacterSize(fontsheet, fcols, frows, &cellWidth, &cellHeight, &charWidth, &charHeight); /* Now convert to a general font */ - MALLOCVAR(fn); - if (fn == NULL) + MALLOCVAR(fontP); + if (fontP == NULL) pm_error("out of memory allocating font structure"); - fn->maxwidth = charWidth; - fn->maxheight = charHeight; - fn->x = fn->y = 0; + fontP->maxwidth = charWidth; + fontP->maxheight = charHeight; + fontP->x = fontP->y = 0; - fn->oldfont = fontsheet; - fn->frows = frows; - fn->fcols = fcols; + fontP->oldfont = fontsheet; + fontP->frows = frows; + fontP->fcols = fcols; /* Now fill in the 0,0 coords. */ row = cellHeight * 2; col = cellWidth * 2; /* Load individual glyphs */ - for ( ch = 0; ch < nCharsInFont; ++ch ) { + for (ch = 0; ch < nCharsInFont; ++ch) { /* Allocate memory separately for each glyph. pbm_loadbdffont2() does this in exactly the same manner. */ - struct glyph * const glyph = + struct glyph * const glyphP = (struct glyph *) malloc (sizeof (struct glyph)); - char * const bmap = (char*) malloc(fn->maxwidth * fn->maxheight); + char * const bmap = (char*) malloc(fontP->maxwidth * fontP->maxheight); - if ( bmap == NULL || glyph == NULL ) - pm_error( "out of memory allocating glyph data" ); + unsigned int r; - glyph->width = fn->maxwidth; - glyph->height = fn->maxheight; - glyph->x = glyph->y = 0; - glyph->xadd = cellWidth; + if (bmap == NULL || glyphP == NULL) + pm_error( "out of memory allocating glyph data" ); - for ( r = 0; r < glyph->height; ++r ) - for ( c = 0; c < glyph->width; ++c ) - bmap[r * glyph->width + c] = fontsheet[row + r][col + c]; + glyphP->width = fontP->maxwidth; + glyphP->height = fontP->maxheight; + glyphP->x = glyphP->y = 0; + glyphP->xadd = cellWidth; - glyph->bmap = bmap; - fn->glyph[firstCodePoint + ch] = glyph; + for (r = 0; r < glyphP->height; ++r) { + unsigned int c; + for (c = 0; c < glyphP->width; ++c) + bmap[r * glyphP->width + c] = fontsheet[row + r][col + c]; + } + glyphP->bmap = bmap; + fontP->glyph[firstCodePoint + ch] = glyphP; col += cellWidth; - if ( col >= cellWidth * 14 ) { + if (col >= cellWidth * 14) { col = cellWidth * 2; row += cellHeight; } @@ -279,12 +284,12 @@ pbm_dissectfont(const bit ** const fontsheet, /* Initialize all remaining character positions to "undefined." */ for (i = 0; i < firstCodePoint; ++i) - fn->glyph[i] = NULL; + fontP->glyph[i] = NULL; for (i = firstCodePoint + nCharsInFont; i <= PM_FONT_MAXGLYPH; ++i) - fn->glyph[i] = NULL; + fontP->glyph[i] = NULL; - return fn; + return fontP; } diff --git a/lib/libpm.c b/lib/libpm.c index 6f9dea3d..78d941fa 100644 --- a/lib/libpm.c +++ b/lib/libpm.c @@ -844,6 +844,9 @@ pm_parse_width(const char * const arg) { Return the image width represented by the decimal ASCIIZ string 'arg'. Fail if it doesn't validly represent a width or represents a width that can't be conveniently used in computation. + + See comments at 'validateComputableSize' in libpam.c for details on + the purpose of these validations. -----------------------------------------------------------------------------*/ unsigned int width; const char * error; diff --git a/lib/libpnm2.c b/lib/libpnm2.c index fa4bb8be..6fec91e9 100644 --- a/lib/libpnm2.c +++ b/lib/libpnm2.c @@ -23,11 +23,11 @@ void -pnm_writepnminit(FILE * const fileP, - int const cols, - int const rows, - xelval const maxval, - int const format, +pnm_writepnminit(FILE * const fileP, + int const cols, + int const rows, + xelval const maxval, + int const format, int const forceplain) { bool const plainFormat = forceplain || pm_plain_output; @@ -47,7 +47,7 @@ pnm_writepnminit(FILE * const fileP, default: pm_error("invalid format argument received by pnm_writepnminit(): %d" - "PNM_FORMAT_TYPE(format) must be %d, %d, or %d", + "PNM_FORMAT_TYPE(format) must be %d, %d, or %d", format, PBM_TYPE, PGM_TYPE, PPM_TYPE); } } @@ -55,19 +55,19 @@ pnm_writepnminit(FILE * const fileP, static void -writepgmrow(FILE * const fileP, - const xel * const xelrow, - unsigned int const cols, - xelval const maxval, - int const format, +writepgmrow(FILE * const fileP, + const xel * const xelrow, + unsigned int const cols, + xelval const maxval, + int const format, bool const plainFormat) { - + jmp_buf jmpbuf; jmp_buf * origJmpbufP; gray * grayrow; - + grayrow = pgm_allocrow(cols); - + if (setjmp(jmpbuf) != 0) { pgm_freerow(grayrow); pm_setjmpbuf(origJmpbufP); @@ -76,10 +76,10 @@ writepgmrow(FILE * const fileP, unsigned int col; pm_setjmpbufsave(&jmpbuf, &origJmpbufP); - + for (col = 0; col < cols; ++col) grayrow[col] = PNM_GET1(xelrow[col]); - + pgm_writepgmrow(fileP, grayrow, cols, (gray) maxval, plainFormat); pm_setjmpbuf(origJmpbufP); @@ -100,7 +100,7 @@ writepbmrow(FILE * const fileP, bit * bitrow; bitrow = pbm_allocrow(cols); - + if (setjmp(jmpbuf) != 0) { pbm_freerow(bitrow); pm_setjmpbuf(origJmpbufP); @@ -112,29 +112,29 @@ writepbmrow(FILE * const fileP, for (col = 0; col < cols; ++col) bitrow[col] = PNM_GET1(xelrow[col]) == 0 ? PBM_BLACK : PBM_WHITE; - + pbm_writepbmrow(fileP, bitrow, cols, plainFormat); pm_setjmpbuf(origJmpbufP); } pbm_freerow(bitrow); -} +} void -pnm_writepnmrow(FILE * const fileP, - const xel * const xelrow, - int const cols, - xelval const maxval, - int const format, +pnm_writepnmrow(FILE * const fileP, + const xel * const xelrow, + int const cols, + xelval const maxval, + int const format, int const forceplain) { bool const plainFormat = forceplain || pm_plain_output; - + switch (PNM_FORMAT_TYPE(format)) { case PPM_TYPE: - ppm_writeppmrow(fileP, (pixel*) xelrow, cols, (pixval) maxval, + ppm_writeppmrow(fileP, (pixel*) xelrow, cols, (pixval) maxval, plainFormat); break; @@ -145,10 +145,10 @@ pnm_writepnmrow(FILE * const fileP, case PBM_TYPE: writepbmrow(fileP, xelrow, cols, plainFormat); break; - + default: pm_error("invalid format argument received by pnm_writepnmrow(): %d" - "PNM_FORMAT_TYPE(format) must be %d, %d, or %d", + "PNM_FORMAT_TYPE(format) must be %d, %d, or %d", format, PBM_TYPE, PGM_TYPE, PPM_TYPE); } } @@ -167,7 +167,10 @@ pnm_writepnm(FILE * const fileP, unsigned int row; pnm_writepnminit(fileP, cols, rows, maxval, format, forceplain); - + for (row = 0; row < rows; ++row) pnm_writepnmrow(fileP, xels[row], cols, maxval, format, forceplain); } + + + diff --git a/lib/pam.h b/lib/pam.h index aebf529a..88b8c2bd 100644 --- a/lib/pam.h +++ b/lib/pam.h @@ -136,6 +136,18 @@ struct pam { /* The plane number of the opacity plane; meaningless if 'haveOpacity' is false or 'visual' is false. */ + int is_seekable; /* boolean */ + /* The file 'file' is seekable -- you can set the position of next + reading or writing to anything and any time. + + If libnetpbm cannot tell if it is seekable or not, this is false. + */ + pm_filepos raster_pos; + /* The file position of the raster (which is also the end of the + header). + + Meaningless if 'is_seekable' is false. + */ }; #define PAM_HAVE_ALLOCATION_DEPTH 1 @@ -341,6 +353,12 @@ void pnm_writepaminit(struct pam * const pamP); void +pnm_formatpamtuples(const struct pam * const pamP, + const tuple * const tuplerow, + unsigned char * const outbuf, + unsigned int const nTuple, + unsigned int * const rowSizeP); +void pnm_formatpamrow(const struct pam * const pamP, const tuple * const tuplerow, unsigned char * const outbuf, @@ -355,6 +373,14 @@ pnm_writepamrowmult(const struct pam * const pamP, unsigned int const rptcnt); void +pnm_writepamrowpart(const struct pam * const pamP, + const tuple * const tuplerow, + unsigned int const firstRow, + unsigned int const firstCol, + unsigned int const rowCt, + unsigned int const colCt); + +void pnm_writepam(struct pam * const pamP, tuple ** const tuplearray); void diff --git a/lib/pbm.h b/lib/pbm.h index 27fd1163..57ab3812 100644 --- a/lib/pbm.h +++ b/lib/pbm.h @@ -47,6 +47,19 @@ pbm_allocrow(unsigned int const cols); ((bit**) pm_allocarray(cols, rows, sizeof(bit))) #define pbm_freearray(bits, rows) pm_freearray((char**) bits, rows) #define pbm_freerow(bitrow) pm_freerow((char*) bitrow) + +/* Beware of arithmetic overflows when using pbm_packed_bytes(), + pbm_allocrow_packed() and pbm_allocarray_packed(). + + When cols is signed int, pbm_packed_bytes(cols + 8) overflows + with large values. Same with pamP->width which is always signed int. + + Function validateComputableSize() called by pbm_readpbminit() + provides a margin of 10, but the "+7" uses much of it. + + To prevent overflows, cast cols or pamP->width to unsigned int + like this: pbm_packed_bytes((unsigned int) cols +8)) +*/ #define pbm_packed_bytes(cols) (((cols)+7)/8) #define pbm_allocrow_packed(cols) \ ((unsigned char *) pm_allocrow(pbm_packed_bytes(cols), \ diff --git a/lib/pm.h b/lib/pm.h index 3fc92fb4..b3c3d202 100644 --- a/lib/pm.h +++ b/lib/pm.h @@ -386,6 +386,9 @@ pm_bs_short(short const s); long pm_bs_long(long const l); +int +pm_is_seekable(FILE * const fileP); + unsigned int pm_tell(FILE * const fileP); diff --git a/lib/pmfileio.c b/lib/pmfileio.c index 4048e74d..1ed71f18 100644 --- a/lib/pmfileio.c +++ b/lib/pmfileio.c @@ -452,27 +452,16 @@ pm_tmpfile_fd(void) { } - -FILE * -pm_openr_seekable(const char name[]) { +static bool +isSeekable(FILE * const fP) { /*---------------------------------------------------------------------------- - Open the file named by name[] such that it is seekable (i.e. it can be - rewound and read in multiple passes with fseek()). + The file is seekable -- we can set its read/write position to anything we + want. - If the file is actually seekable, this reduces to the same as - pm_openr(). If not, we copy the named file to a temporary file - and return that file's stream descriptor. - - We use a file that the operating system recognizes as temporary, so - it picks the filename and deletes the file when Caller closes it. + If we can't tell if it is seekable, we return false. -----------------------------------------------------------------------------*/ - int stat_rc; - int seekable; /* logical: file is seekable */ + int statRc; struct stat statbuf; - FILE * original_file; - FILE * seekable_file; - - original_file = pm_openr((char *) name); /* I would use fseek() to determine if the file is seekable and be a little more general than checking the type of file, but I @@ -486,41 +475,62 @@ pm_openr_seekable(const char name[]) { some other file is, it doesn't hurt much to assume it isn't. */ - stat_rc = fstat(fileno(original_file), &statbuf); - if (stat_rc == 0 && S_ISREG(statbuf.st_mode)) - seekable = TRUE; - else - seekable = FALSE; + statRc = fstat(fileno(fP), &statbuf); + + return statRc == 0 && S_ISREG(statbuf.st_mode); +} + + + +FILE * +pm_openr_seekable(const char name[]) { +/*---------------------------------------------------------------------------- + Open the file named by name[] such that it is seekable (i.e. it can be + rewound and read in multiple passes with fseek()). + + If the file is actually seekable, this reduces to the same as + pm_openr(). If not, we copy the named file to a temporary file + and return that file's stream descriptor. - if (seekable) { - seekable_file = original_file; + We use a file that the operating system recognizes as temporary, so + it picks the filename and deletes the file when Caller closes it. +-----------------------------------------------------------------------------*/ + FILE * originalFileP; + FILE * seekableFileP; + + originalFileP = pm_openr((char *) name); + + if (isSeekable(originalFileP)) { + seekableFileP = originalFileP; } else { - seekable_file = pm_tmpfile(); + seekableFileP = pm_tmpfile(); /* Copy the input into the temporary seekable file */ - while (!feof(original_file) && !ferror(original_file) - && !ferror(seekable_file)) { + while (!feof(originalFileP) && !ferror(originalFileP) + && !ferror(seekableFileP)) { char buffer[4096]; - int bytes_read; - bytes_read = fread(buffer, 1, sizeof(buffer), original_file); - fwrite(buffer, 1, bytes_read, seekable_file); + size_t nBytesRead; + + nBytesRead = fread(buffer, 1, sizeof(buffer), originalFileP); + fwrite(buffer, 1, nBytesRead, seekableFileP); } - if (ferror(original_file)) + if (ferror(originalFileP)) pm_error("Error reading input file into temporary file. " "Errno = %s (%d)", strerror(errno), errno); - if (ferror(seekable_file)) + if (ferror(seekableFileP)) pm_error("Error writing input into temporary file. " "Errno = %s (%d)", strerror(errno), errno); - pm_close(original_file); + pm_close(originalFileP); { - int seek_rc; - seek_rc = fseek(seekable_file, 0, SEEK_SET); - if (seek_rc != 0) + int seekRc; + + seekRc = fseek(seekableFileP, 0, SEEK_SET); + if (seekRc != 0) pm_error("fseek() failed to rewind temporary file. " "Errno = %s (%d)", strerror(errno), errno); } } - return seekable_file; + return seekableFileP; } @@ -968,6 +978,20 @@ pm_bs_long(long const l) { +int +pm_is_seekable(FILE * const fP) { + + return isSeekable(fP) ? 1 : 0; +} + + + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpragmas" +#pragma GCC diagnostic ignored "-Wduplicated-cond" + + + void pm_tell2(FILE * const fileP, void * const fileposP, @@ -1008,6 +1032,10 @@ pm_tell2(FILE * const fileP, +#pragma GCC diagnostic pop + + + unsigned int pm_tell(FILE * const fileP) { @@ -1020,6 +1048,12 @@ pm_tell(FILE * const fileP) { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpragmas" +#pragma GCC diagnostic ignored "-Wduplicated-cond" + + + void pm_seek2(FILE * const fileP, const pm_filepos * const fileposP, @@ -1047,6 +1081,10 @@ pm_seek2(FILE * const fileP, +#pragma GCC diagnostic pop + + + void pm_seek(FILE * const fileP, unsigned long filepos) { /*---------------------------------------------------------------------------- diff --git a/lib/util/rand.c b/lib/util/rand.c index 2f60de83..6a0a2cdb 100644 --- a/lib/util/rand.c +++ b/lib/util/rand.c @@ -72,8 +72,8 @@ https://wnww.gnu.org/software/gsl/doc/html/rng.html Twister method and does not rely on any randomness facility of the operating system, but it is easy to compile an alternative version that uses others. - The Mersenne Twister method was new to Netpbm in Netpbm 10.94 - (March 2021). Before that, Netpbm used standard OS-provided facilities. + The Mersenne Twister method was new to Netpbm in Netpbm 10.94 (March 2021). + Before that, Netpbm used standard OS-provided facilities. Programs that use random numbers have existed in Netpbm since PBMPlus days. The system rand() function was used in instances randomness was required; @@ -87,15 +87,15 @@ https://wnww.gnu.org/software/gsl/doc/html/rng.html This was not considered a problem in the early days. Deterministic operation was not a feature users requested and it was impossible regardless - of the random number generation method on most programs because they did - not allow a user to specify a seed for the generator. + of the random number generation method on most programs because they did not + allow a user to specify a seed for the generator. This state of affairs changed as Netpbm got firmly established as a base-level system package. Security became critical for many users. A crucial component of quality control is automated regression tests (="make check"). Unpredictable behavior gets in the way of testing. One by one programs were given the -randomseed (or -seed) option to ensure reproducible - results. Often this was done as new tests cases were written. However, + results. Often this was done as new test cases were written. However, inconsistent output caused by system-level differences in rand() implementation remained a major obstacle. @@ -219,6 +219,39 @@ pm_gaussrand(struct pm_randSt * const randStP) { +uint32_t +pm_rand32(struct pm_randSt * const randStP) { +/*----------------------------------------------------------------------------- + Generate a 32-bit random number. + + This is a provision for users who select a non-default random number + generator which returns less than 32 bits per call. Many system generators + are known to return 31 bits (max = 2147483647 or 0x7FFFFFFF). + + This does not work with generators that return less than 11 bits per call. + The least we know of is the archaic RANDU, which generates 15 bits (max = + 32767 or 0x7FFF). +-----------------------------------------------------------------------------*/ + unsigned int const randMax = randStP->max; + + uint32_t retval; + + if (randMax >= 0xFFFFFFFF) + retval = pm_rand(randStP); + else { + uint32_t scale; + + retval = 0; /* Initial value */ + + for (scale = 0xFFFFFFFF; scale > 0; scale /= (randMax +1)) + retval *= (randMax + 1) + pm_rand(randStP); + } + + return retval;; +} + + + void pm_randinit(struct pm_randSt * const randStP) { /*---------------------------------------------------------------------------- diff --git a/lib/util/rand.h b/lib/util/rand.h index c441890a..44095243 100644 --- a/lib/util/rand.h +++ b/lib/util/rand.h @@ -3,6 +3,8 @@ #ifndef RAND_H_INCLUDED #define RAND_H_INCLUDED +#include <inttypes.h> + #include "netpbm/pm_c_util.h" #include "netpbm/mallocvar.h" @@ -103,5 +105,8 @@ pm_gaussrand2(struct pm_randSt * const randStP, extern double pm_gaussrand(struct pm_randSt * const randStP); +extern uint32_t +pm_rand32(struct pm_randSt * const randStP); + #endif |