Package: netpbm
Version: 2:10.0-10.1
Severity: wishlist
Tags: patch

Upstream netpbm has an option "-image=all" for giftopnm, which extracts
all frames of an animated gif.  The Debian version only supports
"-image=n".  Please may we have the extra option, which is useful on my
non-Debian system for detecting image spam comprised of such gifs (the
FuzzyOCR plugin to SpamAssassin uses giftopnm).

I've attached a patch which basically consists of taking the upstream
giftopnm.c, if you think it is suitable for use.  It also uses upstream's
"mallocvar.h".  The .h doesn't have an explicit copyright statement so
I will understand if this means you can't take it, it was just the
quickest way for me to get the feature I need.

Nick Leverton

-- System Information:
Debian Release: testing/unstable
  APT prefers testing
  APT policy: (800, 'testing'), (60, 'stable'), (3, 'unstable')
Architecture: i386 (i686)
Shell:  /bin/sh linked to /bin/bash
Kernel: Linux 2.6.17.20061003.0-k7-njl
Locale: LANG=en_GB.UTF-8, LC_CTYPE=en_GB.UTF-8 (charmap=UTF-8)

Versions of packages netpbm depends on:
ii  libc6                       2.3.6.ds1-4  GNU C Library: Shared libraries
ii  libjpeg62                   6b-13        The Independent JPEG Group's JPEG 
ii  libnetpbm10                 2:10.0-10.1  Shared libraries for netpbm
ii  libpng12-0                  1.2.8rel-5.2 PNG library - runtime
ii  libtiff4                    3.8.2-6      Tag Image File Format (TIFF) libra
ii  zlib1g                      1:1.2.3-13   compression library - runtime

Versions of packages netpbm recommends:
ii  gs                       8.54.dfsg.1-4   Transitional package
ii  gs-esp [gs]              8.15.3.dfsg.1-1 The Ghostscript PostScript interpr
ii  gs-gpl [gs]              8.54.dfsg.1-4   The GPL Ghostscript PostScript int

-- no debconf information
diff -Nurw netpbm-free-10.0/include/mallocvar.h 
netpbm-free-10.0.new/include/mallocvar.h
--- netpbm-free-10.0/include/mallocvar.h        1970-01-01 01:00:00.000000000 
+0100
+++ netpbm-free-10.0.new/include/mallocvar.h    2004-11-26 20:02:34.000000000 
+0000
@@ -0,0 +1,82 @@
+/* These are some dynamic memory allocation facilities.  They are essentially
+   an extension to C, as they do allocations with a cognizance of C 
+   variables.  You can use them to make C read more like a high level
+   language.
+*/
+
+#ifndef MALLOCVAR_INCLUDED
+#define MALLOCVAR_INCLUDED
+
+#include "pm_config.h"
+
+#include <limits.h>
+#include <stdlib.h>
+
+static __inline__ void
+mallocProduct(void **      const resultP, 
+              unsigned int const factor1,
+              unsigned int const factor2) {
+/*----------------------------------------------------------------------------
+   malloc a space whose size in bytes is the product of 'factor1' and
+   'factor2'.  But if that size cannot be represented as an unsigned int,
+   return NULL without allocating anything.  Also return NULL if the malloc
+   fails.
+
+   Note that malloc() actually takes a size_t size argument, so the
+   proper test would be whether the size can be represented by size_t,
+   not unsigned int.  But there is no reliable indication available to
+   us, like UINT_MAX, of what the limitations of size_t are.  We
+   assume size_t is at least as expressive as unsigned int and that
+   nobody really needs to allocate more than 4GB of memory.
+-----------------------------------------------------------------------------*/
+    if (UINT_MAX / factor2 < factor1) 
+        *resultP = NULL; \
+    else 
+        *resultP = malloc(factor1 * factor2); 
+}
+
+
+
+static __inline__ void
+reallocProduct(void **      const blockP,
+               unsigned int const factor1,
+               unsigned int const factor2) {
+    
+    if (UINT_MAX / factor2 < factor1) 
+        *blockP = NULL; \
+    else 
+        *blockP = realloc(*blockP, factor1 * factor2); 
+}
+
+
+
+#define MALLOCARRAY(arrayName, nElements) \
+    mallocProduct((void **)&arrayName, nElements, sizeof(arrayName[0]))
+
+#define REALLOCARRAY(arrayName, nElements) \
+    reallocProduct((void **)&arrayName, nElements, sizeof(arrayName[0]))
+
+
+#define MALLOCARRAY_NOFAIL(arrayName, nElements) \
+do { \
+    MALLOCARRAY(arrayName, nElements); \
+    if ((arrayName) == NULL) \
+        abort(); \
+} while(0)
+
+#define REALLOCARRAY_NOFAIL(arrayName, nElements) \
+do { \
+    REALLOCARRAY(arrayName, nElements); \
+    if ((arrayName) == NULL) \
+        abort(); \
+} while(0)
+
+
+#define MALLOCVAR(varName) \
+    varName = malloc(sizeof(*varName))
+
+#define MALLOCVAR_NOFAIL(varName) \
+    do {if ((varName = malloc(sizeof(*varName))) == NULL) abort();} while(0)
+
+#endif
+
diff -Nurw netpbm-free-10.0/pnm/giftopnm.1 netpbm-free-10.0.new/pnm/giftopnm.1
--- netpbm-free-10.0/pnm/giftopnm.1     2003-08-14 20:38:51.000000000 +0100
+++ netpbm-free-10.0.new/pnm/giftopnm.1 2004-10-18 12:13:48.000000000 +0100
@@ -1,119 +1,113 @@
-.TH giftopnm 1 "13 January 2001"
-.IX giftopnm
-.SH NAME
-giftopnm - convert a GIF file into a portable anymap
-.SH SYNOPSIS
-.B giftopnm
-\fR[\fB--alphaout=\fR{\fIalpha-filename\fR,\fB-\fR}]
-.RB [ -verbose ]
-.RB [ -comments ]
-.RB [ -image
-.IR N ]
-.RI [ GIFfile ]
-
-.SH DESCRIPTION
-This is a graphics format converter from the GIF format to the PNM 
-(i.e. PBM, PGM, or PPM) format.
-
-If the image contains only black and maximally bright white, the output 
-is PBM.  If the image contains more than those two colors, but only grays,
-the output is PGM.  If the image contains other colors, the output is PPM.
-
-If you have an animated GIF file, you can extract individual frames from it
-with 
-.B gifsicle
-and then convert those using 
-.BR giftopnm .
-.IX GIF
-
-A GIF image contains rectangular pixels.  They all have the same
-aspect ratio, but may not be square (it's actually quite unusual for
-them not to be square, but it could happen).  The pixels of a Netpbm
-image are always square.  Because of the engineering complexity to 
-do otherwise, 
-.B giftopnm
-converts a GIF image to a Netpbm image pixel-for-pixel.
-This means if the GIF pixels are not square, the Netpbm output image has
-the wrong aspect ratio.  In this case, 
-.B giftopnm
-issues an informational message telling you to run 
-.B pnmscale
-to correct the output.
-
-.SH OPTIONS
-.TP
-.BI --alphaout= alpha-filename
-.B giftopnm 
-creates a PGM (portable graymap) file containing the alpha channel
-values in the input image.  If the input image doesn't contain an alpha
-channel, the 
-.I alpha-filename
-file contains all zero (transparent) alpha values.  If you don't specify
-.BR --alphaout ,
-.B giftopnm
-does not generate an alpha file, and if the input image has an alpha channel,
-.B giftopnm
-simply discards it.
-
-If you specify
-.B -
-as the filename, 
-.B giftopnm
-writes the alpha output to Standard Output and discards the image.
-
-See 
-.BR pnmcomp (1)
-for one way to use the alpha output file.
-.TP
-.B -verbose
+
+                                   giftopnm
+
+   Updated: 16 June 2003
+   Table Of Contents
+
+NAME
+
+   giftopnm - convert a GIF file into a PNM image
+
+SYNOPSIS
+
+   giftopnm    [--alphaout={alpha-filename,-}]   [-verbose]   [-comments]
+   [-image={N,all}] [GIFfile]
+
+   Minimum unique abbreviation of option is acceptable. You may use double
+   hyphens instead of single hyphen to denote options. You may use white space
+   in place of the equals sign to separate an option name from its value.
+
+DESCRIPTION
+
+   This program is part of Netpbm.
+
+   This is a graphics format converter from the GIF format to the PNM (i.e.
+   PBM, PGM, or PPM) format.
+
+   If the image contains only black and maximally bright white, the output is
+   PBM. If the image contains more than those two colors, but only grays, the
+   output is PGM. If the image contains other colors, the output is PPM.
+
+   A GIF image contains rectangular pixels. They all have the same aspect
+   ratio, but may not be square (it's actually quite unusual for them not to be
+   square,  but it could happen). The pixels of a Netpbm image are always
+   square. Because of the engineering complexity to do otherwise, giftopnm
+   converts a GIF image to a Netpbm image pixel-for-pixel. This means if the
+   GIF pixels are not square, the Netpbm output image has the wrong aspect
+   ratio. In this case, giftopnm issues an informational message telling you to
+   run pnmscale to correct the output.
+
+OPTIONS
+
+   --alphaout=alpha-filename
+          giftopnm creates a PGM (portable graymap) file containing the alpha
+          channel values in the input image. If the input image doesn't contain
+          an  alpha  channel,  the  alpha-filename file contains all zero
+          (transparent) alpha values. If you don't specify --alphaout, giftopnm
+          does not generate an alpha file, and if the input image has an alpha
+          channel, giftopnm simply discards it.
+          If you specify - as the filename, giftopnm writes the alpha output to
+          Standard Output and discards the image.
+          See pamcomp for one way to use the alpha output file.
+   -verbose
 Produce verbose output about the GIF file input.
-.TP
-.B -comments
+   -comments
 Only output GIF89 comment fields.
-.TP
-.B -image \fIN
-Output the specified gif image from the input GIF archive (where
-.I N
-is '1', '2', '20'...).
-Normally there is only one image per file, so this option
-is not needed.
-.PP
-All flags can be abbreviated to their shortest unique prefix.
+   -image={N,all}
+          This option identifies which image from the GIF stream you want. You
+          can select either one image or all the images. Select al the images
+          with all. Select one image by specifying its sequence number in the
+          stream: 1, 2, 3, etc.
+          The default is just Image 1.
+          A GIF stream normally contains only one image, so you don't need this
+          option. But some streams, including animated GIFs, have multiple
+          images.
+          When you select multiple GIF images, the output is a PNM stream with
+          multiple images.
+          The all value was added in Netpbm 10.16 (June 2003). Earlier giftopnm
+          can extract only one image.
+
+
+
+RESTRICTIONS
 
-.SH RESTRICTIONS
 This does not correctly handle the Plain Text Extension of the GIF89
 standard, since I did not have any example input files containing them.
 
-.SH "SEE ALSO"
-.BR ppmtogif (1), 
-.BR ppmcolormask (1),
-.BR pnmcomp (1),
-.BR gifsicle (1)
-<http://www.lcdf.org/gifsicle>,
-.BR ppm (5).
-.SH AUTHOR
+SEE ALSO
+
+   ppmtogif, ppmcolormask, pamcomp, <http://www.lcdf.org/gifsicle>, ppm.
+
+AUTHOR
+
 Copyright (c) 1993 by David Koblas ([EMAIL PROTECTED])
-.\" Permission to use, copy, modify, and distribute this software and its
-.\" documentation for any purpose and without fee is hereby granted,
-.\" provided that the above copyright notice appear in all copies and that
-.\" both that copyright notice and this permission notice appear in
-.\" supporting documentation.
-
-.SH LICENSE
-If you use
-.BR giftopnm ,
-you are using a patent on the LZW compression method which is owned by
-Unisys, and in all probability you do not have a license from Unisys
-to do so.  Unisys typically asks $5000 for a license for trivial use
-of the patent.  Unisys has never enforced the patent against trivial
-users, and has made statements that it is much less concerned about
-people using the patent for decompression (which is what
-.B giftopnm
-does than for compression.  The patent expires in 2003 / 2004, depending on
-the country.
 
-Rumor has it that IBM also owns a patent covering 
-.BR giftopnm .
+LICENSE
+
+   As a historical note, for a long time if you used giftopnm, you were using a
+   patent on the LZW compression method which was owned by Unisys, and in all
+   probability  you  did  not have a license from Unisys to do so. Unisys
+   typically asked $5000 for a license for trivial use of the patent. Unisys
+   never enforced the patent against trivial users, and made statements that it
+   is much less concerned about people using the patent for decompression
+   (which is what giftopnm does than for compression. The patent expired in
+   2003.
+
+   Rumor has it that IBM also owns a patent covering giftopnm.
+
+   A replacement for the GIF format that has never required any patent license
+   to use is the PNG format.
+     _________________________________________________________________
+
+
 
-A replacement for the GIF format that does not require any patents to use
-is the PNG format.
+Table Of Contents
+
+     * NAME
+     * SYNOPSIS
+     * DESCRIPTION
+     * OPTIONS
+     * RESTRICTIONS
+     * SEE ALSO
+     * AUTHOR
+     * LICENSE
diff -Nurw netpbm-free-10.0/pnm/giftopnm.c netpbm-free-10.0.new/pnm/giftopnm.c
--- netpbm-free-10.0/pnm/giftopnm.c     2003-08-15 15:30:17.000000000 +0100
+++ netpbm-free-10.0.new/pnm/giftopnm.c 2006-10-24 00:50:33.000000000 +0100
@@ -10,10 +10,18 @@
 
 /* There is a copy of the GIF89 specification, as defined by its
    inventor, Compuserve, in 1989, at http://members.aol.com/royalef/gif89a.txt
+
+   This covers the high level format, but does not cover how the "data"
+   contents of a GIF image represent the raster of color table indices.
+   An appendix describes extensions to Lempel-Ziv that GIF makes (variable
+   length compression codes and the clear and end codes), but does not
+   describe the Lempel-Ziv base.
 */
 
 #include <string.h>
 #include "pnm.h"
+#include "shhopt.h"
+#include "mallocvar.h"
 
 #define GIFMAXVAL 255
 #define MAXCOLORMAPSIZE 256
@@ -32,16 +40,21 @@
 
 #define LM_to_uint(a,b)                        (((b)<<8)|(a))
 
+static int const maxnum_lzwCode = (1<<MAX_LZW_BITS);
+
 struct cmdline_info {
     /* All the information the user supplied in the command line,
        in a form easy for the program to use.
     */
-    char *input_filespec;  /* Filespecs of input files */
-    int verbose;    /* -verbose option */
-    int comments;   /* -comments option */
-    int image_no;   /* image number he wants from input */
-    char *alpha_filename;
-    int alpha_stdout;
+    const char *input_filespec;  /* Filespecs of input files */
+    unsigned int verbose;    /* -verbose option */
+    unsigned int comments;   /* -comments option */
+    bool all_images;  /* He wants all the images */
+    unsigned int image_no;
+        /* image number he wants from input, starting at 0.  Undefined
+           if all_images is TRUE
+        */
+    const char *alpha_filename;
 };
 
 
@@ -52,35 +65,58 @@
    Note that the file spec array we return is stored in the storage that
    was passed to us as the argv array.
 -----------------------------------------------------------------------------*/
-    optStruct *option_def = malloc(100*sizeof(optStruct));
-        /* Instructions to OptParseOptions2 on how to parse our options.
+    optEntry *option_def = malloc(100*sizeof(optEntry));
+        /* Instructions to optParseOptions3 on how to parse our options.
          */
-    optStruct2 opt;
+    optStruct3 opt;
+    
+    unsigned int alphaSpec, imageSpec;
+    const char * image;
 
     unsigned int option_def_index;
 
     option_def_index = 0;   /* incremented by OPTENTRY */
-    OPTENTRY(0,   "verbose",     OPT_FLAG,   &cmdlineP->verbose,         0);
-    OPTENTRY(0,   "comments",    OPT_FLAG,   &cmdlineP->comments,        0);
-    OPTENTRY(0,   "image",       OPT_UINT,   &cmdlineP->image_no,        0);
-    OPTENTRY(0,   "alphaout",    OPT_STRING, &cmdlineP->alpha_filename, 0);
-
-    /* Set the defaults */
-    cmdlineP->verbose = FALSE;
-    cmdlineP->comments = FALSE;
-    cmdlineP->image_no = 1;
-    cmdlineP->alpha_filename = NULL;
+    OPTENT3(0, "verbose",     OPT_FLAG, NULL, 
+            &cmdlineP->verbose,         0);
+    OPTENT3(0, "comments",    OPT_FLAG, NULL,
+            &cmdlineP->comments,        0);
+    OPTENT3(0, "image",       OPT_STRING, &image,
+            &imageSpec,                  0);
+    OPTENT3(0, "alphaout",    OPT_STRING, &cmdlineP->alpha_filename, 
+            &alphaSpec,                 0);
 
     opt.opt_table = option_def;
     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
 
-    pm_optParseOptions2(&argc, argv, opt, 0);
+    pm_optParseOptions3( &argc, argv, opt, sizeof(opt), 0);
         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
 
-    if (cmdlineP->image_no < 1)
-        pm_error("Invalid -image option: %d.  Must be at least 1.",
-                 cmdlineP->image_no);
+    if (!imageSpec) {
+        cmdlineP->image_no = 0;
+        cmdlineP->all_images = FALSE;
+    } else {
+        if (strcasecmp(image, "all") == 0) 
+            cmdlineP->all_images = TRUE;
+        else {
+            char * tailptr;
+
+            long const imageNo = strtol(image, &tailptr, 10);
+
+            if (*tailptr != '\0')
+                pm_error("Invalid value for '-image' option.  Must be either "
+                         "a number or 'all'.  You specified '%s'", image);
+            else if (imageNo < 0)
+                pm_error("Invalid value for '-image' option.  Must be "
+                         "positive.  You specified %ld", imageNo);
+            else if (imageNo == 0)
+                pm_error("Invalid value for 'image' option.  You specified "
+                         "zero.  The first image is 1.");
+
+            cmdlineP->all_images = FALSE;
+            cmdlineP->image_no = (unsigned int) imageNo - 1;
+        }
+    }
     
     if (argc-1 == 0) 
         cmdlineP->input_filespec = "-";
@@ -90,11 +126,8 @@
     else
         cmdlineP->input_filespec = argv[1];
 
-    if (cmdlineP->alpha_filename && 
-        strcmp(cmdlineP->alpha_filename, "-") == 0 )
-        cmdlineP->alpha_stdout = 1;
-    else 
-        cmdlineP->alpha_stdout = 0;
+    if (!alphaSpec) 
+        cmdlineP->alpha_filename = NULL;
 }
 
 
@@ -105,6 +138,11 @@
     unsigned int    Height;
     gifColorMap     ColorMap;
     unsigned int    ColorMapSize;
+        /* Base 2 logarithm of number of colors in the color map.
+           Note that the color map portion must be a power of two slots in 
+           size.  Each color takes 3 bytes (1 R, 1G, 1B), so 
+           e.g. 3 means 8 colors = 24 bytes of color map.
+        */
     unsigned int    ColorResolution;
     unsigned int    Background;
     unsigned int    AspectRatio;
@@ -199,12 +237,15 @@
     unsigned char count;
     bool successfulRead;
     
+    long const pos=ftell(ifP);
     successfulRead = ReadOK(ifP, &count, 1);
     if (!successfulRead) {
         pm_message("EOF or error in reading DataBlock size from file" );
         *eofP = TRUE;
         *lengthP = 0;
     } else {
+        if (verbose)
+            pm_message("%d byte block at Position %ld", count, pos);
         *eofP = FALSE;
         *lengthP = count;
 
@@ -309,7 +350,7 @@
 
 static void
 doExtension(FILE * const ifP, int const label, struct gif89 * const gif89P) {
-    char * str;
+    const char * str;
     
     switch (label) {
     case 0x01:              /* Plain Text Extension */
@@ -364,47 +405,64 @@
 
 
 
-static int
-getCode(FILE * const ifP, 
-        int    const codeSize, 
-        bool   const first)
-{
-/*----------------------------------------------------------------------------
-   If 'first', initialize the code getter.
+struct getCodeState {
+    unsigned char buf[280];
+        /* This is the buffer through which we read the data from the 
+           stream.  We must buffer it because we have to read whole data
+           blocks at a time, but our client wants one code at a time.
+           The buffer typically contains the contents of one data block
+           plus two bytes from the previous data block.
+        */
+    int bufCount;
+        /* This is the number of bytes of contents in buf[]. */
+    int curbit;
+        /* The bit number (starting at 0) within buf[] of the next bit
+           to be returned.  If the next bit to be returned is not yet in
+           buf[] (we've already returned everything in there), this points
+           one beyond the end of the buffer contents.
+        */
+    bool streamExhausted;
+        /* The last time we read from the input stream, we got an EOD marker
+           or EOF
+        */
+};
 
-   Otherwise, read and return the next lzw code from the file *ifP.
------------------------------------------------------------------------------*/
 
-    static unsigned char buf[280];
-    static int           curbit, lastbit, last_byte;
-    static bool          done;
-    int retval;
 
-    if (first) {
+static void
+initGetCode(struct getCodeState * const getCodeStateP) {
+    
         /* Fake a previous data block */
-        buf[0] = 0;
-        buf[1] = 0;
-        last_byte  = 2;
-        curbit = 16;
-        lastbit = 16;
+    getCodeStateP->buf[0] = 0;
+    getCodeStateP->buf[1] = 0;
+    getCodeStateP->bufCount = 2;
+    getCodeStateP->curbit = getCodeStateP->bufCount * 8;
+    getCodeStateP->streamExhausted = FALSE;
+}
+
+
+
+static void
+getAnotherBlock(FILE * const ifP, 
+                struct getCodeState * const gsP) {
 
-        done = FALSE;
-        retval = 0;
-    } else {
-        if ( (curbit+codeSize) >= lastbit) {
             unsigned int count;
             unsigned int assumed_count;
             bool eof;
 
-            if (done) {
-                if (curbit >= lastbit)
-                    pm_error("ran off the end of my bits" );
-                return -1;
-            }
-            buf[0] = buf[last_byte-2];
-            buf[1] = buf[last_byte-1];
+    /* Shift buffer down so last two bytes are now the
+       first two bytes.  Shift 'curbit' cursor, which must
+       be somewhere in or immediately after those two
+       bytes, accordingly.
+    */
+    gsP->buf[0] = gsP->buf[gsP->bufCount-2];
+    gsP->buf[1] = gsP->buf[gsP->bufCount-1];
 
-            getDataBlock(ifP, &buf[2], &eof, &count);
+    gsP->curbit -= (gsP->bufCount-2)*8;
+    gsP->bufCount = 2;
+        
+    /* Add the next block to the buffer */
+    getDataBlock(ifP, &gsP->buf[gsP->bufCount], &eof, &count);
             if (eof) {
                 pm_message("EOF encountered in image "
                            "before EOD marker.  The GIF "
@@ -414,140 +472,412 @@
                 assumed_count = 0;
             } else
                 assumed_count = count;
-            if (assumed_count == 0)
-                done = TRUE;
 
-            last_byte = 2 + assumed_count;
-            curbit = (curbit - lastbit) + 16;
-            lastbit = (2+assumed_count)*8 ;
+    gsP->streamExhausted = (assumed_count == 0);
+
+    gsP->bufCount += assumed_count;
         }
 
-        retval = 0;
+
+
+static void
+doGetCode(FILE *                const ifP, 
+          int                   const codeSize,
+          struct getCodeState * const gsP,
+          int *                 const retvalP) {
+
+    if ((gsP->curbit+codeSize) > gsP->bufCount*8 && !gsP->streamExhausted) 
+        /* Not enough left in buffer to satisfy request.  Get the next
+           data block into the buffer.
+        */
+        getAnotherBlock(ifP, gsP);
+
+    if ((gsP->curbit+codeSize) > gsP->bufCount*8) {
+        /* If the buffer still doesn't have enough bits in it, that means
+           there were no data blocks left to read.
+        */
+        *retvalP = -1;  /* EOF */
+
         {
+            int const bitsUnused = gsP->bufCount*8 - gsP->curbit;
+            if (bitsUnused > 0)
+                pm_message("Stream ends with a partial code "
+                           "(%d bits left in file; "
+                           "expected a %d bit code).  Ignoring.",
+                           bitsUnused, codeSize);
+        }
+    } else {
             int i, j;
-            for (i = curbit, j = 0; j < codeSize; ++i, ++j)
-                retval |= ((buf[ i / 8 ] & (1 << (i % 8))) != 0) << j;
+        int code;
+        unsigned char * const buf = gsP->buf;
+
+        code = 0;  /* initial value */
+        for (i = gsP->curbit, j = 0; j < codeSize; ++i, ++j)
+            code |= ((buf[ i / 8 ] & (1 << (i % 8))) != 0) << j;
+        gsP->curbit += codeSize;
+        *retvalP = code;
         }
-        curbit += codeSize;
     }
+
+
+
+static int
+getCode(FILE * const ifP, 
+        int    const codeSize, 
+        bool   const init)
+{
+/*----------------------------------------------------------------------------
+   If 'init', initialize the code getter.
+
+   Otherwise, read and return the next lzw code from the file *ifP.
+
+   'codeSize' is the number of bits in the code we are to get.
+
+   Return -1 instead of a code if we encounter the end of the file.
+-----------------------------------------------------------------------------*/
+    static struct getCodeState getCodeState;
+
+    int retval;
+
+    if (init) {
+        initGetCode(&getCodeState);
+        retval = 0;
+    } else 
+        doGetCode(ifP, codeSize, &getCodeState, &retval);
+
     return retval;
 }
 
 
+struct stack {
+    /* Stack grows from low addresses to high addresses */
+    int * stack;  /* malloc'ed array */
+    int * sp;     /* stack pointer */
+    int * top;    /* next word above top of stack */
+};
+
+
+
+static void 
+initStack(struct stack * const stackP, unsigned int const size) {
+
+    MALLOCARRAY(stackP->stack, size);
+    if (stackP->stack == NULL)
+        pm_error("Unable to allocate %d-word stack.", size);
+    stackP->sp = stackP->stack;
+    stackP->top = stackP->stack + size;
+}
+
+
+
+static void
+pushStack(struct stack * const stackP, int const value) {
+
+    if (stackP->sp >= stackP->top)
+        pm_error("stack overflow");
+
+    *(stackP->sp++) = value;
+}
+
+
+
+static bool
+stackIsEmpty(const struct stack * const stackP) {
+    return stackP->sp == stackP->stack;
+}
+
+
 
 static int
-lzwReadByte(FILE * const ifP, bool const first, int const input_codeSize) {
+popStack(struct stack * const stackP) {
+
+    if (stackP->sp <= stackP->stack)
+        pm_error("stack underflow");
+    
+    return *(--stackP->sp);
+}
+
+
+
+static void
+termStack(struct stack * const stackP) {
+    free(stackP->stack);
+    stackP->stack = NULL;
+}
+
+
 /*----------------------------------------------------------------------------
-  Return the next byte of the decompressed image.
+   Some notes on LZW.
 
-  Return -1 if we hit EOF prematurely (i.e. before an "end" code.  We
-  forgive the case that the "end" code is followed by EOF instead of
-  an EOD marker (zero length DataBlock)).
+   LZW is an extension of Limpel-Ziv.  The two extensions are:
+
+     1) in Limpel-Ziv, codes are all the same number of bits.  In
+        LZW, they start out small and increase as the stream progresses.
+
+     2) LZW has a clear code that resets the string table and code
+        size.
+
+   The LZW code space is allocated as follows:
+
+   The true data elements are dataWidth bits wide, so the maximum
+   value of a true data element is 2**dataWidth-1.  We call that
+   max_dataVal.  The first byte in the stream tells you what dataWidth
+   is.
+
+   LZW codes 0 - max_dataVal are direct codes.  Each on represents
+   the true data element whose value is that of the LZW code itself.
+   No decompression is required.
+
+   max_dataVal + 1 and up are compression codes.  They encode
+   true data elements:
+
+   max_dataVal + 1 is the clear code.
+         
+   max_dataVal + 2 is the end code.
+
+   max_dataVal + 3 and up are string codes.  Each string code 
+   represents a string of true data elements.  The translation from a
+   string code to the string of true data elements varies as the stream
+   progresses.  In the beginning and after every clear code, the
+   translation table is empty, so no string codes are valid.  As the
+   stream progresses, the table gets filled and more string codes 
+   become valid.
 
-  Return -2 if there are no more bytes in the image.
 -----------------------------------------------------------------------------*/
-    static int      fresh = FALSE;
-    int             code, incode;
-    static int      codeSize, set_codeSize;
-    static int      max_code, max_codeSize;
-    static int      firstcode, oldcode;
-    static int      clear_code, end_code;
-    static int      table[2][(1<< MAX_LZW_BITS)];
-    static int      stack[(1<<(MAX_LZW_BITS))*2], *sp;
 
-    if (first) {
-        int    i;
 
-        set_codeSize = input_codeSize;
-        codeSize = set_codeSize+1;
-        clear_code = 1 << set_codeSize ;
-        end_code = clear_code + 1;
-        max_codeSize = 2*clear_code;
-        max_code = clear_code+2;
+struct decompressor {
+    struct stack stack;
+    int      fresh;
+        /* The stream is right after a clear code or at the very beginning */
+    int      codeSize;
+        /* The current code size -- each LZW code in this part of the image
+           is this many bits.  Ergo, we read this many bits at a time from
+           the stream.
+        */
+    int      maxnum_code;
+        /* The maximum number of LZW codes that can be represented with the 
+           current code size.  (1 << codeSize)
+        */
+    int      next_tableSlot;
+        /* Index in the code translation table of the next free entry */
+    int      firstcode;
+        /* This is always a true data element code */
+    int      prevcode;
+        /* The code just before, in the image, the one we're processing now */
+    int      table[2][(1 << MAX_LZW_BITS)];
 
-        getCode(ifP, 0, TRUE);
+    /* The following are constant for the life of the decompressor */
+    FILE * ifP;
+    int init_codeSize;
+    int max_dataVal;
+    int clear_code;
+    int end_code; 
+};
                
-        fresh = TRUE;
 
-        for (i = 0; i < clear_code; ++i) {
-            table[0][i] = 0;
-            table[1][i] = i;
-        }
-        for (; i < (1<<MAX_LZW_BITS); ++i)
-            table[0][i] = table[1][i] = 0;
 
-        sp = stack;
+static void
+resetDecompressor(struct decompressor * const decompP) {
 
-        return 0;
-    } else if (fresh) {
-        fresh = FALSE;
-        do {
-            firstcode = oldcode = getCode(ifP, codeSize, FALSE);
-        } while (firstcode == clear_code);
-        return firstcode;
+    decompP->codeSize = decompP->init_codeSize+1;
+    decompP->maxnum_code = 1 << decompP->codeSize;
+    decompP->next_tableSlot = decompP->max_dataVal + 3;
+    decompP->fresh = 1;
     }
 
-    if (sp > stack)
-        return *--sp;
 
-    while ((code = getCode(ifP, codeSize, FALSE)) >= 0) {
-        if (code == clear_code) {
-            int    i;
-            for (i = 0; i < clear_code; ++i) {
-                table[0][i] = 0;
-                table[1][i] = i;
-            }
-            for (; i < (1<<MAX_LZW_BITS); ++i)
-                table[0][i] = table[1][i] = 0;
-            codeSize = set_codeSize+1;
-            max_codeSize = 2*clear_code;
-            max_code = clear_code+2;
-            sp = stack;
-            firstcode = oldcode = getCode(ifP, codeSize, FALSE);
-            return firstcode;
-        } else if (code == end_code) {
-            if (zeroDataBlock)
-                return -2;
 
-            readThroughEod(ifP);
+static void
+lzwInit(struct decompressor * const decompP, 
+        FILE *                const ifP,
+        int                   const init_codeSize) {
                        
-            return -2;
+    if (verbose)
+        pm_message("Image says the initial compression code size is "
+                   "%d bits", 
+                   init_codeSize);
+    
+    decompP->ifP = ifP;
+    decompP->init_codeSize = init_codeSize;
+
+    decompP->max_dataVal = (1 << init_codeSize) - 1;
+    decompP->clear_code = decompP->max_dataVal + 1;
+    decompP->end_code = decompP->max_dataVal + 2;
+    
+    /* The entries in the translation table for true data codes are
+       constant throughout the stream.  We set them now and they never
+       change.
+    */
+    {
+        unsigned int i;
+        for (i = 0; i <= decompP->max_dataVal; ++i) {
+            decompP->table[0][i] = 0;
+            decompP->table[1][i] = i;
         }
+    }
+    resetDecompressor(decompP);
+
+    getCode(decompP->ifP, 0, TRUE);
 
-        incode = code;
+    decompP->fresh = TRUE;
 
-        if (code >= max_code) {
-            *sp++ = firstcode;
-            code = oldcode;
+    initStack(&decompP->stack, maxnum_lzwCode*2);
         }
 
-        while (code >= clear_code) {
-            *sp++ = table[1][code];
-            if (code == table[0][code])
-                pm_error("circular table entry BIG ERROR");
-            code = table[0][code];
+
+
+static void
+lzwTerm(struct decompressor * const decompP) {
+
+    termStack(&decompP->stack);
         }
 
-        *sp++ = firstcode = table[1][code];
 
-        if ((code = max_code) <(1<<MAX_LZW_BITS)) {
-            table[0][code] = oldcode;
-            table[1][code] = firstcode;
-            ++max_code;
-            if ((max_code >= max_codeSize) &&
-                (max_codeSize < (1<<MAX_LZW_BITS))) {
-                max_codeSize *= 2;
-                ++codeSize;
+
+static void
+expandCodeOntoStack(struct decompressor * const decompP,
+                    int                   const incode,
+                    bool *                const errorP) {
+/*----------------------------------------------------------------------------
+   'incode' is an LZW string code.  It represents a string of true data
+   elements, as defined by the string translation table in *decompP.
+
+   Expand the code to a string of LZW direct codes and push them onto the
+   stack such that the leftmost code is on top.
+
+   Also add to the translation table where appropriate.
+
+   Iff the translation table contains a cycle (which means the LZW stream
+   from which it was built is invalid), return *errorP == TRUE.
+-----------------------------------------------------------------------------*/
+    int code;
+    bool error;
+
+    error = FALSE;
+
+    if (incode < decompP->next_tableSlot) 
+        code = incode;
+    else {
+        /* It's a code that isn't in our translation table yet */
+        pushStack(&decompP->stack, decompP->firstcode);
+        code = decompP->prevcode;
             }
+
+    {
+        /* Get the whole string that this compression code
+           represents and push it onto the code stack so the
+           leftmost code is on top.  Set decompP->firstcode to the
+           first (leftmost) code in that string.
+            */
+
+        unsigned int stringCount;
+        stringCount = 0;
+
+        while (code > decompP->max_dataVal && !error) {
+            if (stringCount > maxnum_lzwCode) {
+                pm_message("Error in GIF image: contains LZW string loop");
+                error = TRUE;
+            } else {
+                ++stringCount;
+                pushStack(&decompP->stack, decompP->table[1][code]);
+                code = decompP->table[0][code];
+            }
+        }
+        decompP->firstcode = decompP->table[1][code];
+        pushStack(&decompP->stack, decompP->firstcode);
+    }
+
+    if (decompP->next_tableSlot < maxnum_lzwCode) {
+        decompP->table[0][decompP->next_tableSlot] = decompP->prevcode;
+        decompP->table[1][decompP->next_tableSlot] = decompP->firstcode;
+        ++decompP->next_tableSlot;
+        if (decompP->next_tableSlot >= decompP->maxnum_code) {
+            /* We've used up all the codes of the current code size.
+               Future codes in the stream will have codes one bit longer.
+               But there's an exception if we're already at the LZW
+               maximum, in which case the codes will simply continue
+               the same size.
+            */
+            if (decompP->codeSize < MAX_LZW_BITS) {
+                ++decompP->codeSize;
+                decompP->maxnum_code = 1 << decompP->codeSize;
+            }
+        }
+    }
+
+    decompP->prevcode = incode;
+    *errorP = error;
         }
 
-        oldcode = incode;
 
-        if (sp > stack)
-            return *--sp;
+
+static int
+lzwReadByte(struct decompressor * const decompP) {
+/*----------------------------------------------------------------------------
+  Return the next data element of the decompressed image.  In the context
+  of a GIF, a data element is the color table index of one pixel.
+
+  We read and return the next byte of the decompressed image:
+
+    Return -1 if we hit EOF prematurely (i.e. before an "end" code.  We
+    forgive the case that the "end" code is followed by EOF instead of
+    an EOD marker (zero length DataBlock)).
+
+    Return -2 if there are no more bytes in the image.
+
+    Return -3 if we encounter errors in the LZW stream.
+-----------------------------------------------------------------------------*/
+    int retval;
+
+    if (!stackIsEmpty(&decompP->stack)) 
+        retval = popStack(&decompP->stack);
+    else if (decompP->fresh) {
+        decompP->fresh = FALSE;
+        /* Read off all initial clear codes, read the first non-clear code,
+           and return it.  There are no strings in the table yet, so the next
+           code must be a direct true data code.
+        */
+        do {
+            decompP->firstcode =
+                getCode(decompP->ifP, decompP->codeSize, FALSE);
+            decompP->prevcode = decompP->firstcode;
+        } while (decompP->firstcode == decompP->clear_code);
+        if (decompP->firstcode == decompP->end_code) {
+            if (!zeroDataBlock)
+                readThroughEod(decompP->ifP);
+            retval = -2;
+        } else
+            retval = decompP->firstcode;
+    } else {
+        int code;
+        code = getCode(decompP->ifP, decompP->codeSize, FALSE);
+        if (code < 0)
+            retval = code;
+        else {
+            if (code == decompP->clear_code) {
+                resetDecompressor(decompP);
+                retval = lzwReadByte( decompP );
+            } else if (code == decompP->end_code) {
+                if (!zeroDataBlock)
+                    readThroughEod(decompP->ifP);
+                retval = -2;
+            } else {
+                bool error;
+                expandCodeOntoStack(decompP, code, &error);
+                if (error)
+                    retval = -3;
+                else
+                    retval = popStack(&decompP->stack);
     }
-    return code;
 }
+    }
+    return retval;
+}
+
+
+
+
 
 
 
@@ -557,11 +887,15 @@
               int    const len, 
               int    const height, 
               gifColorMap  cmap, 
-              bool   const interlace) {
+              unsigned int const cmapSize,
+              bool   const interlace,
+              int    const transparentIndex,
+              bit ** const alphabits) {
 
     unsigned char lzwMinCodeSize;      
     int v;
     int xpos, ypos, pass;
+    struct decompressor decomp;
 
     pass = 0;
     xpos = 0;
@@ -576,18 +910,29 @@
                  "right after an image separator; no "
                  "image data follows.");
 
-    if (lzwReadByte(ifP, TRUE, lzwMinCodeSize) < 0)
-        pm_error("GIF stream ends (or read error) right after the "
-                 "minimum lzw code size field; no image data follows.");
+    if (lzwMinCodeSize > MAX_LZW_BITS)
+        pm_error("Invalid minimum code size value in image data: %u.  "
+                 "Maximum allowable code size in GIF is %u", 
+                 lzwMinCodeSize, MAX_LZW_BITS);
+
+    lzwInit(&decomp, ifP, lzwMinCodeSize);
 
     if (verbose)
         pm_message("reading %d by %d%s GIF image",
                    len, height, interlace ? " interlaced" : "" );
 
-    while ((v = lzwReadByte(ifP,FALSE,lzwMinCodeSize)) >= 0 ) {
+    while ((v = lzwReadByte(&decomp)) >= 0 ) {
+        if (v >= cmapSize)
+            pm_error("Invalid color index %u in an image that has only "
+                     "%u colors in the color map.", v, cmapSize);
+
         PPM_ASSIGN(xels[ypos][xpos], 
                    cmap[CM_RED][v], cmap[CM_GRN][v], cmap[CM_BLU][v]);
 
+        if (alphabits) 
+            alphabits[ypos][xpos] = (v == transparentIndex) ? 
+                PBM_BLACK : PBM_WHITE;
+
         ++xpos;
         if (xpos == len) {
             xpos = 0;
@@ -602,7 +947,7 @@
                     ypos += 2; break;
                 }
                 
-                if (ypos >= height) {
+                while (ypos >= height) {
                     ++pass;
                     switch (pass) {
                     case 1:
@@ -622,11 +967,14 @@
         if (ypos >= height)
             break;
     }
-    
 fini:
-    if (lzwReadByte(ifP,FALSE,lzwMinCodeSize)>=0)
+    if (v == -3)
+        pm_error("Error in GIF input stream");
+    else {
+        if (lzwReadByte(&decomp)>=0)
         pm_message("too much input data, ignoring extra...");
-
+    }
+    lzwTerm(&decomp);
 }
 
 
@@ -635,6 +983,12 @@
 writePnm(FILE *outfile, xel ** const xels, 
          const int cols, const int rows,
          const int hasGray, const int hasColor) {
+/*----------------------------------------------------------------------------
+   Write a PNM image to the current position of file 'outfile' with
+   dimensions 'cols' x 'rows' and raster 'xels'.
+   
+   Make it PBM, PGM, or PBM according to 'hasGray' and 'hasColor'.
+-----------------------------------------------------------------------------*/
     int format;
     const char *format_name;
            
@@ -659,78 +1013,28 @@
 
 
 static void
-transparencyMessage(int const transparent_index, 
+transparencyMessage(int const transparentIndex, 
                     gifColorMap cmap) {
 /*----------------------------------------------------------------------------
    If user wants verbose output, tell him that the color with index
-   'transparent_index' is supposed to be a transparent background color.
+   'transparentIndex' is supposed to be a transparent background color.
    
-   If transparent_index == -1, tell him there is no transparent background
+   If transparentIndex == -1, tell him there is no transparent background
    color.
 -----------------------------------------------------------------------------*/
     if (verbose) {
-        if (transparent_index == -1)
+        if (transparentIndex == -1)
             pm_message("no transparency");
         else
             pm_message("transparent background color: rgb:%02x/%02x/%02x "
                        "Index %d",
-                       cmap[CM_RED][transparent_index],
-                       cmap[CM_GRN][transparent_index],
-                       cmap[CM_BLU][transparent_index],
-                       transparent_index
-                );
-    }
-}
-
-
-
-static void
-outputAlpha(FILE *alpha_file, pixel ** const xels, 
-            const int cols, const int rows, const int transparent_index,
-            unsigned char cmap[3][MAXCOLORMAPSIZE]) {
-/*----------------------------------------------------------------------------
-   Output to file 'alpha_file' (unless it is NULL) the alpha mask for the
-   image 'xels', given that the color whose index in the color map 'cmap' is
-   'transparent_index' is the transparent color.  The image, and thus the
-   alpha mask have dimensions 'cols' by 'rows'.
-
-   transparent_index == -1 means there are no transparent pixels.
------------------------------------------------------------------------------*/
-
-    if (alpha_file) {
-        bit *alpha_row;  /* malloc'ed */
-        xel transparent_color;
-
-        if (transparent_index != -1) 
-            PPM_ASSIGN(transparent_color, 
-                       cmap[CM_RED][transparent_index],
-                       cmap[CM_GRN][transparent_index],
-                       cmap[CM_BLU][transparent_index]
+                       cmap[CM_RED][transparentIndex],
+                       cmap[CM_GRN][transparentIndex],
+                       cmap[CM_BLU][transparentIndex],
+                       transparentIndex
                 );
-        
-        alpha_row = pbm_allocrow(cols);
-
-        pbm_writepbminit(alpha_file, cols, rows, FALSE);
-
-        {
-            int row;
-            for (row = 0; row < rows; row++) {
-                int col;
-                for (col = 0; col < cols; col++) {
-                    if (transparent_index != -1 && 
-                        PNM_EQUAL(xels[row][col], transparent_color))
-                        alpha_row[col] = PBM_BLACK;
-                    else 
-                        alpha_row[col] = PBM_WHITE;
                 }
-                pbm_writepbmrow(alpha_file, alpha_row, cols, FALSE);
             }
-        }
-        pbm_freerow(alpha_row);
-    }
-}
-
-
 
 static void
 readGifHeader(FILE * const gifFile, struct gifScreen * const gifScreenP) {
@@ -856,10 +1160,20 @@
              FILE *           const alphafile, 
              struct gifScreen       gifScreen,
              struct gif89     const gif89) {
+/*----------------------------------------------------------------------------
+   Read a single GIF image from the current position of file 'ifP'.
 
+   If 'skipIt' is TRUE, don't do anything else.  Otherwise, write the
+   image to the current position of files 'imageout_file' and 'alphafile'.
+   If 'alphafile' is NULL, though, don't write any alpha information.
+-----------------------------------------------------------------------------*/
     unsigned char buf[16];
     bool useGlobalColormap;
     xel **xels;  /* The image raster, in libpnm format */
+    bit **alphabits;  
+        /* The image alpha mask, in libpbm format.  NULL if we aren't computing
+           an alpha mask.
+        */
     int cols, rows;  /* Dimensions of the image */
     gifColorMap localColorMap;
     int localColorMapSize;
@@ -878,42 +1192,65 @@
     if (!xels)
         pm_error("couldn't alloc space for image" );
 
+    if (alphafile) {
+        alphabits = pbm_allocarray(cols, rows);
+        if (!alphabits)
+            pm_error("couldn't alloc space for alpha image" );
+    } else
+        alphabits = NULL;
+    
     if (! useGlobalColormap) {
         int hasGray, hasColor;
 
         readColorMap(ifP, localColorMapSize, localColorMap, 
                      &hasGray, &hasColor);
         transparencyMessage(gif89.transparent, localColorMap);
-        readImageData(ifP, xels, cols, rows, localColorMap, 
-                      BitSet(buf[8], INTERLACE));
+        readImageData(ifP, xels, cols, rows, localColorMap, localColorMapSize,
+                      BitSet(buf[8], INTERLACE), gif89.transparent,
+                      alphabits);
         if (!skipIt) {
             writePnm(imageout_file, xels, cols, rows,
                      hasGray, hasColor);
-            outputAlpha(alphafile, xels, cols, rows, 
-                        gif89.transparent, localColorMap);
         }
     } else {
         transparencyMessage(gif89.transparent, gifScreen.ColorMap);
-        readImageData(ifP, xels, cols, rows, gifScreen.ColorMap, 
-                      BitSet(buf[8], INTERLACE));
+        readImageData(ifP, xels, cols, rows,
+                      gifScreen.ColorMap, gifScreen.ColorMapSize,
+                      BitSet(buf[8], INTERLACE), gif89.transparent,
+                      alphabits);
         if (!skipIt) {
             writePnm(imageout_file, xels, cols, rows,
                      gifScreen.hasGray, gifScreen.hasColor);
-            outputAlpha(alphafile, xels, cols, rows, 
-                        gif89.transparent, gifScreen.ColorMap);
         }
     }
+
+    if (!skipIt && alphafile && alphabits)
+        pbm_writepbm(alphafile, alphabits, cols, rows, FALSE);
+
     pnm_freearray(xels, rows);
+    if (alphabits)
+        pbm_freearray(alphabits, rows);
 }
 
 
 
 static void
 convertImages(FILE * const ifP, 
+              bool   const allImages,
               int    const requestedImageSeq, 
               FILE * const imageout_file, 
               FILE * const alphafile) {
-
+/*----------------------------------------------------------------------------
+   Read a GIF stream from file 'ifP' and write one or more images from
+   it as PNM images to file 'imageout_file'.  If the images have transparency
+   and 'alphafile' is non-NULL, write PGM alpha masks to file 'alphafile'.
+
+   'allImages' means Caller wants all the images in the stream.  
+
+   'requestedImageSeq' is meaningful only when 'allImages' is FALSE.  It 
+   is the sequence number of the one image Caller wants from the stream,
+   with the first image being 0.
+-----------------------------------------------------------------------------*/
     int imageSeq;
         /* Sequence within GIF stream of image we are currently processing.
            First is 0.
@@ -933,16 +1270,18 @@
 
         if (eod) {
             /* GIF stream ends before image with sequence imageSeq */
-            if (imageSeq <= requestedImageSeq)
+            if (!allImages && (imageSeq <= requestedImageSeq))
                 pm_error("You requested Image %d, but "
                          "only %d image%s found in GIF stream",
                          requestedImageSeq+1,
                          imageSeq, imageSeq>1?"s":"" );
-        } else 
-            convertImage(ifP, imageSeq != requestedImageSeq, 
+        } else {
+            pm_message("Reading Image Sequence %d", imageSeq);
+            convertImage(ifP, !allImages && (imageSeq != requestedImageSeq), 
                          imageout_file, alphafile, gifScreen, gif89);
     }
 }
+}
 
 
 
@@ -961,20 +1300,18 @@
    
     ifP = pm_openr(cmdline.input_filespec);
 
-    if (cmdline.alpha_stdout)
-        alpha_file = stdout;
-    else if (cmdline.alpha_filename == NULL) 
+    if (cmdline.alpha_filename == NULL)
         alpha_file = NULL;
-    else {
+    else
         alpha_file = pm_openw(cmdline.alpha_filename);
-    }
 
-    if (cmdline.alpha_stdout) 
+    if (alpha_file && strcmp(cmdline.alpha_filename, "-") == 0)
         imageout_file = NULL;
     else
         imageout_file = stdout;
 
-    convertImages(ifP, cmdline.image_no-1, imageout_file, alpha_file);
+    convertImages(ifP, cmdline.all_images, cmdline.image_no, 
+                  imageout_file, alpha_file);
 
     pm_close(ifP);
     if (imageout_file != NULL) 
@@ -984,7 +1321,3 @@
 
     exit(0);
 }
-
-
-
-

Reply via email to