about summary refs log tree commit diff
path: root/converter/ppm/winicontoppm.c
diff options
context:
space:
mode:
Diffstat (limited to 'converter/ppm/winicontoppm.c')
-rw-r--r--converter/ppm/winicontoppm.c899
1 files changed, 899 insertions, 0 deletions
diff --git a/converter/ppm/winicontoppm.c b/converter/ppm/winicontoppm.c
new file mode 100644
index 00000000..2d9de567
--- /dev/null
+++ b/converter/ppm/winicontoppm.c
@@ -0,0 +1,899 @@
+/* winicontoppm.c - read a MS Windows .ico file and write portable pixmap(s)
+**
+** Copyright (C) 2000,2003 by Lee Benfield - lee@benf.org
+**
+** 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.  This software is provided "as is" without express or
+** implied warranty.
+**
+** Changes:
+** 
+** 03/2003 - Added 24+32 bpp capability.
+*/
+
+#define _BSD_SOURCE 1      /* Make sure strdup() is in string.h */
+#define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
+
+#include <math.h>
+#include <string.h>
+#include <assert.h>
+
+#include "ppm.h"
+#include "shhopt.h"
+#include "nstring.h"
+#include "mallocvar.h"
+#include "winico.h"
+
+#define MAJVERSION 0
+#define MINVERSION 4
+
+static int file_offset = 0;    /* not actually used, but useful for debug */
+static const char     er_read[] = "%s: read error";
+static const char *   infname;
+static FILE *   ifp;
+
+struct cmdlineInfo {
+    /* All the information the user supplied in the command line,
+       in a form easy for the program to use.
+    */
+    const char * inputFilespec;
+    const char * outputFilespec;
+    unsigned int allicons;
+    unsigned int bestqual;
+    unsigned int writeands;
+    unsigned int multippm;
+    unsigned int verbose;
+};
+
+
+
+
+static void
+parseCommandLine ( int argc, char ** argv,
+                   struct cmdlineInfo *cmdlineP ) {
+/*----------------------------------------------------------------------------
+   parse program command line described in Unix standard form by argc
+   and argv.  Return the information in the options as *cmdlineP.  
+
+   If command line is internally inconsistent (invalid options, etc.),
+   issue error message to stderr and abort program.
+
+   Note that the strings we return are stored in the storage that
+   was passed to us as the argv array.  We also trash *argv.
+-----------------------------------------------------------------------------*/
+    optEntry *option_def = malloc(100*sizeof(optEntry));
+        /* Instructions to optParseOptions3 on how to parse our options.
+         */
+    optStruct3 opt;
+
+    unsigned int option_def_index;
+
+    option_def_index = 0;   /* incremented by OPTENT3 */
+    OPTENT3(0, "allicons",     OPT_FLAG,   NULL,                  
+            &cmdlineP->allicons,       0 );
+    OPTENT3(0, "bestqual",     OPT_FLAG,   NULL,                  
+            &cmdlineP->bestqual,       0 );
+    OPTENT3(0, "writeands",    OPT_FLAG,   NULL,                  
+            &cmdlineP->writeands,      0 );
+    OPTENT3(0, "multippm",     OPT_FLAG,   NULL,                  
+            &cmdlineP->multippm,       0 );
+    OPTENT3(0, "verbose",      OPT_FLAG,   NULL,                  
+            &cmdlineP->verbose,        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 */
+
+    optParseOptions3( &argc, argv, opt, sizeof(opt), 0);
+        /* Uses and sets argc, argv, and some of *cmdlineP and others. */
+
+
+    if (argc-1 < 1) 
+        cmdlineP->inputFilespec = "-";
+    else
+        cmdlineP->inputFilespec = argv[1];
+
+    if (argc-1 < 2) {
+        cmdlineP->outputFilespec = "-";
+        
+        if (cmdlineP->writeands || cmdlineP->allicons)
+            pm_error("If you specify the -writeands or -allicons option, "
+                     "you must also specify an output file name argument.");
+    } else
+        cmdlineP->outputFilespec = argv[2];
+
+    if (argc-1 > 2)
+        pm_error("Too many arguments (%d).  Input filespec and "
+                 "output filespec are the only possible arguments.",
+                 argc-1);
+}
+
+
+
+
+static int 
+GetByte(void) {
+    int v;
+   
+    if ((v = getc(ifp)) == EOF)
+    {
+        pm_error(er_read, infname);
+    }
+   
+    return v;
+}
+   
+static short 
+GetShort(void) {
+    short v;
+   
+    if (pm_readlittleshort(ifp, &v) == -1)
+    {
+        pm_error(er_read, infname);
+    }
+   
+    return v;
+}
+   
+static long 
+GetLong(void) {
+    long v;
+   
+    if (pm_readlittlelong(ifp, &v) == -1)
+    {
+        pm_error(er_read, infname);
+    }
+   
+    return v;
+}
+   
+
+
+/*
+ * These have no purpose but to wrapper the Byte, Short & Long 
+ * functions.
+ */
+static u1 
+readU1 (void) {
+    file_offset++;
+    return GetByte();
+}
+
+static u1 * 
+readU1String (int length)
+{
+   
+    u1 * string;
+    
+    MALLOCARRAY(string, length + 1);
+    if (string == NULL)
+        pm_error("out of memory");
+
+    fread(string,sizeof(u1),length,ifp);
+    string[length] = 0;
+    file_offset += length * sizeof(u1);
+    return string;
+}
+
+static u2 
+readU2 (void) {
+    file_offset +=2;
+    return GetShort();
+}
+
+static u4 
+readU4 (void) {
+    file_offset += 4;
+    return GetLong();
+}
+
+static IC_Entry 
+readICEntry (void) 
+{
+    IC_Entry entry;
+
+    MALLOCVAR(entry);
+
+    if (entry == NULL)
+        pm_error("Unable to allcoate memory for IC entry");
+
+    entry->width         = readU1();
+    entry->height        = readU1();
+    entry->color_count   = readU1();
+    entry->reserved      = readU1();
+    entry->planes        = readU2();
+    entry->bitcount      = readU2();
+    entry->size_in_bytes = readU4();
+    entry->file_offset   = readU4();
+    entry->colors        = NULL;
+    entry->ih            = NULL;
+    entry->xorBitmap     = NULL;
+    entry->andBitmap     = NULL;
+    
+    return entry;
+}
+
+
+
+static IC_InfoHeader 
+readInfoHeader (IC_Entry entry) 
+{
+    IC_InfoHeader ih;
+
+    MALLOCVAR(ih);
+    
+    if (ih == NULL)
+        pm_error("Unable to allocate memory for info header");
+
+    ih->size            = readU4();
+    ih->width           = readU4();
+    ih->height          = readU4();
+    ih->planes          = readU2();
+    ih->bitcount        = readU2();
+    ih->compression     = readU4();
+    ih->imagesize       = readU4();
+    ih->x_pixels_per_m  = readU4();
+    ih->y_pixels_per_m  = readU4();
+    ih->colors_used     = readU4();
+    ih->colors_important = readU4();
+    
+    if (!entry->bitcount) entry->bitcount = ih->bitcount;
+    if (entry->color_count == 0 && 
+        entry->bitcount <= 8) entry->color_count = 256;
+    if (ih->compression) {
+        pm_error("Can't handle compressed icons");
+    }
+    return ih;
+}
+
+/*
+ * I don't know why this isn't the same as the spec, it just <b>isn't</b>
+ * The colors honestly seem to be stored BGR.  Bizarre.
+ * 
+ * I've checked this in the BMP code for bmptoppm and the gimp.  Guess the
+ * spec I have is just plain wrong.
+ */
+static IC_Color 
+readICColor (void) 
+{
+    IC_Color col;
+
+    MALLOCVAR(col);
+
+    if (col == NULL)
+        pm_error("Unable to allocate memory for color");
+
+    col->blue     = readU1();
+    col->green    = readU1();
+    col->red      = readU1();
+    col->reserved = readU1();
+    return col;
+}
+   
+
+
+/*
+ * Depending on if the image is stored as 1bpp, 4bpp or 8bpp, the 
+ * encoding mechanism is different.
+ * 
+ * 8bpp => 1 byte/palette index.
+ * 4bpp => High Nibble, Low Nibble
+ * 1bpp => 1 palette value per bit, high bit 1st.
+ */
+static u1 * 
+read1Bitmap (int width, int height) 
+{
+    int tmp;
+    int xBytes;
+    u1 * bitmap;
+    int wt = width;
+
+    MALLOCARRAY(bitmap, width * height);
+    if (bitmap == NULL)
+        pm_error("out of memory");
+
+    wt >>= 3;
+    if (wt & 3) {
+        wt = (wt & ~3) + 4;
+    }
+    xBytes = wt;
+    for (tmp = 0; tmp<height; tmp++ ) {
+        int x;
+        int rowByte = 0;
+        int xOrVal = 128;
+        u1 * row = readU1String(xBytes);
+        for (x = 0; x< width; x++) {
+            *(bitmap+((height-tmp-1)*width) + (x)) = 
+                (row[rowByte] & xOrVal) / xOrVal;
+            if (xOrVal == 1) {
+                xOrVal = 128;
+                rowByte++;
+            } else {
+                xOrVal >>= 1;
+            }
+        }
+        free(row);
+    }
+    return bitmap;
+}
+
+
+   
+static u1 * 
+read4Bitmap (int width, int height) 
+{
+    int tmp;
+    u1 * bitmap;
+
+    int wt = width;
+    int xBytes;
+
+    MALLOCARRAY(bitmap, width * height);
+    if (bitmap == NULL)
+        pm_error("out of memory");
+
+
+    wt >>= 1;
+    if (wt & 3) {
+        wt = (wt & ~3) + 4;
+    }
+    xBytes = wt;
+    for (tmp = 0; tmp<height ; tmp++ ) {
+        int rowByte = 0;
+        int bottom = 1;
+        int x;
+        u1 * row = readU1String(xBytes);
+        for (x = 0; x< width; x++) {
+            /*
+             * 2 nibbles, 2 values.
+             */
+            if (bottom) {
+                *(bitmap+((height-tmp-1)*width) + (x)) = 
+                    (row[rowByte] & 0xF0) >> 4;
+            } else {
+                *(bitmap+((height-tmp-1)*width) + (x)) = (row[rowByte] & 0xF);
+                rowByte++;
+            }
+            bottom = !bottom;
+        }
+    free(row);
+    }
+    return bitmap;
+}
+
+
+   
+static u1 * 
+read8Bitmap (int width, int height) 
+{
+    int tmp;
+    unsigned int xBytes;
+    unsigned int wt = width;
+    u1 * bitmap;
+   
+    MALLOCARRAY(bitmap, width * height);
+    if (bitmap == NULL)
+        pm_error("out of memory");
+
+    if (wt & 3) {
+        wt = (wt & ~3) + 4;
+    }
+    xBytes = wt;
+    for (tmp = 0; tmp<height ; tmp++ ) {
+        int rowByte = 0;
+        int x;
+        u1 * row = readU1String(xBytes);
+        for ( x = 0; x< width; x++) {
+            *(bitmap+((height-tmp-1)*width) + (x)) = row[rowByte];
+            rowByte++;
+        }
+        free(row);
+    }
+    return bitmap;
+}
+
+
+
+/*
+ * Read a true color bitmap. (24/32 bits)
+ * 
+ * The output routine deplanarizes it for us, we keep it flat here.
+ */
+static u1 *
+readXBitmap (int const width, 
+             int const height, 
+             int const bpp) {
+    int          const bytes  = bpp >> 3;
+    unsigned int const xBytes = width * bytes;
+
+    u1 * bitmap;
+        /* remember - bmp (dib) stored upside down, so reverse */
+
+    MALLOCARRAY(bitmap, bytes * width * height);
+    if (bitmap == NULL)
+        pm_error("out of memory allocating bitmap array");
+
+    {
+        unsigned int i;
+        u1 * bitcurptr;
+
+        for (i = 0, bitcurptr = &bitmap[bytes * width * (height-1)];
+             i < height; 
+             ++i, bitcurptr -= xBytes) {
+
+            u1 * const row = readU1String(xBytes);
+            memcpy(bitcurptr, row, xBytes);
+            free(row);
+        }
+    }
+    return bitmap;
+}
+
+
+
+static MS_Ico 
+readIconFile (bool const verbose) {
+    int iter,iter2;
+
+    MS_Ico MSIconData;
+
+    MALLOCVAR(MSIconData);
+   
+    /*
+     * reserved - should equal 0.
+     */
+    MSIconData->reserved = readU2();
+    /*
+     * Type - should equal 1
+     */
+    MSIconData->type     = readU2();
+    /*
+     * count - no of icons in file..
+     */
+    MSIconData->count    = readU2();
+    /*
+     * Allocate "count" array of entries.
+     */
+    if (verbose) 
+        pm_message("Icon file contains %d icons.", MSIconData->count);
+
+    MALLOCARRAY(MSIconData->entries, MSIconData->count);
+    if (MSIconData->entries == NULL)
+        pm_error("out of memory");
+    /*
+     * Read in each of the entries
+     */
+    for (iter = 0;iter < MSIconData->count ; iter++ ) {
+        MSIconData->entries[iter] = readICEntry();
+    }
+    /* After that, we have to read in the infoheader, color map (if
+     * any) and the actual bit/pix maps for the icons.  
+     */
+    if (verbose) 
+        fprintf (stderr,"#\tColors\tBPP\tWidth\tHeight\n");
+    for (iter = 0;iter < MSIconData->count ; iter++ ) {
+        int bpp;
+        MSIconData->entries[iter]->ih =
+            readInfoHeader (MSIconData->entries[iter]);
+       
+        /* What's the bits per pixel? */
+        bpp = MSIconData->entries[iter]->bitcount; 
+        /* Read the palette, if appropriate */
+        switch (bpp) {
+        case 24:
+        case 32:
+            /* 24/32 bpp icon has no palette */
+            break;
+        default:
+            MALLOCARRAY(MSIconData->entries[iter]->colors, 
+                        MSIconData->entries[iter]->color_count);
+            if (MSIconData->entries[iter]->colors == NULL)
+                pm_error("out of memory");
+
+            for (iter2 = 0;
+                 iter2 < MSIconData->entries[iter]->color_count ; 
+                 iter2++ ) {
+                MSIconData->entries[iter]->colors[iter2] = readICColor();
+            }
+            break;
+        }
+        if (verbose) {
+            char cols_text[10];
+            sprintf (cols_text, "%d", MSIconData->entries[iter]->color_count);
+            fprintf (stderr,
+                     "%d\t%s\t%d\t%d\t%d\n", iter,
+                     MSIconData->entries[iter]->color_count ? 
+                     cols_text : "TRUE",
+                     bpp, MSIconData->entries[iter]->width, 
+                     MSIconData->entries[iter]->height);
+        }
+        /* Pixels are stored bottom-up, left-to-right. Pixel lines are
+         * padded with zeros to end on a 32bit (4byte) boundary. Every
+         * line will have the same number of bytes. Color indices are
+         * zero based, meaning a pixel color of 0 represents the first
+         * color table entry, a pixel color of 255 (if there are that
+         * many) represents the 256th entry.  
+         * 
+         * 24+32 bit (16 is an abomination, which I'll avoid, and expect
+         * no-one to mind) are stored 1byte/plane with a spare (alpha?)
+         * byte for 32 bit.
+         */
+        {
+            /*
+             * Read XOR Bitmap
+             */
+            switch (bpp) {
+            case 1:
+                MSIconData->entries[iter]->xorBitmap = 
+                    read1Bitmap(MSIconData->entries[iter]->width,
+                                MSIconData->entries[iter]->height);
+                break;
+            case 4:
+                MSIconData->entries[iter]->xorBitmap = 
+                    read4Bitmap(MSIconData->entries[iter]->width,
+                                MSIconData->entries[iter]->height);
+                break;
+            case 8:
+                MSIconData->entries[iter]->xorBitmap = 
+                    read8Bitmap(MSIconData->entries[iter]->width,
+                                MSIconData->entries[iter]->height);
+                break;
+            case 24:
+            case 32:
+                MSIconData->entries[iter]->xorBitmap = 
+                    readXBitmap(MSIconData->entries[iter]->width,
+                                MSIconData->entries[iter]->height,bpp);
+                break;
+            default:
+                pm_error("Uncatered bit depth %d",bpp);
+            }
+            /*
+             * Read AND Bitmap
+             */
+            MSIconData->entries[iter]->andBitmap = 
+                read1Bitmap(MSIconData->entries[iter]->width,
+                            MSIconData->entries[iter]->height);
+        }
+      
+    }
+    return MSIconData;
+}
+
+
+
+static char * 
+trimOutputName(const char inputName[])
+{
+    /*
+     * Just trim off the final ".ppm", if there is one, else return as is.
+     * oh, for =~ ... :)
+     */
+    char * outFile = strdup(inputName);
+    if (STREQ(outFile + (strlen (outFile) - 4), ".ppm")) {
+        *(outFile + (strlen (outFile) - 4)) = 0;
+    }
+    return outFile;
+
+}
+
+
+
+static int 
+getBestQualityIcon(MS_Ico MSIconData)
+{
+    int x,best,best_size,best_bpp,bpp,size;
+    IC_Entry entry;
+
+    best_size = best_bpp = 0;
+    for (x = 0; x < MSIconData->count; x++) {
+        entry =  MSIconData->entries[x];
+        size = entry->width * entry->height;
+        bpp  = entry->bitcount ? entry->bitcount : entry->ih->bitcount;
+        if (size > best_size) {
+            best = x;
+            best_size = size;
+        } else if (size == best_size && bpp > best_bpp) {
+            best = x;
+            best_bpp = bpp;
+        }
+    }
+    return best;
+}
+
+static void
+writeXors(FILE *   const multiOutF,
+          char           outputFileBase[], 
+          IC_Entry const entry,
+          int      const entryNum,
+          bool     const multiple, 
+          bool     const xor) {
+/*----------------------------------------------------------------------------
+   Write an "xor" image (i.e. the main image) out.
+
+   'multiple' means this is one of multiple images that are being written.
+   'entryNum' is the sequence number within the winicon file of the image
+   we are writing.
+
+   'xor' means to include "xor" in the output file name.
+
+   if 'multiOutF' is non-null, it is the stream descriptor of an open
+   stream to which we are to write the image.  If it is null, 
+   we are to open a file using outputFileBase[] and 'entryNum' and 'xor'
+   to derive its name, and close it afterward.
+-----------------------------------------------------------------------------*/
+    FILE * outF;
+    pixel ** ppm_array;
+    int row;
+    int pel_size;
+    const char *outputFile;
+    int maxval;
+    int forcetext;
+
+    if (multiOutF) {
+        outF = multiOutF;
+        outputFile = strdup("");
+    } else {
+        if (outputFileBase) {
+            if (multiple) {
+                asprintfN(&outputFile, "%s%s_%d.ppm",
+                          outputFileBase,(xor ? "_xor" : ""), entryNum);
+            } else { 
+                asprintfN(&outputFile, "%s%s.ppm",
+                          outputFileBase,(xor ? "_xor" : ""));
+            }
+        } else
+            outputFile = strdup("-");
+        
+        outF = pm_openw(outputFile);
+    }
+    /* 
+     * allocate an array to save the bmp data into.
+     * note that entry->height will be 1/2 entry->ih->height,
+     * as the latter adds "and" and "xor" height.
+     */
+    ppm_array = ppm_allocarray(entry->width, entry->height);
+    for (row=0; row < entry->height; row++) {
+        u1 * xorRow;
+        int col;
+        switch (entry->bitcount) {
+        case 24:
+        case 32:
+            pel_size = entry->bitcount >> 3;
+            xorRow = entry->xorBitmap + row * entry->width * pel_size;
+            for (col=0; col < entry->width*pel_size;col+=pel_size) {
+                PPM_ASSIGN(ppm_array[row][col/pel_size],
+                           xorRow[col+2],xorRow[col+1],xorRow[col]);
+            }
+            break;
+        default:
+            xorRow = entry->xorBitmap + row * entry->width;
+            for (col=0; col < entry->width; col++) {
+                int colorIndex;
+                IC_Color color;
+                colorIndex  = xorRow[col];
+                color = entry->colors[colorIndex];
+                PPM_ASSIGN(ppm_array[row][col],
+                           color->red,color->green,color->blue);
+            }
+            break;
+        }
+    }    
+    
+    maxval = 255;
+    forcetext = 0;
+
+    ppm_writeppm(outF,ppm_array,entry->width, entry->height, 
+                 (pixval) maxval, forcetext);
+    ppm_freearray(ppm_array,entry->height);
+
+    strfree(outputFile);
+    
+    if (!multiOutF) 
+        pm_close(outF);
+}
+            
+
+
+static void
+writeAnds(FILE * const multiOutF, 
+          char outputFileBase[], IC_Entry const entry, int const entryNum, 
+          bool multiple) {
+/*----------------------------------------------------------------------------
+   Write the "and" image (i.e. the alpha mask) of the image 'IC_Entry' out.
+
+   'multiple' means this is one of multiple images that are being written.
+   'entryNum' is the sequence number within the winicon file of the image
+   we are writing.
+
+   if 'multiOutF' is non-null, it is the stream descriptor of an open
+   stream to which we are to write the image.  If it is null, 
+   we are to open a file using outputFileBase[] and 'entryNum' and 'xor'
+   to derive its name, and close it afterward.
+-----------------------------------------------------------------------------*/
+    FILE * outF;
+    bit ** pbm_array;
+    u1 * andRow;
+    int row;
+
+    if (multiOutF)
+        outF = multiOutF;
+    else {
+        const char *outputFile;
+
+        assert(outputFileBase);
+
+        if (multiple) 
+            asprintfN(&outputFile, "%s_and_%d.pbm", outputFileBase, entryNum);
+        else 
+            asprintfN(&outputFile, "%s_and.pbm", outputFileBase);
+        outF = pm_openw(outputFile);
+        strfree(outputFile);
+    }
+    pbm_array = pbm_allocarray(entry->width, entry->height);
+    for (row=0; row < entry->height; row++) {
+        int col;
+        andRow = entry->andBitmap + row * entry->width;
+        for (col=0; col < entry->width; col++) {
+            /* Note: black is transparent in a Netpbm alpha mask */
+            pbm_array[row][col] = andRow[col] ? PBM_BLACK: PBM_WHITE;
+        }
+    }
+
+    pbm_writepbm(outF, pbm_array, entry->width, entry->height, 0);
+
+    pbm_freearray(pbm_array, entry->height);
+    if (!multiOutF)
+        pm_close (outF);     
+}
+
+
+
+static void
+openMultiXor(char          outputFileBase[], 
+             bool    const writeands,
+             FILE ** const multiOutFP) {
+
+    const char *outputFile;
+
+    if (outputFileBase) {
+        asprintfN(&outputFile, "%s%s.ppm",
+                  outputFileBase, (writeands ? "_xor" : ""));
+    } else
+        outputFile = strdup("-");
+
+    /*
+     * Open the output file now, it'll stay open the whole time.
+     */
+    *multiOutFP = pm_openw(outputFile);
+
+    strfree(outputFile);
+}
+
+
+
+static void
+openMultiAnd(char outputFileBase[], FILE ** const multiAndOutFP) {
+
+    const char *outputFile;
+
+    assert(outputFileBase);
+
+    asprintfN(&outputFile, "%s_and.pbm", outputFileBase);
+    
+    *multiAndOutFP = pm_openw(outputFile);
+
+    strfree(outputFile);
+}
+
+static void free_iconentry(IC_Entry entry) {
+    int x;
+    if (entry->colors && entry->color_count) {
+        for (x=0;x<entry->color_count;x++) free(entry->colors[x]);
+        free(entry->colors);
+    }
+    if (entry->andBitmap) free(entry->andBitmap);
+    if (entry->xorBitmap) free(entry->xorBitmap);
+    if (entry->ih) free(entry->ih);
+    free(entry);
+}
+
+static void free_icondata(MS_Ico MSIconData)
+{
+    int x;
+    for (x=0;x<MSIconData->count;x++) {
+    free_iconentry(MSIconData->entries[x]);
+    }
+    free(MSIconData);
+}
+
+
+int 
+main(int argc, char *argv[]) {
+
+    struct cmdlineInfo cmdline;
+    int startEntry, endEntry;
+    MS_Ico MSIconData;
+    char * outputFileBase;
+    FILE * multiOutF;
+    FILE * multiAndOutF;
+   
+    ppm_init (&argc, argv);
+
+    parseCommandLine(argc, argv, &cmdline);
+
+    if (cmdline.bestqual && cmdline.allicons)
+        pm_message("-bestqual doesn't make sense with -allicons.  "
+                   "Ignoring -bestqual.");
+   
+    if (STREQ(cmdline.outputFilespec, "-"))
+        outputFileBase = NULL;
+    else
+        outputFileBase = trimOutputName(cmdline.outputFilespec);
+
+    ifp = pm_openr(cmdline.inputFilespec);
+
+    infname = cmdline.inputFilespec;
+
+    MSIconData = readIconFile(cmdline.verbose);
+    /*
+     * Now we've read the icon file in (Hopefully! :)
+     * Go through each of the entries, and write out files of the
+     * form
+     * 
+     * fname_0_xor.ppm
+     * fname_0_and.ppm
+     * 
+     * (or to stdout, depending on args parsing above).
+     */
+    /*
+     * If allicons is set, we want everything, if not, just go through once.
+     */
+    startEntry = 0;
+    if (cmdline.allicons) {
+        endEntry = MSIconData->count;
+    } else {
+        endEntry = 1;
+    }
+    /*
+     * If bestqual is set, find the icon with highest size & bpp.
+     */
+    if (cmdline.bestqual) {
+        startEntry = getBestQualityIcon(MSIconData);
+        endEntry = startEntry+1;
+    }
+   
+    if (cmdline.multippm) 
+        openMultiXor(outputFileBase, cmdline.writeands, &multiOutF);
+    else
+        multiOutF = NULL;
+
+    if (cmdline.writeands && cmdline.multippm) 
+        openMultiAnd(outputFileBase, &multiAndOutF);
+    else
+        multiAndOutF = NULL;
+
+    {
+        int entryNum;
+
+        for (entryNum = startEntry ; entryNum < endEntry ; entryNum++ ) {
+            IC_Entry const entry = MSIconData->entries[entryNum];
+
+            writeXors(multiOutF, outputFileBase, entry, entryNum, 
+                      cmdline.allicons, cmdline.writeands);
+            if (cmdline.writeands)
+                writeAnds(multiAndOutF, outputFileBase, 
+                          entry, entryNum, cmdline.allicons);
+        }
+    }
+    if (multiOutF)
+        pm_close (multiOutF);    
+    if (multiAndOutF)
+        pm_close(multiAndOutF);
+    
+    /* free up the image data here. */
+    free_icondata(MSIconData);
+    return 0;
+}