about summary refs log tree commit diff
path: root/lib
diff options
context:
space:
mode:
authorgiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2023-12-28 19:53:34 +0000
committergiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2023-12-28 19:53:34 +0000
commit5d16663331afd0bc2edaeb2e49042dc219ce9c2f (patch)
tree476fbb2ab4311d4bb6d65b250825e254a7a2c1ef /lib
parent42f0bf8e7f1ff88000a3584c265e6f1631662ec4 (diff)
downloadnetpbm-mirror-5d16663331afd0bc2edaeb2e49042dc219ce9c2f.tar.gz
netpbm-mirror-5d16663331afd0bc2edaeb2e49042dc219ce9c2f.tar.xz
netpbm-mirror-5d16663331afd0bc2edaeb2e49042dc219ce9c2f.zip
promote Development to Advanced
git-svn-id: http://svn.code.sf.net/p/netpbm/code/advanced@4827 9d0c8265-081b-0410-96cb-a4ca84ce46f8
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile5
-rw-r--r--lib/libpam.c20
-rw-r--r--lib/libpammap.c7
-rw-r--r--lib/libpbm.h6
-rw-r--r--lib/libpbm1.c23
-rw-r--r--lib/libpbm2.c57
-rw-r--r--lib/libpbm3.c7
-rw-r--r--lib/libpgm.h7
-rw-r--r--lib/libpgm1.c33
-rw-r--r--lib/libpgm2.c8
-rw-r--r--lib/libpm.c29
-rw-r--r--lib/libpnm3.c13
-rw-r--r--lib/libppm.h4
-rw-r--r--lib/libppm1.c55
-rw-r--r--lib/libppm2.c11
-rw-r--r--lib/libsystem.c184
-rw-r--r--lib/libsystem_dummy.c30
-rw-r--r--lib/pam.h2
-rw-r--r--lib/pm.h25
-rw-r--r--lib/pm_system.h12
-rw-r--r--lib/pmfileio.c6
-rw-r--r--lib/pnm.h3
-rw-r--r--lib/util/rand.c74
-rw-r--r--lib/util/rand.h6
24 files changed, 459 insertions, 168 deletions
diff --git a/lib/Makefile b/lib/Makefile
index ffe2a143..a633cf7b 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -96,7 +96,10 @@ $(LIBOBJECTS): CFLAGS_TARGET=$(CFLAGS_SHLIB)
 
 libpbm3.o: CFLAGS_TARGET+=$(CFLAGS_SSE)
 
-$(LIBOBJECTS): %.o: %.c importinc
+# libsystem_dummy.o is in the following rule because we like to compile it
+# even when it's not part of the library, just for a test.
+
+$(LIBOBJECTS) libsystem_dummy.o: %.o: %.c importinc
 	$(CC) -c $(INCLUDES) $(CFLAGS_ALL) -o $@ $<
 
 # The major number increases when there is a non-backward-compatible change
diff --git a/lib/libpam.c b/lib/libpam.c
index 07241e9e..9875331d 100644
--- a/lib/libpam.c
+++ b/lib/libpam.c
@@ -132,19 +132,7 @@ validateComputableMaxval(const struct pam * const pamP) {
 /*----------------------------------------------------------------------------
   This is similar to validateComputableSize, but for the maxval.
 -----------------------------------------------------------------------------*/
-    /* Code sometimes allocates an array indexed by sample values and
-       represents the size of that array as an INT.  (UNSIGNED INT would be
-       more proper, but there's no need to be that permissive).
-
-       Code also sometimes iterates through sample values and quits when the
-       value is greater than the maxval.
-    */
-
-    if (pamP->maxval == 0)
-        pm_error("Maxval is zero.  Must be at least one.");
-
-    if (pamP->maxval > INT_MAX-1)
-        pm_error("Maxval (%lu) is too large to be processed", pamP->maxval);
+    pgm_validateComputableMaxval(pamP->maxval);
 }
 
 
@@ -154,7 +142,7 @@ pnm_allocpamtuple(const struct pam * const pamP) {
 
     tuple retval;
 
-    retval = malloc(allocationDepth(pamP) * sizeof(retval[0]));
+    MALLOCARRAY(retval, allocationDepth(pamP));
 
     if (retval == NULL)
         pm_error("Out of memory allocating %u-plane tuple",
@@ -334,7 +322,7 @@ pnm_allocrowimage(const struct pam * const pamP) {
 
     unsigned char * retval;
 
-    retval = malloc(size);
+    MALLOCARRAY(retval, size);
 
     if (retval == NULL)
         pm_error("Unable to allocate %u bytes for a row image buffer",
@@ -1121,6 +1109,8 @@ pnm_writepaminit(struct pam * const pamP) {
 
     switch (PAM_FORMAT_TYPE(pamP->format)) {
     case PAM_TYPE:
+        validateComputableSize(pamP);
+        validateComputableMaxval(pamP);
         /* See explanation below of why we ignore 'pm_plain_output' here. */
         fprintf(pamP->file, "P7\n");
         writeComments(pamP);
diff --git a/lib/libpammap.c b/lib/libpammap.c
index 569156fe..a0e7fb55 100644
--- a/lib/libpammap.c
+++ b/lib/libpammap.c
@@ -102,12 +102,15 @@ static struct tupleint_list_item *
 allocTupleIntListItem(struct pam * const pamP) {
 
 
-    /* This is complicated by the fact that the last element of a
-       tupleint_list_item is of variable length, because the last element
+    /* This is complicated by the fact that the last member of a
+       tupleint_list_item is of variable length, because the last member
        of _it_ is of variable length
     */
     struct tupleint_list_item * retval;
 
+    if (pamP->depth > (UINT_MAX - sizeof(*retval)) / sizeof(sample))
+        pm_error("Depth %u is too large for computation", pamP->depth);
+
     unsigned int const size =
         sizeof(*retval) - sizeof(retval->tupleint.tuple)
         + pamP->depth * sizeof(sample);
diff --git a/lib/libpbm.h b/lib/libpbm.h
index 827c9d7e..f3e6962a 100644
--- a/lib/libpbm.h
+++ b/lib/libpbm.h
@@ -4,9 +4,15 @@
 #ifndef LIBPBM_H_INCLUDED
 #define LIBPBM_H_INCLUDED
 
+#include <stdio.h>
+
 void
 pbm_readpbminitrest(FILE * file,
                     int  * colsP,
                     int *  rowsP);
 
+void
+pbm_validateComputableSize(unsigned int const cols,
+                           unsigned int const rows);
+
 #endif
diff --git a/lib/libpbm1.c b/lib/libpbm1.c
index d3403311..9e51970c 100644
--- a/lib/libpbm1.c
+++ b/lib/libpbm1.c
@@ -22,6 +22,7 @@
 #include "netpbm/mallocvar.h"
 #include "netpbm/shhopt.h"
 
+#include "libpbm.h"
 #include "pbm.h"
 
 
@@ -85,6 +86,28 @@ pbm_check(FILE *               const fileP,
 
 
 
+void
+pbm_validateComputableSize(unsigned int const cols,
+                           unsigned int const rows) {
+/*----------------------------------------------------------------------------
+   Validate that the dimensions of the image are such that it can be
+   processed in typical ways on this machine without worrying about
+   overflows.  Note that in C, arithmetic is always modulus
+   arithmetic, so if your values are too big, the result is not what
+   you expect.  That failed expectation can be disastrous if you use
+   it to allocate memory.
+
+   See comments at 'validateComputableSize' in libpam.c for details on
+   the purpose of these validations.
+-----------------------------------------------------------------------------*/
+    if (cols > INT_MAX - 10)
+        pm_error("image width (%u) too large to be processed", cols);
+    if (rows > INT_MAX - 10)
+        pm_error("image height (%u) too large to be processed", rows);
+}
+
+
+
 static unsigned int
 bitpop8(unsigned char const x) {
 /*----------------------------------------------------------------------------
diff --git a/lib/libpbm2.c b/lib/libpbm2.c
index 244d5835..8668356e 100644
--- a/lib/libpbm2.c
+++ b/lib/libpbm2.c
@@ -35,47 +35,36 @@ getbit (FILE * const file) {
 
 
 void
-pbm_readpbminitrest( FILE * const file,
-                     int  * const colsP,
-                     int  * const rowsP ) {
+pbm_readpbminitrest(FILE * const ifP,
+                    int  * const colsP,
+                    int  * const rowsP ) {
+
+    unsigned int cols;
+    unsigned int rows;
+
     /* Read size. */
-    *colsP = (int)pm_getuint( file );
-    *rowsP = (int)pm_getuint( file );
+    cols = pm_getuint(ifP);
+    rows = pm_getuint(ifP);
 
     /* *colsP and *rowsP really should be unsigned int, but they come
        from the time before unsigned ints (or at least from a person
-       trained in that tradition), so they are int.  We could simply
-       consider negative numbers to mean values > INT_MAX/2 and much
+       trained in that tradition), so they are int.  Caller could simply
+       consider negative numbers to mean values > INT_MAX and much
        code would just automatically work.  But some code would fail
        miserably.  So we consider values that won't fit in an int to
        be unprocessable.
     */
-    if (*colsP < 0)
-        pm_error("Number of columns in header is too large.");
-    if (*rowsP < 0)
-        pm_error("Number of rows in header is too large.");
-}
-
-
-
-static void
-validateComputableSize(unsigned int const cols,
-                       unsigned int const rows) {
-/*----------------------------------------------------------------------------
-   Validate that the dimensions of the image are such that it can be
-   processed in typical ways on this machine without worrying about
-   overflows.  Note that in C, arithmetic is always modulus
-   arithmetic, so if your values are too big, the result is not what
-   you expect.  That failed expectation can be disastrous if you use
-   it to allocate memory.
-
-   See comments at 'validateComputableSize' in libpam.c for details on
-   the purpose of these validations.
------------------------------------------------------------------------------*/
-    if (cols > INT_MAX - 10)
-        pm_error("image width (%u) too large to be processed", cols);
-    if (rows > INT_MAX - 10)
-        pm_error("image height (%u) too large to be processed", rows);
+    if (cols > INT_MAX)
+        pm_error("Number of columns in header is too large (%u).  "
+                 "The maximum allowed by the format is %u",
+                 cols, INT_MAX);
+    if (rows > INT_MAX)
+        pm_error("Number of rows in header is too large (%u).  "
+                 "The maximum allowed by the format is %u",
+                 rows, INT_MAX);
+
+    *colsP = (int)cols;
+    *rowsP = (int)rows;
 }
 
 
@@ -115,7 +104,7 @@ pbm_readpbminit(FILE * const ifP,
         pm_error("bad magic number 0x%x - not a PPM, PGM, PBM, or PAM file",
                  realFormat);
     }
-    validateComputableSize(*colsP, *rowsP);
+    pbm_validateComputableSize(*colsP, *rowsP);
 }
 
 
diff --git a/lib/libpbm3.c b/lib/libpbm3.c
index 456d3986..5d4e614b 100644
--- a/lib/libpbm3.c
+++ b/lib/libpbm3.c
@@ -14,6 +14,7 @@
 
 #include "netpbm/pm_c_util.h"
 
+#include "libpbm.h"
 #include "pbm.h"
 
 #ifndef PACKBITS_SSE
@@ -45,6 +46,12 @@ pbm_writepbminit(FILE * const fileP,
                  int    const rows,
                  int    const forceplain) {
 
+    /* For Caller's convenience, we include validating computability of the
+       image dimensions, since Caller may be using them in arithmetic after
+       our return.
+    */
+    pbm_validateComputableSize(cols, rows);
+
     if (!forceplain && !pm_plain_output) {
         fprintf(fileP, "%c%c\n%d %d\n", PBM_MAGIC1, RPBM_MAGIC2, cols, rows);
     } else
diff --git a/lib/libpgm.h b/lib/libpgm.h
index eb292c80..49939a26 100644
--- a/lib/libpgm.h
+++ b/lib/libpgm.h
@@ -12,4 +12,11 @@ pgm_readpgminitrest(FILE * const file,
                     int *  const rowsP,
                     gray * const maxvalP);
 
+void
+pgm_validateComputableSize(unsigned int const cols,
+                           unsigned int const rows);
+
+void
+pgm_validateComputableMaxval(gray const maxval);
+
 #endif
diff --git a/lib/libpgm1.c b/lib/libpgm1.c
index 6feb1c8b..0656e1e0 100644
--- a/lib/libpgm1.c
+++ b/lib/libpgm1.c
@@ -65,7 +65,7 @@ pgm_nextimage(FILE * const file,
 
 
 void
-pgm_readpgminitrest(FILE * const fileP,
+pgm_readpgminitrest(FILE * const ifP,
                     int *  const colsP,
                     int *  const rowsP,
                     gray * const maxvalP) {
@@ -73,14 +73,13 @@ pgm_readpgminitrest(FILE * const fileP,
     gray maxval;
 
     /* Read size. */
-    *colsP = (int)pm_getuint(fileP);
-    *rowsP = (int)pm_getuint(fileP);
+    pbm_readpbminitrest(ifP, colsP, rowsP);
 
     /* Read maxval. */
-    maxval = pm_getuint(fileP);
+    maxval = pm_getuint(ifP);
     if (maxval > PGM_OVERALLMAXVAL)
         pm_error("maxval of input image (%u) is too large.  "
-                 "The maximum allowed by PGM is %u.",
+                 "The maximum allowed by the format is %u.",
                  maxval, PGM_OVERALLMAXVAL);
     if (maxval == 0)
         pm_error("maxval of input image is zero.");
@@ -90,9 +89,9 @@ pgm_readpgminitrest(FILE * const fileP,
 
 
 
-static void
-validateComputableSize(unsigned int const cols,
-                       unsigned int const rows) {
+void
+pgm_validateComputableSize(unsigned int const cols,
+                           unsigned int const rows) {
 /*----------------------------------------------------------------------------
    Validate that the dimensions of the image are such that it can be
    processed in typical ways on this machine without worrying about
@@ -115,8 +114,8 @@ validateComputableSize(unsigned int const cols,
 
 
 
-static void
-validateComputableMaxval(gray const maxval) {
+void
+pgm_validateComputableMaxval(gray const maxval) {
 /*----------------------------------------------------------------------------
   This is similar to validateComputableSize, but for the maxval.
 -----------------------------------------------------------------------------*/
@@ -126,10 +125,14 @@ validateComputableMaxval(gray const maxval) {
 
        Code also sometimes iterates through sample values and quits when the
        value is greater than the maxval.
-    */
 
-    if (maxval == 0)
-        pm_error("Maxval is zero.  Must be at least one.");
+       Code often divides by the maxval, but we don't have to check for maxval
+       == 0 as a computability problem because that is not a valid maxval.
+
+       Note that in the PNM Plain formats, there is no upper limit for a
+       maxval, though the 'gray' type does constrain what has been passed to
+       us.
+    */
 
     if (maxval > INT_MAX-1)
         pm_error("Maxval (%u) is too large to be processed", maxval);
@@ -193,9 +196,9 @@ pgm_readpgminit(FILE * const fileP,
         pm_error("bad magic number 0x%x - not a PPM, PGM, PBM, or PAM file",
                  realFormat);
     }
-    validateComputableSize(*colsP, *rowsP);
+    pgm_validateComputableSize(*colsP, *rowsP);
 
-    validateComputableMaxval(*maxvalP);
+    pgm_validateComputableMaxval(*maxvalP);
 }
 
 
diff --git a/lib/libpgm2.c b/lib/libpgm2.c
index 2e3aba90..ec3539a2 100644
--- a/lib/libpgm2.c
+++ b/lib/libpgm2.c
@@ -15,6 +15,7 @@
 
 #include "netpbm/pm_c_util.h"
 #include "netpbm/mallocvar.h"
+#include "libpgm.h"
 #include "pgm.h"
 
 
@@ -28,6 +29,13 @@ pgm_writepgminit(FILE * const fileP,
 
     bool const plainFormat = forceplain || pm_plain_output;
 
+    /* For Caller's convenience, we include validating computability of the
+       image parameters, since Caller may be using them in arithmetic after
+       our return.
+    */
+    pgm_validateComputableSize(cols, rows);
+    pgm_validateComputableMaxval(maxval);
+
     if (maxval > PGM_OVERALLMAXVAL && !plainFormat)
         pm_error("too-large maxval passed to ppm_writepgminit(): %d.\n"
                  "Maximum allowed by the PGM format is %d.",
diff --git a/lib/libpm.c b/lib/libpm.c
index b27b47d5..d1fc6c42 100644
--- a/lib/libpm.c
+++ b/lib/libpm.c
@@ -855,6 +855,11 @@ pm_parse_width(const char * const arg) {
 
    See comments at 'validateComputableSize' in libpam.c for details on
    the purpose of these validations.
+
+   This isn't just for Netpbm format images.
+
+   Typical places from which widths come: headers of non-Netpbm images;
+   command line options.
 -----------------------------------------------------------------------------*/
     unsigned int width;
     const char * error;
@@ -901,3 +906,27 @@ pm_parse_height(const char * const arg) {
 
 
 
+unsigned int
+pm_parse_maxval(const char * const arg) {
+/*----------------------------------------------------------------------------
+  Same as pm_parse_width(), but for maxval.
+-----------------------------------------------------------------------------*/
+    unsigned int maxval;
+    const char * error;
+
+    pm_string_to_uint(arg, &maxval, &error);
+
+    if (error) {
+        pm_error("'%s' is invalid as a maxval.  %s", arg, error);
+        pm_strfree(error);
+    } else {
+        if (maxval > INT_MAX-1)
+            pm_error("Maxval %u is too large for computations.", maxval);
+        if (maxval == 0)
+            pm_error("Maxval argument must be a positive number.  You "
+                     "specified 0.");
+    }
+    return maxval;
+}
+
+
diff --git a/lib/libpnm3.c b/lib/libpnm3.c
index ba408bd1..4d88a2c1 100644
--- a/lib/libpnm3.c
+++ b/lib/libpnm3.c
@@ -240,6 +240,19 @@ pnm_invertxel(xel*   const xP,
 
 
 
+const char *
+pnm_formattypenm(int const format) {
+
+    switch (PPM_FORMAT_TYPE(format)) {
+    case PPM_TYPE: return "PPM"; break;
+    case PGM_TYPE: return "PGM"; break;
+    case PBM_TYPE: return "PBM"; break;
+    default: return "???";
+    }
+}
+
+
+
 void
 pnm_promoteformat(xel ** const xels,
                   int    const cols,
diff --git a/lib/libppm.h b/lib/libppm.h
index 0c561022..620768a2 100644
--- a/lib/libppm.h
+++ b/lib/libppm.h
@@ -12,4 +12,8 @@ ppm_readppminitrest(FILE *   const file,
                     int *    const rowsP,
                     pixval * const maxvalP);
 
+void
+ppm_validateComputableSize(unsigned int const cols,
+                           unsigned int const rows);
+
 #endif
diff --git a/lib/libppm1.c b/lib/libppm1.c
index 427adf4d..01539169 100644
--- a/lib/libppm1.c
+++ b/lib/libppm1.c
@@ -64,33 +64,19 @@ ppm_nextimage(FILE * const fileP,
 
 
 void
-ppm_readppminitrest(FILE *   const fileP,
+ppm_readppminitrest(FILE *   const ifP,
                     int *    const colsP,
                     int *    const rowsP,
                     pixval * const maxvalP) {
-    unsigned int maxval;
-
-    /* Read size. */
-    *colsP = (int)pm_getuint(fileP);
-    *rowsP = (int)pm_getuint(fileP);
-
-    /* Read maxval. */
-    maxval = pm_getuint(fileP);
-    if (maxval > PPM_OVERALLMAXVAL)
-        pm_error("maxval of input image (%u) is too large.  "
-                 "The maximum allowed by the PPM format is %u.",
-                 maxval, PPM_OVERALLMAXVAL);
-    if (maxval == 0)
-        pm_error("maxval of input image is zero.");
-
-    *maxvalP = maxval;
+
+    pgm_readpgminitrest(ifP, colsP, rowsP, maxvalP);
 }
 
 
 
-static void
-validateComputableSize(unsigned int const cols,
-                       unsigned int const rows) {
+void
+ppm_validateComputableSize(unsigned int const cols,
+                           unsigned int const rows) {
 /*----------------------------------------------------------------------------
    Validate that the dimensions of the image are such that it can be
    processed in typical ways on this machine without worrying about
@@ -113,28 +99,6 @@ validateComputableSize(unsigned int const cols,
 
 
 
-static void
-validateComputableMaxval(pixval const maxval) {
-/*----------------------------------------------------------------------------
-  This is similar to validateComputableSize, but for the maxval.
------------------------------------------------------------------------------*/
-    /* Code sometimes allocates an array indexed by sample values and
-       represents the size of that array as an INT.  (UNSIGNED INT would be
-       more proper, but there's no need to be that permissive).
-
-       Code also sometimes iterates through sample values and quits when the
-       value is greater than the maxval.
-    */
-
-    if (maxval == 0)
-        pm_error("Maxval is zero.  Must be at least one.");
-
-    if (maxval > INT_MAX-1)
-        pm_error("Maxval (%u) is too large to be processed", maxval);
-}
-
-
-
 void
 ppm_readppminit(FILE *   const fileP,
                 int *    const colsP,
@@ -172,9 +136,9 @@ ppm_readppminit(FILE *   const fileP,
         pm_error("bad magic number 0x%x - not a PPM, PGM, PBM, or PAM file",
                  realFormat);
     }
-    validateComputableSize(*colsP, *rowsP);
+    ppm_validateComputableSize(*colsP, *rowsP);
 
-    validateComputableMaxval(*maxvalP);
+    pgm_validateComputableMaxval(*maxvalP);
 }
 
 
@@ -526,3 +490,6 @@ ppm_check(FILE *               const fileP,
         pm_check(fileP, checkType, needRasterSize, retvalP);
     }
 }
+
+
+
diff --git a/lib/libppm2.c b/lib/libppm2.c
index b4690b71..f54f2cef 100644
--- a/lib/libppm2.c
+++ b/lib/libppm2.c
@@ -16,8 +16,12 @@
 
 #include "netpbm/pm_c_util.h"
 #include "netpbm/mallocvar.h"
+#include "libpgm.h"
+#include "libppm.h"
 #include "ppm.h"
 
+
+
 void
 ppm_writeppminit(FILE*  const fileP,
                  int    const cols,
@@ -27,6 +31,13 @@ ppm_writeppminit(FILE*  const fileP,
 
     bool const plainFormat = forceplain || pm_plain_output;
 
+    /* For Caller's convenience, we include validating computability of the
+       image parameters, since Caller may be using them in arithmetic after
+       our return.
+    */
+    ppm_validateComputableSize(cols, rows);
+    pgm_validateComputableMaxval(maxval);
+
     if (maxval > PPM_OVERALLMAXVAL && !plainFormat)
         pm_error("too-large maxval passed to ppm_writeppminit(): %d."
                  "Maximum allowed by the PPM format is %d.",
diff --git a/lib/libsystem.c b/lib/libsystem.c
index 30b6b2be..332cc5e2 100644
--- a/lib/libsystem.c
+++ b/lib/libsystem.c
@@ -523,10 +523,7 @@ pm_system2_lp(const char *    const progName,
 
     va_start(args, termStatusP);
 
-    endOfArgs = FALSE;
-    argArray = NULL;
-
-    for (endOfArgs = FALSE, argArray = NULL, n = 0;
+    for (endOfArgs = false, argArray = NULL, n = 0;
          !endOfArgs;
         ) {
         const char * const arg = va_arg(args, const char *);
@@ -536,7 +533,7 @@ pm_system2_lp(const char *    const progName,
         argArray[n++] = arg;
 
         if (!arg)
-            endOfArgs = TRUE;
+            endOfArgs = true;
     }
 
     va_end(args);
@@ -628,9 +625,6 @@ pm_system_lp(const char *    const progName,
 
     va_start(args, accepterParm);
 
-    endOfArgs = FALSE;
-    argArray = NULL;
-
     for (endOfArgs = FALSE, argArray = NULL, n = 0;
          !endOfArgs;
         ) {
@@ -641,7 +635,7 @@ pm_system_lp(const char *    const progName,
         argArray[n++] = arg;
 
         if (!arg)
-            endOfArgs = TRUE;
+            endOfArgs = true;
     }
 
     va_end(args);
@@ -690,7 +684,7 @@ pm_feed_null(int    const pipeToFeedFd,
 
 
 void
-pm_accept_null(int    const pipetosuckFd,
+pm_accept_null(int    const pipeToSuckFd,
                void * const accepterParm ) {
 
     size_t const bufferSize = 4096;
@@ -705,7 +699,7 @@ pm_accept_null(int    const pipetosuckFd,
         for (eof = false; !eof; ) {
             ssize_t rc;
 
-            rc = read(pipetosuckFd, buffer, bufferSize);
+            rc = read(pipeToSuckFd, buffer, bufferSize);
 
             if (rc < 0) {
                 /* No way to report the problem; just say we're done */
@@ -716,7 +710,7 @@ pm_accept_null(int    const pipetosuckFd,
         }
         free(buffer);
     }
-    close(pipetosuckFd);
+    close(pipeToSuckFd);
 }
 
 
@@ -729,16 +723,16 @@ pm_feed_from_memory(int    const pipeToFeedFd,
 
     FILE * const outFileP = fdopen(pipeToFeedFd, "w");
 
-    size_t bytesTransferred;
+    size_t byteCtTransferred;
 
     /* The following signals (and normally kills) the process with
        SIGPIPE if the pipe does not take all 'size' bytes.
     */
-    bytesTransferred =
+    byteCtTransferred =
         fwrite(inputBufferP->buffer, 1, inputBufferP->size, outFileP);
 
     if (inputBufferP->bytesTransferredP)
-        *(inputBufferP->bytesTransferredP) = bytesTransferred;
+        *(inputBufferP->bytesTransferredP) = byteCtTransferred;
 
     fclose(outFileP);
 }
@@ -746,23 +740,173 @@ pm_feed_from_memory(int    const pipeToFeedFd,
 
 
 void
-pm_accept_to_memory(int             const pipetosuckFd,
+pm_accept_to_memory(int             const pipeToSuckFd,
                     void *          const accepterParm ) {
 
     pm_bufferDesc * const outputBufferP = accepterParm;
 
-    FILE * const inFileP = fdopen(pipetosuckFd, "r");
+    FILE * const inFileP = fdopen(pipeToSuckFd, "r");
 
-    size_t bytesTransferred;
+    size_t byteCtTransferred;
 
-    bytesTransferred =
+    byteCtTransferred =
         fread(outputBufferP->buffer, 1, outputBufferP->size, inFileP);
 
     fclose(inFileP);
 
     if (outputBufferP->bytesTransferredP)
-        *(outputBufferP->bytesTransferredP) = bytesTransferred;
+        *(outputBufferP->bytesTransferredP) = byteCtTransferred;
+}
+
+
+
+void
+pm_feed_from_file(int    const pipeToFeedFd,
+                  void * const feederParm) {
+
+    const char * const inFileNm = feederParm;
+
+    size_t const bufferSz = 64*1024;
+
+    FILE * const outFileP = fdopen(pipeToFeedFd, "w");
+
+    FILE * inFileP;
+    unsigned char * buffer;
+    bool eof;
+
+    inFileP = pm_openr(inFileNm);
+
+    MALLOCARRAY(buffer, bufferSz);
+
+    if (!buffer)
+        pm_error("Failed to allocate %u bytes for I/O buffer",
+                 (unsigned) bufferSz);
+
+    for (eof = false; !eof; ) {
+        size_t byteCtRead;
+
+        byteCtRead = fread(buffer, 1, bufferSz, inFileP);
+
+        if (ferror(inFileP))
+            pm_error("Error reading file.  errno=%d (%s)",
+                     errno, strerror(errno));
+
+        if (byteCtRead > 0) {
+            /* The following signals (and normally kills) the process with
+               SIGPIPE if the pipe does not take all 'size' bytes.
+            */
+            fwrite(buffer, 1, byteCtRead, outFileP);
+        } else
+            eof = true;
+    }
+
+    pm_close(inFileP);
+    fclose(outFileP);
+
+    free(buffer);
+}
+
+
+
+void
+pm_accept_to_file(int             const pipeToSuckFd,
+                  void *          const accepterParm ) {
+
+    const char * const outFileNm = accepterParm;
+
+    size_t const bufferSz = 64*1024;
+
+    FILE * const inFileP = fdopen(pipeToSuckFd, "r");
+
+    FILE * outFileP;
+    unsigned char * buffer;
+    bool eof;
+
+    outFileP = pm_openw(outFileNm);
+
+    MALLOCARRAY(buffer, bufferSz);
+
+    if (!buffer)
+        pm_error("Failed to allocate %u bytes for I/O buffer",
+                 (unsigned) bufferSz);
+
+    for (eof = false; !eof; ) {
+        size_t byteCtRead;
+
+        byteCtRead = fread(buffer, 1, bufferSz, inFileP);
+
+        if (ferror(inFileP))
+            pm_error("Error reading Standard Output accepter pipe.  "
+                     "errno=%d (%s)",
+                     errno, strerror(errno));
+
+        if (byteCtRead > 0) {
+            fwrite(buffer, 1, byteCtRead, outFileP);
+
+            if (ferror(outFileP))
+                pm_error("Error writing to file.  errno=%d (%s)",
+                         errno, strerror(errno));
+        } else
+            eof = true;
+    }
+
+    pm_close(outFileP);
+    fclose(inFileP);
+
+    free(buffer);
 }
 
 
 
+/* Note that pm_feed_from_filestream is not possible because Standard Input is
+   feed by a child process and we can't properly pass a FILE * to a child
+   process.
+*/
+
+
+
+void
+pm_accept_to_filestream(int             const pipeToSuckFd,
+                        void *          const accepterParm ) {
+
+    FILE * const outFileP = accepterParm;
+
+    size_t const bufferSz = 64*1024;
+
+    FILE * const inFileP = fdopen(pipeToSuckFd, "r");
+
+    unsigned char * buffer;
+    bool eof;
+
+    MALLOCARRAY(buffer, bufferSz);
+
+    if (!buffer)
+        pm_error("Failed to allocate %u bytes for I/O buffer",
+                 (unsigned) bufferSz);
+
+    for (eof = false; !eof; ) {
+        size_t byteCtRead;
+
+        byteCtRead = fread(buffer, 1, bufferSz, inFileP);
+
+        if (ferror(inFileP))
+            pm_error("Error reading Standard Output accepter pipe.  "
+                     "errno=%d (%s)",
+                     errno, strerror(errno));
+
+        if (byteCtRead > 0) {
+            fwrite(buffer, 1, byteCtRead, outFileP);
+
+            if (ferror(outFileP))
+                pm_error("Error writing to file.  errno=%d (%s)",
+                         errno, strerror(errno));
+        } else
+            eof = true;
+    }
+
+    fclose(inFileP);
+
+    free(buffer);
+}
+
+
diff --git a/lib/libsystem_dummy.c b/lib/libsystem_dummy.c
index 97dd8984..e8acf387 100644
--- a/lib/libsystem_dummy.c
+++ b/lib/libsystem_dummy.c
@@ -9,6 +9,7 @@
   the facility is not available.
 =============================================================================*/
 
+#include <stdbool.h>
 #include <assert.h>
 
 #include "pm.h"
@@ -29,6 +30,8 @@ pm_system_vp(const char *    const progName,
              "requires.");
 }
 
+
+
 void
 pm_system_lp(const char *    const progName,
              void stdinFeeder(int, void *),
@@ -44,6 +47,8 @@ pm_system_lp(const char *    const progName,
              "requires.");
 }
 
+
+
 void
 pm_system(void                  stdinFeeder(int, void *),
           void *          const feederParm,
@@ -59,11 +64,30 @@ pm_system(void                  stdinFeeder(int, void *),
 }
 
 
+
+void
+pm_feed_null(int    const pipeToFeedFd,
+             void * const feederParm) {
+
+    assert(false);  /* Can't ever run, since pm_system() is a dummy */
+}
+
+
+
+void
+pm_accept_null(int    const pipetosuckFd,
+               void * const accepterParm ) {
+
+    assert(false);  /* Can't ever run, since pm_system() is a dummy */
+}
+
+
+
 void
 pm_feed_from_memory(int    const pipeToFeedFd,
                     void * const feederParm) {
 
-    assert(FALSE);  /* Can't ever run, since pm_system() is a dummy */
+    assert(false);  /* Can't ever run, since pm_system() is a dummy */
 }
 
 
@@ -72,6 +96,8 @@ void
 pm_accept_to_memory(int    const pipetosuckFd,
                     void * const accepterParm) {
 
-    assert(FALSE);  /* Can't ever run, since pm_system() is a dummy */
+    assert(false);  /* Can't ever run, since pm_system() is a dummy */
 }
 
+
+
diff --git a/lib/pam.h b/lib/pam.h
index 99498ed1..0b8c068a 100644
--- a/lib/pam.h
+++ b/lib/pam.h
@@ -263,8 +263,6 @@ pnm_scalesample(sample const source,
         return (source * newmaxval + (oldmaxval/2)) / oldmaxval;
 }
 
-
-
 void
 pnm_scaletuple(const struct pam * const pamP,
                tuple              const dest,
diff --git a/lib/pm.h b/lib/pm.h
index 8d5973eb..62ad5355 100644
--- a/lib/pm.h
+++ b/lib/pm.h
@@ -111,13 +111,16 @@ extern int pm_plain_output;
 extern const char * pm_progname;
 
 void
-pm_init(const char * const progname, unsigned int const flags);
+pm_init(const char * const progname,
+        unsigned int const flags);
 
 void
-pm_proginit(int * const argcP, const char * argv[]);
+pm_proginit(int *         const argcP,
+            const char ** const argv);
 
 void
-pm_setMessage(int const newState, int * const oldStateP);
+pm_setMessage(int   const newState,
+              int * const oldStateP);
 
 int
 pm_getMessage(void);
@@ -137,19 +140,23 @@ pm_make_tmpfile_fd(int *         const fdP,
                    const char ** const filenameP);
 
 void
-pm_nextimage(FILE * const file, int * const eofP);
+pm_nextimage(FILE * const file,
+             int *  const eofP);
 
 /* Variable-sized arrays definitions. */
 
 char**
-pm_allocarray (int const cols, int const rows, int const size );
+pm_allocarray (int const cols,
+               int const rows,
+               int const size );
 
 void *
 pm_allocrow(unsigned int const cols,
             unsigned int const size);
 
 void
-pm_freearray (char** const its, int const rows);
+pm_freearray (char ** const its,
+              int     const rows);
 
 void
 pm_freerow(void * const row);
@@ -413,7 +420,8 @@ pm_seek2(FILE *             const fileP,
          unsigned int       const fileposSize);
 
 void
-pm_seek(FILE * const fileP, unsigned long filepos);
+pm_seek(FILE *        const fileP,
+        unsigned long const filepos);
 
 enum pm_check_code {
     PM_CHECK_OK,
@@ -450,6 +458,9 @@ pm_parse_width(const char * const arg);
 unsigned int
 pm_parse_height(const char * const arg);
 
+unsigned int
+pm_parse_maxval(const char * const arg);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/pm_system.h b/lib/pm_system.h
index 58e17f05..5cfc50cf 100644
--- a/lib/pm_system.h
+++ b/lib/pm_system.h
@@ -101,6 +101,18 @@ void
 pm_accept_to_memory(int    const pipetosuckFd,
                     void * const accepterParm);
 
+void
+pm_feed_from_file(int    const pipeToFeedFd,
+                  void * const feederParm);
+
+void
+pm_accept_to_file(int    const pipetosuckFd,
+                  void * const accepterParm);
+
+void
+pm_accept_to_filestream(int    const pipetosuckFd,
+                        void * const accepterParm);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/pmfileio.c b/lib/pmfileio.c
index 5d6d9bc1..65d61dff 100644
--- a/lib/pmfileio.c
+++ b/lib/pmfileio.c
@@ -1152,7 +1152,8 @@ pm_seek2(FILE *             const fileP,
 
 
 void
-pm_seek(FILE * const fileP, unsigned long filepos) {
+pm_seek(FILE *        const fileP,
+        unsigned long const filepos) {
 /*----------------------------------------------------------------------------
 -----------------------------------------------------------------------------*/
 
@@ -1166,7 +1167,8 @@ pm_seek(FILE * const fileP, unsigned long filepos) {
 
 
 void
-pm_nextimage(FILE * const file, int * const eofP) {
+pm_nextimage(FILE * const file,
+             int *  const eofP) {
 /*----------------------------------------------------------------------------
    Position the file 'file' to the next image in the stream, assuming it is
    now positioned just after the current image.  I.e. read off any white
diff --git a/lib/pnm.h b/lib/pnm.h
index a8aad161..fd262566 100644
--- a/lib/pnm.h
+++ b/lib/pnm.h
@@ -136,6 +136,9 @@ pnm_invertxel(xel *  const x,
               xelval const maxval,
               int    const format);
 
+const char *
+pnm_formattypenm(int const format);
+
 void
 pnm_promoteformat(xel** xels, int cols, int rows, xelval maxval, int format,
                   xelval newmaxval, int newformat);
diff --git a/lib/util/rand.c b/lib/util/rand.c
index 6a0a2cdb..fd083c3b 100644
--- a/lib/util/rand.c
+++ b/lib/util/rand.c
@@ -67,22 +67,24 @@ https://wnww.gnu.org/software/gsl/doc/html/rng.html
                             Design note
 -------------------------------------------------------------------------------
 
-  Netpbm code contains multiple random number generators.  Stock Netpbm always
-  uses an internal pseudo-random number generator that implements the Mersenne
-  Twister method and does not rely on any randomness facility of the operating
-  system, but it is easy to compile an alternative version that uses others.
+  Netpbm code contains multiple instances where random numbers are used.
+  Stock Netpbm always uses an internal pseudo-random number generator
+  that implements the Mersenne Twister method and does not rely on any
+  randomness facility of the operating system, but it is easy to compile
+  an alternative version that uses the operating system function, or some
+  other generator.
 
   The Mersenne Twister method was new to Netpbm in Netpbm 10.94 (March 2021).
   Before that, Netpbm used standard OS-provided facilities.
 
   Programs that use random numbers have existed in Netpbm since PBMPlus days.
-  The system rand() function was used in instances randomness was required;
+  The system rand() function was used where randomness was required;
   exceptions were rare and all of them appear to be errors on the part of the
   original author.
 
   Although the rand() function is available in every system on which Netpbm
   runs, differences exist in the underlying algorithm, so that Netpbm programs
-  produce different output on different systems even when the user specifies
+  produced different output on different systems even when the user specified
   the same random number seed.
 
   This was not considered a problem in the early days.  Deterministic
@@ -127,7 +129,7 @@ pm_srand2(struct pm_randSt * const randStP,
           bool               const seedValid,
           unsigned int       const seed) {
 /*----------------------------------------------------------------------------
-  Seed the random number generator.  If 'seedValid' is true, use 'seed"..
+  Seed the random number generator.  If 'seedValid' is true, use 'seed'.
   Otherwise, use pm_randseed().
 
   For historical reasons pm_randseed() is defined in libpm.c rather than
@@ -152,7 +154,7 @@ pm_rand(struct pm_randSt * const randStP) {
 double
 pm_drand(struct pm_randSt * const randStP) {
 /*----------------------------------------------------------------------------
-  A floating point random number in the interval [0, 1).
+  A floating point random number in the interval [0, 1].
 
   Although the return value is declared as double, the actual value will have
   no more precision than a single call to pm_rand() provides.  This is 32 bits
@@ -163,6 +165,26 @@ pm_drand(struct pm_randSt * const randStP) {
 
 
 
+double
+pm_drand1(struct pm_randSt * const randStP) {
+/*----------------------------------------------------------------------------
+  A floating point random number in the interval [0, 1).
+-----------------------------------------------------------------------------*/
+    return (double) pm_rand(randStP) / ((double) randStP->max + 1.0);
+}
+
+
+
+double
+pm_drand2(struct pm_randSt * const randStP) {
+/*----------------------------------------------------------------------------
+  A floating point random number in the interval (0, 1).
+-----------------------------------------------------------------------------*/
+    return ((double) pm_rand(randStP) + 0.5) / ((double) randStP->max + 1.0);
+}
+
+
+
 void
 pm_gaussrand2(struct pm_randSt * const randStP,
               double *           const r1P,
@@ -182,11 +204,8 @@ pm_gaussrand2(struct pm_randSt * const randStP,
 -----------------------------------------------------------------------------*/
     double u1, u2;
 
-    u1 = pm_drand(randStP);
-    u2 = pm_drand(randStP);
-
-    if (u1 < DBL_EPSILON)
-        u1 = DBL_EPSILON;
+    u1 = pm_drand2(randStP);
+    u2 = pm_drand1(randStP);
 
     *r1P = sqrt(-2.0 * log(u1)) * cos(2.0 * M_PI * u2);
     *r2P = sqrt(-2.0 * log(u1)) * sin(2.0 * M_PI * u2);
@@ -223,17 +242,18 @@ uint32_t
 pm_rand32(struct pm_randSt * const randStP) {
 /*-----------------------------------------------------------------------------
   Generate a 32-bit random number.
-
-  This is a provision for users who select a non-default random number
-  generator which returns less than 32 bits per call.  Many system generators
-  are known to return 31 bits (max = 2147483647 or 0x7FFFFFFF).
-
-  This does not work with generators that return less than 11 bits per call.
-  The least we know of is the archaic RANDU, which generates 15 bits (max =
-  32767 or 0x7FFF).
 -----------------------------------------------------------------------------*/
     unsigned int const randMax = randStP->max;
 
+    /* 'randMax is a power of 2 minus 1.  Function pm_randinit() rejects
+       generators which do not satisfy this condition.  It is unlikely that
+       such odd generators actually exist.
+
+       Many system generators are known to return 31 bits (max = 2147483647 or
+       0x7FFFFFFF).  Historically, there were generators that returned only 15
+       bits.
+    */
+
     uint32_t retval;
 
     if (randMax >= 0xFFFFFFFF)
@@ -244,10 +264,10 @@ pm_rand32(struct pm_randSt * const randStP) {
         retval = 0;  /* Initial value */
 
         for (scale = 0xFFFFFFFF; scale > 0; scale /= (randMax +1))
-            retval *= (randMax + 1) + pm_rand(randStP);
+            retval = retval * (randMax + 1) + pm_rand(randStP);
     }
 
-    return retval;;
+    return retval;
 }
 
 
@@ -275,6 +295,13 @@ pm_randinit(struct pm_randSt * const randStP) {
 
     randStP->vtable.init(randStP);
 
+    if (randStP->max == 0)
+        pm_error("Random number generator maximum value must be positive.");
+    else if (((long int) randStP->max & (long int) (randStP->max + 1)) != 0x0L)
+        pm_error("Non-standard random number generator with maximum value "
+                 "%u.  Cannot handle maximum values which are not powers "
+                 "of 2 minus 1", randStP->max);
+
     randStP->gaussCacheValid = false;
 }
 
@@ -291,4 +318,3 @@ pm_randterm(struct pm_randSt * const randStP) {
 
 
 
-
diff --git a/lib/util/rand.h b/lib/util/rand.h
index 44095243..fedf5aa0 100644
--- a/lib/util/rand.h
+++ b/lib/util/rand.h
@@ -97,6 +97,12 @@ pm_rand(struct pm_randSt * const randStP);
 extern double
 pm_drand(struct pm_randSt * const randStP);
 
+extern double
+pm_drand1(struct pm_randSt * const randStP);
+
+extern double
+pm_drand2(struct pm_randSt * const randStP);
+
 extern void
 pm_gaussrand2(struct pm_randSt * const randStP,
               double *           const r1P,