/* This file contains code for unified image loading from XPM file  */
/********************************************************************/
/* Copyright (c) 2001 Sasha Vasko <sasha at aftercode.net>           */
/********************************************************************/
/*
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#undef LOCAL_DEBUG
#undef DO_CLOCKING

#ifdef _WIN32
#include "win32/config.h"
#include <io.h>
#define read _read
#else
#include "config.h"
#endif

#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <ctype.h>
#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
#  include <sys/time.h>
# else
#  include <time.h>
# endif
#endif
#include <sys/stat.h>
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_STDDEF_H
#include <stddef.h>
#endif
#include <fcntl.h>

#ifdef HAVE_LIBXPM      /* XPM XPM XPM XPM XPM XPM XPM XPM XPM XPM XPM XPM XPM XPM XPM XPM */
#ifdef HAVE_LIBXPM_X11
#include <X11/xpm.h>
#else
#include <xpm.h>
#endif
#endif

#ifdef _WIN32
# include "win32/afterbase.h"
#else
# include "afterbase.h"
#endif
#include "asimage.h"
#include "ascmap.h"
#include "xpm.h"

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

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

static struct {
	char 	*name ;
	ARGB32   argb ;
	} XpmRGB_Colors[] =
{/* this entire table is taken from libXpm 	       */
 /* Developed by HeDu 3/94 (hedu@cul-ipn.uni-kiel.de)  */
    {"AliceBlue", MAKE_ARGB32(255, 240, 248, 255)},
    {"AntiqueWhite", MAKE_ARGB32(255, 250, 235, 215)},
    {"Aquamarine", MAKE_ARGB32(255, 50, 191, 193)},
    {"Azure", MAKE_ARGB32(255, 240, 255, 255)},
    {"Beige", MAKE_ARGB32(255, 245, 245, 220)},
    {"Bisque", MAKE_ARGB32(255, 255, 228, 196)},
    {"Black", MAKE_ARGB32(255, 0, 0, 0)},
    {"BlanchedAlmond", MAKE_ARGB32(255, 255, 235, 205)},
    {"Blue", MAKE_ARGB32(255, 0, 0, 255)},
    {"BlueViolet", MAKE_ARGB32(255, 138, 43, 226)},
    {"Brown", MAKE_ARGB32(255, 165, 42, 42)},
    {"burlywood", MAKE_ARGB32(255, 222, 184, 135)},
    {"CadetBlue", MAKE_ARGB32(255, 95, 146, 158)},
    {"chartreuse", MAKE_ARGB32(255, 127, 255, 0)},
    {"chocolate", MAKE_ARGB32(255, 210, 105, 30)},
    {"Coral", MAKE_ARGB32(255, 255, 114, 86)},
    {"CornflowerBlue", MAKE_ARGB32(255, 34, 34, 152)},
    {"cornsilk", MAKE_ARGB32(255, 255, 248, 220)},
    {"Cyan", MAKE_ARGB32(255, 0, 255, 255)},
    {"DarkGoldenrod", MAKE_ARGB32(255, 184, 134, 11)},
    {"DarkGreen", MAKE_ARGB32(255, 0, 86, 45)},
    {"DarkKhaki", MAKE_ARGB32(255, 189, 183, 107)},
    {"DarkOliveGreen", MAKE_ARGB32(255, 85, 86, 47)},
    {"DarkOrange", MAKE_ARGB32(255, 255, 140, 0)},
    {"DarkOrchid", MAKE_ARGB32(255, 139, 32, 139)},
    {"DarkSalmon", MAKE_ARGB32(255, 233, 150, 122)},
    {"DarkSeaGreen", MAKE_ARGB32(255, 143, 188, 143)},
    {"DarkSlateBlue", MAKE_ARGB32(255, 56, 75, 102)},
    {"DarkSlateGray", MAKE_ARGB32(255, 47, 79, 79)},
    {"DarkTurquoise", MAKE_ARGB32(255, 0, 166, 166)},
    {"DarkViolet", MAKE_ARGB32(255, 148, 0, 211)},
    {"DeepPink", MAKE_ARGB32(255, 255, 20, 147)},
    {"DeepSkyBlue", MAKE_ARGB32(255, 0, 191, 255)},
    {"DimGray", MAKE_ARGB32(255, 84, 84, 84)},
    {"DodgerBlue", MAKE_ARGB32(255, 30, 144, 255)},
    {"Firebrick", MAKE_ARGB32(255, 142, 35, 35)},
    {"FloralWhite", MAKE_ARGB32(255, 255, 250, 240)},
    {"ForestGreen", MAKE_ARGB32(255, 80, 159, 105)},
    {"gainsboro", MAKE_ARGB32(255, 220, 220, 220)},
    {"GhostWhite", MAKE_ARGB32(255, 248, 248, 255)},
    {"Gold", MAKE_ARGB32(255, 218, 170, 0)},
    {"Goldenrod", MAKE_ARGB32(255, 239, 223, 132)},
    {"Gray", MAKE_ARGB32(255, 126, 126, 126)},
    {"Gray0", MAKE_ARGB32(255, 0, 0, 0)},
    {"Gray1", MAKE_ARGB32(255, 3, 3, 3)},
    {"Gray10", MAKE_ARGB32(255, 26, 26, 26)},
    {"Gray100", MAKE_ARGB32(255, 255, 255, 255)},
    {"Gray11", MAKE_ARGB32(255, 28, 28, 28)},
    {"Gray12", MAKE_ARGB32(255, 31, 31, 31)},
    {"Gray13", MAKE_ARGB32(255, 33, 33, 33)},
    {"Gray14", MAKE_ARGB32(255, 36, 36, 36)},
    {"Gray15", MAKE_ARGB32(255, 38, 38, 38)},
    {"Gray16", MAKE_ARGB32(255, 41, 41, 41)},
    {"Gray17", MAKE_ARGB32(255, 43, 43, 43)},
    {"Gray18", MAKE_ARGB32(255, 46, 46, 46)},
    {"Gray19", MAKE_ARGB32(255, 48, 48, 48)},
    {"Gray2", MAKE_ARGB32(255, 5, 5, 5)},
    {"Gray20", MAKE_ARGB32(255, 51, 51, 51)},
    {"Gray21", MAKE_ARGB32(255, 54, 54, 54)},
    {"Gray22", MAKE_ARGB32(255, 56, 56, 56)},
    {"Gray23", MAKE_ARGB32(255, 59, 59, 59)},
    {"Gray24", MAKE_ARGB32(255, 61, 61, 61)},
    {"Gray25", MAKE_ARGB32(255, 64, 64, 64)},
    {"Gray26", MAKE_ARGB32(255, 66, 66, 66)},
    {"Gray27", MAKE_ARGB32(255, 69, 69, 69)},
    {"Gray28", MAKE_ARGB32(255, 71, 71, 71)},
    {"Gray29", MAKE_ARGB32(255, 74, 74, 74)},
    {"Gray3", MAKE_ARGB32(255, 8, 8, 8)},
    {"Gray30", MAKE_ARGB32(255, 77, 77, 77)},
    {"Gray31", MAKE_ARGB32(255, 79, 79, 79)},
    {"Gray32", MAKE_ARGB32(255, 82, 82, 82)},
    {"Gray33", MAKE_ARGB32(255, 84, 84, 84)},
    {"Gray34", MAKE_ARGB32(255, 87, 87, 87)},
    {"Gray35", MAKE_ARGB32(255, 89, 89, 89)},
    {"Gray36", MAKE_ARGB32(255, 92, 92, 92)},
    {"Gray37", MAKE_ARGB32(255, 94, 94, 94)},
    {"Gray38", MAKE_ARGB32(255, 97, 97, 97)},
    {"Gray39", MAKE_ARGB32(255, 99, 99, 99)},
    {"Gray4", MAKE_ARGB32(255, 10, 10, 10)},
    {"Gray40", MAKE_ARGB32(255, 102, 102, 102)},
    {"Gray41", MAKE_ARGB32(255, 105, 105, 105)},
    {"Gray42", MAKE_ARGB32(255, 107, 107, 107)},
    {"Gray43", MAKE_ARGB32(255, 110, 110, 110)},
    {"Gray44", MAKE_ARGB32(255, 112, 112, 112)},
    {"Gray45", MAKE_ARGB32(255, 115, 115, 115)},
    {"Gray46", MAKE_ARGB32(255, 117, 117, 117)},
    {"Gray47", MAKE_ARGB32(255, 120, 120, 120)},
    {"Gray48", MAKE_ARGB32(255, 122, 122, 122)},
    {"Gray49", MAKE_ARGB32(255, 125, 125, 125)},
    {"Gray5", MAKE_ARGB32(255, 13, 13, 13)},
    {"Gray50", MAKE_ARGB32(255, 127, 127, 127)},
    {"Gray51", MAKE_ARGB32(255, 130, 130, 130)},
    {"Gray52", MAKE_ARGB32(255, 133, 133, 133)},
    {"Gray53", MAKE_ARGB32(255, 135, 135, 135)},
    {"Gray54", MAKE_ARGB32(255, 138, 138, 138)},
    {"Gray55", MAKE_ARGB32(255, 140, 140, 140)},
    {"Gray56", MAKE_ARGB32(255, 143, 143, 143)},
    {"Gray57", MAKE_ARGB32(255, 145, 145, 145)},
    {"Gray58", MAKE_ARGB32(255, 148, 148, 148)},
    {"Gray59", MAKE_ARGB32(255, 150, 150, 150)},
    {"Gray6", MAKE_ARGB32(255, 15, 15, 15)},
    {"Gray60", MAKE_ARGB32(255, 153, 153, 153)},
    {"Gray61", MAKE_ARGB32(255, 156, 156, 156)},
    {"Gray62", MAKE_ARGB32(255, 158, 158, 158)},
    {"Gray63", MAKE_ARGB32(255, 161, 161, 161)},
    {"Gray64", MAKE_ARGB32(255, 163, 163, 163)},
    {"Gray65", MAKE_ARGB32(255, 166, 166, 166)},
    {"Gray66", MAKE_ARGB32(255, 168, 168, 168)},
    {"Gray67", MAKE_ARGB32(255, 171, 171, 171)},
    {"Gray68", MAKE_ARGB32(255, 173, 173, 173)},
    {"Gray69", MAKE_ARGB32(255, 176, 176, 176)},
    {"Gray7", MAKE_ARGB32(255, 18, 18, 18)},
    {"Gray70", MAKE_ARGB32(255, 179, 179, 179)},
    {"Gray71", MAKE_ARGB32(255, 181, 181, 181)},
    {"Gray72", MAKE_ARGB32(255, 184, 184, 184)},
    {"Gray73", MAKE_ARGB32(255, 186, 186, 186)},
    {"Gray74", MAKE_ARGB32(255, 189, 189, 189)},
    {"Gray75", MAKE_ARGB32(255, 191, 191, 191)},
    {"Gray76", MAKE_ARGB32(255, 194, 194, 194)},
    {"Gray77", MAKE_ARGB32(255, 196, 196, 196)},
    {"Gray78", MAKE_ARGB32(255, 199, 199, 199)},
    {"Gray79", MAKE_ARGB32(255, 201, 201, 201)},
    {"Gray8", MAKE_ARGB32(255, 20, 20, 20)},
    {"Gray80", MAKE_ARGB32(255, 204, 204, 204)},
    {"Gray81", MAKE_ARGB32(255, 207, 207, 207)},
    {"Gray82", MAKE_ARGB32(255, 209, 209, 209)},
    {"Gray83", MAKE_ARGB32(255, 212, 212, 212)},
    {"Gray84", MAKE_ARGB32(255, 214, 214, 214)},
    {"Gray85", MAKE_ARGB32(255, 217, 217, 217)},
    {"Gray86", MAKE_ARGB32(255, 219, 219, 219)},
    {"Gray87", MAKE_ARGB32(255, 222, 222, 222)},
    {"Gray88", MAKE_ARGB32(255, 224, 224, 224)},
    {"Gray89", MAKE_ARGB32(255, 227, 227, 227)},
    {"Gray9", MAKE_ARGB32(255, 23, 23, 23)},
    {"Gray90", MAKE_ARGB32(255, 229, 229, 229)},
    {"Gray91", MAKE_ARGB32(255, 232, 232, 232)},
    {"Gray92", MAKE_ARGB32(255, 235, 235, 235)},
    {"Gray93", MAKE_ARGB32(255, 237, 237, 237)},
    {"Gray94", MAKE_ARGB32(255, 240, 240, 240)},
    {"Gray95", MAKE_ARGB32(255, 242, 242, 242)},
    {"Gray96", MAKE_ARGB32(255, 245, 245, 245)},
    {"Gray97", MAKE_ARGB32(255, 247, 247, 247)},
    {"Gray98", MAKE_ARGB32(255, 250, 250, 250)},
    {"Gray99", MAKE_ARGB32(255, 252, 252, 252)},
    {"Green", MAKE_ARGB32(255, 0, 255, 0)},
    {"GreenYellow", MAKE_ARGB32(255, 173, 255, 47)},
    {"honeydew", MAKE_ARGB32(255, 240, 255, 240)},
    {"HotPink", MAKE_ARGB32(255, 255, 105, 180)},
    {"IndianRed", MAKE_ARGB32(255, 107, 57, 57)},
    {"ivory", MAKE_ARGB32(255, 255, 255, 240)},
    {"Khaki", MAKE_ARGB32(255, 179, 179, 126)},
    {"lavender", MAKE_ARGB32(255, 230, 230, 250)},
    {"LavenderBlush", MAKE_ARGB32(255, 255, 240, 245)},
    {"LawnGreen", MAKE_ARGB32(255, 124, 252, 0)},
    {"LemonChiffon", MAKE_ARGB32(255, 255, 250, 205)},
    {"LightBlue", MAKE_ARGB32(255, 176, 226, 255)},
    {"LightCoral", MAKE_ARGB32(255, 240, 128, 128)},
    {"LightCyan", MAKE_ARGB32(255, 224, 255, 255)},
    {"LightGoldenrod", MAKE_ARGB32(255, 238, 221, 130)},
    {"LightGoldenrodYellow", MAKE_ARGB32(255, 250, 250, 210)},
    {"LightGray", MAKE_ARGB32(255, 168, 168, 168)},
    {"LightPink", MAKE_ARGB32(255, 255, 182, 193)},
    {"LightSalmon", MAKE_ARGB32(255, 255, 160, 122)},
    {"LightSeaGreen", MAKE_ARGB32(255, 32, 178, 170)},
    {"LightSkyBlue", MAKE_ARGB32(255, 135, 206, 250)},
    {"LightSlateBlue", MAKE_ARGB32(255, 132, 112, 255)},
    {"LightSlateGray", MAKE_ARGB32(255, 119, 136, 153)},
    {"LightSteelBlue", MAKE_ARGB32(255, 124, 152, 211)},
    {"LightYellow", MAKE_ARGB32(255, 255, 255, 224)},
    {"LimeGreen", MAKE_ARGB32(255, 0, 175, 20)},
    {"linen", MAKE_ARGB32(255, 250, 240, 230)},
    {"Magenta", MAKE_ARGB32(255, 255, 0, 255)},
    {"Maroon", MAKE_ARGB32(255, 143, 0, 82)},
    {"MediumAquamarine", MAKE_ARGB32(255, 0, 147, 143)},
    {"MediumBlue", MAKE_ARGB32(255, 50, 50, 204)},
    {"MediumForestGreen", MAKE_ARGB32(255, 50, 129, 75)},
    {"MediumGoldenrod", MAKE_ARGB32(255, 209, 193, 102)},
    {"MediumOrchid", MAKE_ARGB32(255, 189, 82, 189)},
    {"MediumPurple", MAKE_ARGB32(255, 147, 112, 219)},
    {"MediumSeaGreen", MAKE_ARGB32(255, 52, 119, 102)},
    {"MediumSlateBlue", MAKE_ARGB32(255, 106, 106, 141)},
    {"MediumSpringGreen", MAKE_ARGB32(255, 35, 142, 35)},
    {"MediumTurquoise", MAKE_ARGB32(255, 0, 210, 210)},
    {"MediumVioletRed", MAKE_ARGB32(255, 213, 32, 121)},
    {"MidnightBlue", MAKE_ARGB32(255, 47, 47, 100)},
    {"MintCream", MAKE_ARGB32(255, 245, 255, 250)},
    {"MistyRose", MAKE_ARGB32(255, 255, 228, 225)},
    {"moccasin", MAKE_ARGB32(255, 255, 228, 181)},
    {"NavajoWhite", MAKE_ARGB32(255, 255, 222, 173)},
    {"Navy", MAKE_ARGB32(255, 35, 35, 117)},
    {"NavyBlue", MAKE_ARGB32(255, 35, 35, 117)},
    {"None", MAKE_ARGB32(0, 0, 0, 1)},
    {"OldLace", MAKE_ARGB32(255, 253, 245, 230)},
    {"OliveDrab", MAKE_ARGB32(255, 107, 142, 35)},
    {"Orange", MAKE_ARGB32(255, 255, 135, 0)},
    {"OrangeRed", MAKE_ARGB32(255, 255, 69, 0)},
    {"Orchid", MAKE_ARGB32(255, 239, 132, 239)},
    {"PaleGoldenrod", MAKE_ARGB32(255, 238, 232, 170)},
    {"PaleGreen", MAKE_ARGB32(255, 115, 222, 120)},
    {"PaleTurquoise", MAKE_ARGB32(255, 175, 238, 238)},
    {"PaleVioletRed", MAKE_ARGB32(255, 219, 112, 147)},
    {"PapayaWhip", MAKE_ARGB32(255, 255, 239, 213)},
    {"PeachPuff", MAKE_ARGB32(255, 255, 218, 185)},
    {"peru", MAKE_ARGB32(255, 205, 133, 63)},
    {"Pink", MAKE_ARGB32(255, 255, 181, 197)},
    {"Plum", MAKE_ARGB32(255, 197, 72, 155)},
    {"PowderBlue", MAKE_ARGB32(255, 176, 224, 230)},
    {"purple", MAKE_ARGB32(255, 160, 32, 240)},
    {"Red", MAKE_ARGB32(255, 255, 0, 0)},
    {"RosyBrown", MAKE_ARGB32(255, 188, 143, 143)},
    {"RoyalBlue", MAKE_ARGB32(255, 65, 105, 225)},
    {"SaddleBrown", MAKE_ARGB32(255, 139, 69, 19)},
    {"Salmon", MAKE_ARGB32(255, 233, 150, 122)},
    {"SandyBrown", MAKE_ARGB32(255, 244, 164, 96)},
    {"SeaGreen", MAKE_ARGB32(255, 82, 149, 132)},
    {"seashell", MAKE_ARGB32(255, 255, 245, 238)},
    {"Sienna", MAKE_ARGB32(255, 150, 82, 45)},
    {"SkyBlue", MAKE_ARGB32(255, 114, 159, 255)},
    {"SlateBlue", MAKE_ARGB32(255, 126, 136, 171)},
    {"SlateGray", MAKE_ARGB32(255, 112, 128, 144)},
    {"snow", MAKE_ARGB32(255, 255, 250, 250)},
    {"SpringGreen", MAKE_ARGB32(255, 65, 172, 65)},
    {"SteelBlue", MAKE_ARGB32(255, 84, 112, 170)},
    {"Tan", MAKE_ARGB32(255, 222, 184, 135)},
    {"Thistle", MAKE_ARGB32(255, 216, 191, 216)},
    {"tomato", MAKE_ARGB32(255, 255, 99, 71)},
    {"Transparent", MAKE_ARGB32(0, 0, 0, 1)},
    {"Turquoise", MAKE_ARGB32(255, 25, 204, 223)},
    {"Violet", MAKE_ARGB32(255, 156, 62, 206)},
    {"VioletRed", MAKE_ARGB32(255, 243, 62, 150)},
    {"Wheat", MAKE_ARGB32(255, 245, 222, 179)},
    {"White", MAKE_ARGB32(255, 255, 255, 255)},
    {"WhiteSmoke", MAKE_ARGB32(255, 245, 245, 245)},
    {"Yellow", MAKE_ARGB32(255, 255, 255, 0)},
    {"YellowGreen", MAKE_ARGB32(255, 50, 216, 56)},
    {NULL,0}
};

/****************************************************************
 * Low level parsing code :
 ****************************************************************/
static inline char
get_xpm_char( ASXpmFile *xpm_file )
{
#ifdef HAVE_LIBXPM
	return '\0';
#else
	char c;
	if( xpm_file->curr_byte >= xpm_file->bytes_in )
	{
		if( xpm_file->bytes_in > AS_XPM_BUFFER_UNDO )
		{
			register char* src = &(xpm_file->buffer[xpm_file->bytes_in-AS_XPM_BUFFER_UNDO]);
			register char* dst = &(xpm_file->buffer[0]);
			register int i;
			for( i = 0 ; i < AS_XPM_BUFFER_UNDO ; i++ )
				dst[i] = src[i];
/*			xpm_file->bytes_in = AS_XPM_BUFFER_UNDO+fread( &(xpm_file->buffer[AS_XPM_BUFFER_UNDO]), 1, AS_XPM_BUFFER_SIZE, xpm_file->fp );*/
			xpm_file->bytes_in = AS_XPM_BUFFER_UNDO+read( xpm_file->fd, &(xpm_file->buffer[AS_XPM_BUFFER_UNDO]), AS_XPM_BUFFER_SIZE );
			xpm_file->curr_byte = AS_XPM_BUFFER_UNDO ;
		}
		if( xpm_file->bytes_in <= AS_XPM_BUFFER_UNDO )
		{
			xpm_file->parse_state = XPM_Outside ;
			return '\0';
		}
	}
	c = xpm_file->buffer[xpm_file->curr_byte];
/*	fprintf( stderr, "curr byte = %d ( of %d ), char = %c\n", xpm_file->curr_byte, xpm_file->bytes_in, c ); */

	xpm_file->curr_byte++;
	return c;
#endif
}

static inline void
unget_xpm_char( ASXpmFile *xpm_file, char c )
{
#ifndef HAVE_LIBXPM
	if( xpm_file->curr_byte > 0 )
	{
		xpm_file->curr_byte--;
		xpm_file->buffer[xpm_file->curr_byte] = c;
	}
#endif
}

static inline void
skip_xpm_comments( ASXpmFile *xpm_file )
{
	char c;
	if((c=get_xpm_char(xpm_file)) != '*')
		unget_xpm_char(xpm_file, c);
	else
	{
		xpm_file->parse_state = XPM_InComments ;
		while( xpm_file->parse_state == XPM_InComments )
		{
			c = get_xpm_char(xpm_file);
			if( c == '*' )
				if( (c=get_xpm_char(xpm_file)) == '/' )
					xpm_file->parse_state--;
		}
	}
}

static Bool
seek_next_xpm_string( ASXpmFile *xpm_file )
{
	while( xpm_file->parse_state == XPM_InImage )
	{
		register char c;
		c = get_xpm_char(xpm_file);
		if( c == '/')
			skip_xpm_comments( xpm_file );
		else if( c == '"')
			xpm_file->parse_state = XPM_InString;
	}
	return (xpm_file->parse_state >= XPM_InString);
}

static Bool
seek_next_xpm_image( ASXpmFile *xpm_file )
{
	while( xpm_file->parse_state == XPM_InFile )
	{
		register char c;
		c = get_xpm_char(xpm_file);
		if( c == '/')
			skip_xpm_comments( xpm_file );
		else if( c == '{')
			xpm_file->parse_state = XPM_InImage;
	}
	return (xpm_file->parse_state >= XPM_InImage);
}

static Bool
read_next_xpm_string( ASXpmFile *xpm_file )
{
	char c;
	int i = 0;
	while( xpm_file->parse_state == XPM_InString )
	{
		c=get_xpm_char(xpm_file);
		if( c == '"' )
		{
			xpm_file->parse_state = XPM_InImage ;
			c = '\0';
		}

		if( i >= (int)xpm_file->str_buf_size )
		{
			xpm_file->str_buf = realloc( xpm_file->str_buf, xpm_file->str_buf_size+16+(xpm_file->str_buf_size>>2));
			xpm_file->str_buf_size += 16+(xpm_file->str_buf_size>>2) ;
		}
		xpm_file->str_buf[i++] = c;
	}
	return True;
}

#ifndef HAVE_LIBXPM
static Bool
parse_xpm_cmap_entry( ASXpmFile *xpm_file, char **colornames )
{
	register char *ptr ;
	int key ;
	Bool success = False ;

	if( xpm_file == NULL || xpm_file->str_buf == NULL )
		return False;
	for( key =0 ; key < 6 ; ++key )
		colornames[key] = NULL ;

	ptr = xpm_file->str_buf+xpm_file->bpp ;
	key = -1;
	do
	{
        while( !isspace((int)*ptr) && *ptr != '\0' ) ++ptr;
        while( isspace((int)*ptr) ) ++ptr;
		if( *ptr )
		{
			if( key >= 0 )
			{
				colornames[key] = ptr ;
				key = -1 ;
				success = True;
			}else
			{
    			if( *ptr == 'c' )				/* key #5: color visual */
					key = 5;
				else
				{
					if( *ptr == 's' ) 				/* key #1: symbol */
						key = 1;
    				else if( *ptr == 'm' )				/* key #2: mono visual */
						key = 2;
    				else if( *ptr == 'g' )				/* key #4: gray visual */
						key = 4;
					else
						key = 0;
				}
			}
		}
	}while( *ptr );
	return success;
}
#endif
/*************************************************************************
 * High level xpm reading interface ;
 *************************************************************************/
void
close_xpm_file( ASXpmFile **xpm_file )
{
	if( xpm_file )
		if( *xpm_file )
		{
			if( (*xpm_file)->fd )
				close( (*xpm_file)->fd );
			if( (*xpm_file)->str_buf )
				free( (*xpm_file)->str_buf );
#ifdef HAVE_LIBXPM
			XpmFreeXpmImage (&((*xpm_file)->xpmImage));
#else
			if( (*xpm_file)->buffer )
				free( (*xpm_file)->buffer );
#endif
			free_scanline(&((*xpm_file)->scl), True);
			if( (*xpm_file)->cmap )
				free( (*xpm_file)->cmap );
			if( (*xpm_file)->cmap2 )
			{
				register int i ;
				for( i = 0 ; i < 256 ; i++ )
					if( (*xpm_file)->cmap2[i] )
						free( (*xpm_file)->cmap2[i] );
				free( (*xpm_file)->cmap2 );
			}
			if( (*xpm_file)->cmap_name_xref )
				destroy_ashash( &((*xpm_file)->cmap_name_xref) );
#if 0
			memset( *xpm_file, 0x00, sizeof(ASXpmFile));
#endif
			free( *xpm_file );
			*xpm_file = NULL ;
		}
}

ASXpmFile*
open_xpm_file( const char *realfilename )
{
	ASXpmFile *xpm_file = NULL;
	if( realfilename )
	{
		Bool success = False ;
		int fd ;
		xpm_file = safecalloc( 1, sizeof(ASXpmFile));
#ifndef HAVE_LIBXPM
		fd = open( realfilename, O_RDONLY );
		if( fd >= 0 )
		{
			xpm_file->fd = fd;
			xpm_file->parse_state = XPM_InFile ;
			xpm_file->buffer = safemalloc(AS_XPM_BUFFER_UNDO+AS_XPM_BUFFER_SIZE+1);
/*			xpm_file->bytes_in = AS_XPM_BUFFER_UNDO+fread( &(xpm_file->buffer[AS_XPM_BUFFER_UNDO]), 1, AS_XPM_BUFFER_SIZE, fp ); */
			xpm_file->bytes_in = AS_XPM_BUFFER_UNDO+read( fd, &(xpm_file->buffer[AS_XPM_BUFFER_UNDO]),  AS_XPM_BUFFER_SIZE );
			xpm_file->curr_byte = AS_XPM_BUFFER_UNDO ;
			if( get_xpm_string( xpm_file ) )
				success = parse_xpm_header( xpm_file );

		}
#else                                          /* libXpm interface : */
		if( XpmReadFileToXpmImage ((char *)realfilename, &(xpm_file->xpmImage), NULL) == XpmSuccess)
		{
			fd = NULL ;
			xpm_file->width = xpm_file->xpmImage.width;
			xpm_file->height= xpm_file->xpmImage.height;
			xpm_file->cmap_size = xpm_file->xpmImage.ncolors;
			xpm_file->bpp = xpm_file->xpmImage.cpp;
			success = True;
		}
#endif
		if( !success )
			close_xpm_file( &xpm_file );
		else
		{
			if( xpm_file->width > MAX_IMPORT_IMAGE_SIZE )
				xpm_file->width = MAX_IMPORT_IMAGE_SIZE ;
			if( xpm_file->height > MAX_IMPORT_IMAGE_SIZE )
				xpm_file->height = MAX_IMPORT_IMAGE_SIZE ;
			if( xpm_file->bpp > MAX_XPM_BPP )
				xpm_file->bpp = MAX_XPM_BPP;
			prepare_scanline( xpm_file->width, 0, &(xpm_file->scl), False );
		}
	}
	return xpm_file ;
}

ASXpmFile*
open_xpm_data( const char **data )
{
	ASXpmFile *xpm_file = NULL;
	if( data )
	{
		Bool success = False ;

		xpm_file = safecalloc( 1, sizeof(ASXpmFile));
		xpm_file->data = (char**)data ;
		xpm_file->parse_state = XPM_InFile ;
		xpm_file->buffer = safemalloc(AS_XPM_BUFFER_UNDO+AS_XPM_BUFFER_SIZE+1);
		xpm_file->curr_byte = AS_XPM_BUFFER_UNDO ;
		if( get_xpm_string( xpm_file ) )
			success = parse_xpm_header( xpm_file );

		if( !success )
			close_xpm_file( &xpm_file );
		else
		{
			if( xpm_file->width > MAX_IMPORT_IMAGE_SIZE )
				xpm_file->width = MAX_IMPORT_IMAGE_SIZE ;
			if( xpm_file->height > MAX_IMPORT_IMAGE_SIZE )
				xpm_file->height = MAX_IMPORT_IMAGE_SIZE ;
			if( xpm_file->bpp > MAX_XPM_BPP )
				xpm_file->bpp = MAX_XPM_BPP;
			prepare_scanline( xpm_file->width, 0, &(xpm_file->scl), False );
		}
	}
	return xpm_file ;
}


ASXpmStatus
get_xpm_string( ASXpmFile *xpm_file )
{
	if( xpm_file == NULL )
		return XPM_Error;;
	if( xpm_file->data )
	{
		xpm_file->str_buf = xpm_file->data[xpm_file->curr_img_line];
		xpm_file->str_buf_size = 0 ;
		if( xpm_file->str_buf == NULL )
			return XPM_EndOfFile;
	}else
	{
		if( xpm_file->parse_state < XPM_InFile )
			return XPM_EndOfFile;
		if( xpm_file->parse_state < XPM_InImage )
		{
			if( !seek_next_xpm_image( xpm_file ) )
				return XPM_EndOfFile;
		}
		if( !seek_next_xpm_string( xpm_file ) )
		{
			xpm_file->curr_img++;
			return XPM_EndOfImage;
		}
		if( !read_next_xpm_string( xpm_file ))
			return XPM_Error;
		xpm_file->curr_img_line++;
	}
	return XPM_Success;
}

Bool
parse_xpm_header( ASXpmFile *xpm_file )
{
	register char *ptr ;
	if( xpm_file == NULL || xpm_file->str_buf == NULL )
		return False;

	ptr = xpm_file->str_buf ;
	while( isspace((int)*ptr) ) ++ptr;
	if( *ptr == '\0' )
		return False;
	xpm_file->width = atoi( ptr );
	while( !isspace((int)*ptr) && *ptr != '\0' ) ++ptr;
	while( isspace((int)*ptr) ) ++ptr;
	if( *ptr == '\0' )
		return False;
	xpm_file->height = atoi( ptr );
	while( !isspace((int)*ptr) && *ptr != '\0' ) ++ptr;
	while( isspace((int)*ptr) ) ++ptr;
	if( *ptr == '\0' )
		return False;
	xpm_file->cmap_size = atoi( ptr );
	while( !isspace((int)*ptr) && *ptr != '\0' ) ++ptr;
	while( isspace((int)*ptr) ) ++ptr;
	if( *ptr == '\0' )
		return False;
	xpm_file->bpp = atoi( ptr );
	return True;
}

ASImage *
create_xpm_image( ASXpmFile *xpm_file, int compression )
{
	ASImage *im = NULL;
	if( xpm_file != NULL && xpm_file->width > 0 && xpm_file->height > 0 )
	{
		im = create_asimage( xpm_file->width, xpm_file->height, compression );
	}
	return im;
}

static ARGB32
lookup_xpm_color( char **colornames, ASHashTable *xpm_color_names )
{
    ARGB32 color = 0;
	register int key = 5 ;
	do
	{
		if( colornames[key] )
		{
			if( *(colornames[key]) != '#' )
			{
				ASHashData hdata ;
                if( get_hash_item( xpm_color_names, AS_HASHABLE(colornames[key]), &hdata.vptr ) == ASH_Success )
				{
                    color = hdata.c32 ;
					LOCAL_DEBUG_OUT(" xpm color \"%s\" matched into 0x%lX", colornames[key], color );
					break;
				}
			}
			if( parse_argb_color( colornames[key], &color ) != colornames[key] )
			{
				LOCAL_DEBUG_OUT(" xpm color \"%s\" parsed into 0x%lX", colornames[key], color );
				break;
			}
			LOCAL_DEBUG_OUT(" xpm color \"%s\" is invalid :(", colornames[key] );
			/* unknown color - leaving it at 0 - that will make it transparent */
		}
	}while ( --key > 0);
	return color;
}

void
string_value_destroy (ASHashableValue value, void *data)
{
	if ((char*)value != NULL)
		free ((char*)value);
}

Bool
build_xpm_colormap( ASXpmFile *xpm_file )
{
	size_t real_cmap_size ;
	size_t i ;
#ifdef HAVE_LIBXPM
	XpmColor *xpm_cmap = (xpm_file)?xpm_file->xpmImage.colorTable: NULL ;
#endif
	static ASHashTable *xpm_color_names = NULL ;

	if( xpm_file == NULL )
	{
		destroy_ashash(&xpm_color_names);
		return False;
	}

	if( xpm_file->cmap_name_xref )
		destroy_ashash( &(xpm_file->cmap_name_xref) );
	if( xpm_file->cmap )
	{
		free( xpm_file->cmap );
		xpm_file->cmap = NULL;
	}
	real_cmap_size = xpm_file->cmap_size;
#ifdef HAVE_LIBXPM
	if( real_cmap_size > 1024 )
	{
		xpm_file->cmap = calloc( real_cmap_size, sizeof(ARGB32));
		if( xpm_file->cmap == NULL ) /* we don't want to bomb out if image is busted */
			real_cmap_size = 1024 ;
	}
	xpm_file->cmap = safecalloc( real_cmap_size, sizeof(ARGB32));
#else
	if( xpm_file->bpp == 1 )
	{
		real_cmap_size = 256 ;
		xpm_file->cmap = safecalloc( real_cmap_size, sizeof(ARGB32));
	}else if( xpm_file->bpp == 2 )
	{
		xpm_file->cmap2 = safecalloc( 256, sizeof(ARGB32*));
	}else
		xpm_file->cmap_name_xref = create_ashash( 0, string_hash_value,
													 string_compare,
													 string_value_destroy );
#endif
	if( xpm_color_names == NULL )
	{
		xpm_color_names = create_ashash( 0, casestring_hash_value, casestring_compare, NULL );
		for( i = 0 ; XpmRGB_Colors[i].name != NULL ; i++ )
			add_hash_item( xpm_color_names, (ASHashableValue)XpmRGB_Colors[i].name, (void*)((long)XpmRGB_Colors[i].argb) );
	}

	for( i = 0 ; i < xpm_file->cmap_size ; ++i )
	{
		ARGB32 color ;
#ifdef HAVE_LIBXPM
		if( i < real_cmap_size )
		{
			color = lookup_xpm_color((char**)&(xpm_cmap[i].string), xpm_color_names);
 LOCAL_DEBUG_OUT( "cmap[%d]: 0x%X\n",  i, color );
			xpm_file->cmap[i] = color;
			if( ARGB32_ALPHA8(color) != 0x00FF )
			{	
				if( ARGB32_ALPHA8(color) != 0 ) 
					xpm_file->full_alpha = True ;
				xpm_file->do_alpha = True ;
			}
		}
#else
		char *colornames[6] ;
		if( !get_xpm_string( xpm_file ) )
			break;
LOCAL_DEBUG_OUT( "cmap[%d]: \"%s\"\n",  i, xpm_file->str_buf );
		if( !parse_xpm_cmap_entry( xpm_file, &(colornames[0])))
			continue;
		color = lookup_xpm_color(&(colornames[0]), xpm_color_names);
LOCAL_DEBUG_OUT( "\t\tcolor = 0x%8.8lX\n",  color );
		if( ARGB32_ALPHA8(color) != 0x00FF )
			xpm_file->do_alpha = True ;
		if( xpm_file->bpp == 1 )
			xpm_file->cmap[(unsigned int)(xpm_file->str_buf[0])] = color ;
		else if( xpm_file->bpp == 2 )
		{
			ARGB32 **slot = &(xpm_file->cmap2[(unsigned int)(xpm_file->str_buf[0])]) ;
			if( *slot == NULL )
				*slot = safecalloc( 256, sizeof(ARGB32));
			(*slot)[(unsigned int)(xpm_file->str_buf[1])] = color ;
		}
		else if( i < real_cmap_size )
		{
			char *name = mystrndup(xpm_file->str_buf, xpm_file->bpp);
LOCAL_DEBUG_OUT( "\t\tname = \"%s\"\n", name );
			add_hash_item( xpm_file->cmap_name_xref, (ASHashableValue)name, (void*)((long)color) );
		}
#endif
	}
	xpm_file->cmap_size = real_cmap_size ;
	return True;
}

Bool
convert_xpm_scanline( ASXpmFile *xpm_file, unsigned int line )
{
	CARD32 *r = xpm_file->scl.red, *g = xpm_file->scl.green,
		   *b = xpm_file->scl.blue,*a = (xpm_file->do_alpha)?xpm_file->scl.alpha:NULL ;
	register int k = xpm_file->width ;
	ARGB32 *cmap = xpm_file->cmap ;
#ifdef HAVE_LIBXPM
	unsigned int *data = xpm_file->xpmImage.data+k*line ;
#else
	unsigned char *data ;
	if( !get_xpm_string( xpm_file ) )
		return False ;
	data = (unsigned char*)xpm_file->str_buf ;
#endif
	if( cmap )
	{
		while( --k >= 0 )
			if( data[k] < xpm_file->cmap_size )
			{
				register CARD32 c = cmap[data[k]] ;
				r[k] = ARGB32_RED8(c);
				g[k] = ARGB32_GREEN8(c);
				b[k] = ARGB32_BLUE8(c);
				if( a )
					a[k]  = ARGB32_ALPHA8(c);
			}
	}else if( xpm_file->cmap2 )
	{
		ARGB32 **cmap2 = xpm_file->cmap2 ;
		while( --k >= 0 )
		{
			ARGB32 *slot = cmap2[data[k<<1]] ;
			if( slot != NULL )
			{
				register CARD32 c = slot[data[(k<<1)+1]] ;
				r[k] = ARGB32_RED8(c);
				g[k] = ARGB32_GREEN8(c);
				b[k] = ARGB32_BLUE8(c);
				if( a )
					a[k]  = ARGB32_ALPHA8(c);
			}
		}
	}else if( xpm_file->cmap_name_xref )
	{
		char *pixel ;
		pixel = safemalloc( xpm_file->bpp+1);
		pixel[xpm_file->bpp] = '\0' ;
		data += (k-1)*xpm_file->bpp ;
		while( --k >= 0 )
		{
			register int i = xpm_file->bpp;
            ASHashData hdata = {0} ;
            CARD32 c = 0;
			while( --i >= 0 )
				pixel[i] = data[i] ;
			data -= xpm_file->bpp ;
            get_hash_item( xpm_file->cmap_name_xref, AS_HASHABLE(pixel), &hdata.vptr );
            /* on 64 bit system we must do that since pointers are 64 bit */
            c = hdata.c32;
			r[k] = ARGB32_RED8(c);
			g[k] = ARGB32_GREEN8(c);
			b[k] = ARGB32_BLUE8(c);
			if( a )
				a[k]  = ARGB32_ALPHA8(c);
		}
		free( pixel );
	}
	return True;
}

/**********************************************************************/
/* XPM writing :                                                      */
/**********************************************************************/

ASXpmCharmap*
build_xpm_charmap( ASColormap *cmap, Bool has_alpha, ASXpmCharmap *reusable_memory )
{
	ASXpmCharmap *xpm_cmap = reusable_memory ;
	char *ptr ;
	int i ;
	int rem ;

	xpm_cmap->count = cmap->count+((has_alpha)?1:0) ;

	xpm_cmap->cpp = 0 ;
	for( rem = xpm_cmap->count ; rem > 0 ; rem = rem/MAXPRINTABLE )
		++(xpm_cmap->cpp) ;
	ptr = xpm_cmap->char_code = safemalloc(xpm_cmap->count*(xpm_cmap->cpp+1)) ;
	for( i = 0 ; i < (int)xpm_cmap->count ; i++ )
	{
		register int k = xpm_cmap->cpp ;
		rem = i ;
		ptr[k] = '\0' ;
		while( --k >= 0 )
		{
			ptr[k] = printable[rem%MAXPRINTABLE] ;
			rem /= MAXPRINTABLE ;
		}
		ptr += xpm_cmap->cpp+1 ;
	}

	return xpm_cmap;
}

void destroy_xpm_charmap( ASXpmCharmap *xpm_cmap, Bool reusable )
{
	if( xpm_cmap )
	{
		if( xpm_cmap->char_code )
			free( xpm_cmap->char_code );
		if( !reusable )
			free( xpm_cmap );
	}
}

