about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--converter/other/ipdb.c4
-rw-r--r--converter/other/pamtopdbimg.c152
-rw-r--r--converter/other/pdbimgtopam.c33
-rw-r--r--doc/HISTORY4
-rw-r--r--lib/util/runlength.c67
-rw-r--r--lib/util/runlength.h3
6 files changed, 120 insertions, 143 deletions
diff --git a/converter/other/ipdb.c b/converter/other/ipdb.c
index 1d72cc31..5e4dc82e 100644
--- a/converter/other/ipdb.c
+++ b/converter/other/ipdb.c
@@ -283,8 +283,8 @@ ipdb_image_alloc(const char * const name,
             if (w != 0 && h != 0) {
                 MALLOCARRAY(imgP->data, w * h);
 
-                if (imgP->data) {
-                    MEMSZERO(imgP->data);
+                if (imgP->data != NULL) {
+                  memset(imgP->data, 0, sizeof(*(imgP->data)) * w * h);
                 } else
                     failed = true;
             }
diff --git a/converter/other/pamtopdbimg.c b/converter/other/pamtopdbimg.c
index 6454292e..ce2f7659 100644
--- a/converter/other/pamtopdbimg.c
+++ b/converter/other/pamtopdbimg.c
@@ -38,6 +38,7 @@
 #include "mallocvar.h"
 #include "nstring.h"
 #include "shhopt.h"
+#include "runlength.h"
 #include "pam.h"
 
 #include "ipdb.h"
@@ -128,6 +129,8 @@ parseCommandLine(int argc, const char ** argv,
         cmdlineP->inputFileName = argv[1];
     else
         pm_error("Program takes at most one argument:  input file name");
+
+    free(option_def);
 }
 
 
@@ -238,91 +241,6 @@ textWrite(TEXT * const textP,
 
 
 
-typedef struct {
-    unsigned int match;
-    uint8_t      buf[128];
-    int          mode;
-    size_t       len;
-    size_t       used;
-    uint8_t *    p;
-} RLE;
-#define MODE_MATCH  0
-#define MODE_LIT    1
-#define MODE_NONE   2
-
-#define reset(r) {                              \
-        (r)->match = 0xffff;                    \
-        (r)->mode  = MODE_NONE;                 \
-        (r)->len   = 0;                         \
-    }
-
-
-
-static void
-putMatch(RLE *  const rleP,
-          size_t const n) {
-
-    *rleP->p++ = 0x80 + n - 1;
-    *rleP->p++ = rleP->match;
-    rleP->used += 2;
-    reset(rleP);
-}
-
-
-
-static void
-putLit(RLE *  const rleP,
-       size_t const n) {
-
-    *rleP->p++ = n - 1;
-    rleP->p = (uint8_t *)memcpy(rleP->p, rleP->buf, n) + n;
-    rleP->used += n + 1;
-    reset(rleP);
-}
-
-
-
-static size_t
-compress(const uint8_t * const inData,
-         size_t          const n_in,
-         uint8_t *       const out) {
-
-    static void (*put[])(RLE *, size_t) = {putMatch, putLit};
-    RLE rle;
-    size_t  i;
-    const uint8_t * p;
-
-    MEMSZERO(&rle);
-    rle.p = out;
-    reset(&rle);
-
-    for (i = 0, p = &inData[0]; i < n_in; ++i, ++p) {
-        if (*p == rle.match) {
-            if (rle.mode == MODE_LIT && rle.len > 1) {
-                putLit(&rle, rle.len - 1);
-                ++rle.len;
-                rle.match = *p;
-            }
-            rle.mode = MODE_MATCH;
-            ++rle.len;
-        } else {
-            if (rle.mode == MODE_MATCH)
-                putMatch(&rle, rle.len);
-            rle.mode         = MODE_LIT;
-            rle.match        = *p;
-            rle.buf[rle.len++] = *p;
-        }
-        if (rle.len == 128)
-            put[rle.mode](&rle, rle.len);
-    }
-    if (rle.len != 0)
-        put[rle.mode](&rle, rle.len);
-
-    return rle.used;
-}
-
-
-
 static void
 compressIfRequired(IPDB *     const pdbP,
                    int        const comp,
@@ -334,38 +252,29 @@ compressIfRequired(IPDB *     const pdbP,
         *compressedSizeP = ipdb_img_size(pdbP->i);
     } else {
         int const uncompressedSz = ipdb_img_size(pdbP->i);
-        
-        /* Allocate for the worst case. */
-        size_t const allocSz = (3 * uncompressedSz + 2)/2;
 
-        uint8_t * data;
-            
-        data = pdbP->i->data;
+        unsigned char * outbuf;
+        size_t          compressedSz;
 
-        MALLOCARRAY(data, allocSz);
-            
-        if (data == NULL)
-            pm_error("Could not get %lu bytes of memory to decompress",
-                     (unsigned long)allocSz);
-        else {
-            size_t compressedSz;
-            compressedSz = compress(pdbP->i->data, uncompressedSz, data);
-            if (comp == IPDB_COMPMAYBE && compressedSz >= uncompressedSz) {
-                /* Return the uncompressed data */
-                free(data);
-                *compressedDataP = pdbP->i->data;
-                *compressedSizeP = uncompressedSz;
-            } else {
-                pdbP->i->compressed = TRUE;
-                if (pdbP->i->type == IMG_GRAY16)
-                    pdbP->i->version = 9;
-                else
-                    pdbP->i->version = 1;
-                if (pdbP->t != NULL)
-                    pdbP->t->r->offset -= uncompressedSz - compressedSz;
-                *compressedDataP = data;
-                *compressedSizeP = compressedSz;
-            }
+        pm_rlenc_allocoutbuf(&outbuf, uncompressedSz, PM_RLE_PALMPDB);
+
+        pm_rlenc_compressbyte(pdbP->i->data, outbuf, PM_RLE_PALMPDB,
+                              uncompressedSz, &compressedSz);
+        if (comp == IPDB_COMPMAYBE && compressedSz >= uncompressedSz) {
+            /* Return the uncompressed data */
+            free(outbuf);
+            *compressedDataP = pdbP->i->data;
+            *compressedSizeP = uncompressedSz;
+        } else {
+            pdbP->i->compressed = TRUE;
+            if (pdbP->i->type == IMG_GRAY16)
+                pdbP->i->version = 9;
+            else
+                pdbP->i->version = 1;
+            if (pdbP->t != NULL)
+                pdbP->t->r->offset -= uncompressedSz - compressedSz;
+            *compressedDataP = outbuf;
+            *compressedSizeP = compressedSz;
         }
     }
 }
@@ -742,6 +651,14 @@ readtxt(IPDB *       const pdbP,
         pm_error("stat of '%s' failed, errno = %d (%s)",
                  noteFileName, errno, strerror(errno));
 
+    /* The maximum size of a memory block that a Palm can allocate is 64K.
+       Abort with error if specified note file is any larger.
+    */
+
+    if (st.st_size + 1 >= 65535)
+        pm_error("Note file is too large: %lu bytes",
+                  (unsigned long) st.st_size);
+
     fP = pm_openr(noteFileName);
 
     MALLOCARRAY(fileContent, st.st_size + 1);
@@ -758,6 +675,8 @@ readtxt(IPDB *       const pdbP,
 
     pm_close(fP);
 
+    fileContent[st.st_size] = 0x00;  /* add terminating NUL char */
+
     /* Chop of trailing newlines */
     for (n = strlen(fileContent) - 1; n >= 0 && fileContent[n] == '\n'; --n)
         fileContent[n] = '\0';
@@ -787,6 +706,9 @@ main(int argc, const char **argv) {
     case MAYBE:        comp = IPDB_COMPMAYBE;  break;
     }
 
+    if (strlen(cmdline.title) > 31)
+        pm_error("Title too long.  Max length is 31 characters.");
+
     pdbP = ipdb_alloc(cmdline.title);
 
     if (pdbP == NULL)
diff --git a/converter/other/pdbimgtopam.c b/converter/other/pdbimgtopam.c
index 3cb4129a..67044109 100644
--- a/converter/other/pdbimgtopam.c
+++ b/converter/other/pdbimgtopam.c
@@ -97,6 +97,8 @@ parseCommandLine(int argc, const char ** argv,
         cmdlineP->inputFileName = argv[1];
     else
         pm_error("Program takes at most one argument:  input file name");
+
+    free(option_def);
 }
 
 
@@ -211,7 +213,8 @@ readCompressed(IMAGE *    const imgP,
 
     if (end_offset == UNKNOWN_OFFSET) {
         /*
-         * Read until EOF. Some of them have an extra zero byte
+         * Read sufficient amount for worst-case compressed image,
+         * or until EOF.  Some of them have an extra zero byte
          * dangling off the end.  I originally thought this was
          * an empty note record (even though there was no record
          * header for it); however, the release notes for Image
@@ -221,12 +224,20 @@ readCompressed(IMAGE *    const imgP,
          * this extra byte and ignore it by paying attention to
          * the image dimensions.
          */
-        MALLOCARRAY(buffer, ipdb_img_size(imgP));
+       size_t const maxCompressedSizeWithBloat = ipdb_img_size(imgP) * 2;
+         /*
+          * Provide a buffer large enough for the worst case.
+          * See note in lib/util/runlength.c .
+          * We do not use pm_rlenc_allocoutbuf() because there is no
+          * guarantee that the encoder that produced the image was
+          * efficient.
+          */
+       MALLOCARRAY(buffer, maxCompressedSizeWithBloat);
 
         if (buffer == NULL)
             retval = ENOMEM;
         else {
-            dataSize = fread(buffer, 1, ipdb_img_size(imgP), fP);
+            dataSize = fread(buffer, 1,  maxCompressedSizeWithBloat, fP);
             if (dataSize <= 0)
                 retval = EIO;
             else
@@ -307,6 +318,8 @@ imageReadData(FILE *   const fileP,
               IMAGE *  const imgP,
               uint32_t const end_offset) {
 
+    size_t const imageSize = ipdb_img_size(imgP);  
+
     int retval;
     size_t dataSize;
     uint8_t * buffer;
@@ -318,14 +331,24 @@ imageReadData(FILE *   const fileP,
          * Compressed data can cross row boundaries so we decompress
          * the data here to avoid messiness in the row access functions.
          */
-        if (dataSize != ipdb_img_size(imgP)) {
-            decompress(buffer, dataSize, ipdb_img_size(imgP), &imgP->data);
+        if (dataSize < imageSize || imgP->version == 1) {
+            if (imgP->version == 0)
+                pm_message("Image header says raster data is uncompressed.  "
+                           "Encountered only %u instead of the "
+                           "required %u bytes.  Assuming compressed mode.",
+                           (unsigned)dataSize, (unsigned)imageSize);
+            decompress(buffer, dataSize, imageSize, &imgP->data);
             if (imgP->data == NULL)
                 retval = ENOMEM;
             else
                 imgP->compressed = true;
             free(buffer);
         } else {
+            if (dataSize > imageSize)
+                pm_message("Image header says raster data is uncompressed. "
+                           "Encountered %u instead of the required %u bytes. "
+                           "Assuming uncompressed mode.",
+                           (unsigned)dataSize, (unsigned)imageSize);
             imgP->compressed = false;
             imgP->data       = buffer;
             /* Storage at 'buffer' now belongs to *imgP */
diff --git a/doc/HISTORY b/doc/HISTORY
index d450d8b8..50dbef6a 100644
--- a/doc/HISTORY
+++ b/doc/HISTORY
@@ -36,6 +36,10 @@ not yet  BJH  Release 10.80.00
               pgmmake: Fix bug: treats non-numeric gray-level argument as zero.
               Always broken (Pgmmake was new in Netpbm 10.32, February 2006).
 
+              pdbimgtopam, pamtopdbimg: fix various cases of incorrect output,
+              some always present (programs were new in Netpbm 10.52.00
+              (October 2010)).
+
               libnetpbm: pnm_parsecolorn(), pnm_parsecolor(): fix parsing of
               rgb: color specifications: yields value slightly too dim.
               Affects many programs.  Broken in Netpbm 10.79 (June 2017).
diff --git a/lib/util/runlength.c b/lib/util/runlength.c
index e5c60db0..a4bb9057 100644
--- a/lib/util/runlength.c
+++ b/lib/util/runlength.c
@@ -12,8 +12,8 @@
   documentation.  This software is provided "as is" without express or implied
   warranty.
 
-  Functions pm_rlenc_byte() and pm_rlenc_word() are based on algorithm
-  originally by Robert A. Knop (rknop@mop.caltech.edu).
+  Functions pm_rlenc_compressbyte() and pm_rlenc_compressword() are based
+  on algorithm originally by Robert A. Knop (rknop@mop.caltech.edu).
 
   Those who wish to incorporate the code herein into their own programs are
   strongly discouraged from removing the comments within borders consisting
@@ -41,12 +41,12 @@
   code.  This redundancy bloated the Netpbm package and made maintenance
   difficult.  This maintenance difficulty surfaced as an issue when tests with
   valgrind revealed multiple memory-related problems in the above programs.
-  Indeed, just determining which programs do this compression by this method
+  Indeed, just determining which programs do compression by this method
   was not simple because of differences in terminology.  "Packbits" is often
   called "run length encoding."  In ppmtoilbm the name is "byterun1."
 
-  Today, all Netpbm programs that do Packbits compression use the facilities
-  in this file for it.
+  Today, all Netpbm programs that do Packbits compression with the exception
+  of pbmtoppa and pbmtogo use the facilities   in this file for it.  
 =============================================================================*/
 
 #include <string.h>
@@ -68,11 +68,11 @@ static const char * const errorUndefinedMode =
 
    In this simple run-length encoding scheme, compressed and uncompressed
    strings follow a single index or "flag" byte N.  There are several
-   variations, differing in the format of the flag byte, maximum string length
-   and element size (byte or word).
+   variations, differing in the format of the flag byte, maximum string
+   length and element size (byte or word).
    
-   In the most widely used version, Packbits, the meaning of the flag byte N
-   is defined as follows:
+   In the most widely used version, Packbits, the meaning of the flag byte
+   N is defined as follows:
 
     0-127 means the next N+1 bytes are uncompressed.
     129-255 means the next byte is to be repeated 257-N times.
@@ -99,7 +99,7 @@ pm_rlenc_compressbyte(const unsigned char * const inbuf,
   Always encode 3-byte repeat sequences.
 
   Encode 2-byte repeat sequences only when they are at the start of the block.
-  This ensures that the output is never unnecessary bloated.
+  This ensures that the output is never unnecessarily bloated.
 
   Original algorithm by Robert A. Knop (rknop@mop.caltech.edu)
 -----------------------------------------------------------------------------*/
@@ -107,8 +107,17 @@ pm_rlenc_compressbyte(const unsigned char * const inbuf,
 
     size_t inCurs, outCurs;
 
-    if (mode != PM_RLE_PACKBITS)
-        pm_error(errorUndefinedMode, mode);
+    int packBase;
+    int packSign;
+ 
+    switch (mode) {
+    case PM_RLE_PACKBITS:
+        packBase = 257 ; packSign = -1; break;
+    case PM_RLE_PALMPDB:
+        packBase = 127 ; packSign = +1; break;
+    default:
+         pm_error(errorUndefinedMode, mode);
+    }
 
     for (inCurs = 0, outCurs = 0; inCurs < inSize; ) {
         if ((inCurs < inSize - 1) && (inbuf[inCurs] == inbuf[inCurs+1])) {
@@ -121,8 +130,7 @@ pm_rlenc_compressbyte(const unsigned char * const inbuf,
                      count < maxRun; 
                  ++inCurs, ++count)
                 ;
-
-            outbuf[outCurs++] = (unsigned char)(257 - count);
+            outbuf[outCurs++] = (unsigned char) (packBase + packSign * count);
             outbuf[outCurs++] = inbuf[hold];
         } else {
             /*Do a non-run*/
@@ -258,6 +266,8 @@ pm_rlenc_compressword(const uint16_t   * const inbuf,
    number of flag bytes.  The worst case happens when there are no repeat
    sequences in the input.
 
+   The encoder in this file is an efficient one.
+
    The key to an efficient encoder is correct handling of short, inefficient
    sequences.  The following algorithm (not actually used in this file)
    describes the idea.
@@ -288,6 +298,16 @@ pm_rlenc_compressword(const uint16_t   * const inbuf,
 
      - Whenever savings are larger than the added overhead, encode the run
        as a compressed block.
+
+   -----------------------------------------------------------------------
+
+   MS-DOS/Windows BMP format note
+
+   The compression method for BMP is a variant of packbits.
+   We could support the 8-bit version with some modifications to functions
+   in this file.  Determining the worst-case output size of an efficient
+   coder is rather complicated because uncompressed blocks may not be less
+   than three bytes long and are indicated by two-byte flags.
 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
 
 
@@ -318,15 +338,22 @@ pm_rlenc_maxbytes(size_t          const inSize,  /* number of elements */
 
     switch (mode) {
     case PM_RLE_PACKBITS:
-        blockSize = 128; flagSize = 1; itemSize = 1; miscSize = 0; break;
+    case PM_RLE_PALMPDB:
+        blockSize = 128; flagSize = 1; itemSize = 1; miscSize = 0;
+        break;
     case PM_RLE_SGI8:
-        blockSize = 127; flagSize = 1; itemSize = 1; miscSize = 0; break;
-    case PM_RLE_GRAPHON:  case PM_RLE_PPA:
-        blockSize =  64; flagSize = 1; itemSize = 1; miscSize = 0; break;
+        blockSize = 127; flagSize = 1; itemSize = 1; miscSize = 0;
+        break;
+    case PM_RLE_GRAPHON:
+    case PM_RLE_PPA:
+        blockSize =  64; flagSize = 1; itemSize = 1; miscSize = 0;
+        break;
     case PM_RLE_SGI16:
-        blockSize = 127; flagSize = 2; itemSize = 2; miscSize = 2; break;
+        blockSize = 127; flagSize = 2; itemSize = 2; miscSize = 2;
+        break;
     case PM_RLE_PALM16:
-        blockSize = 128; flagSize = 1; itemSize = 2; miscSize = 0; break;
+        blockSize = 128; flagSize = 1; itemSize = 2; miscSize = 0;
+        break;
     default:
         pm_error(errorUndefinedMode, mode);
     }
diff --git a/lib/util/runlength.h b/lib/util/runlength.h
index 4857ae61..de921650 100644
--- a/lib/util/runlength.h
+++ b/lib/util/runlength.h
@@ -18,7 +18,8 @@ enum pm_RleMode { PM_RLE_PACKBITS,          /* most common mode */
                   PM_RLE_PPA,               /* reserved */
                   PM_RLE_SGI8,              /* reserved */
                   PM_RLE_SGI16,
-                  PM_RLE_PALM16
+                  PM_RLE_PALM16,
+                  PM_RLE_PALMPDB  
                 };
 
 size_t