/*
 * This file contains most of the source files of Xpm, concatenated and with
 * the public names changed (to have an Xpm prefix).
 *
 * $Id: Xpm.c,v 1.10 2002/02/25 13:17:19 amai Exp $
 */
/*
 * Copyright (C) 1989-95 GROUPE BULL
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * GROUPE BULL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * Except as contained in this notice, the name of GROUPE BULL shall not be
 * used in advertising or otherwise to promote the sale, use or other dealings
 * in this Software without prior written authorization from GROUPE BULL.
 */

#include <LTconfig.h>

/* amai: we can't include DebugUtil.h before system headers.
   So we assume that system headers are idempotent, #include
   all of those used below here and then try with our
   DebugUtil.h! */
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#if defined(FOR_MSW) || defined(WIN32)
#include <io.h>
#endif

#include <X11/Intrinsic.h> /* Avoid re-definition of Pixel-type */

#include <Xm/XpmP.h>
#include <XmI/XpmI.h>

/* #if !defined(WITH_DBMALLOC) && !defined(WITH_DMALLOC) */
#include <XmI/DebugUtil.h>

/*****************************************************************************\
* Attrib.c:                                                                   *
*                                                                             *
*  XPM library                                                                *
*  Functions related to the XpmAttributes structure                        *
*                                                                             *
*  Developed by Arnaud Le Hors                                                *
\*****************************************************************************/

#include <stdio.h>
#include <string.h>

#include <Xm/XpmP.h>
#include <XmI/XpmI.h>

void XpmFreeXpmImage(XpmImage *);
void XpmFreeXpmInfo(XpmInfo *);
int xpmParseDataAndCreate(Display *display, xpmData *data, XImage **image_return,
	XImage **shapeimage_return, XpmImage *image, XpmInfo *info,
	XpmAttributes *attributes);

/* 3.2 backward compatibility code */
LFUNC(CreateOldColorTable, int, (XpmColor *ct, int ncolors,
				 XpmColor ***oldct));

LFUNC(FreeOldColorTable, void, (XpmColor **colorTable, int ncolors));

/*
 * Create a colortable compatible with the old style colortable
 */
static int
CreateOldColorTable(ct, ncolors, oldct)
    XpmColor *ct;
    int ncolors;
    XpmColor ***oldct;
{
    XpmColor **colorTable, **color;
    int a;

    colorTable = (XpmColor **) XpmMalloc(ncolors * sizeof(XpmColor *));
    if (!colorTable) {
	*oldct = NULL;
	return (XpmNoMemory);
    }
    for (a = 0, color = colorTable; a < ncolors; a++, color++, ct++)
	*color = ct;
    *oldct = colorTable;
    return (XpmSuccess);
}

static void
FreeOldColorTable(colorTable, ncolors)
    XpmColor **colorTable;
    int ncolors;
{
    int a, b;
    XpmColor **color;
    char **sptr;

    if (colorTable) {
	for (a = 0, color = colorTable; a < ncolors; a++, color++) {
	    for (b = 0, sptr = (char **) *color; b <= NKEYS; b++, sptr++)
		if (*sptr)
		    XpmFree(*sptr);
	}
	XpmFree(*colorTable);
	XpmFree(colorTable);
    }
}

/* end 3.2 bc */

/*
 * Free the computed color table
 */
void
xpmFreeColorTable(colorTable, ncolors)
    XpmColor *colorTable;
    int ncolors;
{
    int a, b;
    XpmColor *color;
    char **sptr;

    if (colorTable) {
	for (a = 0, color = colorTable; a < ncolors; a++, color++) {
	    for (b = 0, sptr = (char **) color; b <= NKEYS; b++, sptr++)
		if (*sptr)
		    XpmFree(*sptr);
	}
	XpmFree(colorTable);
    }
}

/*
 * Free array of extensions
 */
void
XpmFreeExtensions(extensions, nextensions)
    XpmExtension *extensions;
    int nextensions;
{
    unsigned int i, j, nlines;
    XpmExtension *ext;
    char **sptr;

    if (extensions) {
	for (i = 0, ext = extensions; i < nextensions; i++, ext++) {
	    if (ext->name)
		XpmFree(ext->name);
	    nlines = ext->nlines;
	    for (j = 0, sptr = ext->lines; j < nlines; j++, sptr++)
		if (*sptr)
		    XpmFree(*sptr);
	    if (ext->lines)
		XpmFree(ext->lines);
	}
	XpmFree(extensions);
    }
}

/*
 * Return the XpmAttributes structure size
 */

int
XpmAttributesSize(void)
{
    return sizeof(XpmAttributes);
}

/*
 * Init returned data to free safely later on
 */
void
xpmInitAttributes(attributes)
    XpmAttributes *attributes;
{
    if (attributes) {
	attributes->pixels = NULL;
	attributes->npixels = 0;
	attributes->colorTable = NULL;
	attributes->ncolors = 0;
/* 3.2 backward compatibility code */
	attributes->hints_cmt = NULL;
	attributes->colors_cmt = NULL;
	attributes->pixels_cmt = NULL;
/* end 3.2 bc */
	if (attributes->valuemask & XpmReturnExtensions) {
	    attributes->extensions = NULL;
	    attributes->nextensions = 0;
	}
	if (attributes->valuemask & XpmReturnAllocPixels) {
	    attributes->alloc_pixels = NULL;
	    attributes->nalloc_pixels = 0;
	}
    }
}

/*
 * Fill in the XpmAttributes with the XpmImage and the XpmInfo
 */
void
xpmSetAttributes(attributes, image, info)
    XpmAttributes *attributes;
    XpmImage *image;
    XpmInfo *info;
{
    if (attributes->valuemask & XpmReturnColorTable) {
	attributes->colorTable = image->colorTable;
	attributes->ncolors = image->ncolors;

	/* avoid deletion of copied data */
	image->ncolors = 0;
	image->colorTable = NULL;
    }
/* 3.2 backward compatibility code */
    else if (attributes->valuemask & XpmReturnInfos) {
	int ErrorStatus;

	ErrorStatus = CreateOldColorTable(image->colorTable, image->ncolors,
					  (XpmColor ***)
					  &attributes->colorTable);

	/* if error just say we can't return requested data */
	if (ErrorStatus != XpmSuccess) {
	    attributes->valuemask &= ~XpmReturnInfos;
	    if (!(attributes->valuemask & XpmReturnPixels)) {
		XpmFree(attributes->pixels);
		attributes->pixels = NULL;
		attributes->npixels = 0;
	    }
	    attributes->ncolors = 0;
	} else {
	    attributes->ncolors = image->ncolors;
	    attributes->hints_cmt = info->hints_cmt;
	    attributes->colors_cmt = info->colors_cmt;
	    attributes->pixels_cmt = info->pixels_cmt;

	    /* avoid deletion of copied data */
	    image->ncolors = 0;
	    image->colorTable = NULL;
	    info->hints_cmt = NULL;
	    info->colors_cmt = NULL;
	    info->pixels_cmt = NULL;
	}
    }
/* end 3.2 bc */
    if (attributes->valuemask & XpmReturnExtensions) {
	attributes->extensions = info->extensions;
	attributes->nextensions = info->nextensions;

	/* avoid deletion of copied data */
	info->extensions = NULL;
	info->nextensions = 0;
    }
    if (info->valuemask & XpmHotspot) {
	attributes->valuemask |= XpmHotspot;
	attributes->x_hotspot = info->x_hotspot;
	attributes->y_hotspot = info->y_hotspot;
    }
    attributes->valuemask |= XpmCharsPerPixel;
    attributes->cpp = image->cpp;
    attributes->valuemask |= XpmSize;
    attributes->width = image->width;
    attributes->height = image->height;
}

/*
 * Free the XpmAttributes structure members
 * but the structure itself
 */
void
XpmFreeAttributes(attributes)
    XpmAttributes *attributes;
{
    if (attributes->valuemask & XpmReturnPixels && attributes->npixels) {
	XpmFree(attributes->pixels);
	attributes->pixels = NULL;
	attributes->npixels = 0;
    }
    if (attributes->valuemask & XpmReturnColorTable) {
	xpmFreeColorTable(attributes->colorTable, attributes->ncolors);
	attributes->colorTable = NULL;
	attributes->ncolors = 0;
    }
/* 3.2 backward compatibility code */
    else if (attributes->valuemask & XpmInfos) {
	if (attributes->colorTable) {
	    FreeOldColorTable((XpmColor **) attributes->colorTable,
			      attributes->ncolors);
	    attributes->colorTable = NULL;
	    attributes->ncolors = 0;
	}
	if (attributes->hints_cmt) {
	    XpmFree(attributes->hints_cmt);
	    attributes->hints_cmt = NULL;
	}
	if (attributes->colors_cmt) {
	    XpmFree(attributes->colors_cmt);
	    attributes->colors_cmt = NULL;
	}
	if (attributes->pixels_cmt) {
	    XpmFree(attributes->pixels_cmt);
	    attributes->pixels_cmt = NULL;
	}
	if (attributes->pixels) {
	    XpmFree(attributes->pixels);
	    attributes->pixels = NULL;
	    attributes->npixels = 0;
	}
    }
/* end 3.2 bc */
    if (attributes->valuemask & XpmReturnExtensions
	&& attributes->nextensions) {
	XpmFreeExtensions(attributes->extensions, attributes->nextensions);
	attributes->extensions = NULL;
	attributes->nextensions = 0;
    }
    if (attributes->valuemask & XpmReturnAllocPixels
	&& attributes->nalloc_pixels) {
	XpmFree(attributes->alloc_pixels);
	attributes->alloc_pixels = NULL;
	attributes->nalloc_pixels = 0;
    }
    attributes->valuemask = 0;
}

/*****************************************************************************\
*  CrBufFrP.c:                                                                *
*                                                                             *
*  XPM library                                                                *
*  Scan a pixmap and possibly its mask and create an XPM buffer               *
*                                                                             *
*  Developed by Arnaud Le Hors                                                *
\*****************************************************************************/

int
XpmCreateBufferFromPixmap(display, buffer_return, pixmap, shapemask,
			  attributes)
    Display *display;
    char **buffer_return;
    Pixmap pixmap;
    Pixmap shapemask;
    XpmAttributes *attributes;
{
    XImage *ximage = NULL;
    XImage *shapeimage = NULL;
    unsigned int width = 0;
    unsigned int height = 0;
    int ErrorStatus;

    /* get geometry */
    if (attributes && attributes->valuemask & XpmSize) {
	width = attributes->width;
	height = attributes->height;
    }
    /* get the ximages */
    if (pixmap)
	xpmCreateImageFromPixmap(display, pixmap, &ximage, &width, &height);
    if (shapemask)
	xpmCreateImageFromPixmap(display, shapemask, &shapeimage,
				 &width, &height);

    /* create the buffer */
    ErrorStatus = XpmCreateBufferFromImage(display, buffer_return, ximage,
					   shapeimage, attributes);

    /* destroy the ximages */
    if (ximage)
	XDestroyImage(ximage);
    if (shapeimage)
	XDestroyImage(shapeimage);

    return (ErrorStatus);
}

/*****************************************************************************\
*  CrDataFP.c:                                                                *
*                                                                             *
*  XPM library                                                                *
*  Scan a pixmap and possibly its mask and create an XPM array                *
*                                                                             *
*  Developed by Arnaud Le Hors                                                *
\*****************************************************************************/

int
XpmCreateDataFromPixmap(display, data_return, pixmap, shapemask, attributes)
    Display *display;
    char ***data_return;
    Pixmap pixmap;
    Pixmap shapemask;
    XpmAttributes *attributes;
{
    XImage *ximage = NULL;
    XImage *shapeimage = NULL;
    unsigned int width = 0;
    unsigned int height = 0;
    int ErrorStatus;

    /* get geometry */
    if (attributes && attributes->valuemask & XpmSize) {
	width = attributes->width;
	height = attributes->height;
    }
    /* get the ximages */
    if (pixmap)
	xpmCreateImageFromPixmap(display, pixmap, &ximage, &width, &height);
    if (shapemask)
	xpmCreateImageFromPixmap(display, shapemask, &shapeimage,
				 &width, &height);

    /* create the data */
    ErrorStatus = XpmCreateDataFromImage(display, data_return, ximage,
					 shapeimage, attributes);

    /* destroy the ximages */
    if (ximage)
	XDestroyImage(ximage);
    if (shapeimage)
	XDestroyImage(shapeimage);

    return (ErrorStatus);
}

/*
 * Set the XpmInfo valuemask to retrieve required info
 */
void
xpmSetInfoMask(XpmInfo *info, XpmAttributes *attributes)
{
    info->valuemask = 0;
    if (attributes->valuemask & XpmReturnInfos)
	info->valuemask |= XpmReturnComments;
    if (attributes->valuemask & XpmReturnExtensions)
	info->valuemask |= XpmReturnExtensions;
}

/*****************************************************************************\
*  CrIFrBuf.c:                                                                *
*                                                                             *
*  XPM library                                                                *
*  Parse an Xpm buffer (file in memory) and create the image and possibly its *
*  mask                                                                       *
*  Developed by Arnaud Le Hors                                                *
\*****************************************************************************/

LFUNC(OpenBuffer, void, (char *buffer, xpmData *mdata));

int
XpmCreateImageFromBuffer(display, buffer, image_return,
			 shapeimage_return, attributes)
    Display *display;
    char *buffer;
    XImage **image_return;
    XImage **shapeimage_return;
    XpmAttributes *attributes;
{
    XpmImage image;
    XpmInfo info;
    int ErrorStatus;
    xpmData mdata;

    xpmInitXpmImage(&image);
    xpmInitXpmInfo(&info);

    /* open buffer to read */
    OpenBuffer(buffer, &mdata);

    /* create the XImage from the XpmData */
    if (attributes) {
	xpmInitAttributes(attributes);
	xpmSetInfoMask(&info, attributes);
	ErrorStatus = xpmParseDataAndCreate(display, &mdata,
					    image_return, shapeimage_return,
					    &image, &info, attributes);
    } else
	ErrorStatus = xpmParseDataAndCreate(display, &mdata,
					    image_return, shapeimage_return,
					    &image, NULL, attributes);
    if (attributes) {
	if (ErrorStatus >= 0)		/* no fatal error */
	    xpmSetAttributes(attributes, &image, &info);
	XpmFreeXpmInfo(&info);
    }

    /* free the XpmImage */
    XpmFreeXpmImage(&image);

    return (ErrorStatus);
}

int
XpmCreateXpmImageFromBuffer(buffer, image, info)
    char *buffer;
    XpmImage *image;
    XpmInfo *info;
{
    xpmData mdata;
    int ErrorStatus;

    /* init returned values */
    xpmInitXpmImage(image);
    xpmInitXpmInfo(info);

    /* open buffer to read */
    OpenBuffer(buffer, &mdata);

    /* create the XpmImage from the XpmData */
    ErrorStatus = xpmParseData(&mdata, image, info);

    return (ErrorStatus);
}

/*
 * open the given buffer to be read or written as an xpmData which is returned
 */
static void
OpenBuffer(buffer, mdata)
    char *buffer;
    xpmData *mdata;
{
    mdata->type = XPMBUFFER;
    mdata->cptr = buffer;
    mdata->CommentLength = 0;
}

/*****************************************************************************\
*  CrIFrData.c:                                                               *
*                                                                             *
*  XPM library                                                                *
*  Parse an Xpm array and create the image and possibly its mask              *
*                                                                             *
*  Developed by Arnaud Le Hors                                                *
\*****************************************************************************/

LFUNC(OpenArray, void, (char **data, xpmData *mdata));

int
XpmCreateImageFromData(display, data, image_return,
		       shapeimage_return, attributes)
    Display *display;
    char **data;
    XImage **image_return;
    XImage **shapeimage_return;
    XpmAttributes *attributes;
{
    XpmImage image;
    XpmInfo info;
    int ErrorStatus;
    xpmData mdata;

    xpmInitXpmImage(&image);
    xpmInitXpmInfo(&info);

    /* open data */
    OpenArray(data, &mdata);

    /* create an XpmImage from the file */
    if (attributes) {
	xpmInitAttributes(attributes);
	xpmSetInfoMask(&info, attributes);
	ErrorStatus = xpmParseDataAndCreate(display, &mdata,
					    image_return, shapeimage_return,
					    &image, &info, attributes);
    } else
	ErrorStatus = xpmParseDataAndCreate(display, &mdata,
					    image_return, shapeimage_return,
					    &image, NULL, attributes);
    if (attributes) {
	if (ErrorStatus >= 0)		/* no fatal error */
	    xpmSetAttributes(attributes, &image, &info);
	XpmFreeXpmInfo(&info);
    }

    /* free the XpmImage */
    XpmFreeXpmImage(&image);

    return (ErrorStatus);
}

int
XpmCreateXpmImageFromData(data, image, info)
    char **data;
    XpmImage *image;
    XpmInfo *info;
{
    xpmData mdata;
    int ErrorStatus;

    /* init returned values */
    xpmInitXpmImage(image);
    xpmInitXpmInfo(info);

    /* open data */
    OpenArray(data, &mdata);

    /* create the XpmImage from the XpmData */
    ErrorStatus = xpmParseData(&mdata, image, info);

    return (ErrorStatus);
}

/*
 * open the given array to be read or written as an xpmData which is returned
 */
static void
OpenArray(data, mdata)
    char **data;
    xpmData *mdata;
{
    mdata->type = XPMARRAY;
    mdata->stream.data = data;
    mdata->cptr = *data;
    mdata->line = 0;
    mdata->CommentLength = 0;
    mdata->Bcmt = mdata->Ecmt = NULL;
    mdata->Bos = mdata->Eos = '\0';
    mdata->format = 0;			/* this can only be Xpm 2 or 3 */
}

/*****************************************************************************\
*  CrIFrP.c:                                                                  *
*                                                                             *
*  XPM library                                                                *
*  Create the XImage related to the given Pixmap.                             *
*                                                                             *
*  Developed by Arnaud Le Hors                                                *
\*****************************************************************************/

void
xpmCreateImageFromPixmap(display, pixmap, ximage_return, width, height)
    Display *display;
    Pixmap pixmap;
    XImage **ximage_return;
    unsigned int *width;
    unsigned int *height;
{
    unsigned int dum;
    int dummy;
    Window win;

    if (*width == 0 && *height == 0)
	XGetGeometry(display, pixmap, &win, &dummy, &dummy,
		     width, height, &dum, &dum);

    *ximage_return = XGetImage(display, pixmap, 0, 0, *width, *height,
			       AllPlanes, ZPixmap);
}

/*****************************************************************************\
*  CrPFrBuf.c:                                                                *
*                                                                             *
*  XPM library                                                                *
*  Parse an Xpm buffer and create the pixmap and possibly its mask            *
*                                                                             *
*  Developed by Arnaud Le Hors                                                *
\*****************************************************************************/

int
XpmCreatePixmapFromBuffer(display, d, buffer, pixmap_return,
			  shapemask_return, attributes)
    Display *display;
    Drawable d;
    char *buffer;
    Pixmap *pixmap_return;
    Pixmap *shapemask_return;
    XpmAttributes *attributes;
{
    XImage *ximage, *shapeimage;
    int ErrorStatus;

    /* initialize return values */
    if (pixmap_return)
	*pixmap_return = 0;
    if (shapemask_return)
	*shapemask_return = 0;

    /* create the images */
    ErrorStatus = XpmCreateImageFromBuffer(display, buffer,
					   (pixmap_return ? &ximage : NULL),
					   (shapemask_return ?
					    &shapeimage : NULL),
					   attributes);

    if (ErrorStatus < 0)		/* fatal error */
	return (ErrorStatus);

    /* create the pixmaps and destroy images */
    if (pixmap_return && ximage) {
	xpmCreatePixmapFromImage(display, d, ximage, pixmap_return);
	XDestroyImage(ximage);
    }
    if (shapemask_return && shapeimage) {
	xpmCreatePixmapFromImage(display, d, shapeimage, shapemask_return);
	XDestroyImage(shapeimage);
    }
    return (ErrorStatus);
}

/*****************************************************************************\
*  CrPFrData.c:                                                               *
*                                                                             *
*  XPM library                                                                *
*  Parse an Xpm array and create the pixmap and possibly its mask             *
*                                                                             *
*  Developed by Arnaud Le Hors                                                *
\*****************************************************************************/

int
XpmCreatePixmapFromData(display, d, data, pixmap_return,
			shapemask_return, attributes)
    Display *display;
    Drawable d;
    char **data;
    Pixmap *pixmap_return;
    Pixmap *shapemask_return;
    XpmAttributes *attributes;
{
    XImage *ximage, *shapeimage;
    int ErrorStatus;

    /* initialize return values */
    if (pixmap_return)
	*pixmap_return = 0;
    if (shapemask_return)
	*shapemask_return = 0;

    /* create the images */
    ErrorStatus = XpmCreateImageFromData(display, data,
					 (pixmap_return ? &ximage : NULL),
					 (shapemask_return ?
					  &shapeimage : NULL),
					 attributes);

    if (ErrorStatus != XpmSuccess)
	return (ErrorStatus);

    if (ErrorStatus < 0)		/* fatal error */
	return (ErrorStatus);

    /* create the pixmaps and destroy images */
    if (pixmap_return && ximage) {
	xpmCreatePixmapFromImage(display, d, ximage, pixmap_return);
	XDestroyImage(ximage);
    }
    if (shapemask_return && shapeimage) {
	xpmCreatePixmapFromImage(display, d, shapeimage, shapemask_return);
	XDestroyImage(shapeimage);
    }
    return (ErrorStatus);
}

/*****************************************************************************\
*  CrPFrI.c:                                                                  *
*                                                                             *
*  XPM library                                                                *
*  Create the Pixmap related to the given XImage.                             *
*                                                                             *
*  Developed by Arnaud Le Hors                                                *
\*****************************************************************************/

void
xpmCreatePixmapFromImage(display, d, ximage, pixmap_return)
    Display *display;
    Drawable d;
    XImage *ximage;
    Pixmap *pixmap_return;
{
    GC gc;
    XGCValues values;

    *pixmap_return = XCreatePixmap(display, d, ximage->width,
				   ximage->height, ximage->depth);
    /* set fg and bg in case we have an XYBitmap */
    values.foreground = 1;
    values.background = 0;
    gc = XCreateGC(display, *pixmap_return,
		   GCForeground | GCBackground, &values);

    XPutImage(display, *pixmap_return, gc, ximage, 0, 0, 0, 0,
	      ximage->width, ximage->height);

    XFreeGC(display, gc);
}

/*****************************************************************************\
*  Image.c:                                                                   *
*                                                                             *
*  XPM library                                                                *
*  Functions to init and free the XpmImage structure.                         *
*                                                                             *
*  Developed by Arnaud Le Hors                                                *
\*****************************************************************************/

/*
 * Init returned data to free safely later on
 */
void
xpmInitXpmImage(image)
    XpmImage *image;
{
    image->ncolors = 0;
    image->colorTable = NULL;
    image->data = NULL;
}

/*
 * Free the XpmImage data which have been allocated
 */
void
XpmFreeXpmImage(image)
    XpmImage *image;
{
    if (image->colorTable)
	xpmFreeColorTable(image->colorTable, image->ncolors);
    if (image->data)
	XpmFree(image->data);
    image->data = NULL;
}

/*****************************************************************************\
*  Info.c:                                                                    *
*                                                                             *
*  XPM library                                                                *
*  Functions related to the XpmInfo structure.                                *
*                                                                             *
*  Developed by Arnaud Le Hors                                                *
\*****************************************************************************/

/*
 * Init returned data to free safely later on
 */
void
xpmInitXpmInfo(info)
    XpmInfo *info;
{
    if (info) {
	info->hints_cmt = NULL;
	info->colors_cmt = NULL;
	info->pixels_cmt = NULL;
	info->extensions = NULL;
	info->nextensions = 0;
    }
}

/*
 * Free the XpmInfo data which have been allocated
 */
void
XpmFreeXpmInfo(info)
    XpmInfo *info;
{
    if (info) {
	if (info->valuemask & XpmComments) {
	    if (info->hints_cmt) {
		XpmFree(info->hints_cmt);
		info->hints_cmt = NULL;
	    }
	    if (info->colors_cmt) {
		XpmFree(info->colors_cmt);
		info->colors_cmt = NULL;
	    }
	    if (info->pixels_cmt) {
		XpmFree(info->pixels_cmt);
		info->pixels_cmt = NULL;
	    }
	}
	if (info->valuemask & XpmReturnExtensions && info->nextensions) {
	    XpmFreeExtensions(info->extensions, info->nextensions);
	    info->extensions = NULL;
	    info->nextensions = 0;
	}
	info->valuemask = 0;
    }
}

/*
 * Fill in the XpmInfo with the XpmAttributes
 */
void
xpmSetInfo(info, attributes)
    XpmInfo *info;
    XpmAttributes *attributes;
{
    info->valuemask = 0;
    if (attributes->valuemask & XpmInfos) {
	info->valuemask |= XpmComments | XpmColorTable;
	info->hints_cmt = attributes->hints_cmt;
	info->colors_cmt = attributes->colors_cmt;
	info->pixels_cmt = attributes->pixels_cmt;
    }
    if (attributes->valuemask & XpmExtensions) {
	info->valuemask |= XpmExtensions;
	info->extensions = attributes->extensions;
	info->nextensions = attributes->nextensions;
    }
    if (attributes->valuemask & XpmHotspot) {
	info->valuemask |= XpmHotspot;
	info->x_hotspot = attributes->x_hotspot;
	info->y_hotspot = attributes->y_hotspot;
    }
}

/*****************************************************************************\
* RdFToBuf.c:                                                                 *
*                                                                             *
*  XPM library                                                                *
*  Copy a file to a malloc'ed buffer, provided as a convenience.              *
*                                                                             *
*  Developed by Arnaud Le Hors                                                *
\*****************************************************************************/

/*
 * The code related to FOR_MSW has been added by
 * HeDu (hedu@cul-ipn.uni-kiel.de) 4/94
 */

#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#if defined(FOR_MSW) || defined(WIN32)
#include <io.h>
#define stat _stat
#define fstat _fstat
#define fdopen _fdopen
#define O_RDONLY _O_RDONLY
#endif

int
XpmReadFileToBuffer(filename, buffer_return)
    char *filename;
    char **buffer_return;
{
    int fd, fcheck, len;
    char *ptr;
    struct stat stats;
    FILE *fp;

    *buffer_return = NULL;

#ifndef VAX11C
    fd = open(filename, O_RDONLY);
#else
    fd = open(filename, O_RDONLY, NULL);
#endif
    if (fd < 0)
	return XpmOpenFailed;

    if (fstat(fd, &stats)) {
	close(fd);
	return XpmOpenFailed;
    }
    fp = fdopen(fd, "r");
    if (!fp) {
	close(fd);
	return XpmOpenFailed;
    }
    len = (int) stats.st_size;
    ptr = (char *) XpmMalloc(len + 1);
    if (!ptr) {
	fclose(fp);
	return XpmNoMemory;
    }
    fcheck = fread(ptr, 1, len, fp);
    fclose(fp);
#ifdef VMS
    /* VMS often stores text files in a variable-length record format,
       where there are two bytes of size followed by the record.  fread	
       converts this so it looks like a record followed by a newline.	
       Unfortunately, the size reported by fstat() (and fseek/ftell)	
       counts the two bytes for the record terminator, while fread()	
       counts only one.  So, fread() sees fewer bytes in the file (size	
       minus # of records) and thus when asked to read the amount	
       returned by stat(), it fails.
       The best solution, suggested by DEC, seems to consider the length
       returned from fstat() as an upper bound and call fread() with
       a record length of 1. Then don't check the return value.
       We'll check for 0 for gross error that's all.
    */
    len = fcheck;
    if (fcheck == 0) {
#else
    if (fcheck != len) {
#endif
	XpmFree(ptr);
	return XpmOpenFailed;
    }
    ptr[len] = '\0';
    *buffer_return = ptr;
    return XpmSuccess;
}

/*****************************************************************************\
*  RdFToDat.c:                                                                *
*                                                                             *
*  XPM library                                                                *
*  Parse an XPM file and create an array of strings corresponding to it.      *
*                                                                             *
*  Developed by Dan Greening dgreen@cs.ucla.edu / dgreen@sti.com              *
\*****************************************************************************/

int
XpmReadFileToData(filename, data_return)
    char *filename;
    char ***data_return;
{
    XpmImage image;
    XpmInfo info;
    int ErrorStatus;

    info.valuemask = XpmReturnComments | XpmReturnExtensions;

    /*
     * initialize return value
     */
    if (data_return)
	*data_return = NULL;

    ErrorStatus = XpmReadFileToXpmImage(filename, &image, &info);
    if (ErrorStatus != XpmSuccess)
	return (ErrorStatus);

    ErrorStatus =
	XpmCreateDataFromXpmImage(data_return, &image, &info);

    XpmFreeXpmImage(&image);
    XpmFreeXpmInfo(&info);

    return (ErrorStatus);
}

/*****************************************************************************\
*  RdFToP.c:                                                                  *
*                                                                             *
*  XPM library                                                                *
*  Parse an XPM file and create the pixmap and possibly its mask              *
*                                                                             *
*  Developed by Arnaud Le Hors                                                *
\*****************************************************************************/

int
XpmReadFileToPixmap(display, d, filename, pixmap_return,
		    shapemask_return, attributes)
    Display *display;
    Drawable d;
    char *filename;
    Pixmap *pixmap_return;
    Pixmap *shapemask_return;
    XpmAttributes *attributes;
{
    XImage *ximage, *shapeimage;
    int ErrorStatus;

    /* initialize return values */
    if (pixmap_return)
	*pixmap_return = 0;
    if (shapemask_return)
	*shapemask_return = 0;

    /* create the images */
    ErrorStatus = XpmReadFileToImage(display, filename,
				     (pixmap_return ? &ximage : NULL),
				     (shapemask_return ? &shapeimage : NULL),
				     attributes);

    if (ErrorStatus < 0)		/* fatal error */
	return (ErrorStatus);

    /* create the pixmaps and destroy images */
    if (pixmap_return && ximage) {
	xpmCreatePixmapFromImage(display, d, ximage, pixmap_return);
	XDestroyImage(ximage);
    }
    if (shapemask_return && shapeimage) {
	xpmCreatePixmapFromImage(display, d, shapeimage, shapemask_return);
	XDestroyImage(shapeimage);
    }
    return (ErrorStatus);
}

/*****************************************************************************\
* WrFFrBuf.c:                                                                 *
*                                                                             *
*  XPM library                                                                *
*  Write a memory buffer to a file, provided as a convenience.                *
*                                                                             *
*  Developed by Arnaud Le Hors                                                *
\*****************************************************************************/

int
XpmWriteFileFromBuffer(filename, buffer)
    char *filename;
    char *buffer;
{
    int fcheck, len;
    FILE *fp = fopen(filename, "w");

    if (!fp)
	return XpmOpenFailed;

    len = strlen(buffer);
    fcheck = fwrite(buffer, len, 1, fp);
    fclose(fp);
    if (fcheck != 1)
	return XpmOpenFailed;

    return XpmSuccess;
}

/*****************************************************************************\
*  WrFFrData.c:                                                               *
*                                                                             *
*  XPM library                                                                *
*  Parse an Xpm array and write a file that corresponds to it.                *
*                                                                             *
*  Developed by Dan Greening dgreen@cs.ucla.edu / dgreen@sti.com              *
\*****************************************************************************/

int
XpmWriteFileFromData(filename, data)
    char *filename;
    char **data;
{
    XpmImage image;
    XpmInfo info;
    int ErrorStatus;

    info.valuemask = XpmReturnComments | XpmReturnExtensions;

    ErrorStatus = XpmCreateXpmImageFromData(data, &image, &info);

    if (ErrorStatus != XpmSuccess)
	return (ErrorStatus);

    ErrorStatus = XpmWriteFileFromXpmImage(filename, &image, &info);

    XpmFreeXpmImage(&image);
    XpmFreeXpmInfo(&info);

    return (ErrorStatus);
}

/*****************************************************************************\
*  WrFFrI.c:                                                                  *
*                                                                             *
*  XPM library                                                                *
*  Write an image and possibly its mask to an XPM file                        *
*                                                                             *
*  Developed by Arnaud Le Hors                                                *
\*****************************************************************************/

/*
 * The code related to AMIGA has been added by
 * Lorens Younes (d93-hyo@nada.kth.se) 4/96
 */

#if !defined(NO_ZPIPE) && defined(WIN32)
# define popen _popen
# define pclose _pclose
#endif

/* MS Windows define a function called WriteFile @#%#&!!! */
LFUNC(xpmWriteFile, int, (FILE *file, XpmImage *image, char *name,
			  XpmInfo *info));

LFUNC(OpenWriteFile, int, (char *filename, xpmData *mdata));
LFUNC(xpmDataClose, void, (xpmData *mdata));

int
XpmWriteFileFromImage(display, filename, image, shapeimage, attributes)
    Display *display;
    char *filename;
    XImage *image;
    XImage *shapeimage;
    XpmAttributes *attributes;
{
    XpmImage xpmimage;
    XpmInfo info;
    int ErrorStatus;

    /* create an XpmImage from the image */
    ErrorStatus = XpmCreateXpmImageFromImage(display, image, shapeimage,
					     &xpmimage, attributes);
    if (ErrorStatus != XpmSuccess)
	return (ErrorStatus);

    /* write the file from the XpmImage */
    if (attributes) {
	xpmSetInfo(&info, attributes);
	ErrorStatus = XpmWriteFileFromXpmImage(filename, &xpmimage, &info);
    } else
	ErrorStatus = XpmWriteFileFromXpmImage(filename, &xpmimage, NULL);

    /* free the XpmImage */
    XpmFreeXpmImage(&xpmimage);

    return (ErrorStatus);
}

int
XpmWriteFileFromXpmImage(filename, image, info)
    char *filename;
    XpmImage *image;
    XpmInfo *info;
{
    xpmData mdata;
    char *name, *dot, *s, new_name[BUFSIZ];
    int ErrorStatus;

    /* open file to write */
    if ((ErrorStatus = OpenWriteFile(filename, &mdata)) != XpmSuccess)
	return (ErrorStatus);

    /* figure out a name */
    if (filename) {
#ifdef VMS
	name = filename;
#else
	if (!(name = strrchr(filename, '/'))
#ifdef AMIGA
	    && !(name = strrchr(filename, ':'))
#endif
     )
	    name = filename;
	else
	    name++;
#endif
	/* let's try to make a valid C syntax name */
	if ((dot = strchr(name, '.'))) {
	    strcpy(new_name, name);
	    /* change '.' to '_' */
	    name = s = new_name;
	    while ((dot = strchr(s, '.'))) {
		*dot = '_';
		s = dot;
	    }
	}
	if ((dot = strchr(name, '-'))) {
	    if (name != new_name) {
		strcpy(new_name, name);
		name = new_name;
	    }
	    /* change '-' to '_' */
	    s = name;
	    while ((dot = strchr(s, '-'))) {
		*dot = '_';
		s = dot;
	    }
	}
    } else
	name = "image_name";

    /* write the XpmData from the XpmImage */
    if (ErrorStatus == XpmSuccess)
	ErrorStatus = xpmWriteFile(mdata.stream.file, image, name, info);

    xpmDataClose(&mdata);

    return (ErrorStatus);
}

static void
WriteColors(FILE *file, XpmColor *colors, unsigned int ncolors)
{
    unsigned int a, key;
    char *s;
    char **defaults;

    for (a = 0; a < ncolors; a++, colors++) {

	defaults = (char **) colors;
	fprintf(file, "\"%s", *defaults++);

	for (key = 1; key <= NKEYS; key++, defaults++) {
	    if ((s = *defaults))
		fprintf(file, "\t%s %s", xpmColorKeys[key - 1], s);
	}
	fprintf(file, "\",\n");
    }
}

static void
WriteExtensions(FILE *file, XpmExtension *ext, unsigned int num)
{
    unsigned int x, y, n;
    char **line;

    for (x = 0; x < num; x++, ext++) {
	fprintf(file, ",\n\"XPMEXT %s\"", ext->name);
	n = ext->nlines;
	for (y = 0, line = ext->lines; y < n; y++, line++)
	    fprintf(file, ",\n\"%s\"", *line);
    }
    fprintf(file, ",\n\"XPMENDEXT\"");
}

static int
WritePixels(FILE *file, unsigned int width, unsigned int height, 
            unsigned int cpp, unsigned int *pixels, XpmColor *colors)
{
    char *s, *p, *buf;
    unsigned int x, y, h;

    h = height - 1;
    p = buf = (char *) XpmMalloc(width * cpp + 3);
    if (!buf)
	return (XpmNoMemory);
    *buf = '"';
    p++;
    for (y = 0; y < h; y++) {
	s = p;
	for (x = 0; x < width; x++, pixels++) {
	    strncpy(s, colors[*pixels].string, cpp);
	    s += cpp;
	}
	*s++ = '"';
	*s = '\0';
	fprintf(file, "%s,\n", buf);
    }
    /* duplicate some code to avoid a test in the loop */
    s = p;
    for (x = 0; x < width; x++, pixels++) {
	strncpy(s, colors[*pixels].string, cpp);
	s += cpp;
    }
    *s++ = '"';
    *s = '\0';
    fprintf(file, "%s", buf);

    XpmFree(buf);
    return (XpmSuccess);
}


static int
xpmWriteFile(file, image, name, info)
    FILE *file;
    XpmImage *image;
    char *name;
    XpmInfo *info;
{
    /* calculation variables */
    unsigned int cmts, extensions;
    int ErrorStatus;

    cmts = info && (info->valuemask & XpmComments);
    extensions = info && (info->valuemask & XpmExtensions)
	&& info->nextensions;

    /* print the header line */
    fprintf(file, "/* XPM */\nstatic char * %s[] = {\n", name);

    /* print the hints line */
    if (cmts && info->hints_cmt)
	fprintf(file, "/*%s*/\n", info->hints_cmt);

    fprintf(file, "\"%d %d %d %d", image->width, image->height,
	    image->ncolors, image->cpp);

    if (info && (info->valuemask & XpmHotspot))
	fprintf(file, " %d %d", info->x_hotspot, info->y_hotspot);

    if (extensions)
	fprintf(file, " XPMEXT");

    fprintf(file, "\",\n");

    /* print colors */
    if (cmts && info->colors_cmt)
	fprintf(file, "/*%s*/\n", info->colors_cmt);

    WriteColors(file, image->colorTable, image->ncolors);

    /* print pixels */
    if (cmts && info->pixels_cmt)
	fprintf(file, "/*%s*/\n", info->pixels_cmt);

    ErrorStatus = WritePixels(file, image->width, image->height, image->cpp,
			      image->data, image->colorTable);
    if (ErrorStatus != XpmSuccess)
	return (ErrorStatus);

    /* print extensions */
    if (extensions)
	WriteExtensions(file, info->extensions, info->nextensions);

    /* close the array */
    fprintf(file, "};\n");

    return (XpmSuccess);
}

/*
 * open the given file to be written as an xpmData which is returned
 */
static int
OpenWriteFile(filename, mdata)
    char *filename;
    xpmData *mdata;
{
#ifndef NO_ZPIPE
    char buf[BUFSIZ];

#endif

    if (!filename) {
	mdata->stream.file = (stdout);
	mdata->type = XPMFILE;
    } else {
#ifndef NO_ZPIPE
	int len = strlen(filename);
	if (len > 2 && !strcmp(".Z", filename + (len - 2))) {
	    sprintf(buf, "compress > \"%s\"", filename);
	    if (!(mdata->stream.file = popen(buf, "w")))
		return (XpmOpenFailed);

	    mdata->type = XPMPIPE;
	} else if (len > 3 && !strcmp(".gz", filename + (len - 3))) {
	    sprintf(buf, "gzip -q > \"%s\"", filename);
	    if (!(mdata->stream.file = popen(buf, "w")))
		return (XpmOpenFailed);

	    mdata->type = XPMPIPE;
	} else {
#endif
	    if (!(mdata->stream.file = fopen(filename, "w")))
		return (XpmOpenFailed);

	    mdata->type = XPMFILE;
#ifndef NO_ZPIPE
	}
#endif
    }
    return (XpmSuccess);
}

/*
 * close the file related to the xpmData if any
 */
static void
xpmDataClose(mdata)
    xpmData *mdata;
{
    switch (mdata->type) {
    case XPMFILE:
	if (mdata->stream.file != (stdout))
	    fclose(mdata->stream.file);
	break;
#ifndef NO_ZPIPE
    case XPMPIPE:
	pclose(mdata->stream.file);
	break;
#endif
    }
}

/*****************************************************************************\
*  WrFFrP.c:                                                                  *
*                                                                             *
*  XPM library                                                                *
*  Write a pixmap and possibly its mask to an XPM file                        *
*                                                                             *
*  Developed by Arnaud Le Hors                                                *
\*****************************************************************************/

int
XpmWriteFileFromPixmap(display, filename, pixmap, shapemask, attributes)
    Display *display;
    char *filename;
    Pixmap pixmap;
    Pixmap shapemask;
    XpmAttributes *attributes;
{
    XImage *ximage = NULL;
    XImage *shapeimage = NULL;
    unsigned int width = 0;
    unsigned int height = 0;
    int ErrorStatus;

    /* get geometry */
    if (attributes && attributes->valuemask & XpmSize) {
	width = attributes->width;
	height = attributes->height;
    }
    /* get the ximages */
    if (pixmap)
	xpmCreateImageFromPixmap(display, pixmap, &ximage, &width, &height);
    if (shapemask)
	xpmCreateImageFromPixmap(display, shapemask, &shapeimage,
				 &width, &height);

    /* write to the file */
    ErrorStatus = XpmWriteFileFromImage(display, filename, ximage, shapeimage,
					attributes);

    /* destroy the ximages */
    if (ximage)
	XDestroyImage(ximage);
    if (shapeimage)
	XDestroyImage(shapeimage);

    return (ErrorStatus);
}

/*****************************************************************************\
* create.c:                                                                   *
*                                                                             *
*  XPM library                                                                *
*  Create an X image and possibly its related shape mask                      *
*  from the given XpmImage.                                                   *
*                                                                             *
*  Developed by Arnaud Le Hors                                                *
\*****************************************************************************/

/*
 * The code related to FOR_MSW has been added by
 * HeDu (hedu@cul-ipn.uni-kiel.de) 4/94
 */

/*
 * The code related to AMIGA has been added by
 * Lorens Younes (d93-hyo@nada.kth.se) 4/96
 */

#include <ctype.h>

LFUNC(xpmVisualType, int, (Visual *visual));

LFUNC(AllocColor, int, (Display *display, Colormap colormap,
			char *colorname, XColor *xcolor, void *closure));
LFUNC(FreeColors, int, (Display *display, Colormap colormap,
			Pixel *pixels, int n, void *closure));

#ifndef FOR_MSW
LFUNC(SetCloseColor, int, (Display *display, Colormap colormap,
			   Visual *visual, XColor *col,
			   Pixel *image_pixel, Pixel *mask_pixel,
			   Pixel *alloc_pixels, unsigned int *nalloc_pixels,
			   XpmAttributes *attributes, XColor *cols, int ncols,
			   XpmAllocColorFunc allocColor, void *closure));
#else
/* let the window system take care of close colors */
#endif

LFUNC(SetColor, int, (Display *display, Colormap colormap, Visual *visual,
		      char *colorname, unsigned int color_index,
		      Pixel *image_pixel, Pixel *mask_pixel,
		      unsigned int *mask_pixel_index,
		      Pixel *alloc_pixels, unsigned int *nalloc_pixels,
		      Pixel *used_pixels, unsigned int *nused_pixels,
		      XpmAttributes *attributes, XColor *cols, int ncols,
		      XpmAllocColorFunc allocColor, void *closure));

LFUNC(CreateXImage, int, (Display *display, Visual *visual,
			  unsigned int depth, int format, unsigned int width,
                          unsigned int height, XImage **image_return));

LFUNC(CreateColors, int, (Display *display, XpmAttributes *attributes,
                          XpmColor *colors, unsigned int ncolors,
                          Pixel *image_pixels, Pixel *mask_pixels,
                          unsigned int *mask_pixel_index,
                          Pixel *alloc_pixels, unsigned int *nalloc_pixels,
                          Pixel *used_pixels, unsigned int *nused_pixels));

#ifndef FOR_MSW
LFUNC(ParseAndPutPixels, int, (xpmData *data, unsigned int width,
			       unsigned int height, unsigned int ncolors,
			       unsigned int cpp, XpmColor *colorTable,
			       xpmHashTable *hashtable,
			       XImage *image, Pixel *image_pixels,
			       XImage *mask, Pixel *mask_pixels));
#else  /* FOR_MSW */
LFUNC(ParseAndPutPixels, int, (Display *dc, xpmData *data, unsigned int width,
			       unsigned int height, unsigned int ncolors,
			       unsigned int cpp, XpmColor *colorTable,
			       xpmHashTable *hashtable,
			       XImage *image, Pixel *image_pixels,
			       XImage *mask, Pixel *mask_pixels));
#endif

#ifndef FOR_MSW
# ifndef AMIGA
/* XImage pixel routines */
LFUNC(PutImagePixels, void, (XImage *image, unsigned int width,
			     unsigned int height, unsigned int *pixelindex,
			     Pixel *pixels));

LFUNC(PutImagePixels32, void, (XImage *image, unsigned int width,
			       unsigned int height, unsigned int *pixelindex,
			       Pixel *pixels));

LFUNC(PutImagePixels16, void, (XImage *image, unsigned int width,
			       unsigned int height, unsigned int *pixelindex,
			       Pixel *pixels));

LFUNC(PutImagePixels8, void, (XImage *image, unsigned int width,
			      unsigned int height, unsigned int *pixelindex,
			      Pixel *pixels));

LFUNC(PutImagePixels1, void, (XImage *image, unsigned int width,
			      unsigned int height, unsigned int *pixelindex,
			      Pixel *pixels));

LFUNC(PutPixel1, int, (XImage *ximage, int x, int y, unsigned long pixel));
LFUNC(PutPixel, int, (XImage *ximage, int x, int y, unsigned long pixel));
LFUNC(PutPixel32, int, (XImage *ximage, int x, int y, unsigned long pixel));
LFUNC(PutPixel32MSB, int, (XImage *ximage, int x, int y, unsigned long pixel));
LFUNC(PutPixel32LSB, int, (XImage *ximage, int x, int y, unsigned long pixel));
LFUNC(PutPixel16MSB, int, (XImage *ximage, int x, int y, unsigned long pixel));
LFUNC(PutPixel16LSB, int, (XImage *ximage, int x, int y, unsigned long pixel));
LFUNC(PutPixel8, int, (XImage *ximage, int x, int y, unsigned long pixel));
LFUNC(PutPixel1MSB, int, (XImage *ximage, int x, int y, unsigned long pixel));
LFUNC(PutPixel1LSB, int, (XImage *ximage, int x, int y, unsigned long pixel));

# else /* AMIGA */
LFUNC(APutImagePixels, void, (XImage *ximage, unsigned int width,
			      unsigned int height, unsigned int *pixelindex,
			      Pixel *pixels));
# endif/* AMIGA */
#else  /* FOR_MSW */
/* FOR_MSW pixel routine */
LFUNC(MSWPutImagePixels, void, (Display *dc, XImage *image,
				unsigned int width, unsigned int height,
				unsigned int *pixelindex, Pixel *pixels));
#endif /* FOR_MSW */

#ifndef HAVE_STRCASECMP
FUNC(xpmstrcasecmp, int, (char *s1, char *s2));

/*
 * in case strcasecmp is not provided by the system here is one
 * which does the trick
 */
int
xpmstrcasecmp(register char *s1, register char *s2)
{
    register int c1, c2;

    while (*s1 && *s2) {
	c1 = tolower(*s1);
	c2 = tolower(*s2);
	if (c1 != c2)
	    return (c1 - c2);
	s1++;
	s2++;
    }
    return (int) (*s1 - *s2);
}

#endif

/*
 * return the default color key related to the given visual
 */
static int
xpmVisualType(Visual *visual)
{
#ifndef FOR_MSW
# ifndef AMIGA
    switch (visual->class) {
    case StaticGray:
    case GrayScale:
	switch (visual->map_entries) {
	case 2:
	    return (XPM_MONO);
	case 4:
	    return (XPM_GRAY4);
	default:
	    return (XPM_GRAY);
	}
    default:
	return (XPM_COLOR);
    }
# else
    /* set the key explicitly in the XpmAttributes to override this */
    return (XPM_COLOR);
# endif
#else
    /* there should be a similar switch for MSW */
    return (XPM_COLOR);
#endif
}


typedef struct {
    int cols_index;
    long closeness;
}      CloseColor;

static int
closeness_cmp(const void *a, const void *b)
{
    CloseColor *x = (CloseColor *) a, *y = (CloseColor *) b;

    /* cast to int as qsort requires */
    return (int) (x->closeness - y->closeness);
}


/* default AllocColor function:
 *   call XParseColor if colorname is given, return negative value if failure
 *   call XAllocColor and return 0 if failure, positive otherwise
 */
static int
AllocColor(Display *display, Colormap colormap, char *colorname,
           XColor *xcolor, void *closure)
	/* not used */
{
    int status;
    if (colorname)
	if (!XParseColor(display, colormap, colorname, xcolor))
	    return -1;
    status = XAllocColor(display, colormap, xcolor);
    return status != 0 ? 1 : 0;
}


#ifndef FOR_MSW
/*
 * set a close color in case the exact one can't be set
 * return 0 if success, 1 otherwise.
 */

static int
SetCloseColor(
    Display *display,
    Colormap colormap,
    Visual *visual,
    XColor *col,
    Pixel *image_pixel, Pixel *mask_pixel,
    Pixel *alloc_pixels,
    unsigned int *nalloc_pixels,
    XpmAttributes *attributes,
    XColor *cols,
    int ncols,
    XpmAllocColorFunc allocColor,
    void *closure)
{

    /*
     * Allocation failed, so try close colors. To get here the visual must
     * be GreyScale, PseudoColor or DirectColor (or perhaps StaticColor?
     * What about sharing systems like QDSS?). Beware: we have to treat
     * DirectColor differently.
     */


    long int red_closeness, green_closeness, blue_closeness;
    int n;
    Bool alloc_color;

    if (attributes && (attributes->valuemask & XpmCloseness))
	red_closeness = green_closeness = blue_closeness =
	    attributes->closeness;
    else {
	red_closeness = attributes->red_closeness;
	green_closeness = attributes->green_closeness;
	blue_closeness = attributes->blue_closeness;
    }
    if (attributes && (attributes->valuemask & XpmAllocCloseColors))
	alloc_color = attributes->alloc_close_colors;
    else
	alloc_color = True;

    /*
     * We sort the colormap by closeness and try to allocate the color
     * closest to the target. If the allocation of this close color fails,
     * which almost never happens, then one of two scenarios is possible.
     * Either the colormap must have changed (since the last close color
     * allocation or possibly while we were sorting the colormap), or the
     * color is allocated as Read/Write by some other client. (Note: X
     * _should_ allow clients to check if a particular color is Read/Write,
     * but it doesn't! :-( ). We cannot determine which of these scenarios
     * occurred, so we try the next closest color, and so on, until no more
     * colors are within closeness of the target. If we knew that the
     * colormap had changed, we could skip this sequence.
     * 
     * If _none_ of the colors within closeness of the target can be allocated,
     * then we can finally be pretty sure that the colormap has actually
     * changed. In this case we try to allocate the original color (again),
     * then try the closecolor stuff (again)...
     * 
     * In theory it would be possible for an infinite loop to occur if another
     * process kept changing the colormap every time we sorted it, so we set
     * a maximum on the number of iterations. After this many tries, we use
     * XGrabServer() to ensure that the colormap remains unchanged.
     * 
     * This approach gives particularly bad worst case performance - as many as
     * <MaximumIterations> colormap reads and sorts may be needed, and as
     * many as <MaximumIterations> * <ColormapSize> attempted allocations
     * may fail. On an 8-bit system, this means as many as 3 colormap reads,
     * 3 sorts and 768 failed allocations per execution of this code!
     * Luckily, my experiments show that in general use in a typical 8-bit
     * color environment only about 1 in every 10000 allocations fails to
     * succeed in the fastest possible time. So virtually every time what
     * actually happens is a single sort followed by a successful allocate.
     * The very first allocation also costs a colormap read, but no further
     * reads are usually necessary.
     */

#define ITERATIONS 2			/* more than one is almost never
					 * necessary */

    for (n = 0; n <= ITERATIONS; ++n) {
	CloseColor *closenesses =
	    (CloseColor *) XpmCalloc(ncols, sizeof(CloseColor));
	int i, c;

	for (i = 0; i < ncols; ++i) {	/* build & sort closenesses table */
#define COLOR_FACTOR       3
#define BRIGHTNESS_FACTOR  1

	    closenesses[i].cols_index = i;
	    closenesses[i].closeness =
		COLOR_FACTOR * (abs((long) col->red - (long) cols[i].red)
				+ abs((long) col->green - (long) cols[i].green)
				+ abs((long) col->blue - (long) cols[i].blue))
		+ BRIGHTNESS_FACTOR * abs(((long) col->red +
					   (long) col->green +
					   (long) col->blue)
					   - ((long) cols[i].red +
					      (long) cols[i].green +
					      (long) cols[i].blue));
	}
	qsort(closenesses, ncols, sizeof(CloseColor), closeness_cmp);

	i = 0;
	c = closenesses[i].cols_index;
	while ((long) cols[c].red >= (long) col->red - red_closeness &&
	       (long) cols[c].red <= (long) col->red + red_closeness &&
	       (long) cols[c].green >= (long) col->green - green_closeness &&
	       (long) cols[c].green <= (long) col->green + green_closeness &&
	       (long) cols[c].blue >= (long) col->blue - blue_closeness &&
	       (long) cols[c].blue <= (long) col->blue + blue_closeness) {
	    if (alloc_color) {
		if ((*allocColor)(display, colormap, NULL, &cols[c], closure)){
		    if (n == ITERATIONS)
			XUngrabServer(display);
		    XpmFree(closenesses);
		    *image_pixel = cols[c].pixel;
		    *mask_pixel = 1;
		    alloc_pixels[(*nalloc_pixels)++] = cols[c].pixel;
		    return (0);
		} else {
		    ++i;
		    if (i == ncols)
			break;
		    c = closenesses[i].cols_index;
		}
	    } else {
		if (n == ITERATIONS)
		    XUngrabServer(display);
		XpmFree(closenesses);
		*image_pixel = cols[c].pixel;
		*mask_pixel = 1;
		return (0);
	    }
	}

	/* Couldn't allocate _any_ of the close colors! */

	if (n == ITERATIONS)
	    XUngrabServer(display);
	XpmFree(closenesses);

	if (i == 0 || i == ncols)	/* no color close enough or cannot */
	    return (1);			/* alloc any color (full of r/w's) */

	if ((*allocColor)(display, colormap, NULL, col, closure)) {
	    *image_pixel = col->pixel;
	    *mask_pixel = 1;
	    alloc_pixels[(*nalloc_pixels)++] = col->pixel;
	    return (0);
	} else {			/* colormap has probably changed, so
					 * re-read... */
	    if (n == ITERATIONS - 1)
		XGrabServer(display);

#if 0
	    if (visual->class == DirectColor) {
		/* TODO */
	    } else
#endif
		XQueryColors(display, colormap, cols, ncols);
	}
    }
    return (1);
}

#define USE_CLOSECOLOR attributes && \
(((attributes->valuemask & XpmCloseness) && attributes->closeness != 0) \
 || ((attributes->valuemask & XpmRGBCloseness) && \
     (attributes->red_closeness != 0 \
      || attributes->green_closeness != 0 \
      || attributes->blue_closeness != 0)))

#else
    /* FOR_MSW part */
    /* nothing to do here, the window system does it */
#endif

/*
 * set the color pixel related to the given colorname,
 * return 0 if success, 1 otherwise.
 */

static int
SetColor(display, colormap, visual, colorname, color_index,
	 image_pixel, mask_pixel, mask_pixel_index,
	 alloc_pixels, nalloc_pixels, used_pixels, nused_pixels,
	 attributes, cols, ncols, allocColor, closure)
    Display *display;
    Colormap colormap;
    Visual *visual;
    char *colorname;
    unsigned int color_index;
    Pixel *image_pixel, *mask_pixel;
    unsigned int *mask_pixel_index;
    Pixel *alloc_pixels;
    unsigned int *nalloc_pixels;
    Pixel *used_pixels;
    unsigned int *nused_pixels;
    XpmAttributes *attributes;
    XColor *cols;
    int ncols;
    XpmAllocColorFunc allocColor;
    void *closure;
{
    XColor xcolor;
    int status;

    if (xpmstrcasecmp(colorname, TRANSPARENT_COLOR)) {
	status = (*allocColor)(display, colormap, colorname, &xcolor, closure);
	if (status < 0)		/* parse color failed */
	    return (1);

	if (status == 0) {
#ifndef FOR_MSW
	    if (USE_CLOSECOLOR)
		return (SetCloseColor(display, colormap, visual, &xcolor,
				      image_pixel, mask_pixel,
				      alloc_pixels, nalloc_pixels,
				      attributes, cols, ncols,
				      allocColor, closure));
	    else
#endif /* ndef FOR_MSW */
		return (1);
	} else
	    alloc_pixels[(*nalloc_pixels)++] = xcolor.pixel;
	*image_pixel = xcolor.pixel;
#ifndef FOR_MSW
	*mask_pixel = 1;
#else
	*mask_pixel = RGB(0,0,0);
#endif
	used_pixels[(*nused_pixels)++] = xcolor.pixel;
    } else {
	*image_pixel = 0;
#ifndef FOR_MSW
	*mask_pixel = 0;
#else
  	*mask_pixel = RGB(255,255,255);
#endif
	/* store the color table index */
	*mask_pixel_index = color_index;
    }
    return (0);
}


static int
CreateColors(display, attributes, colors, ncolors, image_pixels, mask_pixels,
             mask_pixel_index, alloc_pixels, nalloc_pixels,
             used_pixels, nused_pixels)
    Display *display;
    XpmAttributes *attributes;
    XpmColor *colors;
    unsigned int ncolors;
    Pixel *image_pixels;
    Pixel *mask_pixels;
    unsigned int *mask_pixel_index;
    Pixel *alloc_pixels;
    unsigned int *nalloc_pixels;
    Pixel *used_pixels;
    unsigned int *nused_pixels;
{
    /* variables stored in the XpmAttributes structure */
    Visual *visual;
    Colormap colormap;
    XpmColorSymbol *colorsymbols = NULL;
    unsigned int numsymbols;
    XpmAllocColorFunc allocColor;
    void *closure;

    char *colorname;
    unsigned int color, key;
    Bool pixel_defined;
    XpmColorSymbol *symbol = NULL;
    char **defaults;
    int ErrorStatus = XpmSuccess;
    char *s;
    int default_index;

    XColor *cols = NULL;
    unsigned int ncols = 0;

    /*
     * retrieve information from the XpmAttributes
     */
    if (attributes && attributes->valuemask & XpmColorSymbols) {
	colorsymbols = attributes->colorsymbols;
	numsymbols = attributes->numsymbols;
    } else
	numsymbols = 0;

    if (attributes && attributes->valuemask & XpmVisual)
	visual = attributes->visual;
    else
	visual = XDefaultVisual(display, XDefaultScreen(display));

    if (attributes && (attributes->valuemask & XpmColormap))
	colormap = attributes->colormap;
    else
	colormap = XDefaultColormap(display, XDefaultScreen(display));

    if (attributes && (attributes->valuemask & XpmColorKey))
	key = attributes->color_key;
    else
	key = xpmVisualType(visual);

    if (attributes && (attributes->valuemask & XpmAllocColor))
	allocColor = attributes->alloc_color;
    else
	allocColor = AllocColor;
    if (attributes && (attributes->valuemask & XpmColorClosure))
	closure = attributes->color_closure;
    else
	closure = NULL;

#ifndef FOR_MSW
    if (USE_CLOSECOLOR) {
	/* originally from SetCloseColor */
#if 0
	if (visual->class == DirectColor) {

	    /*
	     * TODO: Implement close colors for DirectColor visuals. This is
	     * difficult situation. Chances are that we will never get here,
	     * because any machine that supports DirectColor will probably
	     * also support TrueColor (and probably PseudoColor). Also,
	     * DirectColor colormaps can be very large, so looking for close
	     * colors may be too slow.
	     */
	} else {
#endif
	    int i;

#ifndef AMIGA
	    ncols = visual->map_entries;
#else
	    ncols = colormap->Count;
#endif
	    cols = (XColor *) XpmCalloc(ncols, sizeof(XColor));
	    for (i = 0; i < ncols; ++i)
		cols[i].pixel = i;
	    XQueryColors(display, colormap, cols, ncols);
#if 0
	}
#endif
    }
#endif /* ndef FOR_MSW */

    switch (key) {
    case XPM_MONO:
	default_index = 2;
	break;
    case XPM_GRAY4:
	default_index = 3;
	break;
    case XPM_GRAY:
	default_index = 4;
	break;
    case XPM_COLOR:
    default:
	default_index = 5;
	break;
    }

    for (color = 0; color < ncolors; color++, colors++,
					 image_pixels++, mask_pixels++) {
	colorname = NULL;
	pixel_defined = False;
	defaults = (char **) colors;

	/*
	 * look for a defined symbol
	 */
	if (numsymbols) {

	    unsigned int n;

	    s = defaults[1];
	    for (n = 0, symbol = colorsymbols; n < numsymbols; n++, symbol++) {
		if (symbol->name && s && !strcmp(symbol->name, s))
		    /* override name */
		    break;
		if (!symbol->name && symbol->value) {	/* override value */
		    int def_index = default_index;

		    while (defaults[def_index] == NULL)	/* find defined
							 * colorname */
			--def_index;
		    if (def_index < 2) {/* nothing towards mono, so try
					 * towards color */
			def_index = default_index + 1;
			while (def_index <= 5 && defaults[def_index] == NULL)
			    ++def_index;
		    }
		    if (def_index >= 2 && defaults[def_index] != NULL &&
			!xpmstrcasecmp(symbol->value, defaults[def_index]))
			break;
		}
	    }
	    if (n != numsymbols) {
		if (symbol->name && symbol->value)
		    colorname = symbol->value;
		else
		    pixel_defined = True;
	    }
	}
	if (!pixel_defined) {		/* pixel not given as symbol value */

	    unsigned int k;

	    if (colorname) {		/* colorname given as symbol value */
		if (!SetColor(display, colormap, visual, colorname, color,
			      image_pixels, mask_pixels, mask_pixel_index,
			      alloc_pixels, nalloc_pixels, used_pixels,
			      nused_pixels, attributes, cols, ncols,
			      allocColor, closure))
		    pixel_defined = True;
		else
		    ErrorStatus = XpmColorError;
	    }
	    k = key;
	    while (!pixel_defined && k > 1) {
		if (defaults[k]) {
		    if (!SetColor(display, colormap, visual, defaults[k],
				  color, image_pixels, mask_pixels,
				  mask_pixel_index, alloc_pixels,
				  nalloc_pixels, used_pixels, nused_pixels,
				  attributes, cols, ncols,
				  allocColor, closure)) {
			pixel_defined = True;
			break;
		    } else
			ErrorStatus = XpmColorError;
		}
		k--;
	    }
	    k = key + 1;
	    while (!pixel_defined && k < NKEYS + 1) {
		if (defaults[k]) {
		    if (!SetColor(display, colormap, visual, defaults[k],
				  color, image_pixels, mask_pixels,
				  mask_pixel_index, alloc_pixels,
				  nalloc_pixels, used_pixels, nused_pixels,
				  attributes, cols, ncols,
				  allocColor, closure)) {
			pixel_defined = True;
			break;
		    } else
			ErrorStatus = XpmColorError;
		}
		k++;
	    }
	    if (!pixel_defined) {
		if (cols)
		    XpmFree(cols);
		return (XpmColorFailed);
	    }
	} else {
	    /* simply use the given pixel */
	    *image_pixels = symbol->pixel;
	    /* the following makes the mask to be built even if none
	       is given a particular pixel */
	    if (symbol->value
		&& !xpmstrcasecmp(symbol->value, TRANSPARENT_COLOR)) {
		*mask_pixels = 0;
		*mask_pixel_index = color;
	    } else
		*mask_pixels = 1;
	    used_pixels[(*nused_pixels)++] = *image_pixels;
	}
    }
    if (cols)
	XpmFree(cols);
    return (ErrorStatus);
}


/* default FreeColors function, simply call XFreeColors */
static int
FreeColors(display, colormap, pixels, n, closure)
    Display *display;
    Colormap colormap;
    Pixel *pixels;
    int n;
    void *closure;		/* not used */
{
    return XFreeColors(display, colormap, pixels, n, 0);
}


/* function call in case of error */
#undef RETURN
#define RETURN(status) \
{ \
      ErrorStatus = status; \
      goto error; \
}

int
XpmCreateImageFromXpmImage(display, image,
			   image_return, shapeimage_return, attributes)
    Display *display;
    XpmImage *image;
    XImage **image_return;
    XImage **shapeimage_return;
    XpmAttributes *attributes;
{
    /* variables stored in the XpmAttributes structure */
    Visual *visual;
    Colormap colormap;
    unsigned int depth;
    int bitmap_format;
    XpmFreeColorsFunc freeColors;
    void *closure;

    /* variables to return */
    XImage *ximage = NULL;
    XImage *shapeimage = NULL;
    unsigned int mask_pixel_index = XpmUndefPixel;
    int ErrorStatus;

    /* calculation variables */
    Pixel *image_pixels = NULL;
    Pixel *mask_pixels = NULL;
    Pixel *alloc_pixels = NULL;
    Pixel *used_pixels = NULL;
    unsigned int nalloc_pixels = 0;
    unsigned int nused_pixels = 0;

    /* initialize return values */
    if (image_return)
	*image_return = NULL;
    if (shapeimage_return)
	*shapeimage_return = NULL;

    /* retrieve information from the XpmAttributes */
    if (attributes && (attributes->valuemask & XpmVisual))
	visual = attributes->visual;
    else
	visual = XDefaultVisual(display, XDefaultScreen(display));

    if (attributes && (attributes->valuemask & XpmColormap))
	colormap = attributes->colormap;
    else
	colormap = XDefaultColormap(display, XDefaultScreen(display));

    if (attributes && (attributes->valuemask & XpmDepth))
	depth = attributes->depth;
    else
	depth = XDefaultDepth(display, XDefaultScreen(display));

    if (attributes && (attributes->valuemask & XpmBitmapFormat))
	bitmap_format = attributes->bitmap_format;
    else
	bitmap_format = ZPixmap;

    if (attributes && (attributes->valuemask & XpmFreeColors))
	freeColors = attributes->free_colors;
    else
	freeColors = FreeColors;
    if (attributes && (attributes->valuemask & XpmColorClosure))
	closure = attributes->color_closure;
    else
	closure = NULL;

    ErrorStatus = XpmSuccess;

    /* malloc pixels index tables */
    image_pixels = (Pixel *) XpmMalloc(sizeof(Pixel) * image->ncolors);
    if (!image_pixels)
	return (XpmNoMemory);

    mask_pixels = (Pixel *) XpmMalloc(sizeof(Pixel) * image->ncolors);
    if (!mask_pixels)
	RETURN(XpmNoMemory);

    /* maximum of allocated pixels will be the number of colors */
    alloc_pixels = (Pixel *) XpmMalloc(sizeof(Pixel) * image->ncolors);
    if (!alloc_pixels)
	RETURN(XpmNoMemory);

    /* maximum of allocated pixels will be the number of colors */
    used_pixels = (Pixel *) XpmMalloc(sizeof(Pixel) * image->ncolors);
    if (!used_pixels)
	RETURN(XpmNoMemory);

    /* get pixel colors, store them in index tables */
    ErrorStatus = CreateColors(display, attributes, image->colorTable,
			       image->ncolors, image_pixels, mask_pixels,
			       &mask_pixel_index, alloc_pixels, &nalloc_pixels,
			       used_pixels, &nused_pixels);

    if (ErrorStatus != XpmSuccess
	&& (ErrorStatus < 0 || (attributes
				&& (attributes->valuemask & XpmExactColors)
				&& attributes->exactColors)))
	RETURN(ErrorStatus);

    /* create the ximage */
    if (image_return) {
	ErrorStatus = CreateXImage(display, visual, depth,
				   (depth == 1 ? bitmap_format : ZPixmap),
				   image->width, image->height, &ximage);
	if (ErrorStatus != XpmSuccess)
	    RETURN(ErrorStatus);

#ifndef FOR_MSW
# ifndef AMIGA

	/*
	 * set the ximage data using optimized functions for ZPixmap
	 */

	if (ximage->bits_per_pixel == 8)
	    PutImagePixels8(ximage, image->width, image->height,
			    image->data, image_pixels);
	else if (((ximage->bits_per_pixel | ximage->depth) == 1) &&
		 (ximage->byte_order == ximage->bitmap_bit_order))
	    PutImagePixels1(ximage, image->width, image->height,
			    image->data, image_pixels);
	else if (ximage->bits_per_pixel == 16)
	    PutImagePixels16(ximage, image->width, image->height,
			     image->data, image_pixels);
	else if (ximage->bits_per_pixel == 32)
	    PutImagePixels32(ximage, image->width, image->height,
			     image->data, image_pixels);
	else
	    PutImagePixels(ximage, image->width, image->height,
			   image->data, image_pixels);
# else /* AMIGA */
	APutImagePixels(ximage, image->width, image->height,
			image->data, image_pixels);
# endif
#else  /* FOR_MSW */
	MSWPutImagePixels(display, ximage, image->width, image->height,
			  image->data, image_pixels);
#endif
    }
    /* create the shape mask image */
    if (mask_pixel_index != XpmUndefPixel && shapeimage_return) {
	ErrorStatus = CreateXImage(display, visual, 1, bitmap_format,
				   image->width, image->height, &shapeimage);
	if (ErrorStatus != XpmSuccess)
	    RETURN(ErrorStatus);

#ifndef FOR_MSW
# ifndef AMIGA
	PutImagePixels1(shapeimage, image->width, image->height,
			image->data, mask_pixels);
# else /* AMIGA */
	APutImagePixels(shapeimage, image->width, image->height,
			image->data, mask_pixels);
# endif
#else  /* FOR_MSW */
	MSWPutImagePixels(display, shapeimage, image->width, image->height,
			  image->data, mask_pixels);
#endif

    }
    XpmFree(image_pixels);
    XpmFree(mask_pixels);

    /* if requested return used pixels in the XpmAttributes structure */
    if (attributes && (attributes->valuemask & XpmReturnPixels ||
/* 3.2 backward compatibility code */
	attributes->valuemask & XpmReturnInfos)) {
/* end 3.2 bc */
	attributes->pixels = used_pixels;
	attributes->npixels = nused_pixels;
	attributes->mask_pixel = mask_pixel_index;
    } else
	XpmFree(used_pixels);

    /* if requested return alloc'ed pixels in the XpmAttributes structure */
    if (attributes && (attributes->valuemask & XpmReturnAllocPixels)) {
	attributes->alloc_pixels = alloc_pixels;
	attributes->nalloc_pixels = nalloc_pixels;
    } else
	XpmFree(alloc_pixels);

    /* return created images */
    if (image_return)
	*image_return = ximage;
    if (shapeimage_return)
	*shapeimage_return = shapeimage;

    return (ErrorStatus);

/* exit point in case of error, free only locally allocated variables */
error:
    if (ximage)
	XDestroyImage(ximage);
    if (shapeimage)
	XDestroyImage(shapeimage);
    if (image_pixels)
	XpmFree(image_pixels);
    if (mask_pixels)
	XpmFree(mask_pixels);
    if (nalloc_pixels)
	(*freeColors)(display, colormap, alloc_pixels, nalloc_pixels, NULL);
    if (alloc_pixels)
	XpmFree(alloc_pixels);
    if (used_pixels)
	XpmFree(used_pixels);

    return (ErrorStatus);
}


/*
 * Create an XImage with its data
 */
static int
CreateXImage(display, visual, depth, format, width, height, image_return)
    Display *display;
    Visual *visual;
    unsigned int depth;
    int format;
    unsigned int width;
    unsigned int height;
    XImage **image_return;
{
    int bitmap_pad;

    /* first get bitmap_pad */
    if (depth > 16)
	bitmap_pad = 32;
    else if (depth > 8)
	bitmap_pad = 16;
    else
	bitmap_pad = 8;

    /* then create the XImage with data = NULL and bytes_per_line = 0 */
    *image_return = XCreateImage(display, visual, depth, format, 0, 0,
				 width, height, bitmap_pad, 0);
    if (!*image_return)
	return (XpmNoMemory);

#if !defined(FOR_MSW) && !defined(AMIGA)
    /* now that bytes_per_line must have been set properly alloc data */
    (*image_return)->data =
	(char *) XpmMalloc((*image_return)->bytes_per_line * height);

    if (!(*image_return)->data) {
	XDestroyImage(*image_return);
	*image_return = NULL;
	return (XpmNoMemory);
    }
#else
    /* under FOR_MSW and AMIGA XCreateImage has done it all */
#endif
    return (XpmSuccess);
}

#ifndef FOR_MSW
# ifndef AMIGA
/*
 * The functions below are written from X11R5 MIT's code (XImUtil.c)
 *
 * The idea is to have faster functions than the standard XPutPixel function
 * to build the image data. Indeed we can speed up things by suppressing tests
 * performed for each pixel. We do the same tests but at the image level.
 * We also assume that we use only ZPixmap images with null offsets.
 */

LFUNC(_putbits, void, (register char *src, int dstoffset,
		       register int numbits, register char *dst));

LFUNC(_XReverse_Bytes, int, (register unsigned char *bpt, register int nb));

static unsigned char const _reverse_byte[0x100] = {
    0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
    0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
    0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
    0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
    0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
    0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
    0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
    0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
    0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
    0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
    0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
    0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
    0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
    0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
    0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
    0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
    0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
    0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
    0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
    0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
    0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
    0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
    0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
    0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
    0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
    0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
    0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
    0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
    0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
    0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
    0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
    0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
};

static int
_XReverse_Bytes(bpt, nb)
    register unsigned char *bpt;
    register int nb;
{
    do {
	*bpt = _reverse_byte[*bpt];
	bpt++;
    } while (--nb > 0);
    return 0;
}


void
xpm_xynormalizeimagebits(bp, img)
    register unsigned char *bp;
    register XImage *img;
{
    register unsigned char c;

    if (img->byte_order != img->bitmap_bit_order) {
	switch (img->bitmap_unit) {

	case 16:
	    c = *bp;
	    *bp = *(bp + 1);
	    *(bp + 1) = c;
	    break;

	case 32:
	    c = *(bp + 3);
	    *(bp + 3) = *bp;
	    *bp = c;
	    c = *(bp + 2);
	    *(bp + 2) = *(bp + 1);
	    *(bp + 1) = c;
	    break;
	}
    }
    if (img->bitmap_bit_order == MSBFirst)
	_XReverse_Bytes(bp, img->bitmap_unit >> 3);
}

void
xpm_znormalizeimagebits(bp, img)
    register unsigned char *bp;
    register XImage *img;
{
    register unsigned char c;

    switch (img->bits_per_pixel) {

    case 2:
	_XReverse_Bytes(bp, 1);
	break;

    case 4:
	*bp = ((*bp >> 4) & 0xF) | ((*bp << 4) & ~0xF);
	break;

    case 16:
	c = *bp;
	*bp = *(bp + 1);
	*(bp + 1) = c;
	break;

    case 24:
	c = *(bp + 2);
	*(bp + 2) = *bp;
	*bp = c;
	break;

    case 32:
	c = *(bp + 3);
	*(bp + 3) = *bp;
	*bp = c;
	c = *(bp + 2);
	*(bp + 2) = *(bp + 1);
	*(bp + 1) = c;
	break;
    }
}

static unsigned char const _lomask[0x09] = {
0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};
static unsigned char const _himask[0x09] = {
0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00};

static void
_putbits(src, dstoffset, numbits, dst)
    register char *src;			/* address of source bit string */
    int dstoffset;			/* bit offset into destination;
					 * range is 0-31 */
    register int numbits;		/* number of bits to copy to
					 * destination */
    register char *dst;			/* address of destination bit string */
{
    register unsigned char chlo, chhi;
    int hibits;

    dst = dst + (dstoffset >> 3);
    dstoffset = dstoffset & 7;
    hibits = 8 - dstoffset;
    chlo = *dst & _lomask[dstoffset];
    for (;;) {
	chhi = (*src << dstoffset) & _himask[dstoffset];
	if (numbits <= hibits) {
	    chhi = chhi & _lomask[dstoffset + numbits];
	    *dst = (*dst & _himask[dstoffset + numbits]) | chlo | chhi;
	    break;
	}
	*dst = chhi | chlo;
	dst++;
	numbits = numbits - hibits;
	chlo = (unsigned char) (*src & _himask[hibits]) >> hibits;
	src++;
	if (numbits <= dstoffset) {
	    chlo = chlo & _lomask[numbits];
	    *dst = (*dst & _himask[numbits]) | chlo;
	    break;
	}
	numbits = numbits - dstoffset;
    }
}

/*
 * Default method to write pixels into a Z image data structure.
 * The algorithm used is:
 *
 *	copy the destination bitmap_unit or Zpixel to temp
 *	normalize temp if needed
 *	copy the pixel bits into the temp
 *	renormalize temp if needed
 *	copy the temp back into the destination image data
 */

static void
PutImagePixels(image, width, height, pixelindex, pixels)
    XImage *image;
    unsigned int width;
    unsigned int height;
    unsigned int *pixelindex;
    Pixel *pixels;
{
    register char *src;
    register char *dst;
    register unsigned int *iptr;
    register int x, y, i;
    register char *data;
    Pixel pixel, px;
    int nbytes, depth, ibu, ibpp;

    data = image->data;
    iptr = pixelindex;
    depth = image->depth;
    if (depth == 1) {
	ibu = image->bitmap_unit;
	for (y = 0; y < height; y++)
	    for (x = 0; x < width; x++, iptr++) {
		pixel = pixels[*iptr];
		for (i = 0, px = pixel; i < sizeof(unsigned long);
		     i++, px >>= 8)
		    ((unsigned char *) &pixel)[i] = px;
		src = &data[XYINDEX(x, y, image)];
		dst = (char *) &px;
		px = 0;
		nbytes = ibu >> 3;
		for (i = nbytes; --i >= 0;)
		    *dst++ = *src++;
		XYNORMALIZE(&px, image);
		_putbits((char *) &pixel, (x % ibu), 1, (char *) &px);
		XYNORMALIZE(&px, image);
		src = (char *) &px;
		dst = &data[XYINDEX(x, y, image)];
		for (i = nbytes; --i >= 0;)
		    *dst++ = *src++;
	    }
    } else {
	ibpp = image->bits_per_pixel;
	for (y = 0; y < height; y++)
	    for (x = 0; x < width; x++, iptr++) {
		pixel = pixels[*iptr];
		if (depth == 4)
		    pixel &= 0xf;
		for (i = 0, px = pixel; i < sizeof(unsigned long); i++,
		     px >>= 8)
		    ((unsigned char *) &pixel)[i] = px;
		src = &data[ZINDEX(x, y, image)];
		dst = (char *) &px;
		px = 0;
		nbytes = (ibpp + 7) >> 3;
		for (i = nbytes; --i >= 0;)
		    *dst++ = *src++;
		ZNORMALIZE(&px, image);
		_putbits((char *) &pixel, (x * ibpp) & 7, ibpp, (char *) &px);
		ZNORMALIZE(&px, image);
		src = (char *) &px;
		dst = &data[ZINDEX(x, y, image)];
		for (i = nbytes; --i >= 0;)
		    *dst++ = *src++;
	    }
    }
}

/*
 * write pixels into a 32-bits Z image data structure
 */

#if !defined(WORD64) && !defined(LONG64)
/* this item is static but deterministic so let it slide; doesn't
 * hurt re-entrancy of this library. Note if it is actually const then would
 * be OK under rules of ANSI-C but probably not C++ which may not
 * want to allocate space for it.
 */
static unsigned long byteorderpixel = MSBFirst << 24;

#endif

/*
   WITHOUT_SPEEDUPS is a flag to be turned on if you wish to use the original
   3.2e code - by default you get the speeded-up version.
*/

static void
PutImagePixels32(image, width, height, pixelindex, pixels)
    XImage *image;
    unsigned int width;
    unsigned int height;
    unsigned int *pixelindex;
    Pixel *pixels;
{
    unsigned char *data;
    unsigned int *iptr;
    int y;
    Pixel pixel;

#ifdef WITHOUT_SPEEDUPS

    int x;
    unsigned char *addr;

    data = (unsigned char *) image->data;
    iptr = pixelindex;
#if !defined(WORD64) && !defined(LONG64)
    if (*((char *) &byteorderpixel) == image->byte_order) {
	for (y = 0; y < height; y++)
	    for (x = 0; x < width; x++, iptr++) {
		addr = &data[ZINDEX32(x, y, image)];
		*((unsigned long *) addr) = pixels[*iptr];
	    }
    } else
#endif
    if (image->byte_order == MSBFirst)
	for (y = 0; y < height; y++)
	    for (x = 0; x < width; x++, iptr++) {
		addr = &data[ZINDEX32(x, y, image)];
		pixel = pixels[*iptr];
		addr[0] = pixel >> 24;
		addr[1] = pixel >> 16;
		addr[2] = pixel >> 8;
		addr[3] = pixel;
	    }
    else
	for (y = 0; y < height; y++)
	    for (x = 0; x < width; x++, iptr++) {
		addr = &data[ZINDEX32(x, y, image)];
		pixel = pixels[*iptr];
		addr[0] = pixel;
		addr[1] = pixel >> 8;
		addr[2] = pixel >> 16;
		addr[3] = pixel >> 24;
	    }

#else  /* WITHOUT_SPEEDUPS */

    int bpl = image->bytes_per_line;
    unsigned char *data_ptr, *max_data;

    data = (unsigned char *) image->data;
    iptr = pixelindex;
#if !defined(WORD64) && !defined(LONG64)
    if (*((char *) &byteorderpixel) == image->byte_order) {
	for (y = 0; y < height; y++) {
	    data_ptr = data;
	    max_data = data_ptr + (width << 2);

	    while (data_ptr < max_data) {
		*((unsigned long *) data_ptr) = pixels[*(iptr++)];
		data_ptr += (1 << 2);
	    }
	    data += bpl;
	}
    } else
#endif
    if (image->byte_order == MSBFirst)
	for (y = 0; y < height; y++) {
	    data_ptr = data;
	    max_data = data_ptr + (width << 2);

	    while (data_ptr < max_data) {
		pixel = pixels[*(iptr++)];

		*data_ptr++ = pixel >> 24;
		*data_ptr++ = pixel >> 16;
		*data_ptr++ = pixel >> 8;
		*data_ptr++ = pixel;

	    }
	    data += bpl;
	}
    else
	for (y = 0; y < height; y++) {
	    data_ptr = data;
	    max_data = data_ptr + (width << 2);

	    while (data_ptr < max_data) {
		pixel = pixels[*(iptr++)];

		*data_ptr++ = pixel;
		*data_ptr++ = pixel >> 8;
		*data_ptr++ = pixel >> 16;
		*data_ptr++ = pixel >> 24;
	    }
	    data += bpl;
	}

#endif /* WITHOUT_SPEEDUPS */
}

/*
 * write pixels into a 16-bits Z image data structure
 */

static void
PutImagePixels16(image, width, height, pixelindex, pixels)
    XImage *image;
    unsigned int width;
    unsigned int height;
    unsigned int *pixelindex;
    Pixel *pixels;
{
    unsigned char *data;
    unsigned int *iptr;
    int y;

#ifdef WITHOUT_SPEEDUPS

    int x;
    unsigned char *addr;

    data = (unsigned char *) image->data;
    iptr = pixelindex;
    if (image->byte_order == MSBFirst)
	for (y = 0; y < height; y++)
	    for (x = 0; x < width; x++, iptr++) {
		addr = &data[ZINDEX16(x, y, image)];
		addr[0] = pixels[*iptr] >> 8;
		addr[1] = pixels[*iptr];
	    }
    else
	for (y = 0; y < height; y++)
	    for (x = 0; x < width; x++, iptr++) {
		addr = &data[ZINDEX16(x, y, image)];
		addr[0] = pixels[*iptr];
		addr[1] = pixels[*iptr] >> 8;
	    }

#else  /* WITHOUT_SPEEDUPS */

    Pixel pixel;

    int bpl = image->bytes_per_line;
    unsigned char *data_ptr, *max_data;

    data = (unsigned char *) image->data;
    iptr = pixelindex;
    if (image->byte_order == MSBFirst)
	for (y = 0; y < height; y++) {
	    data_ptr = data;
	    max_data = data_ptr + (width << 1);

	    while (data_ptr < max_data) {
		pixel = pixels[*(iptr++)];

		data_ptr[0] = pixel >> 8;
		data_ptr[1] = pixel;

		data_ptr += (1 << 1);
	    }
	    data += bpl;
	}
    else
	for (y = 0; y < height; y++) {
	    data_ptr = data;
	    max_data = data_ptr + (width << 1);

	    while (data_ptr < max_data) {
		pixel = pixels[*(iptr++)];

		data_ptr[0] = pixel;
		data_ptr[1] = pixel >> 8;

		data_ptr += (1 << 1);
	    }
	    data += bpl;
	}

#endif /* WITHOUT_SPEEDUPS */
}

/*
 * write pixels into a 8-bits Z image data structure
 */

static void
PutImagePixels8(image, width, height, pixelindex, pixels)
    XImage *image;
    unsigned int width;
    unsigned int height;
    unsigned int *pixelindex;
    Pixel *pixels;
{
    char *data;
    unsigned int *iptr;
    int y;

#ifdef WITHOUT_SPEEDUPS

    int x;

    data = image->data;
    iptr = pixelindex;
    for (y = 0; y < height; y++)
	for (x = 0; x < width; x++, iptr++)
	    data[ZINDEX8(x, y, image)] = pixels[*iptr];

#else  /* WITHOUT_SPEEDUPS */

    int bpl = image->bytes_per_line;
    char *data_ptr, *max_data;

    data = image->data;
    iptr = pixelindex;

    for (y = 0; y < height; y++) {
	data_ptr = data;
	max_data = data_ptr + width;

	while (data_ptr < max_data)
	    *(data_ptr++) = pixels[*(iptr++)];

	data += bpl;
    }

#endif /* WITHOUT_SPEEDUPS */
}

/*
 * write pixels into a 1-bit depth image data structure and **offset null**
 */

static void
PutImagePixels1(image, width, height, pixelindex, pixels)
    XImage *image;
    unsigned int width;
    unsigned int height;
    unsigned int *pixelindex;
    Pixel *pixels;
{
    if (image->byte_order != image->bitmap_bit_order)
	PutImagePixels(image, width, height, pixelindex, pixels);
    else {
	unsigned int *iptr;
	int y;
	char *data;

#ifdef WITHOUT_SPEEDUPS

	int x;

	data = image->data;
	iptr = pixelindex;
	if (image->bitmap_bit_order == MSBFirst)
	    for (y = 0; y < height; y++)
		for (x = 0; x < width; x++, iptr++) {
		    if (pixels[*iptr] & 1)
			data[ZINDEX1(x, y, image)] |= 0x80 >> (x & 7);
		    else
			data[ZINDEX1(x, y, image)] &= ~(0x80 >> (x & 7));
		}
	else
	    for (y = 0; y < height; y++)
		for (x = 0; x < width; x++, iptr++) {
		    if (pixels[*iptr] & 1)
			data[ZINDEX1(x, y, image)] |= 1 << (x & 7);
		    else
			data[ZINDEX1(x, y, image)] &= ~(1 << (x & 7));
		}

#else  /* WITHOUT_SPEEDUPS */

	char value;
	char *data_ptr, *max_data;
	int bpl = image->bytes_per_line;
	int diff, count;

	data = image->data;
	iptr = pixelindex;

	diff = width & 7;
	width >>= 3;

	if (image->bitmap_bit_order == MSBFirst)
	    for (y = 0; y < height; y++) {
		data_ptr = data;
		max_data = data_ptr + width;
		while (data_ptr < max_data) {
		    value = 0;

		    value = (value << 1) | (pixels[*(iptr++)] & 1);
		    value = (value << 1) | (pixels[*(iptr++)] & 1);
		    value = (value << 1) | (pixels[*(iptr++)] & 1);
		    value = (value << 1) | (pixels[*(iptr++)] & 1);
		    value = (value << 1) | (pixels[*(iptr++)] & 1);
		    value = (value << 1) | (pixels[*(iptr++)] & 1);
		    value = (value << 1) | (pixels[*(iptr++)] & 1);
		    value = (value << 1) | (pixels[*(iptr++)] & 1);

		    *(data_ptr++) = value;
		}
		if (diff) {
		    value = 0;
		    for (count = 0; count < diff; count++) {
			if (pixels[*(iptr++)] & 1)
			    value |= (0x80 >> count);
		    }
		    *(data_ptr) = value;
		}
		data += bpl;
	    }
	else
	    for (y = 0; y < height; y++) {
		data_ptr = data;
		max_data = data_ptr + width;
		while (data_ptr < max_data) {
		    value = 0;
		    iptr += 8;

		    value = (value << 1) | (pixels[*(--iptr)] & 1);
		    value = (value << 1) | (pixels[*(--iptr)] & 1);
		    value = (value << 1) | (pixels[*(--iptr)] & 1);
		    value = (value << 1) | (pixels[*(--iptr)] & 1);
		    value = (value << 1) | (pixels[*(--iptr)] & 1);
		    value = (value << 1) | (pixels[*(--iptr)] & 1);
		    value = (value << 1) | (pixels[*(--iptr)] & 1);
		    value = (value << 1) | (pixels[*(--iptr)] & 1);

		    iptr += 8;
		    *(data_ptr++) = value;
		}
		if (diff) {
		    value = 0;
		    for (count = 0; count < diff; count++) {
			if (pixels[*(iptr++)] & 1)
			    value |= (1 << count);
		    }
		    *(data_ptr) = value;
		}
		data += bpl;
	    }

#endif /* WITHOUT_SPEEDUPS */
    }
}

int
XpmCreatePixmapFromXpmImage(display, d, image,
			    pixmap_return, shapemask_return, attributes)
    Display *display;
    Drawable d;
    XpmImage *image;
    Pixmap *pixmap_return;
    Pixmap *shapemask_return;
    XpmAttributes *attributes;
{
    XImage *ximage, *shapeimage;
    int ErrorStatus;

    /* initialize return values */
    if (pixmap_return)
	*pixmap_return = 0;
    if (shapemask_return)
	*shapemask_return = 0;

    /* create the ximages */
    ErrorStatus = XpmCreateImageFromXpmImage(display, image,
					     (pixmap_return ? &ximage : NULL),
					     (shapemask_return ?
					      &shapeimage : NULL),
					     attributes);
    if (ErrorStatus < 0)
	return (ErrorStatus);

    /* create the pixmaps and destroy images */
    if (pixmap_return && ximage) {
	xpmCreatePixmapFromImage(display, d, ximage, pixmap_return);
	XDestroyImage(ximage);
    }
    if (shapemask_return && shapeimage) {
	xpmCreatePixmapFromImage(display, d, shapeimage, shapemask_return);
	XDestroyImage(shapeimage);
    }
    return (ErrorStatus);
}

# else /* AMIGA */

static void
APutImagePixels (
    XImage        *image,
    unsigned int   width,
    unsigned int   height,
    unsigned int  *pixelindex,
    Pixel         *pixels)
{
    unsigned int   *data = pixelindex;
    unsigned int    x, y;
    unsigned char  *array;
    XImage         *tmp_img;
    BOOL            success = FALSE;
    
    array = XpmMalloc ((((width+15)>>4)<<4)*sizeof (*array));
    if (array != NULL)
    {
	tmp_img = AllocXImage ((((width+15)>>4)<<4), 1,
			       image->rp->BitMap->Depth);
	if (tmp_img != NULL)
	{
	    for (y = 0; y < height; ++y)
	    {
		for (x = 0; x < width; ++x)
		    array[x] = pixels[*(data++)];
		WritePixelLine8 (image->rp, 0, y, width, array, tmp_img->rp);
	    }
	    FreeXImage (tmp_img);
	    success = TRUE;
	}
	XpmFree (array);
    }
    
    if (!success)
    {
	for (y = 0; y < height; ++y)
	    for (x = 0; x < width; ++x)
		XPutPixel (image, x, y, pixels[*(data++)]);
    }
}

# endif/* AMIGA */
#else  /* FOR_MSW part follows */
static void
MSWPutImagePixels(dc, image, width, height, pixelindex, pixels)
    Display *dc;
    XImage *image;
    unsigned int width;
    unsigned int height;
    unsigned int *pixelindex;
    Pixel *pixels;
{
    unsigned int *data = pixelindex;
    unsigned int x, y;
    HBITMAP obm;

    obm = SelectObject(*dc, image->bitmap);
    for (y = 0; y < height; y++) {
	for (x = 0; x < width; x++) {
	    SetPixel(*dc, x, y, pixels[*(data++)]); /* data is [x+y*width] */
	}
    }
    SelectObject(*dc, obm);
}

#endif /* FOR_MSW */



#if !defined(FOR_MSW) && !defined(AMIGA)

static int
PutPixel1(ximage, x, y, pixel)
    register XImage *ximage;
    int x;
    int y;
    unsigned long pixel;
{
    register char *src;
    register char *dst;
    register int i;
    Pixel px;
    int nbytes;

    for (i=0, px=pixel; i<sizeof(unsigned long); i++, px>>=8)
	((unsigned char *)&pixel)[i] = px;
    src = &ximage->data[XYINDEX(x, y, ximage)];
    dst = (char *)&px;
    px = 0;
    nbytes = ximage->bitmap_unit >> 3;
    for (i = nbytes; --i >= 0; ) *dst++ = *src++;
    XYNORMALIZE(&px, ximage);
    i = ((x + ximage->xoffset) % ximage->bitmap_unit);
    _putbits ((char *)&pixel, i, 1, (char *)&px);
    XYNORMALIZE(&px, ximage);
    src = (char *) &px;
    dst = &ximage->data[XYINDEX(x, y, ximage)];
    for (i = nbytes; --i >= 0; )
	*dst++ = *src++;

    return 1;
}

static int
PutPixel(ximage, x, y, pixel)
    register XImage *ximage;
    int x;
    int y;
    unsigned long pixel;
{
    register char *src;
    register char *dst;
    register int i;
    Pixel px;
    int nbytes, ibpp;

    ibpp = ximage->bits_per_pixel;
    if (ximage->depth == 4)
	pixel &= 0xf;
    for (i = 0, px = pixel; i < sizeof(unsigned long); i++, px >>= 8)
	((unsigned char *) &pixel)[i] = px;
    src = &ximage->data[ZINDEX(x, y, ximage)];
    dst = (char *) &px;
    px = 0;
    nbytes = (ibpp + 7) >> 3;
    for (i = nbytes; --i >= 0;)
	*dst++ = *src++;
    ZNORMALIZE(&px, ximage);
    _putbits((char *) &pixel, (x * ibpp) & 7, ibpp, (char *) &px);
    ZNORMALIZE(&px, ximage);
    src = (char *) &px;
    dst = &ximage->data[ZINDEX(x, y, ximage)];
    for (i = nbytes; --i >= 0;)
	*dst++ = *src++;

    return 1;
}

static int
PutPixel32(ximage, x, y, pixel)
    register XImage *ximage;
    int x;
    int y;
    unsigned long pixel;
{
    unsigned char *addr;

    addr = &((unsigned char *)ximage->data) [ZINDEX32(x, y, ximage)];
    *((unsigned long *)addr) = pixel;
    return 1;
}

static int
PutPixel32MSB(ximage, x, y, pixel)
    register XImage *ximage;
    int x;
    int y;
    unsigned long pixel;
{
    unsigned char *addr;

    addr = &((unsigned char *)ximage->data) [ZINDEX32(x, y, ximage)];
    addr[0] = pixel >> 24;
    addr[1] = pixel >> 16;
    addr[2] = pixel >> 8;
    addr[3] = pixel;
    return 1;
}

static int
PutPixel32LSB(ximage, x, y, pixel)
    register XImage *ximage;
    int x;
    int y;
    unsigned long pixel;
{
    unsigned char *addr;

    addr = &((unsigned char *)ximage->data) [ZINDEX32(x, y, ximage)];
    addr[3] = pixel >> 24;
    addr[2] = pixel >> 16;
    addr[1] = pixel >> 8;
    addr[0] = pixel;
    return 1;
}

static int
PutPixel16MSB(ximage, x, y, pixel)
    register XImage *ximage;
    int x;
    int y;
    unsigned long pixel;
{
    unsigned char *addr;
    
    addr = &((unsigned char *)ximage->data) [ZINDEX16(x, y, ximage)];
    addr[0] = pixel >> 8;
    addr[1] = pixel;
    return 1;
}

static int
PutPixel16LSB(ximage, x, y, pixel)
    register XImage *ximage;
    int x;
    int y;
    unsigned long pixel;
{
    unsigned char *addr;
    
    addr = &((unsigned char *)ximage->data) [ZINDEX16(x, y, ximage)];
    addr[1] = pixel >> 8;
    addr[0] = pixel;
    return 1;
}

static int
PutPixel8(ximage, x, y, pixel)
    register XImage *ximage;
    int x;
    int y;
    unsigned long pixel;
{
    ximage->data[ZINDEX8(x, y, ximage)] = pixel;
    return 1;
}

static int
PutPixel1MSB(ximage, x, y, pixel)
    register XImage *ximage;
    int x;
    int y;
    unsigned long pixel;
{
    if (pixel & 1)
	ximage->data[ZINDEX1(x, y, ximage)] |= 0x80 >> (x & 7);
    else
	ximage->data[ZINDEX1(x, y, ximage)] &= ~(0x80 >> (x & 7));
    return 1;
}

static int
PutPixel1LSB(ximage, x, y, pixel)
    register XImage *ximage;
    int x;
    int y;
    unsigned long pixel;
{
    if (pixel & 1)
	ximage->data[ZINDEX1(x, y, ximage)] |= 1 << (x & 7);
    else
	ximage->data[ZINDEX1(x, y, ximage)] &= ~(1 << (x & 7));
    return 1;
}

#endif /* not FOR_MSW && not AMIGA */

/*
 * This function parses an Xpm file or data and directly create an XImage
 */
int
xpmParseDataAndCreate(display, data, image_return, shapeimage_return,
		      image, info, attributes)
    Display *display;
    xpmData *data;
    XImage **image_return;
    XImage **shapeimage_return;
    XpmImage *image;
    XpmInfo *info;
    XpmAttributes *attributes;
{
    /* variables stored in the XpmAttributes structure */
    Visual *visual;
    Colormap colormap;
    unsigned int depth;
    int bitmap_format;
    XpmFreeColorsFunc freeColors;
    void *closure;

    /* variables to return */
    XImage *ximage = NULL;
    XImage *shapeimage = NULL;
    unsigned int mask_pixel_index = XpmUndefPixel;

    /* calculation variables */
    Pixel *image_pixels = NULL;
    Pixel *mask_pixels = NULL;
    Pixel *alloc_pixels = NULL;
    Pixel *used_pixels = NULL;
    unsigned int nalloc_pixels = 0;
    unsigned int nused_pixels = 0;
    unsigned int width, height, ncolors, cpp;
    unsigned int x_hotspot, y_hotspot, hotspot = 0, extensions = 0;
    XpmColor *colorTable = NULL;
    char *hints_cmt = NULL;
    char *colors_cmt = NULL;
    char *pixels_cmt = NULL;

    unsigned int cmts;
    int ErrorStatus;
    xpmHashTable hashtable;


    /* initialize return values */
    if (image_return)
	*image_return = NULL;
    if (shapeimage_return)
	*shapeimage_return = NULL;


    /* retrieve information from the XpmAttributes */
    if (attributes && (attributes->valuemask & XpmVisual))
	visual = attributes->visual;
    else
	visual = XDefaultVisual(display, XDefaultScreen(display));

    if (attributes && (attributes->valuemask & XpmColormap))
	colormap = attributes->colormap;
    else
	colormap = XDefaultColormap(display, XDefaultScreen(display));

    if (attributes && (attributes->valuemask & XpmDepth))
	depth = attributes->depth;
    else
	depth = XDefaultDepth(display, XDefaultScreen(display));

    if (attributes && (attributes->valuemask & XpmBitmapFormat))
	bitmap_format = attributes->bitmap_format;
    else
	bitmap_format = ZPixmap;

    if (attributes && (attributes->valuemask & XpmFreeColors))
	freeColors = attributes->free_colors;
    else
	freeColors = FreeColors;
    if (attributes && (attributes->valuemask & XpmColorClosure))
	closure = attributes->color_closure;
    else
	closure = NULL;

    cmts = info && (info->valuemask & XpmReturnComments);

    /*
     * parse the header
     */
    ErrorStatus = xpmParseHeader(data);
    if (ErrorStatus != XpmSuccess)
	return (ErrorStatus);

    /*
     * read values
     */
    ErrorStatus = xpmParseValues(data, &width, &height, &ncolors, &cpp,
				 &x_hotspot, &y_hotspot, &hotspot,
				 &extensions);
    if (ErrorStatus != XpmSuccess)
	return (ErrorStatus);

    /*
     * store the hints comment line
     */
    if (cmts)
	xpmGetCmt(data, &hints_cmt);

    /*
     * init the hastable
     */
    if (USE_HASHTABLE) {
	ErrorStatus = xpmHashTableInit(&hashtable);
	if (ErrorStatus != XpmSuccess)
	    return (ErrorStatus);
    }

    /*
     * read colors
     */
    ErrorStatus = xpmParseColors(data, ncolors, cpp, &colorTable, &hashtable);
    if (ErrorStatus != XpmSuccess)
	RETURN(ErrorStatus);

    /*
     * store the colors comment line
     */
    if (cmts)
	xpmGetCmt(data, &colors_cmt);

    /* malloc pixels index tables */
    image_pixels = (Pixel *) XpmMalloc(sizeof(Pixel) * ncolors);
    if (!image_pixels)
	RETURN(XpmNoMemory);

    mask_pixels = (Pixel *) XpmMalloc(sizeof(Pixel) * ncolors);
    if (!mask_pixels)
	RETURN(XpmNoMemory);

    /* maximum of allocated pixels will be the number of colors */
    alloc_pixels = (Pixel *) XpmMalloc(sizeof(Pixel) * ncolors);
    if (!alloc_pixels)
	RETURN(XpmNoMemory);

    /* maximum of allocated pixels will be the number of colors */
    used_pixels = (Pixel *) XpmMalloc(sizeof(Pixel) * ncolors);
    if (!used_pixels)
	RETURN(XpmNoMemory);

    /* get pixel colors, store them in index tables */
    ErrorStatus = CreateColors(display, attributes, colorTable, ncolors,
			       image_pixels, mask_pixels, &mask_pixel_index,
			       alloc_pixels, &nalloc_pixels, used_pixels,
			       &nused_pixels);

    if (ErrorStatus != XpmSuccess
	&& (ErrorStatus < 0 || (attributes
				&& (attributes->valuemask & XpmExactColors)
				&& attributes->exactColors)))
	RETURN(ErrorStatus);

    /* now create the ximage */
    if (image_return) {
	ErrorStatus = CreateXImage(display, visual, depth,
				   (depth == 1 ? bitmap_format : ZPixmap),
				   width, height, &ximage);
	if (ErrorStatus != XpmSuccess)
	    RETURN(ErrorStatus);

#if !defined(FOR_MSW) && !defined(AMIGA)

	/*
	 * set the XImage pointer function, to be used with XPutPixel,
	 * to an internal optimized function
	 */

	if (ximage->bits_per_pixel == 8)
	    ximage->f.put_pixel = PutPixel8;
	else if (((ximage->bits_per_pixel | ximage->depth) == 1) &&
		 (ximage->byte_order == ximage->bitmap_bit_order)) {
	    if (ximage->bitmap_bit_order == MSBFirst)
		ximage->f.put_pixel = PutPixel1MSB;
	    else
		ximage->f.put_pixel = PutPixel1LSB;
    }
	else if (ximage->bits_per_pixel == 16) {
	    if (ximage->bitmap_bit_order == MSBFirst)
		ximage->f.put_pixel = PutPixel16MSB;
	    else
		ximage->f.put_pixel = PutPixel16LSB;
    }
	else if (ximage->bits_per_pixel == 32) {
#if !defined(WORD64) && !defined(LONG64)
	    if (*((char *)&byteorderpixel) == ximage->byte_order)
		ximage->f.put_pixel = PutPixel32;
	    else
#endif
		if (ximage->bitmap_bit_order == MSBFirst)
		    ximage->f.put_pixel = PutPixel32MSB;
		else
		    ximage->f.put_pixel = PutPixel32LSB;
      }
	else if ((ximage->bits_per_pixel | ximage->depth) == 1)
	    ximage->f.put_pixel = PutPixel1;
	else
	    ximage->f.put_pixel = PutPixel;
#endif /* not FOR_MSW && not AMIGA */
    }

    /* create the shape mask image */
    if (mask_pixel_index != XpmUndefPixel && shapeimage_return) {
	ErrorStatus = CreateXImage(display, visual, 1, bitmap_format,
				   width, height, &shapeimage);
	if (ErrorStatus != XpmSuccess)
	    RETURN(ErrorStatus);

#if !defined(FOR_MSW) && !defined(AMIGA)
	if (shapeimage->bitmap_bit_order == MSBFirst)
	    shapeimage->f.put_pixel = PutPixel1MSB;
	else
	    shapeimage->f.put_pixel = PutPixel1LSB;
#endif
    }

    /*
     * read pixels and put them in the XImage
     */
    ErrorStatus = ParseAndPutPixels(
#ifdef FOR_MSW
				    display,
#endif
				    data, width, height, ncolors, cpp,
				    colorTable, &hashtable,
				    ximage, image_pixels,
				    shapeimage, mask_pixels);
    XpmFree(image_pixels);
    image_pixels = NULL;
    XpmFree(mask_pixels);
    mask_pixels = NULL;

    /*
     * free the hastable
     */
    if (ErrorStatus != XpmSuccess)
	RETURN(ErrorStatus)
    else if (USE_HASHTABLE)
	xpmHashTableFree(&hashtable);

    /*
     * store the pixels comment line
     */
    if (cmts)
	xpmGetCmt(data, &pixels_cmt);

    /*
     * parse extensions
     */
    if (info && (info->valuemask & XpmReturnExtensions))
    {
	if (extensions) {
	    ErrorStatus = xpmParseExtensions(data, &info->extensions,
					     &info->nextensions);
	    if (ErrorStatus != XpmSuccess)
		RETURN(ErrorStatus);
	} else {
	    info->extensions = NULL;
	    info->nextensions = 0;
	}
    }
    /*
     * store found informations in the XpmImage structure
     */
    image->width = width;
    image->height = height;
    image->cpp = cpp;
    image->ncolors = ncolors;
    image->colorTable = colorTable;
    image->data = NULL;

    if (info) {
	if (cmts) {
	    info->hints_cmt = hints_cmt;
	    info->colors_cmt = colors_cmt;
	    info->pixels_cmt = pixels_cmt;
	}
	if (hotspot) {
	    info->x_hotspot = x_hotspot;
	    info->y_hotspot = y_hotspot;
	    info->valuemask |= XpmHotspot;
	}
    }
    /* if requested return used pixels in the XpmAttributes structure */
    if (attributes && (attributes->valuemask & XpmReturnPixels ||
/* 3.2 backward compatibility code */
	attributes->valuemask & XpmReturnInfos)) {
/* end 3.2 bc */
	attributes->pixels = used_pixels;
	attributes->npixels = nused_pixels;
	attributes->mask_pixel = mask_pixel_index;
    } else
	XpmFree(used_pixels);

    /* if requested return alloc'ed pixels in the XpmAttributes structure */
    if (attributes && (attributes->valuemask & XpmReturnAllocPixels)) {
	attributes->alloc_pixels = alloc_pixels;
	attributes->nalloc_pixels = nalloc_pixels;
    } else
	XpmFree(alloc_pixels);

    /* return created images */
    if (image_return)
	*image_return = ximage;
    if (shapeimage_return)
	*shapeimage_return = shapeimage;

    return (XpmSuccess);

/* exit point in case of error, free only locally allocated variables */
error:
    if (USE_HASHTABLE)
	xpmHashTableFree(&hashtable);
    if (colorTable)
	xpmFreeColorTable(colorTable, ncolors);
    if (hints_cmt)
	XpmFree(hints_cmt);
    if (colors_cmt)
	XpmFree(colors_cmt);
    if (pixels_cmt)
	XpmFree(pixels_cmt);
    if (ximage)
	XDestroyImage(ximage);
    if (shapeimage)
	XDestroyImage(shapeimage);
    if (image_pixels)
	XpmFree(image_pixels);
    if (mask_pixels)
	XpmFree(mask_pixels);
    if (nalloc_pixels)
	(*freeColors)(display, colormap, alloc_pixels, nalloc_pixels, NULL);
    if (alloc_pixels)
	XpmFree(alloc_pixels);
    if (used_pixels)
	XpmFree(used_pixels);

    return (ErrorStatus);
}

static int
ParseAndPutPixels(
#ifdef FOR_MSW
		  dc,
#endif
		  data, width, height, ncolors, cpp, colorTable, hashtable,
		  image, image_pixels, shapeimage, shape_pixels)
#ifdef FOR_MSW
    Display *dc;
#endif
    xpmData *data;
    unsigned int width;
    unsigned int height;
    unsigned int ncolors;
    unsigned int cpp;
    XpmColor *colorTable;
    xpmHashTable *hashtable;
    XImage *image;
    Pixel *image_pixels;
    XImage *shapeimage;
    Pixel *shape_pixels;
{
    unsigned int a, x, y;

    switch (cpp) {

    case (1):				/* Optimize for single character
					 * colors */
	{
	    unsigned short colidx[256];
#ifdef FOR_MSW
	    HDC shapedc;
	    HBITMAP obm, sobm;

	    if ( shapeimage ) {
		shapedc = CreateCompatibleDC(*dc);
		sobm = SelectObject(shapedc, shapeimage->bitmap);
	    } else {
	        shapedc = NULL;
	    }
	    obm = SelectObject(*dc, image->bitmap);
#endif


	    memset((char *)colidx, 0, 256 * sizeof(short));
	    for (a = 0; a < ncolors; a++)
		colidx[(unsigned char)colorTable[a].string[0]] = a + 1;

	    for (y = 0; y < height; y++) {
		xpmNextString(data);
		for (x = 0; x < width; x++) {
		    int c = xpmGetC(data);

		    if (c > 0 && c < 256 && colidx[c] != 0) {
#ifndef FOR_MSW
			XPutPixel(image, x, y, image_pixels[colidx[c] - 1]);
			if (shapeimage)
			    XPutPixel(shapeimage, x, y,
				      shape_pixels[colidx[c] - 1]);
#else
			SetPixel(*dc, x, y, image_pixels[colidx[c] - 1]);
			if (shapedc) {
			    SetPixel(shapedc, x, y, shape_pixels[colidx[c] - 1]);
			}
#endif
		    } else
			return (XpmFileInvalid);
		}
	    }
#ifdef FOR_MSW
	    if ( shapedc ) {
	        SelectObject(shapedc, sobm);
		DeleteDC(shapedc);
	    }
	    SelectObject(*dc, obm);
#endif
	}
	break;

    case (2):				/* Optimize for double character
					 * colors */
	{

/* free all allocated pointers at all exits */
#define FREE_CIDX {int f; for (f = 0; f < 256; f++) \
if (cidx[f]) XpmFree(cidx[f]);}

	    /* array of pointers malloced by need */
	    unsigned short *cidx[256];
	    int char1;

	    memset((char *)cidx, 0, 256 * sizeof(unsigned short *)); /* init */
	    for (a = 0; a < ncolors; a++) {
		char1 = colorTable[a].string[0];
		if (cidx[char1] == NULL) { /* get new memory */
		    cidx[char1] = (unsigned short *)
			XpmCalloc(256, sizeof(unsigned short));
		    if (cidx[char1] == NULL) { /* new block failed */
			FREE_CIDX;
			return (XpmNoMemory);
		    }
		}
		cidx[char1][(unsigned char)colorTable[a].string[1]] = a + 1;
	    }

	    for (y = 0; y < height; y++) {
		xpmNextString(data);
		for (x = 0; x < width; x++) {
		    int cc1 = xpmGetC(data);
		    if (cc1 > 0 && cc1 < 256) {
			int cc2 = xpmGetC(data);
			if (cc2 > 0 && cc2 < 256 && cidx[cc1][cc2] != 0) {
#ifndef FOR_MSW
			    XPutPixel(image, x, y,
				      image_pixels[cidx[cc1][cc2] - 1]);
			    if (shapeimage)
				XPutPixel(shapeimage, x, y,
					  shape_pixels[cidx[cc1][cc2] - 1]);
#else
			SelectObject(*dc, image->bitmap);
			SetPixel(*dc, x, y, image_pixels[cidx[cc1][cc2] - 1]);
			if (shapeimage) {
			    SelectObject(*dc, shapeimage->bitmap);
			    SetPixel(*dc, x, y,
				     shape_pixels[cidx[cc1][cc2] - 1]);
			}
#endif
			} else {
			    FREE_CIDX;
			    return (XpmFileInvalid);
			}
		    } else {
			FREE_CIDX;
			return (XpmFileInvalid);
		    }
		}
	    }
	    FREE_CIDX;
	}
	break;

    default:				/* Non-optimized case of long color
					 * names */
	{
	    char *s;
	    char buf[BUFSIZ];

	    buf[cpp] = '\0';
	    if (USE_HASHTABLE) {
		xpmHashAtom *slot;

		for (y = 0; y < height; y++) {
		    xpmNextString(data);
		    for (x = 0; x < width; x++) {
			for (a = 0, s = buf; a < cpp; a++, s++)
			    *s = xpmGetC(data);
			slot = xpmHashSlot(hashtable, buf);
			if (!*slot)	/* no color matches */
			    return (XpmFileInvalid);
#ifndef FOR_MSW
			XPutPixel(image, x, y,
				  image_pixels[HashColorIndex(slot)]);
			if (shapeimage)
			    XPutPixel(shapeimage, x, y,
				      shape_pixels[HashColorIndex(slot)]);
#else
			SelectObject(*dc, image->bitmap);
			SetPixel(*dc, x, y,
				 image_pixels[HashColorIndex(slot)]);
			if (shapeimage) {
			    SelectObject(*dc, shapeimage->bitmap);
			    SetPixel(*dc, x, y,
				     shape_pixels[HashColorIndex(slot)]);
			}
#endif
		    }
		}
	    } else {
		for (y = 0; y < height; y++) {
		    xpmNextString(data);
		    for (x = 0; x < width; x++) {
			for (a = 0, s = buf; a < cpp; a++, s++)
			    *s = xpmGetC(data);
			for (a = 0; a < ncolors; a++)
			    if (!strcmp(colorTable[a].string, buf))
				break;
			if (a == ncolors)	/* no color matches */
			    return (XpmFileInvalid);
#ifndef FOR_MSW
			XPutPixel(image, x, y, image_pixels[a]);
			if (shapeimage)
			    XPutPixel(shapeimage, x, y, shape_pixels[a]);
#else
			SelectObject(*dc, image->bitmap);
			SetPixel(*dc, x, y, image_pixels[a]);
			if (shapeimage) {
			    SelectObject(*dc, shapeimage->bitmap);
			    SetPixel(*dc, x, y, shape_pixels[a]);
			}
#endif
		    }
		}
	    }
	}
	break;
    }
    return (XpmSuccess);
}

/*****************************************************************************\
* data.c:                                                                     *
*                                                                             *
*  XPM library                                                                *
*  IO utilities                                                               *
*                                                                             *
*  Developed by Arnaud Le Hors                                                *
\*****************************************************************************/

/* Official version number */
static const char RCS_Version[] = "$XpmVersion: 3.4j $";

/* Internal version number */
static const char RCS_Id[] = "$Id: Xpm.c,v 1.10 2002/02/25 13:17:19 amai Exp $";


LFUNC(ParseComment, int, (xpmData * mdata));

static int
ParseComment(mdata)
    xpmData *mdata;
{
    if (mdata->type == XPMBUFFER) {
	register char c;
	register unsigned int n = 0;
	unsigned int notend;
	char *s, *s2;

	s = mdata->Comment;
	*s = mdata->Bcmt[0];

	/* skip the string beginning comment */
	s2 = mdata->Bcmt;
	do {
	    c = *mdata->cptr++;
	    *++s = c;
	    n++;
	    s2++;
	} while (c == *s2 && *s2 != '\0' && c);

	if (*s2 != '\0') {
	    /* this wasn't the beginning of a comment */
	    mdata->cptr -= n;
	    return 0;
	}
	/* store comment */
	mdata->Comment[0] = *s;
	s = mdata->Comment;
	notend = 1;
	n = 0;
	while (notend) {
	    s2 = mdata->Ecmt;
	    while (*s != *s2 && c) {
		c = *mdata->cptr++;
		if (n == XPMMAXCMTLEN - 1)  { /* forget it */
		    s = mdata->Comment;
		    n = 0;
		}
		*++s = c;
		n++;
	    }
	    mdata->CommentLength = n;
	    do {
		c = *mdata->cptr++;
		if (n == XPMMAXCMTLEN - 1)  { /* forget it */
		    s = mdata->Comment;
		    n = 0;
		}
		*++s = c;
		n++;
		s2++;
	    } while (c == *s2 && *s2 != '\0' && c);
	    if (*s2 == '\0') {
		/* this is the end of the comment */
		notend = 0;
		mdata->cptr--;
	    }
	}
	return 0;
    } else {
	FILE *file = mdata->stream.file;
	register int c;
	register unsigned int n = 0, a;
	unsigned int notend;
	char *s, *s2;

	s = mdata->Comment;
	*s = mdata->Bcmt[0];

	/* skip the string beginning comment */
	s2 = mdata->Bcmt;
	do {
	    c = getc(file);
	    *++s = c;
	    n++;
	    s2++;
	} while (c == *s2 && *s2 != '\0' && c != EOF);

	if (*s2 != '\0') {
	    /* this wasn't the beginning of a comment */
	    /* put characters back in the order that we got them */
	    for (a = n; a > 0; a--, s--)
		ungetc(*s, file);
	    return 0;
	}
	/* store comment */
	mdata->Comment[0] = *s;
	s = mdata->Comment;
	notend = 1;
	n = 0;
	while (notend) {
	    s2 = mdata->Ecmt;
	    while (*s != *s2 && c != EOF) {
		c = getc(file);
		if (n == XPMMAXCMTLEN - 1)  { /* forget it */
		    s = mdata->Comment;
		    n = 0;
		}
		*++s = c;
		n++;
	    }
	    mdata->CommentLength = n;
	    do {
		c = getc(file);
		if (n == XPMMAXCMTLEN - 1)  { /* forget it */
		    s = mdata->Comment;
		    n = 0;
		}
		*++s = c;
		n++;
		s2++;
	    } while (c == *s2 && *s2 != '\0' && c != EOF);
	    if (*s2 == '\0') {
		/* this is the end of the comment */
		notend = 0;
		ungetc(*s, file);
	    }
	}
	return 0;
    }
}

/*
 * skip to the end of the current string and the beginning of the next one
 */
int
xpmNextString(mdata)
    xpmData *mdata;
{
    if (!mdata->type)
	mdata->cptr = (mdata->stream.data)[++mdata->line];
    else if (mdata->type == XPMBUFFER) {
	register char c;

	/* get to the end of the current string */
	if (mdata->Eos)
	    while ((c = *mdata->cptr++) && c != mdata->Eos);

	/*
	 * then get to the beginning of the next string looking for possible
	 * comment
	 */
	if (mdata->Bos) {
	    while ((c = *mdata->cptr++) && c != mdata->Bos)
		if (mdata->Bcmt && c == mdata->Bcmt[0])
		    ParseComment(mdata);
	} else if (mdata->Bcmt) {	/* XPM2 natural */
	    while ((c = *mdata->cptr++) == mdata->Bcmt[0])
		ParseComment(mdata);
	    mdata->cptr--;
	}
    } else {
	register int c;
	FILE *file = mdata->stream.file;

	/* get to the end of the current string */
	if (mdata->Eos)
	    while ((c = getc(file)) != mdata->Eos && c != EOF);

	/*
	 * then get to the beginning of the next string looking for possible
	 * comment
	 */
	if (mdata->Bos) {
	    while ((c = getc(file)) != mdata->Bos && c != EOF)
		if (mdata->Bcmt && c == mdata->Bcmt[0])
		    ParseComment(mdata);

	} else if (mdata->Bcmt) {	/* XPM2 natural */
	    while ((c = getc(file)) == mdata->Bcmt[0])
		ParseComment(mdata);
	    ungetc(c, file);
	}
    }
    return 0;
}


/*
 * skip whitespace and compute the following unsigned int,
 * returns 1 if one is found and 0 if not
 */
int
xpmNextUI(mdata, ui_return)
    xpmData *mdata;
    unsigned int *ui_return;
{
    char buf[BUFSIZ];
    int l;

    l = xpmNextWord(mdata, buf, BUFSIZ);
    return xpmatoui(buf, l, ui_return);
}

/*
 * skip whitespace and return the following word
 */
unsigned int
xpmNextWord(mdata, buf, buflen)
    xpmData *mdata;
    char *buf;
    unsigned int buflen;
{
    register unsigned int n = 0;
    int c;

    if (!mdata->type || mdata->type == XPMBUFFER) {
	while (isspace(c = *mdata->cptr) && c != mdata->Eos)
	    mdata->cptr++;
	do {
	    c = *mdata->cptr++;
	    *buf++ = c;
	    n++;
	} while (!isspace(c) && c != mdata->Eos && n < buflen);
	n--;
	mdata->cptr--;
    } else {
	FILE *file = mdata->stream.file;

	while ((c = getc(file)) != EOF && isspace(c) && c != mdata->Eos);
	while (!isspace(c) && c != mdata->Eos && c != EOF && n < buflen) {
	    *buf++ = c;
	    n++;
	    c = getc(file);
	}
	ungetc(c, file);
    }
    return (n);
}

/*
 * return end of string - WARNING: malloc!
 */
int
xpmGetString(mdata, sptr, l)
    xpmData *mdata;
    char **sptr;
    unsigned int *l;
{
    unsigned int i, n = 0;
    int c;
    char *p = NULL, *q, buf[BUFSIZ];

    if (!mdata->type || mdata->type == XPMBUFFER) {
	if (mdata->cptr) {
	    char *start = mdata->cptr;
	    while ((c = *mdata->cptr) && c != mdata->Eos)
		mdata->cptr++;
	    n = mdata->cptr - start + 1;
	    p = (char *) XpmMalloc(n);
	    if (!p)
		return (XpmNoMemory);
	    strncpy(p, start, n);
	    if (mdata->type)		/* XPMBUFFER */
		p[n - 1] = '\0';
	}
    } else {
	FILE *file = mdata->stream.file;

	if ((c = getc(file)) == EOF)
	    return (XpmFileInvalid);

	i = 0;
	q = buf;
	p = (char *) XpmMalloc(1);
	while (c != mdata->Eos && c != EOF) {
	    if (i == BUFSIZ) {
		/* get to the end of the buffer */
		/* malloc needed memory */
		q = (char *) XpmRealloc(p, n + i);
		if (!q) {
		    XpmFree(p);
		    return (XpmNoMemory);
		}
		p = q;
		q += n;
		/* and copy what we already have */
		strncpy(q, buf, i);
		n += i;
		i = 0;
		q = buf;
	    }
	    *q++ = c;
	    i++;
	    c = getc(file);
	}
	if (c == EOF) {
	    XpmFree(p);
	    return (XpmFileInvalid);
	}
	if (n + i != 0) {
	    /* malloc needed memory */
	    q = (char *) XpmRealloc(p, n + i + 1);
	    if (!q) {
		XpmFree(p);
		return (XpmNoMemory);
	    }
	    p = q;
	    q += n;
	    /* and copy the buffer */
	    strncpy(q, buf, i);
	    n += i;
	    p[n++] = '\0';
	} else {
	    *p = '\0';
	    n = 1;
	}
	ungetc(c, file);
    }
    *sptr = p;
    *l = n;
    return (XpmSuccess);
}

/*
 * get the current comment line
 */
int
xpmGetCmt(mdata, cmt)
    xpmData *mdata;
    char **cmt;
{
    if (!mdata->type)
	*cmt = NULL;
    else if (mdata->CommentLength) {
	*cmt = (char *) XpmMalloc(mdata->CommentLength + 1);
	strncpy(*cmt, mdata->Comment, mdata->CommentLength);
	(*cmt)[mdata->CommentLength] = '\0';
	mdata->CommentLength = 0;
    } else
	*cmt = NULL;
    return 0;
}

xpmDataType xpmDataTypes[] =
{
    { "", "!", "\n", '\0', '\n', "", "", "", ""},	/* Natural type */
    { "C", "/*", "*/", '"', '"', ",\n", "static char *", "[] = {\n", "};\n"},
    { "Lisp", ";", "\n", '"', '"', "\n", "(setq ", " '(\n", "))\n"},
    { NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, NULL }
};

/*
 * parse xpm header
 */
int
xpmParseHeader(mdata)
    xpmData *mdata;
{
    char buf[BUFSIZ];
    int l, n = 0;

    if (mdata->type) {
	mdata->Bos = '\0';
	mdata->Eos = '\n';
	mdata->Bcmt = mdata->Ecmt = NULL;
	l = xpmNextWord(mdata, buf, BUFSIZ);
	if (l == 7 && !strncmp("#define", buf, 7)) {
	    /* this maybe an XPM 1 file */
	    char *ptr;

	    l = xpmNextWord(mdata, buf, BUFSIZ);
	    if (!l)
		return (XpmFileInvalid);
	    buf[l] = '\0';
	    ptr = strrchr(buf, '_');
	    if (!ptr || strncmp("_format", ptr, l - (ptr - buf)))
		return XpmFileInvalid;
	    /* this is definitely an XPM 1 file */
	    mdata->format = 1;
	    n = 1;			/* handle XPM1 as mainly XPM2 C */
	} else {

	    /*
	     * skip the first word, get the second one, and see if this is
	     * XPM 2 or 3
	     */
	    l = xpmNextWord(mdata, buf, BUFSIZ);
	    if ((l == 3 && !strncmp("XPM", buf, 3)) ||
		(l == 4 && !strncmp("XPM2", buf, 4))) {
		if (l == 3)
		    n = 1;		/* handle XPM as XPM2 C */
		else {
		    /* get the type key word */
		    l = xpmNextWord(mdata, buf, BUFSIZ);

		    /*
		     * get infos about this type
		     */
		    while (xpmDataTypes[n].type
			   && strncmp(xpmDataTypes[n].type, buf, l))
			n++;
		}
		mdata->format = 0;
	    } else
		/* nope this is not an XPM file */
		return XpmFileInvalid;
	}
	if (xpmDataTypes[n].type) {
	    if (n == 0) {		/* natural type */
		mdata->Bcmt = xpmDataTypes[n].Bcmt;
		mdata->Ecmt = xpmDataTypes[n].Ecmt;
		xpmNextString(mdata);	/* skip the end of the headerline */
		mdata->Bos = xpmDataTypes[n].Bos;
		mdata->Eos = xpmDataTypes[n].Eos;
	    } else {
		mdata->Bcmt = xpmDataTypes[n].Bcmt;
		mdata->Ecmt = xpmDataTypes[n].Ecmt;
		if (!mdata->format) {	/* XPM 2 or 3 */
		    mdata->Bos = xpmDataTypes[n].Bos;
		    mdata->Eos = '\0';
		    /* get to the beginning of the first string */
		    xpmNextString(mdata);
		    mdata->Eos = xpmDataTypes[n].Eos;
		} else			/* XPM 1 skip end of line */
		    xpmNextString(mdata);
	    }
	} else
	    /* we don't know about that type of XPM file... */
	    return XpmFileInvalid;
    }
    return XpmSuccess;
}

/*****************************************************************************\
* hashtab.c:                                                                  *
*                                                                             *
*  XPM library                                                                *
*                                                                             *
*  Developed by Arnaud Le Hors                                                *
*  this originaly comes from Colas Nahaboo as a part of Wool                  *
*                                                                             *
\*****************************************************************************/

LFUNC(AtomMake, xpmHashAtom, (char *name, void *data));
LFUNC(HashTableGrows, int, (xpmHashTable * table));

static xpmHashAtom
AtomMake(name, data)			/* makes an atom */
    char *name;				/* WARNING: is just pointed to */
    void *data;
{
    xpmHashAtom object = (xpmHashAtom) XpmMalloc(sizeof(struct _xpmHashAtom));

    if (object) {
	object->name = name;
	object->data = data;
    }
    return object;
}

/************************\
* 			 *
*  hash table routines 	 *
* 			 *
\************************/

/*
 * Hash function definition:
 * HASH_FUNCTION: hash function, hash = hashcode, hp = pointer on char,
 *				 hash2 = temporary for hashcode.
 * INITIAL_TABLE_SIZE in slots
 * HASH_TABLE_GROWS how hash table grows.
 */

/* Mock lisp function */
#define HASH_FUNCTION 	  hash = (hash << 5) - hash + *hp++;
/* #define INITIAL_HASH_SIZE 2017 */
#define INITIAL_HASH_SIZE 256		/* should be enough for colors */
#define HASH_TABLE_GROWS  size = size * 2;

/* aho-sethi-ullman's HPJ (sizes should be primes)*/
#ifdef notdef
#define HASH_FUNCTION	hash <<= 4; hash += *hp++; \
    if(hash2 = hash & 0xf0000000) hash ^= (hash2 >> 24) ^ hash2;
#define INITIAL_HASH_SIZE 4095		/* should be 2^n - 1 */
#define HASH_TABLE_GROWS  size = size << 1 + 1;
#endif

/* GNU emacs function */
/*
#define HASH_FUNCTION 	  hash = (hash << 3) + (hash >> 28) + *hp++;
#define INITIAL_HASH_SIZE 2017
#define HASH_TABLE_GROWS  size = size * 2;
*/

/* end of hash functions */

/*
 * The hash table is used to store atoms via their NAME:
 *
 * NAME --hash--> ATOM |--name--> "foo"
 *		       |--data--> any value which has to be stored
 *
 */

/*
 * xpmHashSlot gives the slot (pointer to xpmHashAtom) of a name
 * (slot points to NULL if it is not defined)
 *
 */

xpmHashAtom *
xpmHashSlot(table, s)
    xpmHashTable *table;
    char *s;
{
    xpmHashAtom *atomTable = table->atomTable;
    unsigned int hash;
    xpmHashAtom *p;
    char *hp = s;
    char *ns;

    hash = 0;
    while (*hp) {			/* computes hash function */
	HASH_FUNCTION
    }
    p = atomTable + hash % table->size;
    while (*p) {
	ns = (*p)->name;
	if (ns[0] == s[0] && strcmp(ns, s) == 0)
	    break;
	p--;
	if (p < atomTable)
	    p = atomTable + table->size - 1;
    }
    return p;
}

static int
HashTableGrows(table)
    xpmHashTable *table;
{
    xpmHashAtom *atomTable = table->atomTable;
    int size = table->size;
    xpmHashAtom *t, *p;
    int i;
    int oldSize = size;

    t = atomTable;
    HASH_TABLE_GROWS
	table->size = size;
    table->limit = size / 3;
    atomTable = (xpmHashAtom *) XpmMalloc(size * sizeof(*atomTable));
    if (!atomTable)
	return (XpmNoMemory);
    table->atomTable = atomTable;
    for (p = atomTable + size; p > atomTable;)
	*--p = NULL;
    for (i = 0, p = t; i < oldSize; i++, p++)
	if (*p) {
	    xpmHashAtom *ps = xpmHashSlot(table, (*p)->name);

	    *ps = *p;
	}
    XpmFree(t);
    return (XpmSuccess);
}

/*
 * xpmHashIntern(table, name, data)
 * an xpmHashAtom is created if name doesn't exist, with the given data.
 */

int
xpmHashIntern(table, tag, data)
    xpmHashTable *table;
    char *tag;
    void *data;
{
    xpmHashAtom *slot;

    if (!*(slot = xpmHashSlot(table, tag))) {
	/* undefined, make a new atom with the given data */
	if (!(*slot = AtomMake(tag, data)))
	    return (XpmNoMemory);
	if (table->used >= table->limit) {
	    int ErrorStatus;

	    if ((ErrorStatus = HashTableGrows(table)) != XpmSuccess)
		return (ErrorStatus);
	    table->used++;
	    return (XpmSuccess);
	}
	table->used++;
    }
    return (XpmSuccess);
}

/*
 *  must be called before allocating any atom
 */

int
xpmHashTableInit(table)
    xpmHashTable *table;
{
    xpmHashAtom *p;
    xpmHashAtom *atomTable;

    table->size = INITIAL_HASH_SIZE;
    table->limit = table->size / 3;
    table->used = 0;
    atomTable = (xpmHashAtom *) XpmMalloc(table->size * sizeof(*atomTable));
    if (!atomTable)
	return (XpmNoMemory);
    for (p = atomTable + table->size; p > atomTable;)
	*--p = NULL;
    table->atomTable = atomTable;
    return (XpmSuccess);
}

/*
 *   frees a hashtable and all the stored atoms
 */

void
xpmHashTableFree(table)
    xpmHashTable *table;
{
    xpmHashAtom *p;
    xpmHashAtom *atomTable = table->atomTable;

    if (!atomTable)
	return;
    for (p = atomTable + table->size; p > atomTable;)
	if (*--p)
	    XpmFree(*p);
    XpmFree(atomTable);
    table->atomTable = NULL;
}

/*****************************************************************************\
* misc.c:                                                                     *
*                                                                             *
*  XPM library                                                                *
*  Miscellaneous utilities                                                    *
*                                                                             *
*  Developed by Arnaud Le Hors                                                *
\*****************************************************************************/

#ifndef HAVE_STRDUP
/*
 * in case strdup is not provided by the system here is one
 * which does the trick
 */
char *
xpmstrdup(s1)
    char *s1;
{
    char *s2;
    int l = strlen(s1) + 1;

    if (s2 = (char *) XpmMalloc(l))
	strcpy(s2, s1);
    return s2;
}

#endif

unsigned int
xpmatoui(p, l, ui_return)
    register char *p;
    unsigned int l;
    unsigned int *ui_return;
{
    register unsigned int n, i;

    n = 0;
    for (i = 0; i < l; i++)
	if (*p >= '0' && *p <= '9')
	    n = n * 10 + *p++ - '0';
	else
	    break;

    if (i != 0 && i == l) {
	*ui_return = n;
	return 1;
    } else
	return 0;
}

/*
 * Function returning a character string related to an error code.
 */
char *
XpmGetErrorString(errcode)
    int errcode;
{
    switch (errcode) {
    case XpmColorError:
	return ("XpmColorError");
    case XpmSuccess:
	return ("XpmSuccess");
    case XpmOpenFailed:
	return ("XpmOpenFailed");
    case XpmFileInvalid:
	return ("XpmFileInvalid");
    case XpmNoMemory:
	return ("XpmNoMemory");
    case XpmColorFailed:
	return ("XpmColorFailed");
    default:
	return ("Invalid XpmError");
    }
}

/*
 * The following function provides a way to figure out if the linked library is
 * newer or older than the one with which a program has been first compiled.
 */
int
XpmLibraryVersion(void)
{
    return XpmIncludeVersion;
}


/* The following should help people wanting to use their own functions */
#ifdef XpmFree
#undef XpmFree
#endif

void
XpmFree(ptr)
    void *ptr;
{
    free(ptr);
}

/*****************************************************************************\
* parse.c:                                                                    *
*                                                                             *
*  XPM library                                                                *
*  Parse an XPM file or array and store the found informations                *
*  in the given XpmImage structure.                                           *
*                                                                             *
*  Developed by Arnaud Le Hors                                                *
\*****************************************************************************/

/*
 * The code related to FOR_MSW has been added by
 * HeDu (hedu@cul-ipn.uni-kiel.de) 4/94
 */

LFUNC(ParsePixels, int, (xpmData *data, unsigned int width,
			 unsigned int height, unsigned int ncolors,
			 unsigned int cpp, XpmColor *colorTable,
			 xpmHashTable *hashtable, unsigned int **pixels));

char *xpmColorKeys[] = {
    "s",				/* key #1: symbol */
    "m",				/* key #2: mono visual */
    "g4",				/* key #3: 4 grays visual */
    "g",				/* key #4: gray visual */
    "c",				/* key #5: color visual */
};


/* function call in case of error */
#undef RETURN
#define RETURN(status) \
{ \
      goto error; \
}

/*
 * This function parses an Xpm file or data and store the found informations
 * in an an XpmImage structure which is returned.
 */
int
xpmParseData(data, image, info)
    xpmData *data;
    XpmImage *image;
    XpmInfo *info;
{
    /* variables to return */
    unsigned int width, height, ncolors, cpp;
    unsigned int x_hotspot, y_hotspot, hotspot = 0, extensions = 0;
    XpmColor *colorTable = NULL;
    unsigned int *pixelindex = NULL;
    char *hints_cmt = NULL;
    char *colors_cmt = NULL;
    char *pixels_cmt = NULL;

    unsigned int cmts;
    int ErrorStatus;
    xpmHashTable hashtable;

    cmts = info && (info->valuemask & XpmReturnComments);

    /*
     * parse the header
     */
    ErrorStatus = xpmParseHeader(data);
    if (ErrorStatus != XpmSuccess)
	return (ErrorStatus);

    /*
     * read values
     */
    ErrorStatus = xpmParseValues(data, &width, &height, &ncolors, &cpp,
				 &x_hotspot, &y_hotspot, &hotspot,
				 &extensions);
    if (ErrorStatus != XpmSuccess)
	return (ErrorStatus);

    /*
     * store the hints comment line
     */
    if (cmts)
	xpmGetCmt(data, &hints_cmt);

    /*
     * init the hastable
     */
    if (USE_HASHTABLE) {
	ErrorStatus = xpmHashTableInit(&hashtable);
	if (ErrorStatus != XpmSuccess)
	    return (ErrorStatus);
    }

    /*
     * read colors
     */
    ErrorStatus = xpmParseColors(data, ncolors, cpp, &colorTable, &hashtable);
    if (ErrorStatus != XpmSuccess) {
	if (USE_HASHTABLE)
	    xpmHashTableFree(&hashtable);
	RETURN(ErrorStatus);
    }

    /*
     * store the colors comment line
     */
    if (cmts)
	xpmGetCmt(data, &colors_cmt);

    /*
     * read pixels and index them on color number
     */
    ErrorStatus = ParsePixels(data, width, height, ncolors, cpp, colorTable,
			      &hashtable, &pixelindex);

    /*
     * free the hastable
     */
    if (USE_HASHTABLE)
	xpmHashTableFree(&hashtable);

    if (ErrorStatus != XpmSuccess)
	RETURN(ErrorStatus);

    /*
     * store the pixels comment line
     */
    if (cmts)
	xpmGetCmt(data, &pixels_cmt);

    /*
     * parse extensions
     */
    if (info && (info->valuemask & XpmReturnExtensions))
    {
	if (extensions) {
	    ErrorStatus = xpmParseExtensions(data, &info->extensions,
					     &info->nextensions);
	    if (ErrorStatus != XpmSuccess)
		RETURN(ErrorStatus);
	} else {
	    info->extensions = NULL;
	    info->nextensions = 0;
	}
    }
    /*
     * store found informations in the XpmImage structure
     */
    image->width = width;
    image->height = height;
    image->cpp = cpp;
    image->ncolors = ncolors;
    image->colorTable = colorTable;
    image->data = pixelindex;

    if (info) {
	if (cmts) {
	    info->hints_cmt = hints_cmt;
	    info->colors_cmt = colors_cmt;
	    info->pixels_cmt = pixels_cmt;
	}
	if (hotspot) {
	    info->x_hotspot = x_hotspot;
	    info->y_hotspot = y_hotspot;
	    info->valuemask |= XpmHotspot;
	}
    }
    return (XpmSuccess);

/* exit point in case of error, free only locally allocated variables */
error:
    if (colorTable)
	xpmFreeColorTable(colorTable, ncolors);
    if (pixelindex)
	XpmFree(pixelindex);
    if (hints_cmt)
	XpmFree(hints_cmt);
    if (colors_cmt)
	XpmFree(colors_cmt);
    if (pixels_cmt)
	XpmFree(pixels_cmt);

    return(ErrorStatus);
}

int
xpmParseValues(data, width, height, ncolors, cpp,
	    x_hotspot, y_hotspot, hotspot, extensions)
    xpmData *data;
    unsigned int *width, *height, *ncolors, *cpp;
    unsigned int *x_hotspot, *y_hotspot, *hotspot;
    unsigned int *extensions;
{
    unsigned int l;
    char buf[BUFSIZ];

    if (!data->format) {		/* XPM 2 or 3 */

	/*
	 * read values: width, height, ncolors, chars_per_pixel
	 */
	if (!(xpmNextUI(data, width) && xpmNextUI(data, height)
	      && xpmNextUI(data, ncolors) && xpmNextUI(data, cpp)))
	    return (XpmFileInvalid);

	/*
	 * read optional information (hotspot and/or XPMEXT) if any
	 */
	l = xpmNextWord(data, buf, BUFSIZ);
	if (l) {
	    *extensions = (l == 6 && !strncmp("XPMEXT", buf, 6));
	    if (*extensions)
		*hotspot = (xpmNextUI(data, x_hotspot)
			    && xpmNextUI(data, y_hotspot));
	    else {
		*hotspot = (xpmatoui(buf, l, x_hotspot)
			    && xpmNextUI(data, y_hotspot));
		l = xpmNextWord(data, buf, BUFSIZ);
		*extensions = (l == 6 && !strncmp("XPMEXT", buf, 6));
	    }
	}
    } else {

	/*
	 * XPM 1 file read values: width, height, ncolors, chars_per_pixel
	 */
	int i;
	char *ptr;
	Bool got_one, saw_width = False, saw_height = False;
	Bool saw_ncolors = False, saw_chars_per_pixel = False;

	for (i = 0; i < 4; i++) {
	    l = xpmNextWord(data, buf, BUFSIZ);
	    if (l != 7 || strncmp("#define", buf, 7))
		return (XpmFileInvalid);
	    l = xpmNextWord(data, buf, BUFSIZ);
	    if (!l)
		return (XpmFileInvalid);
	    buf[l] = '\0';
	    ptr = buf;
	    got_one = False;
	    while (!got_one) {
		ptr = strchr(ptr, '_');
		if (!ptr)
		    return (XpmFileInvalid);
		switch (l - (ptr - buf)) {
		case 6:
		    if (saw_width || strncmp("_width", ptr, 6)
			|| !xpmNextUI(data, width))
			return (XpmFileInvalid);
		    else
			saw_width = True;
		    got_one = True;
		    break;
		case 7:
		    if (saw_height || strncmp("_height", ptr, 7)
			|| !xpmNextUI(data, height))
			return (XpmFileInvalid);
		    else
			saw_height = True;
		    got_one = True;
		    break;
		case 8:
		    if (saw_ncolors || strncmp("_ncolors", ptr, 8)
			|| !xpmNextUI(data, ncolors))
			return (XpmFileInvalid);
		    else
			saw_ncolors = True;
		    got_one = True;
		    break;
		case 16:
		    if (saw_chars_per_pixel
			|| strncmp("_chars_per_pixel", ptr, 16)
			|| !xpmNextUI(data, cpp))
			return (XpmFileInvalid);
		    else
			saw_chars_per_pixel = True;
		    got_one = True;
		    break;
		default:
		    ptr++;
		}
	    }
	    /* skip the end of line */
	    xpmNextString(data);
	}
	if (!saw_width || !saw_height || !saw_ncolors || !saw_chars_per_pixel)
	  return (XpmFileInvalid);

	*hotspot = 0;
	*extensions = 0;
    }
    return (XpmSuccess);
}

int
xpmParseColors(data, ncolors, cpp, colorTablePtr, hashtable)
    xpmData *data;
    unsigned int ncolors;
    unsigned int cpp;
    XpmColor **colorTablePtr;
    xpmHashTable *hashtable;
{
    unsigned int key, l, a, b;
    unsigned int curkey;		/* current color key */
    unsigned int lastwaskey;		/* key read */
    char buf[BUFSIZ];
    char curbuf[BUFSIZ];		/* current buffer */
    char **sptr, *s;
    XpmColor *color;
    XpmColor *colorTable;
    char **defaults;
    int ErrorStatus;

    colorTable = (XpmColor *) XpmCalloc(ncolors, sizeof(XpmColor));
    if (!colorTable)
	return (XpmNoMemory);

    if (!data->format) {		/* XPM 2 or 3 */
	for (a = 0, color = colorTable; a < ncolors; a++, color++) {
	    xpmNextString(data);	/* skip the line */

	    /*
	     * read pixel value
	     */
	    color->string = (char *) XpmMalloc(cpp + 1);
	    if (!color->string) {
		xpmFreeColorTable(colorTable, ncolors);
		return (XpmNoMemory);
	    }
	    for (b = 0, s = color->string; b < cpp; b++, s++)
		*s = xpmGetC(data);
	    *s = '\0';

	    /*
	     * store the string in the hashtable with its color index number
	     */
	    if (USE_HASHTABLE) {
		ErrorStatus =
		    xpmHashIntern(hashtable, color->string, HashAtomData(a));
		if (ErrorStatus != XpmSuccess) {
		    xpmFreeColorTable(colorTable, ncolors);
		    return (ErrorStatus);
		}
	    }

	    /*
	     * read color keys and values
	     */
	    defaults = (char **) color;
	    curkey = 0;
	    lastwaskey = 0;
	    *curbuf = '\0';		/* init curbuf */
	    while ((l = xpmNextWord(data, buf, BUFSIZ))) {
		if (!lastwaskey) {
		    for (key = 0, sptr = xpmColorKeys; key < NKEYS; key++,
			 sptr++)
			if ((strlen(*sptr) == l) && (!strncmp(*sptr, buf, l)))
			    break;
		}
		if (!lastwaskey && key < NKEYS) {	/* open new key */
		    if (curkey) {	/* flush string */
			s = (char *) XpmMalloc(strlen(curbuf) + 1);
			if (!s) {
			    xpmFreeColorTable(colorTable, ncolors);
			    return (XpmNoMemory);
			}
			defaults[curkey] = s;
			strcpy(s, curbuf);
		    }
		    curkey = key + 1;	/* set new key  */
		    *curbuf = '\0';	/* reset curbuf */
		    lastwaskey = 1;
		} else {
		    if (!curkey) {	/* key without value */
			xpmFreeColorTable(colorTable, ncolors);
			return (XpmFileInvalid);
		    }
		    if (!lastwaskey)
			strcat(curbuf, " ");	/* append space */
		    buf[l] = '\0';
		    strcat(curbuf, buf);/* append buf */
		    lastwaskey = 0;
		}
	    }
	    if (!curkey) {		/* key without value */
		xpmFreeColorTable(colorTable, ncolors);
		return (XpmFileInvalid);
	    }
	    s = defaults[curkey] = (char *) XpmMalloc(strlen(curbuf) + 1);
	    if (!s) {
		xpmFreeColorTable(colorTable, ncolors);
		return (XpmNoMemory);
	    }
	    strcpy(s, curbuf);
	}
    } else {				/* XPM 1 */
	/* get to the beginning of the first string */
	data->Bos = '"';
	data->Eos = '\0';
	xpmNextString(data);
	data->Eos = '"';
	for (a = 0, color = colorTable; a < ncolors; a++, color++) {

	    /*
	     * read pixel value
	     */
	    color->string = (char *) XpmMalloc(cpp + 1);
	    if (!color->string) {
		xpmFreeColorTable(colorTable, ncolors);
		return (XpmNoMemory);
	    }
	    for (b = 0, s = color->string; b < cpp; b++, s++)
		*s = xpmGetC(data);
	    *s = '\0';

	    /*
	     * store the string in the hashtable with its color index number
	     */
	    if (USE_HASHTABLE) {
		ErrorStatus =
		    xpmHashIntern(hashtable, color->string, HashAtomData(a));
		if (ErrorStatus != XpmSuccess) {
		    xpmFreeColorTable(colorTable, ncolors);
		    return (ErrorStatus);
		}
	    }

	    /*
	     * read color values
	     */
	    xpmNextString(data);	/* get to the next string */
	    *curbuf = '\0';		/* init curbuf */
	    while ((l = xpmNextWord(data, buf, BUFSIZ))) {
		if (*curbuf != '\0')
		    strcat(curbuf, " ");/* append space */
		buf[l] = '\0';
		strcat(curbuf, buf);	/* append buf */
	    }
	    s = (char *) XpmMalloc(strlen(curbuf) + 1);
	    if (!s) {
		xpmFreeColorTable(colorTable, ncolors);
		return (XpmNoMemory);
	    }
	    strcpy(s, curbuf);
	    color->c_color = s;
	    *curbuf = '\0';		/* reset curbuf */
	    if (a < ncolors - 1)
		xpmNextString(data);	/* get to the next string */
	}
    }
    *colorTablePtr = colorTable;
    return (XpmSuccess);
}

static int
ParsePixels(data, width, height, ncolors, cpp, colorTable, hashtable, pixels)
    xpmData *data;
    unsigned int width;
    unsigned int height;
    unsigned int ncolors;
    unsigned int cpp;
    XpmColor *colorTable;
    xpmHashTable *hashtable;
    unsigned int **pixels;
{
    unsigned int *iptr, *iptr2;
    unsigned int a, x, y;

#ifndef FOR_MSW
    iptr2 = (unsigned int *) XpmMalloc(sizeof(unsigned int) * width * height);
#else

    /*
     * special treatment to trick DOS malloc(size_t) where size_t is 16 bit!!
     * XpmMalloc is defined to longMalloc(long) and checks the 16 bit boundary
     */
    iptr2 = (unsigned int *)
	XpmMalloc((long) sizeof(unsigned int) * (long) width * (long) height);
#endif
    if (!iptr2)
	return (XpmNoMemory);

    iptr = iptr2;

    switch (cpp) {

    case (1):				/* Optimize for single character
					 * colors */
	{
	    unsigned short colidx[256];

	    memset((char *)colidx, 0, 256 * sizeof(short));
	    for (a = 0; a < ncolors; a++)
		colidx[(unsigned char)colorTable[a].string[0]] = a + 1;

	    for (y = 0; y < height; y++) {
		xpmNextString(data);
		for (x = 0; x < width; x++, iptr++) {
		    int c = xpmGetC(data);

		    if (c > 0 && c < 256 && colidx[c] != 0)
			*iptr = colidx[c] - 1;
		    else {
			XpmFree(iptr2);
			return (XpmFileInvalid);
		    }
		}
	    }
	}
	break;

    case (2):				/* Optimize for double character
					 * colors */
	{

/* free all allocated pointers at all exits */
#define FREE_CIDX {int f; for (f = 0; f < 256; f++) \
if (cidx[f]) XpmFree(cidx[f]);}

	    /* array of pointers malloced by need */
	    unsigned short *cidx[256];
	    int char1;

	    memset((char *)cidx, 0, 256 * sizeof(unsigned short *)); /* init */
	    for (a = 0; a < ncolors; a++) {
		char1 = colorTable[a].string[0];
		if (cidx[char1] == NULL) { /* get new memory */
		    cidx[char1] = (unsigned short *)
			XpmCalloc(256, sizeof(unsigned short));
		    if (cidx[char1] == NULL) { /* new block failed */
			FREE_CIDX;
			XpmFree(iptr2);
			return (XpmNoMemory);
		    }
		}
		cidx[char1][(unsigned char)colorTable[a].string[1]] = a + 1;
	    }

	    for (y = 0; y < height; y++) {
		xpmNextString(data);
		for (x = 0; x < width; x++, iptr++) {
		    int cc1 = xpmGetC(data);
		    if (cc1 > 0 && cc1 < 256) {
			int cc2 = xpmGetC(data);
			if (cc2 > 0 && cc2 < 256 && cidx[cc1][cc2] != 0)
			    *iptr = cidx[cc1][cc2] - 1;
			else {
			    FREE_CIDX;
			    XpmFree(iptr2);
			    return (XpmFileInvalid);
			}
		    } else {
			FREE_CIDX;
			XpmFree(iptr2);
			return (XpmFileInvalid);
		    }
		}
	    }
	    FREE_CIDX;
	}
	break;

    default:				/* Non-optimized case of long color
					 * names */
	{
	    char *s;
	    char buf[BUFSIZ];

	    buf[cpp] = '\0';
	    if (USE_HASHTABLE) {
		xpmHashAtom *slot;

		for (y = 0; y < height; y++) {
		    xpmNextString(data);
		    for (x = 0; x < width; x++, iptr++) {
			for (a = 0, s = buf; a < cpp; a++, s++)
			    *s = xpmGetC(data);
			slot = xpmHashSlot(hashtable, buf);
			if (!*slot) {	/* no color matches */
			    XpmFree(iptr2);
			    return (XpmFileInvalid);
			}
			*iptr = HashColorIndex(slot);
		    }
		}
	    } else {
		for (y = 0; y < height; y++) {
		    xpmNextString(data);
		    for (x = 0; x < width; x++, iptr++) {
			for (a = 0, s = buf; a < cpp; a++, s++)
			    *s = xpmGetC(data);
			for (a = 0; a < ncolors; a++)
			    if (!strcmp(colorTable[a].string, buf))
				break;
			if (a == ncolors) {	/* no color matches */
			    XpmFree(iptr2);
			    return (XpmFileInvalid);
			}
			*iptr = a;
		    }
		}
	    }
	}
	break;
    }
    *pixels = iptr2;
    return (XpmSuccess);
}

int
xpmParseExtensions(data, extensions, nextensions)
    xpmData *data;
    XpmExtension **extensions;
    unsigned int *nextensions;
{
    XpmExtension *exts = NULL, *ext;
    unsigned int num = 0;
    unsigned int nlines, a, l, notstart, notend = 0;
    int status;
    char *string, *s, *s2, **sp;

    xpmNextString(data);
    exts = (XpmExtension *) XpmMalloc(sizeof(XpmExtension));
    /* get the whole string */
    status = xpmGetString(data, &string, &l);
    if (status != XpmSuccess) {
	XpmFree(exts);
	return (status);
    }
    /* look for the key word XPMEXT, skip lines before this */
    while ((notstart = strncmp("XPMEXT", string, 6))
	   && (notend = strncmp("XPMENDEXT", string, 9))) {
	XpmFree(string);
	xpmNextString(data);
	status = xpmGetString(data, &string, &l);
	if (status != XpmSuccess) {
	    XpmFree(exts);
	    return (status);
	}
    }
    if (!notstart)
	notend = strncmp("XPMENDEXT", string, 9);
    while (!notstart && notend) {
	/* there starts an extension */
	ext = (XpmExtension *)
	    XpmRealloc(exts, (num + 1) * sizeof(XpmExtension));
	if (!ext) {
	    XpmFree(string);
	    XpmFreeExtensions(exts, num);
	    return (XpmNoMemory);
	}
	exts = ext;
	ext += num;
	/* skip whitespace and store its name */
	s2 = s = string + 6;
	while (isspace(*s2))
	    s2++;
	a = s2 - s;
	ext->name = (char *) XpmMalloc(l - a - 6);
	if (!ext->name) {
	    XpmFree(string);
	    ext->lines = NULL;
	    ext->nlines = 0;
	    XpmFreeExtensions(exts, num + 1);
	    return (XpmNoMemory);
	}
	strncpy(ext->name, s + a, l - a - 6);
	XpmFree(string);
	/* now store the related lines */
	xpmNextString(data);
	status = xpmGetString(data, &string, &l);
	if (status != XpmSuccess) {
	    ext->lines = NULL;
	    ext->nlines = 0;
	    XpmFreeExtensions(exts, num + 1);
	    return (status);
	}
	ext->lines = (char **) XpmMalloc(sizeof(char *));
	nlines = 0;
	while ((notstart = strncmp("XPMEXT", string, 6))
	       && (notend = strncmp("XPMENDEXT", string, 9))) {
	    sp = (char **)
		XpmRealloc(ext->lines, (nlines + 1) * sizeof(char *));
	    if (!sp) {
		XpmFree(string);
		ext->nlines = nlines;
		XpmFreeExtensions(exts, num + 1);
		return (XpmNoMemory);
	    }
	    ext->lines = sp;
	    ext->lines[nlines] = string;
	    nlines++;
	    xpmNextString(data);
	    status = xpmGetString(data, &string, &l);
	    if (status != XpmSuccess) {
		ext->nlines = nlines;
		XpmFreeExtensions(exts, num + 1);
		return (status);
	    }
	}
	if (!nlines) {
	    XpmFree(ext->lines);
	    ext->lines = NULL;
	}
	ext->nlines = nlines;
	num++;
    }
    if (!num) {
	XpmFree(string);
	XpmFree(exts);
	exts = NULL;
    } else if (!notend)
	XpmFree(string);
    *nextensions = num;
    *extensions = exts;
    return (XpmSuccess);
}

/*****************************************************************************\
* rgb.c:                                                                      *
*                                                                             *
*  XPM library                                                                *
*  Rgb file utilities                                                         *
*                                                                             *
*  Developed by Arnaud Le Hors                                                *
\*****************************************************************************/

/*
 * The code related to FOR_MSW has been added by
 * HeDu (hedu@cul-ipn.uni-kiel.de) 4/94
 */

/*
 * Part of this code has been taken from the ppmtoxpm.c file written by Mark
 * W. Snitily but has been modified for my special need
 */

#ifndef FOR_MSW				/* normal part first, MSW part at
					 * the end, (huge ifdef!) */
/*
 * Read a rgb text file.  It stores the rgb values (0->65535)
 * and the rgb mnemonics (malloc'ed) into the "rgbn" array.  Returns the
 * number of entries stored.
 */
int
xpmReadRgbNames(rgb_fname, rgbn)
    char *rgb_fname;
    xpmRgbName rgbn[];

{
    FILE *rgbf;
    int n, items, red, green, blue;
    char line[512], name[512], *rgbname, *s1, *s2;
    xpmRgbName *rgb;

    /* Open the rgb text file.  Abort if error. */
    if ((rgbf = fopen(rgb_fname, "r")) == NULL)
	return 0;

    /* Loop reading each line in the file. */
    n = 0;
    rgb = rgbn; 
    /* Quit if rgb text file has too many entries. */
    while (fgets(line, sizeof(line), rgbf) && n < MAX_RGBNAMES) {

	/* Skip silently if line is bad. */
	items = sscanf(line, "%d %d %d %[^\n]\n", &red, &green, &blue, name);
	if (items != 4)
	    continue;

	/*
	 * Make sure rgb values are within 0->255 range. Skip silently if
	 * bad.
	 */
	if (red < 0 || red > 0xFF ||
	    green < 0 || green > 0xFF ||
	    blue < 0 || blue > 0xFF)
	    continue;

	/* Allocate memory for ascii name. If error give up here. */
	if (!(rgbname = (char *) XpmMalloc(strlen(name) + 1)))
	    break;

	/* Copy string to ascii name and lowercase it. */
	for (s1 = name, s2 = rgbname; *s1; s1++)
	    *s2++ = tolower(*s1);
	*s2 = '\0';

	/* Save the rgb values and ascii name in the array. */
	rgb->r = red * 257;		/* 65535/255 = 257 */
	rgb->g = green * 257;
	rgb->b = blue * 257;
	rgb->name = rgbname;
	rgb++;
	n++;
    }

    fclose(rgbf);

    /* Return the number of read rgb names. */
    return n < 0 ? 0 : n;
}

/*
 * Return the color name corresponding to the given rgb values
 */
char *
xpmGetRgbName(rgbn, rgbn_max, red, green, blue)
    xpmRgbName rgbn[];			/* rgb mnemonics from rgb text file */
    int rgbn_max;			/* number of rgb mnemonics in table */
    int red, green, blue;		/* rgb values */

{
    int i;
    xpmRgbName *rgb;

    /*
     * Just perform a dumb linear search over the rgb values of the color
     * mnemonics.  One could speed things up by sorting the rgb values and
     * using a binary search, or building a hash table, etc...
     */
    for (i = 0, rgb = rgbn; i < rgbn_max; i++, rgb++)
	if (red == rgb->r && green == rgb->g && blue == rgb->b)
	    return rgb->name;

    /* if not found return NULL */
    return NULL;
}

/*
 * Free the strings which have been malloc'ed in xpmReadRgbNames
 */
void
xpmFreeRgbNames(rgbn, rgbn_max)
    xpmRgbName rgbn[];
    int rgbn_max;

{
    int i;
    xpmRgbName *rgb;

    for (i = 0, rgb = rgbn; i < rgbn_max; i++, rgb++)
	XpmFree(rgb->name);
}

#else					/* here comes the MSW part, the
					 * second part of the  huge ifdef */

#include "rgbtab.h"			/* hard coded rgb.txt table */

int
xpmReadRgbNames(rgb_fname, rgbn)
    char *rgb_fname;
    xpmRgbName rgbn[];
{
    /*
     * check for consistency???
     * table has to be sorted for calls on strcasecmp
     */
    return (numTheRGBRecords);
}

/*
 * MSW rgb values are made from 3 BYTEs, this is different from X XColor.red,
 * which has something like #0303 for one color
 */
char *
xpmGetRgbName(rgbn, rgbn_max, red, green, blue)
    xpmRgbName rgbn[];			/* rgb mnemonics from rgb text file
					 * not used */
    int rgbn_max;			/* not used */
    int red, green, blue;		/* rgb values */

{
    int i;
    unsigned long rgbVal;

    i = 0;
    while (i < numTheRGBRecords) {
	rgbVal = theRGBRecords[i].rgb;
	if (GetRValue(rgbVal) == red &&
	    GetGValue(rgbVal) == green &&
	    GetBValue(rgbVal) == blue)
	    return (theRGBRecords[i].name);
	i++;
    }
    return (NULL);
}

/* used in XParseColor in simx.c */
int
xpmGetRGBfromName(inname, r, g, b)
    char *inname;
    int *r, *g, *b;
{
    int left, right, middle;
    int cmp;
    unsigned long rgbVal;
    char *name;
    char *grey, *p;

    name = xpmstrdup(inname);

    /*
     * the table in rgbtab.c has no names with spaces, and no grey, but a
     * lot of gray
     */
    /* so first extract ' ' */
    while (p = strchr(name, ' ')) {
	while (*(p)) {			/* till eof of string */
	    *p = *(p + 1);		/* copy to the left */
	    p++;
	}
    }
    /* fold to lower case */
    p = name;
    while (*p) {
	*p = tolower(*p);
	p++;
    }

    /*
     * substitute Grey with Gray, else rgbtab.h would have more than 100
     * 'duplicate' entries
     */
    if (grey = strstr(name, "grey"))
	grey[2] = 'a';

    /* binary search */
    left = 0;
    right = numTheRGBRecords - 1;
    do {
	middle = (left + right) / 2;
	cmp = xpmstrcasecmp(name, theRGBRecords[middle].name);
	if (cmp == 0) {
	    rgbVal = theRGBRecords[middle].rgb;
	    *r = GetRValue(rgbVal);
	    *g = GetGValue(rgbVal);
	    *b = GetBValue(rgbVal);
	    free(name);
	    return (1);
	} else if (cmp < 0) {
	    right = middle - 1;
	} else {			/* > 0 */
	    left = middle + 1;
	}
    } while (left <= right);

    /*
     * I don't like to run in a ColorInvalid error and to see no pixmap at
     * all, so simply return a red pixel. Should be wrapped in an #ifdef
     * HeDu
     */

    *r = 255;
    *g = 0;
    *b = 0;				/* red error pixel */

    free(name);
    return (1);
}

void
xpmFreeRgbNames(rgbn, rgbn_max)
    xpmRgbName rgbn[];
    int rgbn_max;

{
    /* nothing to do */
}

#endif					/* MSW part */

/*****************************************************************************\
* scan.c:                                                                     *
*                                                                             *
*  XPM library                                                                *
*  Scanning utility for XPM file format                                       *
*                                                                             *
*  Developed by Arnaud Le Hors                                                *
\*****************************************************************************/

/*
 * The code related to FOR_MSW has been added by
 * HeDu (hedu@cul-ipn.uni-kiel.de) 4/94
 */

/*
 * The code related to AMIGA has been added by
 * Lorens Younes (d93-hyo@nada.kth.se) 4/96
 */

#define MAXPRINTABLE 92			/* number of printable ascii chars
					 * minus \ and " for string compat
					 * and ? to avoid ANSI trigraphs. */

static char *printable =
" .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjklzxcvbnmMNBVCZ\
ASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";

/*
 * printable begin with a space, so in most case, due to my algorithm, when
 * the number of different colors is less than MAXPRINTABLE, it will give a
 * char follow by "nothing" (a space) in the readable xpm file
 */


typedef struct {
    Pixel *pixels;
    unsigned int *pixelindex;
    unsigned int size;
    unsigned int ncolors;
    unsigned int mask_pixel;		/* whether there is or not */
}      PixelsMap;

LFUNC(storePixel, int, (Pixel pixel, PixelsMap *pmap,
			unsigned int *index_return));

LFUNC(storeMaskPixel, int, (Pixel pixel, PixelsMap *pmap,
			    unsigned int *index_return));

#ifndef FOR_MSW
# ifndef AMIGA
LFUNC(GetImagePixels, int, (XImage *image, unsigned int width,
			    unsigned int height, PixelsMap *pmap));

LFUNC(GetImagePixels32, int, (XImage *image, unsigned int width,
			      unsigned int height, PixelsMap *pmap));

LFUNC(GetImagePixels16, int, (XImage *image, unsigned int width,
			      unsigned int height, PixelsMap *pmap));

LFUNC(GetImagePixels8, int, (XImage *image, unsigned int width,
			     unsigned int height, PixelsMap *pmap));

LFUNC(GetImagePixels1, int, (XImage *image, unsigned int width,
			     unsigned int height, PixelsMap *pmap,
			     int (*storeFunc) ()));
# else /* AMIGA */
LFUNC(AGetImagePixels, int, (XImage *image, unsigned int width,
			     unsigned int height, PixelsMap *pmap,
			     int (*storeFunc) ()));
# endif/* AMIGA */
#else  /* ndef FOR_MSW */
LFUNC(MSWGetImagePixels, int, (Display *d, XImage *image, unsigned int width,
			       unsigned int height, PixelsMap *pmap,
			       int (*storeFunc) ()));
#endif
LFUNC(ScanTransparentColor, int, (XpmColor *color, unsigned int cpp,
				  XpmAttributes *attributes));

LFUNC(ScanOtherColors, int, (Display *display, XpmColor *colors, int ncolors,
			     Pixel *pixels, unsigned int mask,
			     unsigned int cpp, XpmAttributes *attributes));

/*
 * This function stores the given pixel in the given arrays which are grown
 * if not large enough.
 */
static int
storePixel(
    Pixel pixel,
    PixelsMap *pmap,
    unsigned int *index_return)
{
    unsigned int i;
    Pixel *p;
    unsigned int ncolors;

    if (*index_return) {		/* this is a transparent pixel! */
	*index_return = 0;
	return 0;
    }
    ncolors = pmap->ncolors;
    p = pmap->pixels + pmap->mask_pixel;
    for (i = pmap->mask_pixel; i < ncolors; i++, p++)
	if (*p == pixel)
	    break;
    if (i == ncolors) {
	if (ncolors >= pmap->size) {
	    pmap->size *= 2;
	    p = (Pixel *) XpmRealloc(pmap->pixels, sizeof(Pixel) * pmap->size);
	    if (!p)
		return (1);
	    pmap->pixels = p;

	}
	(pmap->pixels)[ncolors] = pixel;
	pmap->ncolors++;
    }
    *index_return = i;
    return 0;
}

static int
storeMaskPixel(pixel, pmap, index_return)
    Pixel pixel;
    PixelsMap *pmap;
    unsigned int *index_return;
{
    if (!pixel) {
	if (!pmap->ncolors) {
	    pmap->ncolors = 1;
	    (pmap->pixels)[0] = 0;
	    pmap->mask_pixel = 1;
	}
	*index_return = 1;
    } else
	*index_return = 0;
    return 0;
}

/* function call in case of error */
#undef RETURN
#define RETURN(status) \
{ \
      ErrorStatus = status; \
      goto error; \
}

/*
 * This function scans the given image and stores the found informations in
 * the given XpmImage structure.
 */
int
XpmCreateXpmImageFromImage(display, image, shapeimage,
			   xpmimage, attributes)
    Display *display;
    XImage *image;
    XImage *shapeimage;
    XpmImage *xpmimage;
    XpmAttributes *attributes;
{
    /* variables stored in the XpmAttributes structure */
    unsigned int cpp;

    /* variables to return */
    PixelsMap pmap;
    XpmColor *colorTable = NULL;
    int ErrorStatus = 0;

    /* calculation variables */
    unsigned int width = 0;
    unsigned int height = 0;
    unsigned int cppm;			/* minimum chars per pixel */
    unsigned int c;

    /* initialize pmap */
    pmap.pixels = NULL;
    pmap.pixelindex = NULL;
    pmap.size = 256;			/* should be enough most of the time */
    pmap.ncolors = 0;
    pmap.mask_pixel = 0;

    /*
     * get geometry
     */
    if (image) {
	width = image->width;
	height = image->height;
    } else if (shapeimage) {
	width = shapeimage->width;
	height = shapeimage->height;
    }

    /*
     * retrieve information from the XpmAttributes
     */
    if (attributes && (attributes->valuemask & XpmCharsPerPixel
/* 3.2 backward compatibility code */
		       || attributes->valuemask & XpmInfos))
/* end 3.2 bc */
	cpp = attributes->cpp;
    else
	cpp = 0;

    pmap.pixelindex =
	(unsigned int *) XpmCalloc(width * height, sizeof(unsigned int));
    if (!pmap.pixelindex)
	RETURN(XpmNoMemory);

    pmap.pixels = (Pixel *) XpmMalloc(sizeof(Pixel) * pmap.size);
    if (!pmap.pixels)
	RETURN(XpmNoMemory);

    /*
     * scan shape mask if any
     */
    if (shapeimage) {
#ifndef FOR_MSW
# ifndef AMIGA
	ErrorStatus = GetImagePixels1(shapeimage, width, height, &pmap,
				      storeMaskPixel);
# else
	ErrorStatus = AGetImagePixels(shapeimage, width, height, &pmap,
				      storeMaskPixel);
# endif
#else
	ErrorStatus = MSWGetImagePixels(display, shapeimage, width, height,
					&pmap, storeMaskPixel);
#endif
	if (ErrorStatus != XpmSuccess)
	    RETURN(ErrorStatus);
    }

    /*
     * scan the image data
     * 
     * In case depth is 1 or bits_per_pixel is 4, 6, 8, 24 or 32 use optimized
     * functions, otherwise use slower but sure general one.
     * 
     */

    if (image) {
#ifndef FOR_MSW
# ifndef AMIGA
	if (((image->bits_per_pixel | image->depth) == 1)  &&
	    (image->byte_order == image->bitmap_bit_order))
	    ErrorStatus = GetImagePixels1(image, width, height, &pmap,
					  storePixel);
	else if (image->format == ZPixmap) {
	    if (image->bits_per_pixel == 8)
		ErrorStatus = GetImagePixels8(image, width, height, &pmap);
	    else if (image->bits_per_pixel == 16)
		ErrorStatus = GetImagePixels16(image, width, height, &pmap);
	    else if (image->bits_per_pixel == 32)
		ErrorStatus = GetImagePixels32(image, width, height, &pmap);
	} else
	    ErrorStatus = GetImagePixels(image, width, height, &pmap);
# else
	ErrorStatus = AGetImagePixels(image, width, height, &pmap,
				      storePixel);
# endif
#else
	ErrorStatus = MSWGetImagePixels(display, image, width, height, &pmap,
					storePixel);
#endif
	if (ErrorStatus != XpmSuccess)
	    RETURN(ErrorStatus);
    }

    /*
     * get rgb values and a string of char, and possibly a name for each
     * color
     */

    colorTable = (XpmColor *) XpmCalloc(pmap.ncolors, sizeof(XpmColor));
    if (!colorTable)
	RETURN(XpmNoMemory);

    /* compute the minimal cpp */
    for (cppm = 1, c = MAXPRINTABLE; pmap.ncolors > c; cppm++)
	c *= MAXPRINTABLE;
    if (cpp < cppm)
	cpp = cppm;

    if (pmap.mask_pixel) {
	ErrorStatus = ScanTransparentColor(colorTable, cpp, attributes);
	if (ErrorStatus != XpmSuccess)
	    RETURN(ErrorStatus);
    }

    ErrorStatus = ScanOtherColors(display, colorTable, pmap.ncolors,
				  pmap.pixels, pmap.mask_pixel, cpp,
				  attributes);
    if (ErrorStatus != XpmSuccess)
	RETURN(ErrorStatus);

    /*
     * store found informations in the XpmImage structure
     */
    xpmimage->width = width;
    xpmimage->height = height;
    xpmimage->cpp = cpp;
    xpmimage->ncolors = pmap.ncolors;
    xpmimage->colorTable = colorTable;
    xpmimage->data = pmap.pixelindex;

    XpmFree(pmap.pixels);
    return (XpmSuccess);

/* exit point in case of error, free only locally allocated variables */
error:
    if (pmap.pixelindex)
	XpmFree(pmap.pixelindex);
    if (pmap.pixels)
	XpmFree(pmap.pixels);
    if (colorTable)
	xpmFreeColorTable(colorTable, pmap.ncolors);

    return (ErrorStatus);
}

static int
ScanTransparentColor(color, cpp, attributes)
    XpmColor *color;
    unsigned int cpp;
    XpmAttributes *attributes;
{
    char *s;
    unsigned int a, b, c;

    /* first get a character string */
    a = 0;
    if (!(s = color->string = (char *) XpmMalloc(cpp + 1)))
	return (XpmNoMemory);
    *s++ = printable[c = a % MAXPRINTABLE];
    for (b = 1; b < cpp; b++, s++)
	*s = printable[c = ((a - c) / MAXPRINTABLE) % MAXPRINTABLE];
    *s = '\0';

    /* then retreive related info from the attributes if any */
    if (attributes && (attributes->valuemask & XpmColorTable
/* 3.2 backward compatibility code */
		       || attributes->valuemask & XpmInfos)
/* end 3.2 bc */
	&& attributes->mask_pixel != XpmUndefPixel) {

	unsigned int key;
	char **defaults = (char **) color;
	char **mask_defaults;

/* 3.2 backward compatibility code */
	if (attributes->valuemask & XpmColorTable)
/* end 3.2 bc */
	    mask_defaults = (char **) (
		attributes->colorTable + attributes->mask_pixel);
/* 3.2 backward compatibility code */
	else
	    mask_defaults = (char **)
		((XpmColor **) attributes->colorTable)[attributes->mask_pixel];
/* end 3.2 bc */
	for (key = 1; key <= NKEYS; key++) {
	    if ((s = mask_defaults[key])) {
		defaults[key] = (char *) xpmstrdup(s);
		if (!defaults[key])
		    return (XpmNoMemory);
	    }
	}
    } else {
	color->c_color = (char *) xpmstrdup(TRANSPARENT_COLOR);
	if (!color->c_color)
	    return (XpmNoMemory);
    }
    return (XpmSuccess);
}

static int
ScanOtherColors(display, colors, ncolors, pixels, mask, cpp, attributes)
    Display *display;
    XpmColor *colors;
    int ncolors;
    Pixel *pixels;
    unsigned int mask;
    unsigned int cpp;
    XpmAttributes *attributes;
{
    /* variables stored in the XpmAttributes structure */
    Colormap colormap;
    char *rgb_fname;

#ifndef FOR_MSW
    xpmRgbName rgbn[MAX_RGBNAMES];
#else
    xpmRgbName *rgbn = NULL; 
#endif    
    int rgbn_max = 0;
    unsigned int i, j, c, i2;
    XpmColor *color;
    XColor *xcolors = NULL, *xcolor;
    char *colorname, *s;
    XpmColor *colorTable = NULL, **oldColorTable = NULL;
    unsigned int ancolors = 0;
    Pixel *apixels = NULL;
    unsigned int mask_pixel = 0;
    Bool found;

    /* retrieve information from the XpmAttributes */
    if (attributes && (attributes->valuemask & XpmColormap))
	colormap = attributes->colormap;
    else
	colormap = XDefaultColormap(display, XDefaultScreen(display));
    if (attributes && (attributes->valuemask & XpmRgbFilename))
	rgb_fname = attributes->rgb_fname;
    else
	rgb_fname = NULL;

    /* start from the right element */
    if (mask) {
	colors++;
	ncolors--;
	pixels++;
    }

    /* first get character strings and rgb values */
    xcolors = (XColor *) XpmMalloc(sizeof(XColor) * ncolors);
    if (!xcolors)
	return (XpmNoMemory);

    for (i = 0, i2 = mask, color = colors, xcolor = xcolors;
	 i < ncolors; i++, i2++, color++, xcolor++, pixels++) {

	if (!(s = color->string = (char *) XpmMalloc(cpp + 1))) {
	    XpmFree(xcolors);
	    return (XpmNoMemory);
	}
	*s++ = printable[c = i2 % MAXPRINTABLE];
	for (j = 1; j < cpp; j++, s++)
	    *s = printable[c = ((i2 - c) / MAXPRINTABLE) % MAXPRINTABLE];
	*s = '\0';

	xcolor->pixel = *pixels;
    }
    XQueryColors(display, colormap, xcolors, ncolors);

#ifndef FOR_MSW
    /* read the rgb file if any was specified */
    if (rgb_fname)
	rgbn_max = xpmReadRgbNames(attributes->rgb_fname, rgbn);
#else
    /* FOR_MSW: rgb names and values are hardcoded in rgbtab.h */
    rgbn_max = xpmReadRgbNames(NULL, NULL);
#endif

    if (attributes && attributes->valuemask & XpmColorTable) {
	colorTable = attributes->colorTable;
	ancolors = attributes->ncolors;
	apixels = attributes->pixels;
	mask_pixel = attributes->mask_pixel;
    }
/* 3.2 backward compatibility code */
    else if (attributes && attributes->valuemask & XpmInfos) {
	oldColorTable = (XpmColor **) attributes->colorTable;
	ancolors = attributes->ncolors;
	apixels = attributes->pixels;
	mask_pixel = attributes->mask_pixel;
    }
/* end 3.2 bc */

    for (i = 0, color = colors, xcolor = xcolors; i < ncolors;
						  i++, color++, xcolor++) {

	/* look for related info from the attributes if any */
	found = False;
	if (ancolors) {
	    unsigned int offset = 0;

	    for (j = 0; j < ancolors; j++) {
		if (j == mask_pixel) {
		    offset = 1;
		    continue;
		}
		if (apixels[j - offset] == xcolor->pixel)
		    break;
	    }
	    if (j != ancolors) {
		unsigned int key;
		char **defaults = (char **) color;
		char **adefaults;

/* 3.2 backward compatibility code */
		if (oldColorTable)
		    adefaults = (char **) oldColorTable[j];
		else
/* end 3.2 bc */
		    adefaults = (char **) (colorTable + j);

		found = True;
		for (key = 1; key <= NKEYS; key++) {
		    if ((s = adefaults[key]))
			defaults[key] = (char *) xpmstrdup(s);
		}
	    }
	}
	if (!found) {
	    /* if nothing found look for a color name */
	    colorname = NULL;
	    if (rgbn_max)
		colorname = xpmGetRgbName(rgbn, rgbn_max, xcolor->red,
					  xcolor->green, xcolor->blue);
	    if (colorname)
		color->c_color = (char *) xpmstrdup(colorname);
	    else {
		/* at last store the rgb value */
		char buf[BUFSIZ];
#ifndef FOR_MSW
		sprintf(buf, "#%04X%04X%04X",
			xcolor->red, xcolor->green, xcolor->blue);
#else   
		sprintf(buf, "#%02x%02x%02x",
			xcolor->red, xcolor->green, xcolor->blue);
#endif			
		color->c_color = (char *) xpmstrdup(buf);
	    }
	    if (!color->c_color) {
		XpmFree(xcolors);
		xpmFreeRgbNames(rgbn, rgbn_max);
		return (XpmNoMemory);
	    }
	}
    }

    XpmFree(xcolors);
    xpmFreeRgbNames(rgbn, rgbn_max);
    return (XpmSuccess);
}

#ifndef FOR_MSW
# ifndef AMIGA
/*
 * The functions below are written from X11R5 MIT's code (XImUtil.c)
 *
 * The idea is to have faster functions than the standard XGetPixel function
 * to scan the image data. Indeed we can speed up things by suppressing tests
 * performed for each pixel. We do exactly the same tests but at the image
 * level.
 */

static unsigned long const low_bits_table[] = {
    0x00000000, 0x00000001, 0x00000003, 0x00000007,
    0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f,
    0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff,
    0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff,
    0x0000ffff, 0x0001ffff, 0x0003ffff, 0x0007ffff,
    0x000fffff, 0x001fffff, 0x003fffff, 0x007fffff,
    0x00ffffff, 0x01ffffff, 0x03ffffff, 0x07ffffff,
    0x0fffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff,
    0xffffffff
};

/*
 * Default method to scan pixels of an image data structure.
 * The algorithm used is:
 *
 *	copy the source bitmap_unit or Zpixel into temp
 *	normalize temp if needed
 *	extract the pixel bits into return value
 *
 */

static int
GetImagePixels(image, width, height, pmap)
    XImage *image;
    unsigned int width;
    unsigned int height;
    PixelsMap *pmap;
{
    char *src;
    char *dst;
    unsigned int *iptr;
    char *data;
    int x, y, i;
    int bits, depth, ibu, ibpp, offset;
    unsigned long lbt;
    Pixel pixel, px;

    data = image->data;
    iptr = pmap->pixelindex;
    depth = image->depth;
    lbt = low_bits_table[depth];
    ibpp = image->bits_per_pixel;
    offset = image->xoffset;

    if ((image->bits_per_pixel | image->depth) == 1) {
	ibu = image->bitmap_unit;
	for (y = 0; y < height; y++)
	    for (x = 0; x < width; x++, iptr++) {
		src = &data[XYINDEX(x, y, image)];
		dst = (char *) &pixel;
		pixel = 0;
		for (i = ibu >> 3; --i >= 0;)
		    *dst++ = *src++;
		XYNORMALIZE(&pixel, image);
		bits = (x + offset) % ibu;
		pixel = ((((char *) &pixel)[bits >> 3]) >> (bits & 7)) & 1;
		if (ibpp != depth)
		    pixel &= lbt;
		if (storePixel(pixel, pmap, iptr))
		    return (XpmNoMemory);
	    }
    } else if (image->format == XYPixmap) {
	int nbytes, bpl, j;
	long plane = 0;
	ibu = image->bitmap_unit;
	nbytes = ibu >> 3;
	bpl = image->bytes_per_line;
	for (y = 0; y < height; y++)
	    for (x = 0; x < width; x++, iptr++) {
		pixel = 0;
		plane = 0;
		for (i = depth; --i >= 0;) {
		    src = &data[XYINDEX(x, y, image) + plane];
		    dst = (char *) &px;
		    px = 0;
		    for (j = nbytes; --j >= 0;)
			*dst++ = *src++;
		    XYNORMALIZE(&px, image);
		    bits = (x + offset) % ibu;
		    pixel = (pixel << 1) |
			    (((((char *) &px)[bits >> 3]) >> (bits & 7)) & 1);
		    plane = plane + (bpl * height);
		}
		if (ibpp != depth)
		    pixel &= lbt;
		if (storePixel(pixel, pmap, iptr))
		    return (XpmNoMemory);
	    }
    } else if (image->format == ZPixmap) {
	for (y = 0; y < height; y++)
	    for (x = 0; x < width; x++, iptr++) {
		src = &data[ZINDEX(x, y, image)];
		dst = (char *) &px;
		px = 0;
		for (i = (ibpp + 7) >> 3; --i >= 0;)
		    *dst++ = *src++;
		ZNORMALIZE(&px, image);
		pixel = 0;
		for (i = sizeof(unsigned long); --i >= 0;)
		    pixel = (pixel << 8) | ((unsigned char *) &px)[i];
		if (ibpp == 4) {
		    if (x & 1)
			pixel >>= 4;
		    else
			pixel &= 0xf;
		}
		if (ibpp != depth)
		    pixel &= lbt;
		if (storePixel(pixel, pmap, iptr))
		    return (XpmNoMemory);
	    }
    } else
	return (XpmColorError); /* actually a bad image */
    return (XpmSuccess);
}

/*
 * scan pixels of a 32-bits Z image data structure
 */

static int
GetImagePixels32(image, width, height, pmap)
    XImage *image;
    unsigned int width;
    unsigned int height;
    PixelsMap *pmap;
{
    unsigned char *addr;
    unsigned char *data;
    unsigned int *iptr;
    int x, y;
    unsigned long lbt;
    Pixel pixel;
    int depth;

    data = (unsigned char *) image->data;
    iptr = pmap->pixelindex;
    depth = image->depth;
    lbt = low_bits_table[depth];
#if !defined(WORD64) && !defined(LONG64)
    if (*((char *) &byteorderpixel) == image->byte_order) {
	for (y = 0; y < height; y++)
	    for (x = 0; x < width; x++, iptr++) {
		addr = &data[ZINDEX32(x, y, image)];
		pixel = *((unsigned long *) addr);
		if (depth != 32)
		    pixel &= lbt;
		if (storePixel(pixel, pmap, iptr))
		    return (XpmNoMemory);
	    }
    } else
#endif
    if (image->byte_order == MSBFirst)
	for (y = 0; y < height; y++)
	    for (x = 0; x < width; x++, iptr++) {
		addr = &data[ZINDEX32(x, y, image)];
		pixel = ((unsigned long) addr[0] << 24 |
			 (unsigned long) addr[1] << 16 |
			 (unsigned long) addr[2] << 8 |
			 addr[4]);
		if (depth != 32)
		    pixel &= lbt;
		if (storePixel(pixel, pmap, iptr))
		    return (XpmNoMemory);
	    }
    else
	for (y = 0; y < height; y++)
	    for (x = 0; x < width; x++, iptr++) {
		addr = &data[ZINDEX32(x, y, image)];
		pixel = (addr[0] |
			 (unsigned long) addr[1] << 8 |
			 (unsigned long) addr[2] << 16 |
			 (unsigned long) addr[3] << 24);
		if (depth != 32)
		    pixel &= lbt;
		if (storePixel(pixel, pmap, iptr))
		    return (XpmNoMemory);
	    }
    return (XpmSuccess);
}

/*
 * scan pixels of a 16-bits Z image data structure
 */

static int
GetImagePixels16(image, width, height, pmap)
    XImage *image;
    unsigned int width;
    unsigned int height;
    PixelsMap *pmap;
{
    unsigned char *addr;
    unsigned char *data;
    unsigned int *iptr;
    int x, y;
    unsigned long lbt;
    Pixel pixel;
    int depth;

    data = (unsigned char *) image->data;
    iptr = pmap->pixelindex;
    depth = image->depth;
    lbt = low_bits_table[depth];
    if (image->byte_order == MSBFirst)
	for (y = 0; y < height; y++)
	    for (x = 0; x < width; x++, iptr++) {
		addr = &data[ZINDEX16(x, y, image)];
		pixel = addr[0] << 8 | addr[1];
		if (depth != 16)
		    pixel &= lbt;
		if (storePixel(pixel, pmap, iptr))
		    return (XpmNoMemory);
	    }
    else
	for (y = 0; y < height; y++)
	    for (x = 0; x < width; x++, iptr++) {
		addr = &data[ZINDEX16(x, y, image)];
		pixel = addr[0] | addr[1] << 8;
		if (depth != 16)
		    pixel &= lbt;
		if (storePixel(pixel, pmap, iptr))
		    return (XpmNoMemory);
	    }
    return (XpmSuccess);
}

/*
 * scan pixels of a 8-bits Z image data structure
 */

static int
GetImagePixels8(image, width, height, pmap)
    XImage *image;
    unsigned int width;
    unsigned int height;
    PixelsMap *pmap;
{
    unsigned int *iptr;
    unsigned char *data;
    int x, y;
    unsigned long lbt;
    Pixel pixel;
    int depth;

    data = (unsigned char *) image->data;
    iptr = pmap->pixelindex;
    depth = image->depth;
    lbt = low_bits_table[depth];
    for (y = 0; y < height; y++)
	for (x = 0; x < width; x++, iptr++) {
	    pixel = data[ZINDEX8(x, y, image)];
	    if (depth != 8)
		pixel &= lbt;
	    if (storePixel(pixel, pmap, iptr))
		return (XpmNoMemory);
	}
    return (XpmSuccess);
}

/*
 * scan pixels of a 1-bit depth Z image data structure
 */

static int
GetImagePixels1(
    XImage *image,
    unsigned int width,
    unsigned int height,
    PixelsMap *pmap,
    int (*storeFunc) ())
{
    unsigned int *iptr;
    int x, y;
    char *data;
    Pixel pixel;
    int xoff, yoff, offset, bpl;

    data = image->data;
    iptr = pmap->pixelindex;
    offset = image->xoffset;
    bpl = image->bytes_per_line;

    if (image->bitmap_bit_order == MSBFirst)
	for (y = 0; y < height; y++)
	    for (x = 0; x < width; x++, iptr++) {
		xoff = x + offset;
		yoff = y * bpl + (xoff >> 3);
		xoff &= 7;
		pixel = (data[yoff] & (0x80 >> xoff)) ? 1 : 0;
		if ((*storeFunc) (pixel, pmap, iptr))
		    return (XpmNoMemory);
	    }
    else
	for (y = 0; y < height; y++)
	    for (x = 0; x < width; x++, iptr++) {
		xoff = x + offset;
		yoff = y * bpl + (xoff >> 3);
		xoff &= 7;
		pixel = (data[yoff] & (1 << xoff)) ? 1 : 0;
		if ((*storeFunc) (pixel, pmap, iptr))
		    return (XpmNoMemory);
	    }
    return (XpmSuccess);
}

# else /* AMIGA */

#define CLEAN_UP(status) \
{\
    if (pixels) XpmFree (pixels);\
    if (tmp_img) FreeXImage (tmp_img);\
    return (status);\
}

static int
AGetImagePixels (
    XImage        *image,
    unsigned int   width,
    unsigned int   height,
    PixelsMap     *pmap,
    int          (*storeFunc) ())
{
    unsigned int   *iptr;
    unsigned int    x, y;
    unsigned char  *pixels;
    XImage         *tmp_img;
    
    pixels = XpmMalloc ((((width+15)>>4)<<4)*sizeof (*pixels));
    if (pixels == NULL)
	return XpmNoMemory;
    
    tmp_img = AllocXImage ((((width+15)>>4)<<4), 1, image->rp->BitMap->Depth);
    if (tmp_img == NULL)
	CLEAN_UP (XpmNoMemory)
    
    iptr = pmap->pixelindex;
    for (y = 0; y < height; ++y)
    {
	ReadPixelLine8 (image->rp, 0, y, width, pixels, tmp_img->rp);
	for (x = 0; x < width; ++x, ++iptr)
	{
	    if ((*storeFunc) (pixels[x], pmap, iptr))
		CLEAN_UP (XpmNoMemory)
	}
    }
    
    CLEAN_UP (XpmSuccess)
}

#undef CLEAN_UP

# endif/* AMIGA */
#else  /* ndef FOR_MSW */
static int
MSWGetImagePixels(display, image, width, height, pmap, storeFunc)
    Display *display;
    XImage *image;
    unsigned int width;
    unsigned int height;
    PixelsMap *pmap;
    int (*storeFunc) ();
{
    unsigned int *iptr;
    unsigned int x, y;
    Pixel pixel;

    iptr = pmap->pixelindex;

    SelectObject(*display, image->bitmap);
    for (y = 0; y < height; y++) {
	for (x = 0; x < width; x++, iptr++) {
	    pixel = GetPixel(*display, x, y);
	    if ((*storeFunc) (pixel, pmap, iptr))
		return (XpmNoMemory);
	}
    }
    return (XpmSuccess);
}

#endif

#ifndef FOR_MSW
# ifndef AMIGA
int
XpmCreateXpmImageFromPixmap(display, pixmap, shapemask,
			    xpmimage, attributes)
    Display *display;
    Pixmap pixmap;
    Pixmap shapemask;
    XpmImage *xpmimage;
    XpmAttributes *attributes;
{
    XImage *ximage = NULL;
    XImage *shapeimage = NULL;
    unsigned int width = 0;
    unsigned int height = 0;
    int ErrorStatus;

    /* get geometry */
    if (attributes && attributes->valuemask & XpmSize) {
	width = attributes->width;
	height = attributes->height;
    }
    /* get the ximages */
    if (pixmap)
	xpmCreateImageFromPixmap(display, pixmap, &ximage, &width, &height);
    if (shapemask)
	xpmCreateImageFromPixmap(display, shapemask, &shapeimage,
				 &width, &height);

    /* create the related XpmImage */
    ErrorStatus = XpmCreateXpmImageFromImage(display, ximage, shapeimage,
					     xpmimage, attributes);

    /* destroy the ximages */
    if (ximage)
	XDestroyImage(ximage);
    if (shapeimage)
	XDestroyImage(shapeimage);

    return (ErrorStatus);
}

# endif/* not AMIGA */
#endif /* ndef FOR_MSW */

/*****************************************************************************\
*  RdFToI.c:                                                                  *
*                                                                             *
*  XPM library                                                                *
*  Parse an XPM file and create the image and possibly its mask               *
*                                                                             *
*  Developed by Arnaud Le Hors                                                *
\*****************************************************************************/

LFUNC(OpenReadFile, int, (char *filename, xpmData *mdata));
LFUNC(xpmDataClose, void, (xpmData *mdata));

int
XpmReadFileToImage(display, filename,
		   image_return, shapeimage_return, attributes)
    Display *display;
    char *filename;
    XImage **image_return;
    XImage **shapeimage_return;
    XpmAttributes *attributes;
{
    XpmImage image;
    XpmInfo info;
    int ErrorStatus;
    xpmData mdata;

    xpmInitXpmImage(&image);
    xpmInitXpmInfo(&info);

    /* open file to read */
    if ((ErrorStatus = OpenReadFile(filename, &mdata)) != XpmSuccess)
	return (ErrorStatus);

    /* create the XImage from the XpmData */
    if (attributes) {
	xpmInitAttributes(attributes);
	xpmSetInfoMask(&info, attributes);
	ErrorStatus = xpmParseDataAndCreate(display, &mdata,
					    image_return, shapeimage_return,
					    &image, &info, attributes);
    } else
	ErrorStatus = xpmParseDataAndCreate(display, &mdata,
					    image_return, shapeimage_return,
					    &image, NULL, attributes);
    if (attributes) {
	if (ErrorStatus >= 0)		/* no fatal error */
	    xpmSetAttributes(attributes, &image, &info);
	XpmFreeXpmInfo(&info);
    }

    xpmDataClose(&mdata);
    /* free the XpmImage */
    XpmFreeXpmImage(&image);

    return (ErrorStatus);
}

int
XpmReadFileToXpmImage(filename, image, info)
    char *filename;
    XpmImage *image;
    XpmInfo *info;
{
    xpmData mdata;
    int ErrorStatus;

    /* init returned values */
    xpmInitXpmImage(image);
    xpmInitXpmInfo(info);

    /* open file to read */
    if ((ErrorStatus = OpenReadFile(filename, &mdata)) != XpmSuccess)
	return (ErrorStatus);

    /* create the XpmImage from the XpmData */
    ErrorStatus = xpmParseData(&mdata, image, info);

    xpmDataClose(&mdata);

    return (ErrorStatus);
}

/*
 * open the given file to be read as an xpmData which is returned.
 */
static int
OpenReadFile(filename, mdata)
    char *filename;
    xpmData *mdata;
{
#ifndef NO_ZPIPE
    char buf[BUFSIZ];
# ifdef STAT_ZFILE
    char *compressfile;
    struct stat status;
# endif
#endif

    if (!filename) {
	mdata->stream.file = (stdin);
	mdata->type = XPMFILE;
    } else {
#ifndef NO_ZPIPE
	int len = strlen(filename);
	if ((len > 2) && !strcmp(".Z", filename + (len - 2))) {
	    mdata->type = XPMPIPE;
	    sprintf(buf, "uncompress -c \"%s\"", filename);
	    if (!(mdata->stream.file = popen(buf, "r")))
		return (XpmOpenFailed);

	} else if ((len > 3) && !strcmp(".gz", filename + (len - 3))) {
	    mdata->type = XPMPIPE;
	    sprintf(buf, "gunzip -qc \"%s\"", filename);
	    if (!(mdata->stream.file = popen(buf, "r")))
		return (XpmOpenFailed);

	} else {
# ifdef STAT_ZFILE
	    if (!(compressfile = (char *) XpmMalloc(len + 4)))
		return (XpmNoMemory);

	    sprintf(compressfile, "%s.Z", filename);
	    if (!stat(compressfile, &status)) {
		sprintf(buf, "uncompress -c \"%s\"", compressfile);
		if (!(mdata->stream.file = popen(buf, "r"))) {
		    XpmFree(compressfile);
		    return (XpmOpenFailed);
		}
		mdata->type = XPMPIPE;
	    } else {
		sprintf(compressfile, "%s.gz", filename);
		if (!stat(compressfile, &status)) {
		    sprintf(buf, "gunzip -c \"%s\"", compressfile);
		    if (!(mdata->stream.file = popen(buf, "r"))) {
			XpmFree(compressfile);
			return (XpmOpenFailed);
		    }
		    mdata->type = XPMPIPE;
		} else {
# endif
#endif
		    if (!(mdata->stream.file = fopen(filename, "r"))) {
#if !defined(NO_ZPIPE) && defined(STAT_ZFILE)
			XpmFree(compressfile);
#endif
			return (XpmOpenFailed);
		    }
		    mdata->type = XPMFILE;
#ifndef NO_ZPIPE
# ifdef STAT_ZFILE
		}
	    }
	    XpmFree(compressfile);
# endif
	}
#endif
    }
    mdata->CommentLength = 0;
    return (XpmSuccess);
}

int
XpmCreateDataFromImage(display, data_return, image, shapeimage, attributes)
    Display *display;
    char ***data_return;
    XImage *image;
    XImage *shapeimage;
    XpmAttributes *attributes;
{
    XpmImage xpmimage;
    XpmInfo info;
    int ErrorStatus;

    /* initialize return value */
    if (data_return)
	*data_return = NULL;

    /* create an XpmImage from the image */
    ErrorStatus = XpmCreateXpmImageFromImage(display, image, shapeimage,
					     &xpmimage, attributes);
    if (ErrorStatus != XpmSuccess)
	return (ErrorStatus);

    /* create the data from the XpmImage */
    if (attributes) {
	xpmSetInfo(&info, attributes);
	ErrorStatus = XpmCreateDataFromXpmImage(data_return, &xpmimage, &info);
    } else
	ErrorStatus = XpmCreateDataFromXpmImage(data_return, &xpmimage, NULL);

    /* free the XpmImage */
    XpmFreeXpmImage(&xpmimage);

    return (ErrorStatus);
}

#undef RETURN
#define RETURN(status) \
{ \
      ErrorStatus = status; \
      goto exit; \
}

static int
CreateColors2(
    char **dataptr,
    unsigned int *data_size,
    XpmColor *colors,
    unsigned int ncolors,
    unsigned int cpp)
{
    char buf[BUFSIZ];
    unsigned int a, key, l;
    char *s, *s2;
    char **defaults;

    for (a = 0; a < ncolors; a++, colors++, dataptr++) {

	defaults = (char **) colors;
	strncpy(buf, *defaults++, cpp);
	s = buf + cpp;

	for (key = 1; key <= NKEYS; key++, defaults++) {
	    if ((s2 = *defaults)) {
#ifndef VOID_SPRINTF
		s +=
#endif
		sprintf(s, "\t%s %s", xpmColorKeys[key - 1], s2);
#ifdef VOID_SPRINTF
		s += strlen(s);
#endif
	    }
	}
	l = s - buf + 1;
	s = (char *) XpmMalloc(l);
	if (!s)
	    return (XpmNoMemory);
	*data_size += l;
	*dataptr = strcpy(s, buf);
    }
    return (XpmSuccess);
}

static void
CreateExtensions(
    char **dataptr,
    unsigned int offset,
    XpmExtension *ext,
    unsigned int num,
    unsigned int ext_nlines)
{
    unsigned int x, y, a, b;
    char **line;

    *(dataptr + 1) = *dataptr + offset;
    dataptr++;
    a = 0;
    for (x = 0; x < num; x++, ext++) {
	sprintf(*dataptr, "XPMEXT %s", ext->name);
	a++;
	if (a < ext_nlines)
	    *(dataptr + 1) = *dataptr + strlen(ext->name) + 8;
	dataptr++;
	b = ext->nlines;
	for (y = 0, line = ext->lines; y < b; y++, line++) {
	    strcpy(*dataptr, *line);
	    a++;
	    if (a < ext_nlines)
		*(dataptr + 1) = *dataptr + strlen(*line) + 1;
	    dataptr++;
	}
    }
    strcpy(*dataptr, "XPMENDEXT");
}

static void
CreatePixels(
    char **dataptr,
    unsigned int width,
    unsigned int height,
    unsigned int cpp,
    unsigned int *pixels,
    XpmColor *colors)
{
    char *s;
    unsigned int x, y, h, offset;

    h = height - 1;
    offset = width * cpp + 1;
    for (y = 0; y < h; y++, dataptr++) {
	s = *dataptr;
	for (x = 0; x < width; x++, pixels++) {
	    strncpy(s, colors[*pixels].string, cpp);
	    s += cpp;
	}
	*s = '\0';
	*(dataptr + 1) = *dataptr + offset;
    }
    /* duplicate some code to avoid a test in the loop */
    s = *dataptr;
    for (x = 0; x < width; x++, pixels++) {
	strncpy(s, colors[*pixels].string, cpp);
	s += cpp;
    }
    *s = '\0';
}

static void
CountExtensions(
    XpmExtension *ext,
    unsigned int num,
    unsigned int *ext_size,
    unsigned int *ext_nlines)
{
    unsigned int x, y, a, size, nlines;
    char **line;

    size = 0;
    nlines = 0;
    for (x = 0; x < num; x++, ext++) {
	/* 1 for the name */
	nlines += ext->nlines + 1;
	/* 8 = 7 (for "XPMEXT ") + 1 (for 0) */
	size += strlen(ext->name) + 8;
	a = ext->nlines;
	for (y = 0, line = ext->lines; y < a; y++, line++)
	    size += strlen(*line) + 1;
    }
    /* 10 and 1 are for the ending "XPMENDEXT" */
    *ext_size = size + 10;
    *ext_nlines = nlines + 1;
}

int
XpmCreateDataFromXpmImage(
    char ***data_return,
    XpmImage *image,
    XpmInfo *info)
{
    /* calculation variables */
    int ErrorStatus;
    char buf[BUFSIZ];
    char **header = NULL, **data, **sptr, **sptr2, *s;
    unsigned int header_size, header_nlines;
    unsigned int data_size, data_nlines;
    unsigned int extensions = 0, ext_size = 0, ext_nlines = 0;
    unsigned int offset, l, n;

    *data_return = NULL;

    extensions = info && (info->valuemask & XpmExtensions)
	&& info->nextensions;

    /* compute the number of extensions lines and size */
    if (extensions)
	CountExtensions(info->extensions, info->nextensions,
			&ext_size, &ext_nlines);

    /*
     * alloc a temporary array of char pointer for the header section which
     * is the hints line + the color table lines
     */
    header_nlines = 1 + image->ncolors;
    header_size = sizeof(char *) * header_nlines;
    header = (char **) XpmCalloc(header_size, sizeof(char *));
    if (!header)
	return (XpmNoMemory);

    /* print the hints line */
    s = buf;
#ifndef VOID_SPRINTF
    s +=
#endif
    sprintf(s, "%d %d %d %d", image->width, image->height,
	    image->ncolors, image->cpp);
#ifdef VOID_SPRINTF
    s += strlen(s);
#endif

    if (info && (info->valuemask & XpmHotspot)) {
#ifndef VOID_SPRINTF
	s +=
#endif
	sprintf(s, " %d %d", info->x_hotspot, info->y_hotspot);
#ifdef VOID_SPRINTF
	s += strlen(s);
#endif
    }
    if (extensions) {
	strcpy(s, " XPMEXT");
	s += 7;
    }
    l = s - buf + 1;
    *header = (char *) XpmMalloc(l);
    if (!*header)
	RETURN(XpmNoMemory);
    header_size += l;
    strcpy(*header, buf);

    /* print colors */
    ErrorStatus = CreateColors2(header + 1, &header_size,
			       image->colorTable, image->ncolors, image->cpp);

    if (ErrorStatus != XpmSuccess)
	RETURN(ErrorStatus);

    /* now we know the size needed, alloc the data and copy the header lines */
    offset = image->width * image->cpp + 1;
    data_size = header_size + (image->height + ext_nlines) * sizeof(char *)
	+ image->height * offset + ext_size;

    data = (char **) XpmMalloc(data_size);
    if (!data)
	RETURN(XpmNoMemory);

    data_nlines = header_nlines + image->height + ext_nlines;
    *data = (char *) (data + data_nlines);
    n = image->ncolors;
    for (l = 0, sptr = data, sptr2 = header; l <= n; l++, sptr++, sptr2++) {
	strcpy(*sptr, *sptr2);
	*(sptr + 1) = *sptr + strlen(*sptr2) + 1;
    }

    /* print pixels */
    data[header_nlines] = (char *) data + header_size
	+ (image->height + ext_nlines) * sizeof(char *);

    CreatePixels(data + header_nlines, image->width, image->height,
		 image->cpp, image->data, image->colorTable);

    /* print extensions */
    if (extensions)
	CreateExtensions(data + header_nlines + image->height - 1, offset,
			 info->extensions, info->nextensions,
			 ext_nlines);

    *data_return = data;
    ErrorStatus = XpmSuccess;

/* exit point, free only locally allocated variables */
exit:
    if (header) {
	for (l = 0; l < header_nlines; l++)
	    if (header[l])
		XpmFree(header[l]);
		XpmFree(header);
    }
    return(ErrorStatus);
}

/*****************************************************************************\
*  CrBufFrI.c:                                                                *
*                                                                             *
*  XPM library                                                                *
*  Scan an image and possibly its mask and create an XPM buffer               *
*                                                                             *
*  Developed by Arnaud Le Hors                                                *
\*****************************************************************************/

int
XpmCreateBufferFromImage(display, buffer_return, image, shapeimage, attributes)
    Display *display;
    char **buffer_return;
    XImage *image;
    XImage *shapeimage;
    XpmAttributes *attributes;
{
    XpmImage xpmimage;
    XpmInfo info;
    int ErrorStatus;

    /* initialize return value */
    if (buffer_return)
	*buffer_return = NULL;

    /* create an XpmImage from the image */
    ErrorStatus = XpmCreateXpmImageFromImage(display, image, shapeimage,
					     &xpmimage, attributes);
    if (ErrorStatus != XpmSuccess)
	return (ErrorStatus);

    /* create the buffer from the XpmImage */
    if (attributes) {
	xpmSetInfo(&info, attributes);
	ErrorStatus =
	    XpmCreateBufferFromXpmImage(buffer_return, &xpmimage, &info);
    } else
	ErrorStatus =
	    XpmCreateBufferFromXpmImage(buffer_return, &xpmimage, NULL);

    /* free the XpmImage */
    XpmFreeXpmImage(&xpmimage);

    return (ErrorStatus);
}


#undef RETURN
#define RETURN(status) \
{ \
      ErrorStatus = status; \
      goto error; \
}

static int
WriteColors2(
    char **dataptr,
    unsigned int *data_size,
    unsigned int *used_size,
    XpmColor *colors,
    unsigned int ncolors,
    unsigned int cpp)
{
    char buf[BUFSIZ];
    unsigned int a, key, l;
    char *s, *s2;
    char **defaults;

    *buf = '"';
    for (a = 0; a < ncolors; a++, colors++) {

	defaults = (char **) colors;
	s = buf + 1;
	strncpy(s, *defaults++, cpp);
	s += cpp;

	for (key = 1; key <= NKEYS; key++, defaults++) {
	    if ((s2 = *defaults)) {
#ifndef VOID_SPRINTF
		s +=
#endif
		sprintf(s, "\t%s %s", xpmColorKeys[key - 1], s2);
#ifdef VOID_SPRINTF
		s += strlen(s);
#endif
	    }
	}
	strcpy(s, "\",\n");
	l = s + 3 - buf;
	s = (char *) XpmRealloc(*dataptr, *data_size + l);
	if (!s)
	    return (XpmNoMemory);
	*data_size += l;
	strcpy(s + *used_size, buf);
	*used_size += l;
	*dataptr = s;
    }
    return (XpmSuccess);
}

static void
WritePixels2(
    char *dataptr,
    unsigned int *used_size,
    unsigned int width,
    unsigned int height,
    unsigned int cpp,
    unsigned int *pixels,
    XpmColor *colors)
{
    char *s = dataptr;
    unsigned int x, y, h;

    h = height - 1;
    for (y = 0; y < h; y++) {
	*s++ = '"';
	for (x = 0; x < width; x++, pixels++) {
	    strncpy(s, colors[*pixels].string, cpp);
	    s += cpp;
	}
	strcpy(s, "\",\n");
	s += 3;
    }
    /* duplicate some code to avoid a test in the loop */
    *s++ = '"';
    for (x = 0; x < width; x++, pixels++) {
	strncpy(s, colors[*pixels].string, cpp);
	s += cpp;
    }
    *s++ = '"';
    *used_size += s - dataptr;
}

static void
WriteExtensions2(
    char *dataptr,
    unsigned int *used_size,
    XpmExtension *ext,
    unsigned int num)
{
    unsigned int x, y, a;
    char **line;
    char *s = dataptr;

    for (x = 0; x < num; x++, ext++) {
#ifndef VOID_SPRINTF
	s +=
#endif
	sprintf(s, ",\n\"XPMEXT %s\"", ext->name);
#ifdef VOID_SPRINTF
	s += strlen(ext->name) + 11;
#endif
	a = ext->nlines;
	for (y = 0, line = ext->lines; y < a; y++, line++) {
#ifndef VOID_SPRINTF
	    s +=
#endif
	    sprintf(s, ",\n\"%s\"", *line);
#ifdef VOID_SPRINTF
	    s += strlen(*line) + 4;
#endif
	}
    }
    strcpy(s, ",\n\"XPMENDEXT\"");
    *used_size += s - dataptr + 13;
}

static int
ExtensionsSize(XpmExtension *ext, unsigned int num)
{
    unsigned int x, y, a, size;
    char **line;

    size = 0;
    for (x = 0; x < num; x++, ext++) {
	/* 11 = 10 (for ',\n"XPMEXT ') + 1 (for '"') */
	size += strlen(ext->name) + 11;
	a = ext->nlines;
	for (y = 0, line = ext->lines; y < a; y++, line++)
	    /* 4 = 3 (for ',\n"') + 1 (for '"') */
	    size += strlen(*line) + 4;
    }
    /* 13 is for ',\n"XPMENDEXT"' */
    return size + 13;
}

static int
CommentsSize(XpmInfo *info)
{
    int size = 0;

    /* 5 = 2 (for "/_*") + 3 (for "*_/\n") */
    if (info->hints_cmt)
	size += 5 + strlen(info->hints_cmt);

    if (info->colors_cmt)
	size += 5 + strlen(info->colors_cmt);

    if (info->pixels_cmt)
	size += 5 + strlen(info->pixels_cmt);

    return size;
}

int
XpmCreateBufferFromXpmImage(buffer_return, image, info)
    char **buffer_return;
    XpmImage *image;
    XpmInfo *info;
{
    /* calculation variables */
    int ErrorStatus;
    char buf[BUFSIZ];
    unsigned int cmts, extensions, ext_size = 0;
    unsigned int l, cmt_size = 0;
    char *ptr = NULL, *p;
    unsigned int ptr_size, used_size;

    *buffer_return = NULL;

    cmts = info && (info->valuemask & XpmComments);
    extensions = info && (info->valuemask & XpmExtensions)
	&& info->nextensions;

    /* compute the extensions and comments size */
    if (extensions)
	ext_size = ExtensionsSize(info->extensions, info->nextensions);
    if (cmts)
	cmt_size = CommentsSize(info);

    /* write the header line */
#ifndef VOID_SPRINTF
    used_size =
#endif
    sprintf(buf, "/* XPM */\nstatic char * image_name[] = {\n");
#ifdef VOID_SPRINTF
    used_size = strlen(buf);
#endif
    ptr_size = used_size + ext_size + cmt_size + 1;
    ptr = (char *) XpmMalloc(ptr_size);
    if (!ptr)
	return XpmNoMemory;
    strcpy(ptr, buf);

    /* write the values line */
    if (cmts && info->hints_cmt) {
#ifndef VOID_SPRINTF
	used_size +=
#endif
	sprintf(ptr + used_size, "/*%s*/\n", info->hints_cmt);
#ifdef VOID_SPRINTF
	used_size += strlen(info->hints_cmt) + 5;
#endif
    }
#ifndef VOID_SPRINTF
    l =
#endif
    sprintf(buf, "\"%d %d %d %d", image->width, image->height,
	    image->ncolors, image->cpp);
#ifdef VOID_SPRINTF
    l = strlen(buf);
#endif

    if (info && (info->valuemask & XpmHotspot)) {
#ifndef VOID_SPRINTF
	l +=
#endif
	sprintf(buf + l, " %d %d", info->x_hotspot, info->y_hotspot);
#ifdef VOID_SPRINTF
	l = strlen(buf);
#endif
    }
    if (extensions) {
#ifndef VOID_SPRINTF
	l +=
#endif
	sprintf(buf + l, " XPMEXT");
#ifdef VOID_SPRINTF
	l = strlen(buf);
#endif
    }
#ifndef VOID_SPRINTF
    l +=
#endif
    sprintf(buf + l, "\",\n");
#ifdef VOID_SPRINTF
    l = strlen(buf);
#endif
    ptr_size += l;
    p = (char *) XpmRealloc(ptr, ptr_size);
    if (!p)
	RETURN(XpmNoMemory);
    ptr = p;
    strcpy(ptr + used_size, buf);
    used_size += l;

    /* write colors */
    if (cmts && info->colors_cmt) {
#ifndef VOID_SPRINTF
	used_size +=
#endif
	sprintf(ptr + used_size, "/*%s*/\n", info->colors_cmt);
#ifdef VOID_SPRINTF
	used_size += strlen(info->colors_cmt) + 5;
#endif
    }
    ErrorStatus = WriteColors2(&ptr, &ptr_size, &used_size,
			      image->colorTable, image->ncolors, image->cpp);
 
    if (ErrorStatus != XpmSuccess)
	RETURN(ErrorStatus);

    /*
     * now we know the exact size we need, realloc the data
     * 4 = 1 (for '"') + 3 (for '",\n')
     * 1 = - 2 (because the last line does not end with ',\n') + 3 (for '};\n')
     */
    ptr_size += image->height * (image->width * image->cpp + 4) + 1;

    p = (char *) XpmRealloc(ptr, ptr_size);
    if (!p)
	RETURN(XpmNoMemory);
    ptr = p;

    /* print pixels */
    if (cmts && info->pixels_cmt) {
#ifndef VOID_SPRINTF
	used_size +=
#endif
	sprintf(ptr + used_size, "/*%s*/\n", info->pixels_cmt);
#ifdef VOID_SPRINTF
	used_size += strlen(info->pixels_cmt) + 5;
#endif
    }
    WritePixels2(ptr + used_size, &used_size, image->width, image->height,
		image->cpp, image->data, image->colorTable);

    /* print extensions */
    if (extensions)
	WriteExtensions2(ptr + used_size, &used_size,
			info->extensions, info->nextensions);

    /* close the array */
    strcpy(ptr + used_size, "};\n");

    *buffer_return = ptr;

    return (XpmSuccess);

/* exit point in case of error, free only locally allocated variables */
error:
    if (ptr)
	XpmFree(ptr);
    return (ErrorStatus);
}
