From 3a2be6f9d910b862d4e5ea237d734315627dd661 Mon Sep 17 00:00:00 2001 From: giraffedata Date: Thu, 21 Sep 2006 18:00:16 +0000 Subject: Release 10.36.0 git-svn-id: http://svn.code.sf.net/p/netpbm/code/advanced@65 9d0c8265-081b-0410-96cb-a4ca84ce46f8 --- lib/Makefile | 6 +- lib/libpammap.c | 20 ++-- lib/libpamn.c | 284 +++++++++++++++++++++++++++++++++++++----------- lib/libpamread.c | 104 ++++++++++++------ lib/libpamwrite.c | 29 +++-- lib/libpbm3.c | 113 ++++++++++++------- lib/libpgm1.c | 186 ++++++++++++++++++++----------- lib/libpm.c | 292 +++++++++++++++++++++++++++++++++++++------------ lib/libpnm1.c | 125 ++++++++++++++++----- lib/libpnm2.c | 102 ++++++++++++----- lib/libppm1.c | 290 +++++++++++++++++++++++++++++++++---------------- lib/libppmcmap.c | 313 +++++++++++++++++++++++++++++++++++++++-------------- lib/libppmcolor.c | 257 +++++++++++++++++++++++++++++++------------ lib/libppmfuzzy.c | 89 ++++++++------- lib/pm.h | 18 ++- lib/ppm.h | 17 +-- lib/util/Makefile | 6 +- lib/util/nstring.c | 26 +++-- lib/util/nstring.h | 9 ++ 19 files changed, 1641 insertions(+), 645 deletions(-) (limited to 'lib') diff --git a/lib/Makefile b/lib/Makefile index bd8eccae..704b838a 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -40,7 +40,7 @@ ifneq (${VMS}x,x) LIBOBJECTS += libpbmvms.o endif # Library objects to be linked but not built by Makefile.common: -LIBOBJECTS_X = util/shhopt.o util/nstring.o util/filename.o +LIBOBJECTS_X = util/shhopt.o util/nstring.o util/vasprintf.o util/filename.o MANUALS3 = libnetpbm MANUALS5 = pbm pgm ppm pnm pam @@ -55,8 +55,6 @@ DATAFILES = rgb.txt .PHONY: all all: libnetpbm extra_staticlib -INCLUDES = -I$(SRCDIR)/$(SUBDIR) -I. -Iimportinc - SUBDIRS = util SCRIPTS = BINARIES = @@ -64,6 +62,8 @@ BINARIES = OMIT_LIBRARY_RULE = 1 include $(SRCDIR)/Makefile.common +INCLUDES = -I$(SRCDIR)/$(SUBDIR) -I. -Iimportinc + # The following must go after Makefile.common because $(LIBNETPBM) may # contain a reference to $(NETPBM_MAJOR_RELEASE). .PHONY: libnetpbm diff --git a/lib/libpammap.c b/lib/libpammap.c index 9e90ade0..98c7f798 100644 --- a/lib/libpammap.c +++ b/lib/libpammap.c @@ -24,12 +24,13 @@ #define HASH_SIZE 20023 unsigned int -pnm_hashtuple(struct pam * const pamP, tuple const tuple) { +pnm_hashtuple(struct pam * const pamP, + tuple const tuple) { /*---------------------------------------------------------------------------- Return the hash value of the tuple 'tuple' -- i.e. an index into a hash table. -----------------------------------------------------------------------------*/ - int i; + unsigned int i; unsigned int hash; const unsigned int hash_factor[] = {33023, 30013, 27011}; @@ -281,7 +282,7 @@ computehashrecoverable(struct pam * const pamP, tuple values. */ for (row = 0; row < pamP->height && !full; ++row) { - int col; + unsigned int col; const tuple * tuplerow; /* The row of tuples we are processing */ if (tupleArray) @@ -354,19 +355,20 @@ computetuplefreqhash(struct pam * const pamP, rowbuffer = NULL; color = NULL; - if (setjmp(jmpbuf) == 0) { - pm_setjmpbufsave(&jmpbuf, &origJmpbufP); - computehashrecoverable(pamP, tupleArray, maxsize, newMaxval, sizeP, - &tuplefreqhash, &rowbuffer, &color); - pm_setjmpbuf(origJmpbufP); - } else { + if (setjmp(jmpbuf) != 0) { if (color) pnm_freepamtuple(color); if (rowbuffer) pnm_freepamrow(rowbuffer); if (tuplefreqhash) pnm_destroytuplehash(tuplefreqhash); + pm_setjmpbuf(origJmpbufP); pm_longjmp(); + } else { + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); + computehashrecoverable(pamP, tupleArray, maxsize, newMaxval, sizeP, + &tuplefreqhash, &rowbuffer, &color); + pm_setjmpbuf(origJmpbufP); } return tuplefreqhash; } diff --git a/lib/libpamn.c b/lib/libpamn.c index c7610100..2ec50e7e 100644 --- a/lib/libpamn.c +++ b/lib/libpamn.c @@ -10,6 +10,7 @@ #include "pm_c_util.h" #include "mallocvar.h" +#include "nstring.h" #include "pam.h" #include "fileio.h" #include "pm_gamma.h" @@ -18,15 +19,19 @@ -tuplen * -pnm_allocpamrown(const struct pam * const pamP) { +static void +allocpamrown(const struct pam * const pamP, + tuplen ** const tuplerownP, + const char ** const errorP) { /*---------------------------------------------------------------------------- We assume that the dimensions of the image are such that arithmetic overflow will not occur in our calculations. NOTE: pnm_readpaminit() ensures this assumption is valid. -----------------------------------------------------------------------------*/ - const int bytes_per_tuple = pamP->depth * sizeof(samplen); + int const bytes_per_tuple = pamP->depth * sizeof(samplen); + tuplen * tuplerown; + const char * error; /* The tuple row data structure starts with 'width' pointers to the tuples, immediately followed by the 'width' tuples @@ -35,64 +40,106 @@ pnm_allocpamrown(const struct pam * const pamP) { tuplerown = malloc(pamP->width * (sizeof(tuplen *) + bytes_per_tuple)); if (tuplerown == NULL) - pm_error("Out of memory allocating space for a tuple row of\n" - "%d tuples by %d samples per tuple by %d bytes per sample.", - pamP->width, pamP->depth, sizeof(samplen)); - - { + asprintfN(&error, "Out of memory allocating space for a tuple row of" + "%u tuples by %u samples per tuple by %u bytes per sample.", + pamP->width, pamP->depth, sizeof(samplen)); + else { /* Now we initialize the pointers to the individual tuples to make this a regulation C two dimensional array. */ - char *p; - int i; + unsigned char * p; + unsigned int i; - p = (char*) (tuplerown + pamP->width); /* location of Tuple 0 */ - for (i = 0; i < pamP->width; i++) { + p = (unsigned char*) (tuplerown + pamP->width); + /* location of Tuple 0 */ + for (i = 0; i < pamP->width; ++i) { tuplerown[i] = (tuplen) p; p += bytes_per_tuple; } + *errorP = NULL; + *tuplerownP = tuplerown; } - return(tuplerown); } -void -pnm_readpamrown(const struct pam * const pamP, - tuplen * const tuplenrow) { +tuplen * +pnm_allocpamrown(const struct pam * const pamP) { +/*---------------------------------------------------------------------------- + We assume that the dimensions of the image are such that arithmetic + overflow will not occur in our calculations. NOTE: pnm_readpaminit() + ensures this assumption is valid. +-----------------------------------------------------------------------------*/ + const char * error; + tuplen * tuplerown; - /* For speed, we don't check any of the inputs for consistency - here (unless it's necessary to avoid crashing). Any consistency - checking should have been done by a prior call to - pnm_writepaminit(). - */ - assert(pamP->maxval != 0); + allocpamrown(pamP, &tuplerown, &error); - /* Need a special case for raw PBM because it has multiple tuples (8) - packed into one byte. - */ - if (PAM_FORMAT_TYPE(pamP->format) == PBM_TYPE) { - int col; - bit *bitrow; - if (pamP->depth != 1) - pm_error("Invalid pam structure passed to pnm_readpamrow(). " - "It says PBM format, but 'depth' member is not 1."); - bitrow = pbm_allocrow(pamP->width); - pbm_readpbmrow(pamP->file, bitrow, pamP->width, pamP->format); - for (col = 0; col < pamP->width; col++) - tuplenrow[col][0] = - bitrow[col] == PBM_BLACK ? 0.0 : 1.0; + if (error) { + pm_errormsg("pnm_allocpamrown() failed. %s", error); + strfree(error); + pm_longjmp(); + } + + return tuplerown; +} + + + +static void +readpbmrow(const struct pam * const pamP, + tuplen * const tuplenrow) { + + bit * bitrow; + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; + + bitrow = pbm_allocrow(pamP->width); + + if (setjmp(jmpbuf) != 0) { pbm_freerow(bitrow); + pm_setjmpbuf(origJmpbufP); + pm_longjmp(); + } else { + unsigned int col; + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); + + pbm_readpbmrow(pamP->file, bitrow, pamP->width, pamP->format); + + for (col = 0; col < pamP->width; ++col) + tuplenrow[col][0] = bitrow[col] == PBM_BLACK ? 0.0 : 1.0; + + pm_setjmpbuf(origJmpbufP); + } + pbm_freerow(bitrow); +} + + + +static void +readpamrow(const struct pam * const pamP, + tuplen * const tuplenrow) { + + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; + tuple * tuplerow; + + tuplerow = pnm_allocpamrow(pamP); + + if (setjmp(jmpbuf) != 0) { + pnm_freepamrow(tuplerow); + pm_setjmpbuf(origJmpbufP); + pm_longjmp(); } else { float const scaler = 1.0 / pamP->maxval; /* Note: multiplication is faster than division, so we divide once here so we can multiply many times later. */ - int col; - tuple * tuplerow; - tuplerow = pnm_allocpamrow(pamP); + unsigned int col; + + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); pnm_readpamrow(pamP, tuplerow); for (col = 0; col < pamP->width; ++col) { @@ -100,15 +147,16 @@ pnm_readpamrown(const struct pam * const pamP, for (plane = 0; plane < pamP->depth; ++plane) tuplenrow[col][plane] = tuplerow[col][plane] * scaler; } - pnm_freepamrow(tuplerow); + pm_setjmpbuf(origJmpbufP); } + pnm_freepamrow(tuplerow); } void -pnm_writepamrown(const struct pam * const pamP, - const tuplen * const tuplenrow) { +pnm_readpamrown(const struct pam * const pamP, + tuplen * const tuplenrow) { /* For speed, we don't check any of the inputs for consistency here (unless it's necessary to avoid crashing). Any consistency @@ -121,20 +169,66 @@ pnm_writepamrown(const struct pam * const pamP, packed into one byte. */ if (PAM_FORMAT_TYPE(pamP->format) == PBM_TYPE) { - int col; - bit *bitrow; - bitrow = pbm_allocrow(pamP->width); - for (col = 0; col < pamP->width; col++) - bitrow[col] = - tuplenrow[col][0] < 0.5 ? PBM_BLACK : PBM_WHITE; + if (pamP->depth != 1) + pm_error("Invalid pam structure passed to pnm_readpamrow(). " + "It says PBM format, but 'depth' member is not 1."); + + readpbmrow(pamP, tuplenrow); + } else + readpamrow(pamP, tuplenrow); +} + + + +static void +writepbmrow(const struct pam * const pamP, + const tuplen * const tuplenrow) { + + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; + bit * bitrow; + + bitrow = pbm_allocrow(pamP->width); + + if (setjmp(jmpbuf) != 0) { + pbm_freerow(bitrow); + pm_setjmpbuf(origJmpbufP); + pm_longjmp(); + } else { + unsigned int col; + + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); + + for (col = 0; col < pamP->width; ++col) + bitrow[col] = tuplenrow[col][0] < 0.5 ? PBM_BLACK : PBM_WHITE; pbm_writepbmrow(pamP->file, bitrow, pamP->width, pamP->format == PBM_FORMAT); - pbm_freerow(bitrow); + + pm_setjmpbuf(origJmpbufP); + } + pbm_freerow(bitrow); +} + + + +static void +writepamrow(const struct pam * const pamP, + const tuplen * const tuplenrow) { + + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; + tuple * tuplerow; + + tuplerow = pnm_allocpamrow(pamP); + + if (setjmp(jmpbuf) != 0) { + pnm_freepamrow(tuplerow); + pm_setjmpbuf(origJmpbufP); + pm_longjmp(); } else { - tuple * tuplerow; - int col; + unsigned int col; - tuplerow = pnm_allocpamrow(pamP); + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); for (col = 0; col < pamP->width; ++col) { unsigned int plane; @@ -143,8 +237,32 @@ pnm_writepamrown(const struct pam * const pamP, (tuplenrow[col][plane] * pamP->maxval + 0.5); } pnm_writepamrow(pamP, tuplerow); - pnm_freepamrow(tuplerow); + + pm_setjmpbuf(origJmpbufP); } + pnm_freepamrow(tuplerow); +} + + + +void +pnm_writepamrown(const struct pam * const pamP, + const tuplen * const tuplenrow) { + + /* For speed, we don't check any of the inputs for consistency + here (unless it's necessary to avoid crashing). Any consistency + checking should have been done by a prior call to + pnm_writepaminit(). + */ + assert(pamP->maxval != 0); + + /* Need a special case for raw PBM because it has multiple tuples (8) + packed into one byte. + */ + if (PAM_FORMAT_TYPE(pamP->format) == PBM_TYPE) + writepbmrow(pamP, tuplenrow); + else + writepamrow(pamP, tuplenrow); } @@ -152,8 +270,8 @@ pnm_writepamrown(const struct pam * const pamP, tuplen ** pnm_allocpamarrayn(const struct pam * const pamP) { - tuplen **tuplenarray; - int row; + tuplen ** tuplenarray; + const char * error; /* If the speed of this is ever an issue, it might be sped up a little by allocating one large chunk. @@ -161,12 +279,32 @@ pnm_allocpamarrayn(const struct pam * const pamP) { MALLOCARRAY(tuplenarray, pamP->height); if (tuplenarray == NULL) - pm_error("Out of memory allocating the row pointer section of " - "a %u row array", pamP->height); - - for (row = 0; row < pamP->height; row++) { - tuplenarray[row] = pnm_allocpamrown(pamP); + asprintfN(&error, + "Out of memory allocating the row pointer section of " + "a %u row array", pamP->height); + else { + unsigned int rowsDone; + + rowsDone = 0; + + while (rowsDone < pamP->height && !error) { + allocpamrown(pamP, &tuplenarray[rowsDone], &error); + if (!error) + ++rowsDone; + } + if (error) { + unsigned int row; + for (row = 0; row < rowsDone; ++row) + pnm_freepamrown(tuplenarray[rowsDone]); + free(tuplenarray); + } + } + if (error) { + pm_errormsg("pnm_allocpamarrayn() failed. %s", error); + strfree(error); + pm_longjmp(); } + return(tuplenarray); } @@ -191,16 +329,28 @@ pnm_readpamn(FILE * const file, int const size) { tuplen **tuplenarray; - int row; + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; pnm_readpaminit(file, pamP, size); tuplenarray = pnm_allocpamarrayn(pamP); - for (row = 0; row < pamP->height; row++) - pnm_readpamrown(pamP, tuplenarray[row]); + if (setjmp(jmpbuf) != 0) { + pnm_freepamarrayn(tuplenarray, pamP); + pm_setjmpbuf(origJmpbufP); + pm_longjmp(); + } else { + unsigned int row; - return(tuplenarray); + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); + + for (row = 0; row < pamP->height; ++row) + pnm_readpamrown(pamP, tuplenarray[row]); + + pm_setjmpbuf(origJmpbufP); + } + return tuplenarray; } @@ -209,11 +359,11 @@ void pnm_writepamn(struct pam * const pamP, tuplen ** const tuplenarray) { - int row; + unsigned int row; pnm_writepaminit(pamP); - for (row = 0; row < pamP->height; row++) + for (row = 0; row < pamP->height; ++row) pnm_writepamrown(pamP, tuplenarray[row]); } @@ -473,6 +623,8 @@ createUngammaMapOffset(const struct pam * const pamP, can be used in a reverse lookup to convert normalized ungamma'ed samplen values to integer sample values. The 0.5 effectively does the rounding. + + This never throws an error. Return value NULL means failed. -----------------------------------------------------------------------------*/ pnm_transformMap * retval; pnm_transformMap ungammaTransformMap; diff --git a/lib/libpamread.c b/lib/libpamread.c index 1b65745a..85701a90 100644 --- a/lib/libpamread.c +++ b/lib/libpamread.c @@ -18,30 +18,46 @@ #include "pam.h" #include "fileio.h" +#include "nstring.h" static void readPbmRow(const struct pam * const pamP, tuple * const tuplerow) { - unsigned char *bitrow; if (pamP->depth != 1) pm_error("Invalid pam structure passed to pnm_readpamrow(). " "It says PBM format, but 'depth' member is not 1."); + else { + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; + unsigned char * bitrow; + + bitrow = (unsigned char *) pbm_allocrow(pbm_packed_bytes(pamP->width)); + + if (setjmp(jmpbuf) != 0) { + pbm_freerow(bitrow); + pm_setjmpbuf(origJmpbufP); + pm_longjmp(); + } else { + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); + + pbm_readpbmrow_packed(pamP->file, bitrow, pamP->width, + pamP->format); - bitrow = (unsigned char *) pbm_allocrow(pbm_packed_bytes(pamP->width)); - pbm_readpbmrow_packed(pamP->file, bitrow, pamP->width, pamP->format); - - if (tuplerow) { - int col; - for (col = 0; col < pamP->width; ++col) { - tuplerow[col][0] = - ( ((bitrow[col/8] >> (7-col%8)) & 1 ) == PBM_BLACK) - ? PAM_PBM_BLACK : PAM_PBM_WHITE - ; + if (tuplerow) { + unsigned int col; + for (col = 0; col < pamP->width; ++col) { + tuplerow[col][0] = + ( ((bitrow[col/8] >> (7-col%8)) & 1 ) == PBM_BLACK) + ? PAM_PBM_BLACK : PAM_PBM_WHITE + ; + } + } + pm_setjmpbuf(origJmpbufP); } - } - pbm_freerow(bitrow); + pbm_freerow(bitrow); + } } @@ -185,6 +201,7 @@ readRawNonPbmRow(const struct pam * const pamP, unsigned char * inbuf; size_t bytesRead; + const char * error; inbuf = pnm_allocrowimage(pamP); @@ -192,25 +209,34 @@ readRawNonPbmRow(const struct pam * const pamP, if (bytesRead != rowImageSize) { if (feof(pamP->file)) - pm_error("End of file encountered when trying to read a row from " - "input file."); + asprintfN(&error, + "End of file encountered when trying to read a row from " + "input file."); else - pm_error("Error reading a row from input file. " - "fread() fails with errno=%d (%s)", - errno, strerror(errno)); - } - if (tuplerow) { - switch (pamP->bytes_per_sample) { - case 1: parse1BpsRow(pamP, tuplerow, inbuf); break; - case 2: parse2BpsRow(pamP, tuplerow, inbuf); break; - case 3: parse3BpsRow(pamP, tuplerow, inbuf); break; - case 4: parse4BpsRow(pamP, tuplerow, inbuf); break; - default: - pm_error("invalid bytes per sample passed to " - "pnm_formatpamrow(): %u", pamP->bytes_per_sample); + asprintfN(&error, "Error reading a row from input file. " + "fread() fails with errno=%d (%s)", + errno, strerror(errno)); + } else { + error = NULL; /* initial assumption */ + if (tuplerow) { + switch (pamP->bytes_per_sample) { + case 1: parse1BpsRow(pamP, tuplerow, inbuf); break; + case 2: parse2BpsRow(pamP, tuplerow, inbuf); break; + case 3: parse3BpsRow(pamP, tuplerow, inbuf); break; + case 4: parse4BpsRow(pamP, tuplerow, inbuf); break; + default: + asprintfN(&error, "invalid bytes per sample passed to " + "pnm_formatpamrow(): %u", pamP->bytes_per_sample); + } } } pnm_freerowimage(inbuf); + + if (error) { + pm_errormsg("%s", error); + strfree(error); + pm_longjmp(); + } } @@ -263,15 +289,27 @@ pnm_readpam(FILE * const fileP, struct pam * const pamP, int const size) { - tuple **tuplearray; - int row; + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; + tuple ** tuplearray; pnm_readpaminit(fileP, pamP, size); tuplearray = pnm_allocpamarray(pamP); - for (row = 0; row < pamP->height; row++) - pnm_readpamrow(pamP, tuplearray[row]); - + if (setjmp(jmpbuf) != 0) { + pnm_freepamarray(tuplearray, pamP); + pm_setjmpbuf(origJmpbufP); + pm_longjmp(); + } else { + unsigned int row; + + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); + + for (row = 0; row < pamP->height; ++row) + pnm_readpamrow(pamP, tuplearray[row]); + + pm_setjmpbuf(origJmpbufP); + } return tuplearray; } diff --git a/lib/libpamwrite.c b/lib/libpamwrite.c index 9184a4b5..83f0f41b 100644 --- a/lib/libpamwrite.c +++ b/lib/libpamwrite.c @@ -308,22 +308,33 @@ writePamRawRow(const struct pam * const pamP, Write mutiple ('count') copies of the same row ('tuplerow') to the file, in raw (not plain) format. -----------------------------------------------------------------------------*/ + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; unsigned int rowImageSize; - unsigned char * outbuf; /* malloc'ed */ - unsigned int i; outbuf = pnm_allocrowimage(pamP); pnm_formatpamrow(pamP, tuplerow, outbuf, &rowImageSize); - for (i = 0; i < count; ++i) { - size_t bytesWritten; - - bytesWritten = fwrite(outbuf, 1, rowImageSize, pamP->file); - if (bytesWritten != rowImageSize) - pm_error("fwrite() failed to write an image row to the file. " - "errno=%d (%s)", errno, strerror(errno)); + if (setjmp(jmpbuf) != 0) { + pnm_freerowimage(outbuf); + pm_setjmpbuf(origJmpbufP); + pm_longjmp(); + } else { + unsigned int i; + + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); + + for (i = 0; i < count; ++i) { + size_t bytesWritten; + + bytesWritten = fwrite(outbuf, 1, rowImageSize, pamP->file); + if (bytesWritten != rowImageSize) + pm_error("fwrite() failed to write an image row to the file. " + "errno=%d (%s)", errno, strerror(errno)); + } + pm_setjmpbuf(origJmpbufP); } pnm_freerowimage(outbuf); } diff --git a/lib/libpbm3.c b/lib/libpbm3.c index 2e7b922c..a5d5ea62 100644 --- a/lib/libpbm3.c +++ b/lib/libpbm3.c @@ -58,8 +58,8 @@ static void packBitsWithMmxSse(FILE * const fileP, const bit * const bitrow, unsigned char * const packedBits, - int const cols, - int * const nextColP) { + unsigned int const cols, + unsigned int * const nextColP) { /*---------------------------------------------------------------------------- Pack the bits of bitrow[] into bytes at 'packedBits'. Going left to right, stop when there aren't enough bits left to fill a whole byte. Return @@ -136,8 +136,8 @@ static void packBitsGeneric(FILE * const fileP, const bit * const bitrow, unsigned char * const packedBits, - int const cols, - int * const nextColP) { + unsigned int const cols, + unsigned int * const nextColP) { /*---------------------------------------------------------------------------- Pack the bits of bitrow[] into byts at 'packedBits'. Going left to right, stop when there aren't enough bits left to fill a whole byte. Return @@ -166,42 +166,64 @@ packBitsGeneric(FILE * const fileP, +static void +packPartialBytes(const bit * const bitrow, + unsigned int const cols, + unsigned int const nextCol, + unsigned char * const packedBits) { + + /* routine for partial byte at the end of packedBits[] + Prior to addition of the above enhancement, + this method was used for the entire process + */ + + unsigned int col; + int bitshift; + unsigned char item; + + bitshift = 7; /* initial value */ + item = 0; /* initial value */ + for (col = nextCol; col < cols; ++col, --bitshift) + if (bitrow[col] != 0) + item |= 1 << bitshift; + + packedBits[col/8] = item; +} + + + static void writePbmRowRaw(FILE * const fileP, const bit * const bitrow, int const cols) { - int nextCol; + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; + unsigned char * packedBits; - unsigned char * const packedBits = pbm_allocrow_packed(cols); + packedBits = pbm_allocrow_packed(cols); - if (HAVE_MMX_SSE) - packBitsWithMmxSse(fileP, bitrow, packedBits, cols, &nextCol); - else - packBitsGeneric(fileP, bitrow, packedBits, cols, &nextCol); + if (setjmp(jmpbuf) != 0) { + pbm_freerow_packed(packedBits); + pm_setjmpbuf(origJmpbufP); + pm_longjmp(); + } else { + unsigned int nextCol; - /* routine for partial byte at the end of packed_bits[] - Prior to addition of the above enhancement, - this method was used for the entire process - */ - - if (cols % 8 > 0) { - int col; - int bitshift; - unsigned char item; - - bitshift = 7; /* initial value */ - item = 0; /* initial value */ - for (col = nextCol; col < cols; ++col, --bitshift ) - if (bitrow[col] !=0) - item |= 1 << bitshift - ; + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); + + if (HAVE_MMX_SSE) + packBitsWithMmxSse(fileP, bitrow, packedBits, cols, &nextCol); + else + packBitsGeneric(fileP, bitrow, packedBits, cols, &nextCol); + + if (cols % 8 > 0) + packPartialBytes(bitrow, cols, nextCol, packedBits); - packedBits[col/8] = item; + writePackedRawRow(fileP, packedBits, cols); + + pm_setjmpbuf(origJmpbufP); } - - writePackedRawRow(fileP, packedBits, cols); - pbm_freerow_packed(packedBits); } @@ -244,22 +266,37 @@ pbm_writepbmrow(FILE * const fileP, void pbm_writepbmrow_packed(FILE * const fileP, - const unsigned char * const packed_bits, + const unsigned char * const packedBits, int const cols, int const forceplain) { if (!forceplain && !pm_plain_output) - writePackedRawRow(fileP, packed_bits, cols); + writePackedRawRow(fileP, packedBits, cols); else { - bit *bitrow; - int col; + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; + bit * bitrow; bitrow = pbm_allocrow(cols); - for (col = 0; col < cols; ++col) - bitrow[col] = - packed_bits[col/8] & (0x80 >> (col%8)) ? PBM_BLACK : PBM_WHITE; - writePbmRowPlain(fileP, bitrow, cols); + if (setjmp(jmpbuf) != 0) { + pbm_freerow(bitrow); + pm_setjmpbuf(origJmpbufP); + pm_longjmp(); + } else { + unsigned int col; + + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); + + for (col = 0; col < cols; ++col) + bitrow[col] = + packedBits[col/8] & (0x80 >> (col%8)) ? + PBM_BLACK : PBM_WHITE; + + writePbmRowPlain(fileP, bitrow, cols); + + pm_setjmpbuf(origJmpbufP); + } pbm_freerow(bitrow); } } diff --git a/lib/libpgm1.c b/lib/libpgm1.c index 75fa365b..5b17910a 100644 --- a/lib/libpgm1.c +++ b/lib/libpgm1.c @@ -28,6 +28,7 @@ #include "libpam.h" #include "fileio.h" #include "mallocvar.h" +#include "nstring.h" gray * @@ -190,105 +191,166 @@ pgm_getrawsample(FILE * const file, -void -pgm_readpgmrow(FILE * const file, +static void +readRpgmRow(FILE * const fileP, gray * const grayrow, int const cols, gray const maxval, int const format) { - switch (format) { - case PGM_FORMAT: { - unsigned int col; - for (col = 0; col < cols; ++col) { - grayrow[col] = pm_getuint(file); - if (grayrow[col] > maxval) - pm_error("value out of bounds (%u > %u)", - grayrow[col], maxval); + unsigned int const bytesPerSample = maxval < 256 ? 1 : 2; + int const bytesPerRow = cols * bytesPerSample; + + unsigned char * rowBuffer; + const char * error; + + MALLOCARRAY(rowBuffer, bytesPerRow); + if (rowBuffer == NULL) + asprintfN(&error, "Unable to allocate memory for row buffer " + "for %u columns", cols); + else { + ssize_t rc; + rc = fread(rowBuffer, 1, bytesPerRow, fileP); + if (rc == 0) + asprintfN(&error, "Error reading row. fread() errno=%d (%s)", + errno, strerror(errno)); + else if (rc != bytesPerRow) + asprintfN(&error, "Error reading row. Short read of %u bytes " + "instead of %u", rc, bytesPerRow); + else { + error = NULL; + if (maxval < 256) { + unsigned int col; + for (col = 0; col < cols; ++col) + grayrow[col] = (gray)rowBuffer[col]; + } else { + unsigned int bufferCursor; + unsigned int col; + + bufferCursor = 0; /* Start at beginning of rowBuffer[] */ + + for (col = 0; col < cols; ++col) { + gray g; + + g = rowBuffer[bufferCursor++] << 8; + g |= rowBuffer[bufferCursor++]; + + grayrow[col] = g; + } + } } + free(rowBuffer); } - break; - - case RPGM_FORMAT: { - unsigned int const bytesPerSample = maxval < 256 ? 1 : 2; - int const bytesPerRow = cols * bytesPerSample; + if (error) { + pm_errormsg("%s", error); + strfree(error); + pm_longjmp(); + } +} - unsigned char * rowBuffer; - ssize_t rc; - MALLOCARRAY(rowBuffer, bytesPerRow); - if (rowBuffer == NULL) - pm_error("Unable to allocate memory for row buffer " - "for %u columns", cols); - rc = fread(rowBuffer, 1, bytesPerRow, file); - if (rc == 0) - pm_error("Error reading row. fread() errno=%d (%s)", - errno, strerror(errno)); - else if (rc != bytesPerRow) - pm_error("Error reading row. Short read of %u bytes " - "instead of %u", rc, bytesPerRow); +static void +readPbmRow(FILE * const fileP, + gray * const grayrow, + int const cols, + gray const maxval, + int const format) { + + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; + bit * bitrow; + + bitrow = pbm_allocrow(cols); + if (setjmp(jmpbuf) != 0) { + pbm_freerow(bitrow); + pm_setjmpbuf(origJmpbufP); + pm_longjmp(); + } else { + unsigned int col; + + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); + + pbm_readpbmrow(fileP, bitrow, cols, format); + for (col = 0; col < cols; ++col) + grayrow[col] = (bitrow[col] == PBM_WHITE ) ? maxval : 0; - if (maxval < 256) { - unsigned int col; - for (col = 0; col < cols; ++col) - grayrow[col] = (gray)rowBuffer[col]; - } else { - unsigned int bufferCursor; - unsigned int col; + pm_setjmpbuf(origJmpbufP); + } + pbm_freerow(bitrow); +} - bufferCursor = 0; /* Start at beginning of rowBuffer[] */ - for (col = 0; col < cols; ++col) { - gray g; - g = rowBuffer[bufferCursor++] << 8; - g |= rowBuffer[bufferCursor++]; +void +pgm_readpgmrow(FILE * const fileP, + gray * const grayrow, + int const cols, + gray const maxval, + int const format) { - grayrow[col] = g; - } + switch (format) { + case PGM_FORMAT: { + unsigned int col; + for (col = 0; col < cols; ++col) { + grayrow[col] = pm_getuint(fileP); + if (grayrow[col] > maxval) + pm_error("value out of bounds (%u > %u)", + grayrow[col], maxval); } - free(rowBuffer); } + break; + + case RPGM_FORMAT: + readRpgmRow(fileP, grayrow, cols, maxval, format); break; case PBM_FORMAT: - case RPBM_FORMAT: { - bit * bitrow; - int col; - - bitrow = pbm_allocrow(cols); - pbm_readpbmrow( file, bitrow, cols, format ); - for (col = 0; col < cols; ++col) - grayrow[col] = (bitrow[col] == PBM_WHITE ) ? maxval : 0; - pbm_freerow(bitrow); - } + case RPBM_FORMAT: + readPbmRow(fileP, grayrow, cols, maxval, format); break; default: - pm_error( "can't happen" ); + pm_error("can't happen"); } } gray ** -pgm_readpgm(FILE * const file, +pgm_readpgm(FILE * const fileP, int * const colsP, int * const rowsP, gray * const maxvalP) { - gray** grays; - int row; + gray ** grays; + int rows, cols; + gray maxval; int format; + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; - pgm_readpgminit( file, colsP, rowsP, maxvalP, &format ); + pgm_readpgminit(fileP, &cols, &rows, &maxval, &format); - grays = pgm_allocarray( *colsP, *rowsP ); - - for ( row = 0; row < *rowsP; ++row ) - pgm_readpgmrow( file, grays[row], *colsP, *maxvalP, format ); + grays = pgm_allocarray(cols, rows); + + if (setjmp(jmpbuf) != 0) { + pgm_freearray(grays, rows); + pm_setjmpbuf(origJmpbufP); + pm_longjmp(); + } else { + unsigned int row; + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); + + for (row = 0; row < rows; ++row) + pgm_readpgmrow(fileP, grays[row], cols, maxval, format); + + pm_setjmpbuf(origJmpbufP); + } + *colsP = cols; + *rowsP = rows; + *maxvalP = maxval; return grays; } diff --git a/lib/libpm.c b/lib/libpm.c index 2e563a09..df59e6c4 100644 --- a/lib/libpm.c +++ b/lib/libpm.c @@ -55,7 +55,7 @@ /* The following are set by pm_init(), then used by subsequent calls to other pm_xxx() functions. */ -static const char* pm_progname; +static const char * pm_progname; static bool pm_showmessages; /* Programs should display informational messages (because the user didn't specify the --quiet option). @@ -76,6 +76,17 @@ static jmp_buf * pm_jmpbufP = NULL; NULL, which is the default value, means when a libnetpbm function encounters an error, it causes the process to exit. */ +static pm_usererrormsgfn * userErrorMsgFn = NULL; + /* A function to call to issue an error message. + + NULL means use the library default: print to Standard Error + */ + +static pm_usermessagefn * userMessageFn = NULL; + /* A function to call to issue an error message. + + NULL means use the library default: print to Standard Error + */ @@ -108,20 +119,24 @@ pm_longjmp(void) { void -pm_usage(const char usage[]) { - pm_error("usage: %s %s", pm_progname, usage); +pm_setusererrormsgfn(pm_usererrormsgfn * fn) { + + userErrorMsgFn = fn; } void -pm_perror(const char reason[] ) { +pm_setusermessagefn(pm_usermessagefn * fn) { - if (reason != NULL && strlen(reason) != 0) - pm_error("%s - errno=%d (%s)", reason, errno, strerror(errno)); - else - pm_error("Something failed with errno=%d (%s)", - errno, strerror(errno)); + userMessageFn = fn; +} + + + +void +pm_usage(const char usage[]) { + pm_error("usage: %s %s", pm_progname, usage); } @@ -134,24 +149,64 @@ pm_message(const char format[], ...) { va_start(args, format); if (pm_showmessages) { - fprintf(stderr, "%s: ", pm_progname); - vfprintf(stderr, format, args); - fputc('\n', stderr); + const char * msg; + vasprintfN(&msg, format, args); + + if (userMessageFn) + userMessageFn(msg); + else + fprintf(stderr, "%s: %s\n", pm_progname, msg); + + strfree(msg); } va_end(args); } +static void +errormsg(const char * const msg) { + + if (userErrorMsgFn) + userErrorMsgFn(msg); + else + fprintf(stderr, "%s: %s\n", pm_progname, msg); +} + + + +void PM_GNU_PRINTF_ATTR(1,2) +pm_errormsg(const char format[], ...) { + + va_list args; + const char * msg; + + va_start(args, format); + + vasprintfN(&msg, format, args); + + errormsg(msg); + + strfree(msg); + + va_end(args); +} + + + void PM_GNU_PRINTF_ATTR(1,2) pm_error(const char format[], ...) { va_list args; + const char * msg; va_start(args, format); - fprintf(stderr, "%s: ", pm_progname); - vfprintf(stderr, format, args); - fputc('\n', stderr); + vasprintfN(&msg, format, args); + + errormsg(msg); + + strfree(msg); + va_end(args); pm_longjmp(); @@ -186,8 +241,64 @@ pm_freerow(char * const itrow) { -char** -pm_allocarray(int const cols, int const rows, int const size ) { +static void +allocarrayNoHeap(unsigned char ** const rowIndex, + unsigned int const cols, + unsigned int const rows, + unsigned int const size, + const char ** const errorP) { + + if (UINT_MAX / cols < size) + asprintfN(errorP, + "Arithmetic overflow multiplying %u by %u to get the " + "size of a row to allocate.", cols, size); + else { + unsigned int rowsDone; + + rowsDone = 0; + *errorP = NULL; + + while (rowsDone < rows && !*errorP) { + unsigned char * const rowSpace = malloc(cols * size); + if (rowSpace == NULL) + asprintfN(errorP, + "Unable to allocate a %u-column by %u byte row", + cols, size); + else + rowIndex[rowsDone++] = rowSpace; + } + if (*errorP) { + unsigned int row; + for (row = 0; row < rowsDone; ++row) + free(rowIndex[row]); + } + } +} + + + +static unsigned char * +allocRowHeap(unsigned int const cols, + unsigned int const rows, + unsigned int const size) { + + unsigned char * retval; + + if (UINT_MAX / cols / rows < size) + /* Too big even to request the memory ! */ + retval = NULL; + else + retval = malloc(rows * cols * size); + + return retval; +} + + + +char ** +pm_allocarray(int const cols, + int const rows, + int const size ) { /*---------------------------------------------------------------------------- Allocate an array of 'rows' rows of 'cols' columns each, with each element 'size' bytes. @@ -206,38 +317,46 @@ pm_allocarray(int const cols, int const rows, int const size ) { We use unfragmented format if possible, but if the allocation of the row heap fails, we fall back to fragmented. -----------------------------------------------------------------------------*/ - char** rowIndex; - char * rowheap; + unsigned char ** rowIndex; + const char * error; MALLOCARRAY(rowIndex, rows + 1); if (rowIndex == NULL) - pm_error("out of memory allocating row index (%u rows) for an array", - rows); - rowheap = malloc(rows * cols * size); - if (rowheap == NULL) { - /* We couldn't get the whole heap in one block, so try fragmented - format. - */ - unsigned int row; - - rowIndex[rows] = NULL; /* Declare it fragmented format */ - - for (row = 0; row < rows; ++row) { - rowIndex[row] = pm_allocrow(cols, size); - if (rowIndex[row] == NULL) - pm_error("out of memory allocating Row %u " - "(%u columns, %u bytes per tuple) " - "of an array", row, cols, size); - } - } else { - /* It's unfragmented format */ - unsigned int row; - rowIndex[rows] = rowheap; /* Declare it unfragmented format */ + asprintfN(&error, + "out of memory allocating row index (%u rows) for an array", + rows); + else { + unsigned char * rowheap; - for (row = 0; row < rows; ++row) - rowIndex[row] = &(rowheap[row * cols * size]); + rowheap = allocRowHeap(cols, rows, size); + + if (rowheap) { + /* It's unfragmented format */ + + rowIndex[rows] = rowheap; /* Declare it unfragmented format */ + + if (rowheap) { + unsigned int row; + + for (row = 0; row < rows; ++row) + rowIndex[row] = &(rowheap[row * cols * size]); + } + error = NULL; + } else { + /* We couldn't get the whole heap in one block, so try fragmented + format. + */ + rowIndex[rows] = NULL; /* Declare it fragmented format */ + + allocarrayNoHeap(rowIndex, cols, rows, size, &error); + } } - return rowIndex; + if (error) { + pm_errormsg("Couldn't allocate %u-row array. %s", rows, error); + strfree(error); + pm_longjmp(); + } + return (char **)rowIndex; } @@ -807,17 +926,65 @@ mkstemp2(char * const filenameBuffer) { +static void +makeTmpfileWithTemplate(const char * const filenameTemplate, + FILE ** const filePP, + const char ** const filenameP, + const char ** const errorP) { + + char * filenameBuffer; /* malloc'ed */ + + filenameBuffer = strdup(filenameTemplate); + + if (filenameBuffer == NULL) + asprintfN(errorP, "Unable to allocate storage for temporary " + "file name"); + else { + int rc; + + rc = mkstemp2(filenameBuffer); + + if (rc < 0) + asprintfN(errorP, + "Unable to create temporary file according to name " + "pattern '%s'. mkstemp() failed with errno %d (%s)", + filenameTemplate, errno, strerror(errno)); + else { + int const fd = rc; + + FILE * fileP; + fileP = fdopen(fd, "w+b"); + + if (fileP == NULL) + asprintfN(errorP, "Unable to create temporary file. " + "fdopen() failed with errno %d (%s)", + errno, strerror(errno)); + else { + *errorP = NULL; + *filePP = fileP; + *filenameP = filenameBuffer; + } + if (*errorP) { + unlink(filenameBuffer); + close(fd); + } + } + if (*errorP) + strfree(filenameBuffer); + } +} + + + void pm_make_tmpfile(FILE ** const filePP, const char ** const filenameP) { - int fd; - FILE * fileP; const char * filenameTemplate; - char * filenameBuffer; /* malloc'ed */ unsigned int fnamelen; const char * tmpdir; const char * dirseparator; + const char * error; fnamelen = strlen (pm_progname) + 10; /* "/" + "_XXXXXX\0" */ @@ -832,27 +999,18 @@ pm_make_tmpfile(FILE ** const filePP, tmpdir, dirseparator, pm_progname, "_XXXXXX"); if (filenameTemplate == NULL) - pm_error("Unable to allocate storage for temporary file name"); - - filenameBuffer = strdup(filenameTemplate); - - fd = mkstemp2(filenameBuffer); - - if (fd < 0) - pm_error("Unable to create temporary file according to name " - "pattern '%s'. mkstemp() failed with " - "errno %d (%s)", filenameTemplate, errno, strerror(errno)); + asprintfN(&error, + "Unable to allocate storage for temporary file name"); else { - fileP = fdopen(fd, "w+b"); + makeTmpfileWithTemplate(filenameTemplate, filePP, filenameP, &error); - if (fileP == NULL) - pm_error("Unable to create temporary file. fdopen() failed " - "with errno %d (%s)", errno, strerror(errno)); + strfree(filenameTemplate); + } + if (error) { + pm_errormsg("%s", error); + strfree(error); + pm_longjmp(); } - strfree(filenameTemplate); - - *filenameP = filenameBuffer; - *filePP = fileP; } @@ -1212,9 +1370,9 @@ pm_readmagicnumber(FILE * const ifP) { Oliver Trepte, oliver@fysik4.kth.se, 930613 */ #define PM_BUF_SIZE 16384 /* First try this size of the buffer, then - double this until we reach PM_MAX_BUF_INC */ + double this until we reach PM_MAX_BUF_INC */ #define PM_MAX_BUF_INC 65536 /* Don't allocate more memory in larger blocks - than this. */ + than this. */ char * pm_read_unknown_size(FILE * const file, diff --git a/lib/libpnm1.c b/lib/libpnm1.c index 82f99b93..536e5dc4 100644 --- a/lib/libpnm1.c +++ b/lib/libpnm1.c @@ -133,6 +133,74 @@ pnm_readpnminit(FILE * const fileP, +static void +readpgmrow(FILE * const fileP, + xel * const xelrow, + int const cols, + xelval const maxval, + int const format) { + + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; + gray * grayrow; + + grayrow = pgm_allocrow(cols); + + if (setjmp(jmpbuf) != 0) { + pgm_freerow(grayrow); + pm_setjmpbuf(origJmpbufP); + pm_longjmp(); + } else { + unsigned int col; + + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); + + pgm_readpgmrow(fileP, grayrow, cols, (gray) maxval, format); + + for (col = 0; col < cols; ++col) + PNM_ASSIGN1(xelrow[col], grayrow[col]); + + pm_setjmpbuf(origJmpbufP); + } + pgm_freerow(grayrow); +} + + + +static void +readpbmrow(FILE * const fileP, + xel * const xelrow, + int const cols, + xelval const maxval, + int const format) { + + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; + bit * bitrow; + + bitrow = pbm_allocrow(cols); + + if (setjmp(jmpbuf) != 0) { + pbm_freerow(bitrow); + pm_setjmpbuf(origJmpbufP); + pm_longjmp(); + } else { + unsigned int col; + + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); + + pbm_readpbmrow(fileP, bitrow, cols, format); + + for (col = 0; col < cols; ++col) + PNM_ASSIGN1(xelrow[col], bitrow[col] == PBM_BLACK ? 0 : maxval); + + pm_setjmpbuf(origJmpbufP); + } + pbm_freerow(bitrow); +} + + + void pnm_readpnmrow(FILE * const fileP, xel * const xelrow, @@ -145,28 +213,13 @@ pnm_readpnmrow(FILE * const fileP, ppm_readppmrow(fileP, (pixel*) xelrow, cols, (pixval) maxval, format); break; - case PGM_TYPE: { - gray * grayrow; - unsigned int col; - - grayrow = pgm_allocrow(cols); - pgm_readpgmrow(fileP, grayrow, cols, (gray) maxval, format); - for (col = 0; col < cols; ++col) - PNM_ASSIGN1(xelrow[col], grayrow[col]); - pgm_freerow(grayrow); - } - break; + case PGM_TYPE: + readpgmrow(fileP, xelrow, cols, maxval, format); + break; - case PBM_TYPE: { - bit * bitrow; - unsigned int col; - bitrow = pbm_allocrow(cols); - pbm_readpbmrow(fileP, bitrow, cols, format); - for (col = 0; col < cols; ++col) - PNM_ASSIGN1(xelrow[col], bitrow[col] == PBM_BLACK ? 0: maxval); - pbm_freerow(bitrow); - } - break; + case PBM_TYPE: + readpbmrow(fileP, xelrow, cols, maxval, format); + break; default: pm_error("INTERNAL ERROR. Impossible format."); @@ -182,15 +235,35 @@ pnm_readpnm(FILE * const fileP, xelval * const maxvalP, int * const formatP) { + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; + int cols, rows; + xelval maxval; + int format; xel ** xels; - int row; - pnm_readpnminit(fileP, colsP, rowsP, maxvalP, formatP); + pnm_readpnminit(fileP, &cols, &rows, &maxval, &format); + + xels = pnm_allocarray(cols, rows); - xels = pnm_allocarray(*colsP, *rowsP); + if (setjmp(jmpbuf) != 0) { + pnm_freearray(xels, rows); + pm_setjmpbuf(origJmpbufP); + pm_longjmp(); + } else { + unsigned int row; - for (row = 0; row < *rowsP; ++row) - pnm_readpnmrow(fileP, xels[row], *colsP, *maxvalP, *formatP); + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); + + for (row = 0; row < rows; ++row) + pnm_readpnmrow(fileP, xels[row], cols, maxval, format); + + pm_setjmpbuf(origJmpbufP); + } + *colsP = cols; + *rowsP = rows; + *maxvalP = maxval; + *formatP = format; return xels; } diff --git a/lib/libpnm2.c b/lib/libpnm2.c index aae78d52..7e4f7e2a 100644 --- a/lib/libpnm2.c +++ b/lib/libpnm2.c @@ -55,6 +55,74 @@ pnm_writepnminit(FILE * const fileP, +static void +writepgmrow(FILE * const fileP, + 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); + pm_longjmp(); + } else { + 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); + } + pgm_freerow(grayrow); +} + + + +static void +writepbmrow(FILE * const fileP, + xel * const xelrow, + unsigned int const cols, + bool const plainFormat) { + + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; + bit * bitrow; + + bitrow = pbm_allocrow(cols); + + if (setjmp(jmpbuf) != 0) { + pbm_freerow(bitrow); + pm_setjmpbuf(origJmpbufP); + pm_longjmp(); + } else { + unsigned int col; + + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); + + for (col = 0; col < cols; ++col) + bitrow[col] = PNM_GET1(xelrow[col]) == 0 ? PBM_BLACK : PBM_WHITE; + + pbm_writepbmrow(fileP, bitrow, cols, plainFormat); + + pm_setjmpbuf(origJmpbufP); + } + pbm_freerow(bitrow); +} + + + void pnm_writepnmrow(FILE * const fileP, xel * const xelrow, @@ -71,35 +139,13 @@ pnm_writepnmrow(FILE * const fileP, plainFormat); break; - case PGM_TYPE: { - gray* grayrow; - unsigned int col; - - grayrow = pgm_allocrow(cols); - - for (col = 0; col < cols; ++col) - grayrow[col] = PNM_GET1(xelrow[col]); - - pgm_writepgmrow(fileP, grayrow, cols, (gray) maxval, plainFormat); - - pgm_freerow( grayrow ); - } - break; - - case PBM_TYPE: { - bit* bitrow; - unsigned int col; - - bitrow = pbm_allocrow(cols); - - for (col = 0; col < cols; ++col) - bitrow[col] = PNM_GET1(xelrow[col]) == 0 ? PBM_BLACK : PBM_WHITE; - - pbm_writepbmrow(fileP, bitrow, cols, plainFormat); + case PGM_TYPE: + writepgmrow(fileP, xelrow, cols, maxval, format, plainFormat); + break; - pbm_freerow(bitrow); - } - break; + case PBM_TYPE: + writepbmrow(fileP, xelrow, cols, plainFormat); + break; default: pm_error("invalid format argument received by pnm_writepnmrow(): %d" diff --git a/lib/libppm1.c b/lib/libppm1.c index 57a1db7d..a7ea78cf 100644 --- a/lib/libppm1.c +++ b/lib/libppm1.c @@ -19,6 +19,7 @@ #include #include #include + #include "ppm.h" #include "libppm.h" #include "pgm.h" @@ -29,6 +30,7 @@ #include "libpam.h" #include "fileio.h" #include "mallocvar.h" +#include "nstring.h" pixel * @@ -150,124 +152,211 @@ ppm_readppminit(FILE * const fileP, -void -ppm_readppmrow(FILE* const fileP, - pixel* const pixelrow, - int const cols, - pixval const maxval, - int const format) { - - switch (format) { - case PPM_FORMAT: { - unsigned int col; - for (col = 0; col < cols; ++col) { - pixval const r = pm_getuint(fileP); - pixval const g = pm_getuint(fileP); - pixval const b = pm_getuint(fileP); - - if (r > maxval) - pm_error("Red sample value %u is greater than maxval (%u)", - r, maxval); - if (g > maxval) - pm_error("Green sample value %u is greater than maxval (%u)", - g, maxval); - if (b > maxval) - pm_error("Blue sample value %u is greater than maxval (%u)", - b, maxval); - - PPM_ASSIGN(pixelrow[col], r, g, b); - } +static void +readppm(FILE * const fileP, + pixel * const pixelrow, + unsigned int const cols, + pixval const maxval, + int const format) { + + unsigned int col; + + for (col = 0; col < cols; ++col) { + pixval const r = pm_getuint(fileP); + pixval const g = pm_getuint(fileP); + pixval const b = pm_getuint(fileP); + + if (r > maxval) + pm_error("Red sample value %u is greater than maxval (%u)", + r, maxval); + if (g > maxval) + pm_error("Green sample value %u is greater than maxval (%u)", + g, maxval); + if (b > maxval) + pm_error("Blue sample value %u is greater than maxval (%u)", + b, maxval); + + PPM_ASSIGN(pixelrow[col], r, g, b); } - break; +} - /* For PAM, we require a depth of 3, which means the raster format - is identical to Raw PPM! How convenient. - */ - case PAM_FORMAT: - case RPPM_FORMAT: { - unsigned int const bytesPerSample = maxval < 256 ? 1 : 2; - unsigned int const bytesPerRow = cols * 3 * bytesPerSample; - - unsigned int bufferCursor; - unsigned char * rowBuffer; - ssize_t rc; - MALLOCARRAY(rowBuffer, bytesPerRow); + +static void +readrppm(FILE * const fileP, + pixel * const pixelrow, + unsigned int const cols, + pixval const maxval, + int const format) { + + unsigned int const bytesPerSample = maxval < 256 ? 1 : 2; + unsigned int const bytesPerRow = cols * 3 * bytesPerSample; - if (rowBuffer == NULL) - pm_error("Unable to allocate memory for row buffer " - "for %u columns", cols); + unsigned char * rowBuffer; + const char * error; + + MALLOCARRAY(rowBuffer, bytesPerRow); + + if (rowBuffer == NULL) + asprintfN(&error, "Unable to allocate memory for row buffer " + "for %u columns", cols); + else { + ssize_t rc; rc = fread(rowBuffer, 1, bytesPerRow, fileP); if (feof(fileP)) - pm_error("Unexpected EOF reading row of PPM image."); + asprintfN(&error, "Unexpected EOF reading row of PPM image."); else if (ferror(fileP)) - pm_error("Error reading row. fread() errno=%d (%s)", - errno, strerror(errno)); + asprintfN(&error, "Error reading row. fread() errno=%d (%s)", + errno, strerror(errno)); else if (rc != bytesPerRow) - pm_error("Error reading row. Short read of %u bytes " - "instead of %u", rc, bytesPerRow); - - bufferCursor = 0; /* start at beginning of rowBuffer[] */ + asprintfN(&error, "Error reading row. Short read of %u bytes " + "instead of %u", rc, bytesPerRow); + else { + unsigned int bufferCursor; + + error = NULL; + + bufferCursor = 0; /* start at beginning of rowBuffer[] */ - if (bytesPerSample == 1) { - unsigned int col; - for (col = 0; col < cols; ++col) { - pixval const r = rowBuffer[bufferCursor++]; - pixval const g = rowBuffer[bufferCursor++]; - pixval const b = rowBuffer[bufferCursor++]; - PPM_ASSIGN(pixelrow[col], r, g, b); - } - } else { - /* two byte samples */ - unsigned int col; - for (col = 0; col < cols; ++col) { - pixval r, g, b; - - r = rowBuffer[bufferCursor++] << 8; - r |= rowBuffer[bufferCursor++]; - - g = rowBuffer[bufferCursor++] << 8; - g |= rowBuffer[bufferCursor++]; - - b = rowBuffer[bufferCursor++] << 8; - b |= rowBuffer[bufferCursor++]; - - PPM_ASSIGN(pixelrow[col], r, g, b); + if (bytesPerSample == 1) { + unsigned int col; + for (col = 0; col < cols; ++col) { + pixval const r = rowBuffer[bufferCursor++]; + pixval const g = rowBuffer[bufferCursor++]; + pixval const b = rowBuffer[bufferCursor++]; + PPM_ASSIGN(pixelrow[col], r, g, b); + } + } else { + /* two byte samples */ + unsigned int col; + for (col = 0; col < cols; ++col) { + pixval r, g, b; + + r = rowBuffer[bufferCursor++] << 8; + r |= rowBuffer[bufferCursor++]; + + g = rowBuffer[bufferCursor++] << 8; + g |= rowBuffer[bufferCursor++]; + + b = rowBuffer[bufferCursor++] << 8; + b |= rowBuffer[bufferCursor++]; + + PPM_ASSIGN(pixelrow[col], r, g, b); + } } } free(rowBuffer); } - break; + if (error) { + pm_errormsg("%s", error); + strfree(error); + pm_longjmp(); + } +} - case PGM_FORMAT: - case RPGM_FORMAT: { - gray * const grayrow = pgm_allocrow(cols); + + +static void +readpgm(FILE * const fileP, + pixel * const pixelrow, + unsigned int const cols, + pixval const maxval, + int const format) { + + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; + gray * grayrow; + + grayrow = pgm_allocrow(cols); + + if (setjmp(jmpbuf) != 0) { + pgm_freerow(grayrow); + pm_setjmpbuf(origJmpbufP); + pm_longjmp(); + } else { unsigned int col; + + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); pgm_readpgmrow(fileP, grayrow, cols, maxval, format); + for (col = 0; col < cols; ++col) { pixval const g = grayrow[col]; PPM_ASSIGN(pixelrow[col], g, g, g); } - pgm_freerow(grayrow); + pm_setjmpbuf(origJmpbufP); } - break; + pgm_freerow(grayrow); +} - case PBM_FORMAT: - case RPBM_FORMAT: { - bit * const bitrow = pbm_allocrow(cols); + + +static void +readpbm(FILE * const fileP, + pixel * const pixelrow, + unsigned int const cols, + pixval const maxval, + int const format) { + + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; + bit * bitrow; + + bitrow = pbm_allocrow(cols); + + if (setjmp(jmpbuf) != 0) { + pbm_freerow(bitrow); + pm_setjmpbuf(origJmpbufP); + pm_longjmp(); + } else { unsigned int col; + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); + pbm_readpbmrow(fileP, bitrow, cols, format); + for (col = 0; col < cols; ++col) { pixval const g = (bitrow[col] == PBM_WHITE) ? maxval : 0; PPM_ASSIGN(pixelrow[col], g, g, g); } - pbm_freerow(bitrow); + pm_setjmpbuf(origJmpbufP); } - break; + pbm_freerow(bitrow); +} + + + +void +ppm_readppmrow(FILE * const fileP, + pixel * const pixelrow, + int const cols, + pixval const maxval, + int const format) { + + switch (format) { + case PPM_FORMAT: + readppm(fileP, pixelrow, cols, maxval, format); + break; + + /* For PAM, we require a depth of 3, which means the raster format + is identical to Raw PPM! How convenient. + */ + case PAM_FORMAT: + case RPPM_FORMAT: + readrppm(fileP, pixelrow, cols, maxval, format); + break; + + case PGM_FORMAT: + case RPGM_FORMAT: + readpgm(fileP, pixelrow, cols, maxval, format); + break; + + case PBM_FORMAT: + case RPBM_FORMAT: + readpbm(fileP, pixelrow, cols, maxval, format); + break; default: pm_error("Invalid format code"); @@ -281,17 +370,36 @@ ppm_readppm(FILE * const fileP, int * const colsP, int * const rowsP, pixval * const maxvalP) { - pixel** pixels; - int row; + + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; + pixel ** pixels; + int cols, rows; + pixval maxval; int format; - ppm_readppminit(fileP, colsP, rowsP, maxvalP, &format); + ppm_readppminit(fileP, &cols, &rows, &maxval, &format); - pixels = ppm_allocarray(*colsP, *rowsP); + pixels = ppm_allocarray(cols, rows); - for (row = 0; row < *rowsP; ++row) - ppm_readppmrow(fileP, pixels[row], *colsP, *maxvalP, format); + if (setjmp(jmpbuf) != 0) { + ppm_freearray(pixels, rows); + pm_setjmpbuf(origJmpbufP); + pm_longjmp(); + } else { + unsigned int row; + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); + + for (row = 0; row < rows; ++row) + ppm_readppmrow(fileP, pixels[row], cols, maxval, format); + + *colsP = cols; + *rowsP = rows; + *maxvalP = maxval; + + pm_setjmpbuf(origJmpbufP); + } return pixels; } diff --git a/lib/libppmcmap.c b/lib/libppmcmap.c index a9efccbc..c1243cb6 100644 --- a/lib/libppmcmap.c +++ b/lib/libppmcmap.c @@ -12,9 +12,11 @@ ** implied warranty. */ -#include "ppm.h" -#include "libppm.h" +#include "pm_c_util.h" +#include "nstring.h" #include "mallocvar.h" +#include "libppm.h" +#include "ppm.h" #include "ppmcmap.h" #define HASH_SIZE 20023 @@ -110,94 +112,124 @@ ppm_addtocolorhist( colorhist_vector chv, -colorhash_table -ppm_alloccolorhash(void) { +static colorhash_table +alloccolorhash(void) { colorhash_table cht; int i; MALLOCARRAY(cht, HASH_SIZE); + if (cht) { + for (i = 0; i < HASH_SIZE; ++i) + cht[i] = NULL; + } + return cht; +} + + + +colorhash_table +ppm_alloccolorhash(void) { + colorhash_table cht; + + cht = alloccolorhash(); + if (cht == NULL) pm_error( "out of memory allocating hash table" ); - for (i = 0; i < HASH_SIZE; ++i) - cht[i] = NULL; - return cht; } -static colorhash_table -computecolorhash(pixel ** const pixels, - const int cols, const int rows, - const int maxcolors, int * const colorsP, - FILE * const ifp, pixval const maxval, int const format) { -/*---------------------------------------------------------------------------- - Compute a color histogram from an image. The input is one of two types: +static void +readppmrow(FILE * const fileP, + pixel * const pixelrow, + int const cols, + pixval const maxval, + int const format, + const char ** const errorP) { - 1) a two-dimensional array of pixels 'pixels'; In this case, 'pixels' - is non-NULL and 'ifp' is NULL. + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; + + if (setjmp(jmpbuf) != 0) { + pm_setjmpbuf(origJmpbufP); + asprintfN(errorP, "Failed to read row of image."); + } else { + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); - 2) an open file, positioned to the image data. In this case, - 'pixels' is NULL and 'ifp' is non-NULL. ifp is the stream - descriptor for the input file, and 'maxval' and 'format' are - parameters of the image data in it. - - We return with the file still open and its position undefined. + ppm_readppmrow(fileP, pixelrow, cols, maxval, format); - In either case, the image is 'cols' by 'rows'. + *errorP = NULL; /* Would have longjmped if anything went wrong */ + + pm_setjmpbuf(origJmpbufP); + } +} - Return the number of colors found as *colorsP. - However, if 'maxcolors' is nonzero and the number of colors is - greater than 'maxcolors', return a null return value and *colorsP - undefined. + +static void +buildHashTable(FILE * const ifP, + pixel ** const pixels, + unsigned int const cols, + unsigned int const rows, + pixval const maxval, + int const format, + unsigned int const maxcolors, + colorhash_table const cht, + pixel * const rowbuffer, + int * const nColorsP, + bool * const tooManyColorsP, + const char ** const errorP) { +/*---------------------------------------------------------------------------- + Look at all the colors in the file *ifP or array pixels[][] and add + them to the hash table 'cht'. + + Even if we fail, we may add some colors to 'cht'. + + As soon as we've seen more that 'maxcolors' colors, we quit. In that + case, only, we return *tooManyColorsP == true. That is not a failure. + 'maxcolors' == 0 means infinity. -----------------------------------------------------------------------------*/ - colorhash_table cht; - int row; - pixel * rowbuffer; /* malloc'ed */ - /* Buffer for a row read from the input file; undefined (but still - allocated) if input is not from a file. - */ - - cht = ppm_alloccolorhash( ); - *colorsP = 0; /* initial value */ + unsigned int row; + unsigned int nColors; - rowbuffer = ppm_allocrow(cols); + nColors = 0; /* initial value */ + *tooManyColorsP = FALSE; /* initial value */ + *errorP = NULL; /* initial value */ /* Go through the entire image, building a hash table of colors. */ - for (row = 0; row < rows; ++row) { - int col; + for (row = 0; row < rows && !*tooManyColorsP && !*errorP; ++row) { + unsigned int col; pixel * pixelrow; /* The row of pixels we are processing */ - if (ifp) { - ppm_readppmrow(ifp, rowbuffer, cols, maxval, format); + if (ifP) { + readppmrow(ifP, rowbuffer, cols, maxval, format, errorP); pixelrow = rowbuffer; } else pixelrow = pixels[row]; - for (col = 0; col < cols; ++col) { + for (col = 0; col < cols && !*tooManyColorsP && !*errorP; ++col) { const pixel apixel = pixelrow[col]; const int hash = ppm_hashpixel(apixel); colorhist_list chl; for (chl = cht[hash]; - chl != (colorhist_list) 0 && - !PPM_EQUAL(chl->ch.color, apixel); + chl && !PPM_EQUAL(chl->ch.color, apixel); chl = chl->next); if (chl) - chl->ch.value++; + ++chl->ch.value; else { /* It's not in the hash yet, so add it (if allowed) */ - ++(*colorsP); - if (maxcolors > 0 && *colorsP > maxcolors) { - ppm_freecolorhash(cht); - return NULL; - } else { + ++nColors; + if (maxcolors > 0 && nColors > maxcolors) + *tooManyColorsP = TRUE; + else { MALLOCVAR(chl); if (chl == NULL) - pm_error("out of memory computing hash table"); + asprintfN(errorP, + "out of memory computing hash table"); chl->ch.color = apixel; chl->ch.value = 1; chl->next = cht[hash]; @@ -206,31 +238,124 @@ computecolorhash(pixel ** const pixels, } } } - ppm_freerow(rowbuffer); - return cht; + *nColorsP = nColors; +} + + + +static void +computecolorhash(pixel ** const pixels, + unsigned int const cols, + unsigned int const rows, + unsigned int const maxcolors, + int * const nColorsP, + FILE * const ifP, + pixval const maxval, + int const format, + colorhash_table * const chtP, + const char ** const errorP) { +/*---------------------------------------------------------------------------- + Compute a color histogram from an image. The input is one of two types: + + 1) a two-dimensional array of pixels 'pixels'; In this case, 'pixels' + is non-NULL and 'ifP' is NULL. + + 2) an open file, positioned to the image data. In this case, + 'pixels' is NULL and 'ifP' is non-NULL. ifP is the stream + descriptor for the input file, and 'maxval' and 'format' are + parameters of the image data in it. + + We return with the file still open and its position undefined. + + In either case, the image is 'cols' by 'rows'. + + Return the number of colors found as *colorsP. + + However, if 'maxcolors' is nonzero and the number of colors is + greater than 'maxcolors', return a null return value and *colorsP + undefined. +-----------------------------------------------------------------------------*/ + pixel * rowbuffer; /* malloc'ed */ + /* Buffer for a row read from the input file; undefined (but still + allocated) if input is not from a file. + */ + + MALLOCARRAY(rowbuffer, cols); + + if (rowbuffer == NULL) + asprintfN(errorP, "Unable to allocate %u-column row buffer.", cols); + else { + colorhash_table cht; + bool tooManyColors; + + cht = alloccolorhash(); + + if (cht == NULL) + asprintfN(errorP, "Unable to allocate color hash."); + else { + buildHashTable(ifP, pixels, cols, rows, maxval, format, maxcolors, + cht, rowbuffer, + nColorsP, &tooManyColors, errorP); + + if (tooManyColors) { + ppm_freecolorhash(cht); + *chtP = NULL; + } else + *chtP = cht; + + if (*errorP) + ppm_freecolorhash(cht); + } + free(rowbuffer); + } } colorhash_table ppm_computecolorhash(pixel ** const pixels, - const int cols, const int rows, - const int maxcolors, int * const colorsP) { + int const cols, + int const rows, + int const maxcolors, + int * const colorsP) { + + colorhash_table cht; + const char * error; + + computecolorhash(pixels, cols, rows, maxcolors, colorsP, + NULL, 0, 0, &cht, &error); - return computecolorhash(pixels, cols, rows, maxcolors, colorsP, - NULL, 0, 0); + if (error) { + pm_errormsg("%s", error); + strfree(error); + pm_longjmp(); + } + return cht; } colorhash_table -ppm_computecolorhash2(FILE * const ifp, - const int cols, const int rows, - const pixval maxval, const int format, - const int maxcolors, int * const colorsP ) { +ppm_computecolorhash2(FILE * const ifP, + int const cols, + int const rows, + pixval const maxval, + int const format, + int const maxcolors, + int * const colorsP ) { + + colorhash_table cht; + const char * error; - return computecolorhash(NULL, cols, rows, maxcolors, colorsP, - ifp, maxval, format); + computecolorhash(NULL, cols, rows, maxcolors, colorsP, + ifP, maxval, format, &cht, &error); + + if (error) { + pm_errormsg("%s", error); + strfree(error); + pm_longjmp(); + } + return cht; } @@ -353,30 +478,50 @@ ppm_colorhashtocolorhist(colorhash_table const cht, int const maxcolors) { colorhash_table ppm_colorhisttocolorhash(colorhist_vector const chv, int const colors) { + + colorhash_table retval; colorhash_table cht; - int i, hash; - pixel color; - colorhist_list chl; + const char * error; - cht = ppm_alloccolorhash( ); /* Initializes to NULLs */ - - for (i = 0; i < colors; ++i) { - color = chv[i].color; - hash = ppm_hashpixel(color); - for (chl = cht[hash]; chl != (colorhist_list) 0; chl = chl->next) - if (PPM_EQUAL(chl->ch.color, color)) - pm_error( - "same color found twice - %d %d %d", PPM_GETR(color), - PPM_GETG(color), PPM_GETB(color) ); - MALLOCVAR(chl); - if (chl == NULL) - pm_error("out of memory"); - chl->ch.color = color; - chl->ch.value = i; - chl->next = cht[hash]; - cht[hash] = chl; + cht = alloccolorhash( ); /* Initializes to NULLs */ + if (cht == NULL) + asprintfN(&error, "Unable to allocate color hash"); + else { + unsigned int i; + + for (i = 0, error = NULL; i < colors && !error; ++i) { + pixel const color = chv[i].color; + int const hash = ppm_hashpixel(color); + + colorhist_list chl; + + for (chl = cht[hash]; chl && !error; chl = chl->next) + if (PPM_EQUAL(chl->ch.color, color)) + asprintfN(&error, "same color found twice: (%u %u %u)", + PPM_GETR(color), + PPM_GETG(color), + PPM_GETB(color)); + MALLOCVAR(chl); + if (chl == NULL) + asprintfN(&error, "out of memory"); + else { + chl->ch.color = color; + chl->ch.value = i; + chl->next = cht[hash]; + cht[hash] = chl; + } + } + if (error) + ppm_freecolorhash(cht); } - return cht; + if (error) { + pm_errormsg("%s", error); + strfree(error); + pm_longjmp(); + } else + retval = cht; + + return retval; } diff --git a/lib/libppmcolor.c b/lib/libppmcolor.c index 7cfacd29..a200ab2b 100644 --- a/lib/libppmcolor.c +++ b/lib/libppmcolor.c @@ -19,6 +19,7 @@ #include "pm_c_util.h" #include "mallocvar.h" +#include "nstring.h" #include "ppm.h" #include "colorname.h" @@ -247,22 +248,6 @@ parseNewDecX11(char const colorname[], -static bool -isHexString(char const string[], - int const hexit[]) { - - bool retval; - const char * p; - - for (p = &string[0], retval = true; *p && retval == true; ++p) { - if (hexit[(unsigned int)*p] == -1) - retval = false; - } - return retval; -} - - - static void parseOldX11(char const colorname[], pixval const maxval, @@ -278,7 +263,7 @@ parseOldX11(char const colorname[], computeHexTable(hexit); - if (!isHexString(&colorname[1], hexit)) + if (!strishex(&colorname[1])) pm_error("Non-hexadecimal characters in #-type color specification"); switch (strlen(colorname) - 1 /* (Number of hex digits) */) { @@ -471,11 +456,12 @@ processColorfileEntry(struct colorfile_entry const ce, colorhash_table const cht, const char ** const colornames, pixel * const colors, - unsigned int * const colornameIndexP) { + unsigned int * const colornameIndexP, + const char ** const errorP) { if (*colornameIndexP >= MAXCOLORNAMES) - pm_error("Too many colors in colorname dictionary. " - "Max allowed is %u", MAXCOLORNAMES); + asprintfN(errorP, "Too many colors in colorname dictionary. " + "Max allowed is %u", MAXCOLORNAMES); else { pixel color; @@ -487,13 +473,17 @@ processColorfileEntry(struct colorfile_entry const ce, file gives for each color, so we just ignore the current entry. */ + *errorP = NULL; } else { ppm_addtocolorhash(cht, &color, *colornameIndexP); colornames[*colornameIndexP] = strdup(ce.colorname); colors[*colornameIndexP] = color; if (colornames[*colornameIndexP] == NULL) - pm_error("Unable to allocate space for color name"); - ++(*colornameIndexP); + asprintfN(errorP, "Unable to allocate space for color name"); + else { + *errorP = NULL; + ++(*colornameIndexP); + } } } } @@ -501,39 +491,173 @@ processColorfileEntry(struct colorfile_entry const ce, static void -readcolordict(const char * const fileName, +openColornameFile(const char * const fileName, + bool const mustOpen, + FILE ** const filePP, + const char ** const errorP) { + + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; + + if (setjmp(jmpbuf) != 0) { + asprintfN(errorP, "Failed to open color name file"); + pm_setjmpbuf(origJmpbufP); + pm_longjmp(); + } else { + *filePP = pm_openColornameFile(fileName, mustOpen); + + *errorP = NULL; /* Would have longjmped if there were a problem */ + + pm_setjmpbuf(origJmpbufP); + } +} + + + +static void +readOpenColorFile(FILE * const colorFileP, + unsigned int * const nColorsP, + const char ** const colornames, + pixel * const colors, + colorhash_table const cht, + const char ** const errorP) { +/*---------------------------------------------------------------------------- + Read the color dictionary file *colorFileP and add the colors in it + to colornames[], colors[], and 'cht'. + + We may add colors to 'cht' even if we fail. +-----------------------------------------------------------------------------*/ + unsigned int nColorsDone; + bool done; + + nColorsDone = 0; + done = FALSE; + *errorP = NULL; + + while (!done && !*errorP) { + struct colorfile_entry const ce = pm_colorget(colorFileP); + + if (!ce.colorname) + done = TRUE; + else + processColorfileEntry(ce, cht, colornames, colors, + &nColorsDone, errorP); + } + if (!*errorP) { + *nColorsP = nColorsDone; + + while (nColorsDone < MAXCOLORNAMES) + colornames[nColorsDone++] = NULL; + } + + if (*errorP) { + unsigned int colorIndex; + + for (colorIndex = 0; colorIndex < nColorsDone; ++colorIndex) + strfree(colornames[colorIndex]); + } +} + + + +static colorhash_table +allocColorHash(void) { + + colorhash_table cht; + jmp_buf jmpbuf; + jmp_buf * origJmpbufP; + + if (setjmp(jmpbuf) != 0) + cht = NULL; + else { + pm_setjmpbufsave(&jmpbuf, &origJmpbufP); + cht = ppm_alloccolorhash(); + } + pm_setjmpbuf(origJmpbufP); + + return cht; +} + + + +static void +readColorFile(const char * const fileName, bool const mustOpen, unsigned int * const nColorsP, const char ** const colornames, - pixel * const colors, - colorhash_table const cht) { + pixel * const colors, + colorhash_table const cht, + const char ** const errorP) { - FILE * colorFile; + FILE * colorFileP; - colorFile = pm_openColornameFile(fileName, mustOpen); + openColornameFile(fileName, mustOpen, &colorFileP, errorP); + if (!*errorP) { + if (colorFileP == NULL) { + /* Couldn't open it, but Caller says treat same as + empty file + */ + *nColorsP = 0; + *errorP = NULL; + } else { + readOpenColorFile(colorFileP, nColorsP, colornames, colors, cht, + errorP); + + fclose(colorFileP); + } + } +} - if (colorFile != NULL) { - unsigned int colornameIndex; - bool done; + - colornameIndex = 0; /* initial value */ - done = FALSE; - while (!done) { - struct colorfile_entry const ce = pm_colorget(colorFile); +static void +readcolordict(const char * const fileName, + bool const mustOpen, + unsigned int * const nColorsP, + const char *** const colornamesP, + pixel ** const colorsP, + colorhash_table * const chtP, + const char ** const errorP) { - if (!ce.colorname) - done = TRUE; - else - processColorfileEntry(ce, cht, colornames, colors, - &colornameIndex); - } + const char ** colornames; - *nColorsP = colornameIndex; + MALLOCARRAY(colornames, MAXCOLORNAMES); - while (colornameIndex < MAXCOLORNAMES) - colornames[colornameIndex++] = NULL; + if (colornames == NULL) + asprintfN(errorP, "Unable to allocate space for colorname table."); + else { + pixel * colors; - fclose(colorFile); + MALLOCARRAY(colors, MAXCOLORNAMES); + + if (colors == NULL) + asprintfN(errorP, "Unable to allocate space for color table."); + else { + colorhash_table cht; + + cht = allocColorHash(); + + if (cht == NULL) + asprintfN(errorP, "Unable to allocate space for color hash"); + else { + readColorFile(fileName, mustOpen, + nColorsP, colornames, colors, cht, + errorP); + + if (*errorP) + ppm_freecolorhash(cht); + else + *chtP = cht; + } + if (*errorP) + free(colors); + else + *colorsP = colors; + } + if (*errorP) + free(colornames); + else + *colornamesP = colornames; } } @@ -551,32 +675,31 @@ ppm_readcolordict(const char * const fileName, const char ** colornames; pixel * colors; unsigned int nColors; + const char * error; - cht = ppm_alloccolorhash(); - - MALLOCARRAY(colornames, MAXCOLORNAMES); - - colors = ppm_allocrow(MAXCOLORNAMES); + readcolordict(fileName, mustOpen, &nColors, &colornames, &colors, &cht, + &error); - if (colornames == NULL) - pm_error("Unable to allocate space for colorname table."); - - readcolordict(fileName, mustOpen, &nColors, colornames, colors, cht); - - if (chtP) - *chtP = cht; - else + if (error) { + pm_errormsg("%s", error); + strfree(error); ppm_freecolorhash(cht); - if (colornamesP) - *colornamesP = colornames; - else - ppm_freecolornames(colornames); - if (colorsP) - *colorsP = colors; - else - ppm_freerow(colors); - if (nColorsP) - *nColorsP = nColors; + } else { + if (chtP) + *chtP = cht; + else + ppm_freecolorhash(cht); + if (colornamesP) + *colornamesP = colornames; + else + ppm_freecolornames(colornames); + if (colorsP) + *colorsP = colors; + else + ppm_freerow(colors); + if (nColorsP) + *nColorsP = nColors; + } } diff --git a/lib/libppmfuzzy.c b/lib/libppmfuzzy.c index 6127d5d5..e149b42a 100644 --- a/lib/libppmfuzzy.c +++ b/lib/libppmfuzzy.c @@ -84,7 +84,7 @@ memberTrapez(fzLog const x1, static fzLog hueIsAround000(double const hue) { - return memberZ(10, 30, hue); + return memberZ(10, 20, hue); } @@ -92,7 +92,7 @@ hueIsAround000(double const hue) { static fzLog hueIsAround015(double const hue) { - return memberZ(30, 40, hue); + return memberZ(20, 40, hue); } @@ -100,7 +100,7 @@ hueIsAround015(double const hue) { static fzLog hueIsAround030(double const hue) { - return memberTrapez(10, 30, 40, 60, hue); + return memberTrapez(10, 20, 40, 60, hue); } @@ -108,7 +108,7 @@ hueIsAround030(double const hue) { static fzLog hueIsAround060(double const hue) { - return memberTrapez(40, 60, 60, 80, hue); + return memberTrapez(40, 50, 60, 70, hue); } @@ -116,7 +116,7 @@ hueIsAround060(double const hue) { static fzLog hueIsAround120(double const hue) { - return memberTrapez(60, 80, 150, 180, hue); + return memberTrapez(60, 70, 150, 180, hue); } @@ -160,14 +160,14 @@ hueIsAround360(double const hue) { static fzLog satIsVeryLow(double const sat) { - return memberZ(0.02, 0.1, sat); + return memberZ(0.03, 0.08, sat); } static fzLog satIsLow(double const sat) { - return memberTrapez(0.02, 0.1, 0.2, 0.3, sat); + return memberTrapez(0.03, 0.08, 0.17, 0.2, sat); } @@ -175,7 +175,7 @@ satIsLow(double const sat) { static fzLog satIsMedium(double const sat) { - return memberTrapez(0.2, 0.3, 0.6, 0.7, sat); + return memberTrapez(0.17, 0.2, 0.6, 0.8, sat); } @@ -183,7 +183,7 @@ satIsMedium(double const sat) { static fzLog satIsHigh(double const sat) { - return memberS(0.6, 0.7, sat); + return memberS(0.6, 0.8, sat); } @@ -195,7 +195,7 @@ satIsHigh(double const sat) { static fzLog valIsVeryLow(double const val) { - return memberZ(0.1, 0.2, val); + return memberZ(0.05, 0.2, val); } @@ -203,7 +203,7 @@ valIsVeryLow(double const val) { static fzLog valIsLow(double const val) { - return memberTrapez(0.1, 0.2, 0.3, 0.6, val); + return memberTrapez(0.05, 0.2, 0.25, 0.3, val); } @@ -211,7 +211,7 @@ valIsLow(double const val) { static fzLog valIsMedium(double const val) { - return memberTrapez(0.3, 0.6, 0.7, 0.8, val); + return memberTrapez(0.25, 0.3, 0.6, 0.7, val); } @@ -219,7 +219,15 @@ valIsMedium(double const val) { static fzLog valIsHigh(double const val) { - return memberS(0.7, 0.8, val); + return memberTrapez(0.6, 0.7, 0.95, 0.97, val); +} + + + +static fzLog +valIsVeryHigh(double const val) { + + return memberS(0.95, 0.97, val); } @@ -269,10 +277,11 @@ matchBk(pixel const color, fzLog const satMedium = satIsMedium(hsv.s); fzLog const satHigh = satIsHigh(hsv.s); - fzLog const valVeryLow = valIsVeryLow(hsv.v); - fzLog const valLow = valIsLow(hsv.v); - fzLog const valMedium = valIsMedium(hsv.v); - fzLog const valHigh = valIsHigh(hsv.v); + fzLog const valVeryLow = valIsVeryLow(hsv.v); + fzLog const valLow = valIsLow(hsv.v); + fzLog const valMedium = valIsMedium(hsv.v); + fzLog const valHigh = valIsHigh(hsv.v); + fzLog const valVeryHigh = valIsVeryHigh(hsv.v); fzLog const hueAround000 = hueIsAround000(hsv.h); fzLog const hueAround015 = hueIsAround015(hsv.h); @@ -285,13 +294,13 @@ matchBk(pixel const color, fzLog const hueAround360 = hueIsAround360(hsv.h); (*bkMatchP)[BKCOLOR_BLACK] = - fzAnd(fzOr(satVeryLow, satLow), valVeryLow); + fzAnd(fzOr(satVeryLow, satLow), fzOr(valVeryLow, valLow)); (*bkMatchP)[BKCOLOR_GRAY] = - fzAnd(fzOr(satVeryLow, satLow), fzOr(valLow, valMedium)); + fzAnd(satVeryLow, fzAnd(fzNot(valVeryLow), fzNot(valVeryHigh))); (*bkMatchP)[BKCOLOR_WHITE] = - fzAnd(fzOr(satVeryLow, satLow), valHigh); + fzAnd(satVeryLow, valVeryHigh); (*bkMatchP)[BKCOLOR_RED] = fzAnd(fzAnd(fzOr(hueAround000, hueAround360), fzNot(satVeryLow)), @@ -300,38 +309,40 @@ matchBk(pixel const color, (*bkMatchP)[BKCOLOR_ORANGE] = fzAnd(fzAnd(hueAround030, fzOr(satMedium, satHigh)), - fzOr(valMedium, valHigh) + fzOr(fzOr(valMedium, valHigh), valVeryHigh) ); (*bkMatchP)[BKCOLOR_YELLOW] = fzAnd(fzAnd(hueAround060, fzOr(satMedium, satHigh)), - fzOr(valMedium, valHigh) + fzOr(valHigh, valVeryHigh) ); (*bkMatchP)[BKCOLOR_GREEN] = - fzAnd(fzAnd(hueAround120, fzNot(satVeryLow)), - fzOr(valMedium, valHigh) + fzAnd(fzAnd(hueAround120, fzOr(satMedium, satHigh)), + fzAnd(fzNot(valVeryLow), fzNot(valLow)) ); (*bkMatchP)[BKCOLOR_BLUE] = - fzAnd(fzAnd(hueAround180, fzAnd(fzNot(satVeryLow), fzNot(satLow))), - fzOr(valMedium, valHigh) + fzAnd(fzAnd(hueAround180, fzNot(satVeryLow)), + fzNot(valVeryLow) ); (*bkMatchP)[BKCOLOR_VIOLET] = - fzAnd(fzAnd(hueAround270, fzNot(satVeryLow)), + fzAnd(fzAnd(hueAround270, fzOr(satMedium, satHigh)), fzOr(valMedium, valHigh) ); (*bkMatchP)[BKCOLOR_PURPLE] = - fzAnd(fzAnd(hueAround320, fzNot(satVeryLow)), + fzAnd(fzAnd(hueAround320, fzOr(satMedium, satHigh)), fzOr(valMedium, valHigh) ); (*bkMatchP)[BKCOLOR_BROWN] = - fzAnd(fzOr(hueAround015, hueAround360), - fzAnd(fzNot(satVeryLow), fzNot(valHigh)) - ); + fzOr( + fzAnd(fzOr(hueAround015, hueAround360), + fzAnd(fzNot(satVeryLow), fzOr(valLow, valMedium))), + fzAnd(hueAround015, satLow) + ); } @@ -359,17 +370,17 @@ ppm_bk_color_from_color(pixel const color, static pixel const bkColorMap[BKCOLOR_COUNT] = { - { 0, 0, 0}, /* BKCOLOR_BLACK */ {174, 174, 174}, /* BKCOLOR_GRAY */ - {255, 255, 255}, /* BKCOLOR_WHITE */ - {255, 0, 0}, /* BKCOLOR_RED */ + {128, 42, 42}, /* BKCOLOR_BROWN */ {255, 128, 0}, /* BKCOLOR_ORANGE */ + {255, 0, 0}, /* BKCOLOR_RED */ {255, 255, 0}, /* BKCOLOR_YELLOW */ { 0, 255, 0}, /* BKCOLOR_GREEN */ { 0, 0, 255}, /* BKCOLOR_BLUE */ {143, 94, 153}, /* BKCOLOR_VIOLET */ {160, 32, 240}, /* BKCOLOR_PURPLE */ - {128, 42, 42} /* BKCOLOR_BROWN */ + {255, 255, 255}, /* BKCOLOR_WHITE */ + { 0, 0, 0} /* BKCOLOR_BLACK */ }; @@ -393,17 +404,17 @@ ppm_color_from_bk_color(bk_color const bkColor, static const char * const bkColorNameMap[BKCOLOR_COUNT] = { - "black", "gray", - "white", - "red", + "brown", "orange", + "red", "yellow", "green", "blue", "violet", "purple", - "brown" + "white", + "black" }; diff --git a/lib/pm.h b/lib/pm.h index 040a6a4b..696d763c 100644 --- a/lib/pm.h +++ b/lib/pm.h @@ -154,15 +154,25 @@ pm_setjmpbufsave(jmp_buf * const jmpbufP, void pm_longjmp(void); + +typedef void pm_usermessagefn(const char * msg); + +void +pm_setusermessagefn(pm_usermessagefn * fn); + +typedef void pm_usererrormsgfn(const char * msg); + +void +pm_setusererrormsgfn(pm_usererrormsgfn * fn); + void PM_GNU_PRINTF_ATTR(1,2) pm_message (const char format[], ...); void PM_GNU_PRINTF_ATTR(1,2) -pm_error (const char reason[], ...); +pm_errormsg(const char format[], ...); -/* Obsolete - use helpful error message instead */ -void -pm_perror (const char reason[]); +void PM_GNU_PRINTF_ATTR(1,2) +pm_error (const char reason[], ...); /* Obsolete - use shhopt and user's manual instead */ void diff --git a/lib/ppm.h b/lib/ppm.h index 033330b9..622d3e09 100644 --- a/lib/ppm.h +++ b/lib/ppm.h @@ -259,22 +259,25 @@ ppm_saturation(pixel const p, typedef enum { /* A color from the set of universally understood colors developed - by Brent Berlin and Paul Kay + by Brent Berlin and Paul Kay. + + Algorithms in libnetpbm depend on the numerical representations + of these values being as follows. */ - BKCOLOR_BLACK = 0, - BKCOLOR_GRAY, - BKCOLOR_WHITE, - BKCOLOR_RED, + BKCOLOR_GRAY = 0, + BKCOLOR_BROWN, BKCOLOR_ORANGE, + BKCOLOR_RED, BKCOLOR_YELLOW, BKCOLOR_GREEN, BKCOLOR_BLUE, BKCOLOR_VIOLET, BKCOLOR_PURPLE, - BKCOLOR_BROWN + BKCOLOR_WHITE, + BKCOLOR_BLACK } bk_color; -#define BKCOLOR_COUNT (BKCOLOR_BROWN+1) +#define BKCOLOR_COUNT (BKCOLOR_BLACK+1) bk_color ppm_bk_color_from_color(pixel const color, diff --git a/lib/util/Makefile b/lib/util/Makefile index 8f461f28..ffa1db5b 100644 --- a/lib/util/Makefile +++ b/lib/util/Makefile @@ -7,11 +7,9 @@ VPATH=.:$(SRCDIR)/$(SUBDIR) include $(BUILDDIR)/Makefile.config -INCLUDES = -I $(BUILDDIR) -I $(SRCDIR)/$(SUBDIR)/.. - # nstring is required for asprintf(), etc. Also some systems don't have # snprintf(), e.g. Solaris 2.5.1. 2002.03.29. -UTILOBJECTS = shhopt.o nstring.o filename.o +UTILOBJECTS = shhopt.o nstring.o vasprintf.o filename.o MERGE_OBJECTS = @@ -19,6 +17,8 @@ all: $(UTILOBJECTS) include $(SRCDIR)/Makefile.common +INCLUDES = -I $(BUILDDIR) -I $(SRCDIR)/$(SUBDIR)/.. + $(UTILOBJECTS):%.o:%.c importinc $(CC) -c $(INCLUDES) -DNDEBUG $(CFLAGS) $(CFLAGS_SHLIB) \ $(CFLAGS_PERSONAL) $(CADD) -o $@ $< diff --git a/lib/util/nstring.c b/lib/util/nstring.c index 702a3c44..58500547 100644 --- a/lib/util/nstring.c +++ b/lib/util/nstring.c @@ -740,15 +740,6 @@ const char * const strsol = "NO MEMORY TO CREATE STRING!"; -/* We would like to have vasprintfN(), but it is difficult because you - can't run through a va_list twice, which we would want to do: once - to measure the length; once actually to build the string. On some - machines, you can simply make two copies of the va_list variable in - normal C fashion, but on others you need va_copy, which is a - relatively recent invention. In particular, the simple va_list copy - failed on an AMD64 Gcc Linux system in March 2006. -*/ - void PM_GNU_PRINTF_ATTR(2,3) asprintfN(const char ** const resultP, const char * const fmt, @@ -904,3 +895,20 @@ memmemN(const char * const haystack, return NULL; } + + + +bool +strishex(const char * const subject) { + + bool retval; + unsigned int i; + + retval = TRUE; /* initial assumption */ + + for (i = 0; i < strlen(subject); ++i) + if (!ISXDIGIT(subject[i])) + retval = FALSE; + + return retval; +} diff --git a/lib/util/nstring.h b/lib/util/nstring.h index 9ed20051..70a53f45 100644 --- a/lib/util/nstring.h +++ b/lib/util/nstring.h @@ -5,6 +5,7 @@ #include #include +#include "pm_c_util.h" #include "pm.h" /* For PM_GNU_PRINTF_ATTR, __inline__ */ #ifdef __cplusplus @@ -134,6 +135,11 @@ asprintfN(const char ** const resultP, const char * const fmt, ...) PM_GNU_PRINTF_ATTR(2,3); +void +vasprintfN(const char ** const resultP, + const char * const format, + va_list args); + void strfree(const char * const string); @@ -150,6 +156,9 @@ memmemN(const char * const haystack, const char * const needle, size_t const needlelen); +bool +strishex(const char * const subject); + #ifdef __cplusplus } #endif -- cgit 1.4.1