diff options
-rw-r--r-- | GNUmakefile | 3 | ||||
-rw-r--r-- | doc/HISTORY | 4 | ||||
-rw-r--r-- | lib/Makefile | 2 | ||||
-rw-r--r-- | lib/compile.h | 2 | ||||
-rw-r--r-- | lib/libpammap.c | 20 | ||||
-rw-r--r-- | lib/libpamn.c | 284 | ||||
-rw-r--r-- | lib/libpamread.c | 104 | ||||
-rw-r--r-- | lib/libpamwrite.c | 29 | ||||
-rw-r--r-- | lib/libpbm3.c | 113 | ||||
-rw-r--r-- | lib/libpgm1.c | 186 | ||||
-rw-r--r-- | lib/libpm.c | 288 | ||||
-rw-r--r-- | lib/libpnm1.c | 125 | ||||
-rw-r--r-- | lib/libpnm2.c | 102 | ||||
-rw-r--r-- | lib/libppm1.c | 290 | ||||
-rw-r--r-- | lib/libppmcmap.c | 313 | ||||
-rw-r--r-- | lib/libppmcolor.c | 239 | ||||
-rw-r--r-- | lib/pm.h | 14 | ||||
-rw-r--r-- | lib/util/Makefile | 2 | ||||
-rw-r--r-- | lib/util/nstring.c | 9 | ||||
-rw-r--r-- | lib/util/nstring.h | 5 |
20 files changed, 1566 insertions, 568 deletions
diff --git a/GNUmakefile b/GNUmakefile index 56c6126c..420d558c 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -117,7 +117,8 @@ $(BUILDDIR)/inttypes_netpbm.h: $(TYPEGEN) # cross compiling. $(BUILDDIR)/pm_config.h: \ - $(SRCDIR)/pm_config.in.h Makefile.config inttypes_netpbm.h $(ENDIANGEN) + $(SRCDIR)/pm_config.in.h Makefile.config inttypes_netpbm.h \ + $(ENDIANGEN) echo '/* pm_config.h GENERATED BY A MAKE RULE */' >$@ || $(DELETEIT) echo '#ifndef PM_CONFIG_H' >>$@ || $(DELETEIT) echo '#define PM_CONFIG_H' >>$@ || $(DELETEIT) diff --git a/doc/HISTORY b/doc/HISTORY index 49eb35be..07ada5f3 100644 --- a/doc/HISTORY +++ b/doc/HISTORY @@ -97,6 +97,10 @@ CHANGE HISTORY not yet BJH Release 10.36.0 + Release allocated memory before longjmping from libnetpbm. + + Add pm_errormsg(), pm_setusererrormsg(), pm_setusermessage(). + pm_accept_to_pamtuples: fix bug: fill in pam structure. Thanks "Christian Schlotter" <schlotter@users.sourceforge.net>. diff --git a/lib/Makefile b/lib/Makefile index bd8eccae..87ad860a 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 diff --git a/lib/compile.h b/lib/compile.h index 4626f1ea..2fbeb753 100644 --- a/lib/compile.h +++ b/lib/compile.h @@ -1,5 +1,5 @@ /* This file tells the package when it was compiled */ /* DO NOT EDIT - THIS FILE IS MAINTAINED AUTOMATICALLY */ /* Created by the program 'stamp-date' */ -#define COMPILE_TIME "Thu Aug 17 03:34:22 GMT 2006" +#define COMPILE_TIME "Tue Aug 29 15:40:16 GMT 2006" #define COMPILED_BY "bryanh" 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 @@ -167,41 +167,63 @@ 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 978657a9..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,6 +119,22 @@ pm_longjmp(void) { void +pm_setusererrormsgfn(pm_usererrormsgfn * fn) { + + userErrorMsgFn = fn; +} + + + +void +pm_setusermessagefn(pm_usermessagefn * fn) { + + userMessageFn = fn; +} + + + +void pm_usage(const char usage[]) { pm_error("usage: %s %s", pm_progname, usage); } @@ -122,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(); @@ -174,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. @@ -194,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); + } + } + if (error) { + pm_errormsg("Couldn't allocate %u-row array. %s", rows, error); + strfree(error); + pm_longjmp(); } - return rowIndex; + return (char **)rowIndex; } @@ -795,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" */ @@ -820,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; } @@ -1200,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 <string.h> #include <stdio.h> #include <errno.h> + #include "ppm.h" #include "libppm.h" #include "pgm.h" @@ -29,6 +30,7 @@ #include "libpam.h" #include "fileio.h" #include "mallocvar.h" +#include "nstring.h" pixel * @@ -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..d5323be3 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" @@ -471,11 +472,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 +489,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 +507,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 +691,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); - - if (colornames == NULL) - pm_error("Unable to allocate space for colorname table."); - - readcolordict(fileName, mustOpen, &nColors, colornames, colors, cht); + readcolordict(fileName, mustOpen, &nColors, &colornames, &colors, &cht, + &error); - 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/pm.h b/lib/pm.h index 199d47f2..696d763c 100644 --- a/lib/pm.h +++ b/lib/pm.h @@ -154,10 +154,24 @@ 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_errormsg(const char format[], ...); + +void PM_GNU_PRINTF_ATTR(1,2) pm_error (const char reason[], ...); /* Obsolete - use shhopt and user's manual instead */ diff --git a/lib/util/Makefile b/lib/util/Makefile index 8f461f28..4ba60ba2 100644 --- a/lib/util/Makefile +++ b/lib/util/Makefile @@ -11,7 +11,7 @@ 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 = diff --git a/lib/util/nstring.c b/lib/util/nstring.c index 702a3c44..749e9060 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, diff --git a/lib/util/nstring.h b/lib/util/nstring.h index 9ed20051..d976e859 100644 --- a/lib/util/nstring.h +++ b/lib/util/nstring.h @@ -134,6 +134,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); |