/*---------------------------------------------------------------------
 * 
 *
 *             Copyright (c) 1992, Visual Edge Software Ltd.
 *
 * ALL  RIGHTS  RESERVED.  Permission  to  use,  copy,  modify,  and
 * distribute  this  software  and its documentation for any purpose
 * and  without  fee  is  hereby  granted,  provided  that the above
 * copyright  notice  appear  in  all  copies  and  that  both  that
 * copyright  notice and this permission notice appear in supporting
 * documentation,  and that  the name of Visual Edge Software not be
 * used  in advertising  or publicity  pertaining to distribution of
 * the software without specific, written prior permission. The year
 * included in the notice is the year of the creation of the work.
 *-------------------------------------------------------------------*/
/*
 * Copyright 1990, 1991 GROUPE BULL
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided
 * that the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of GROUPE BULL not be used in advertising
 * or publicity pertaining to distribution of the software without specific,
 * written prior permission.  GROUPE BULL makes no representations about the
 * suitability of this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 *
 * GROUPE BULL disclaims all warranties with regard to this software,
 * including all implied warranties of merchantability and fitness,
 * in no event shall GROUPE BULL be liable for any special,
 * indirect or consequential damages or any damages
 * whatsoever resulting from loss of use, data or profits,
 * whether in an action of contract, negligence or other tortious
 * action, arising out of or in connection with the use 
 * or performance of this software.
 *

.VERSION
 090818		last modif

 */

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


#include "xpmP.h"
#ifdef VMS
#include "sys$library:ctype.h"
#else
#include <ctype.h>
#endif

#include <stdlib.h>

extern unsigned int atoui();
extern int xpmGetString();


LFUNC(ParseValues, int, (xpmData *data, unsigned int *width,
			 unsigned int *height, unsigned int *ncolors,
			 unsigned int *cpp,  unsigned int *x_hotspot,
			 unsigned int *y_hotspot, unsigned int *hotspot,
			 unsigned int *extensions));

LFUNC(ParseColors, int, (xpmData *data, unsigned int ncolors, unsigned int cpp,
			 char ****colorTablePtr, xpmHashTable *hashtable));

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

LFUNC(ParseExtensions, int, (xpmData *data, XpmExtension **extensions,
			     unsigned int *nextensions));

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, frees only locally allocated variables */
#undef RETURN
#define RETURN(status) \
  { if (colorTable) xpmFreeColorTable(colorTable, ncolors); \
    if (pixelindex) free(pixelindex); \
    if (hints_cmt)  free(hints_cmt); \
    if (colors_cmt) free(colors_cmt); \
    if (pixels_cmt) free(pixels_cmt); \
    return(status); }

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

    int ErrorStatus;
    xpmHashTable hashtable;

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

    /*
     * store the hints comment line 
     */
    if (attributes && (attributes->valuemask & XpmReturnInfos))
	xpmGetCmt(data, &hints_cmt);

    /*
     * init the hastable
     */
    if (USE_HASHTABLE)
	xpmHashTableInit(&hashtable);
    
    /*
     * read colors 
     */
    ErrorStatus = ParseColors(data, ncolors, cpp, &colorTable, &hashtable);
    if (ErrorStatus != XpmSuccess)
	RETURN(ErrorStatus);

    /*
     * store the colors comment line 
     */
    if (attributes && (attributes->valuemask & XpmReturnInfos))
	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 (attributes && (attributes->valuemask & XpmReturnInfos))
	xpmGetCmt(data, &pixels_cmt);

    /*
     * parse extensions
     */
    if (extensions && attributes
	&& (attributes->valuemask & XpmReturnExtensions)) {
	ErrorStatus = ParseExtensions(data, &attributes->extensions,
				      &attributes->nextensions);
	if (ErrorStatus != XpmSuccess)
	    RETURN(ErrorStatus);
    }

    /*
     * store found informations in the xpmInternAttrib structure 
     */
    attrib_return->width = width;
    attrib_return->height = height;
    attrib_return->cpp = cpp;
    attrib_return->ncolors = ncolors;
    attrib_return->colorTable = colorTable;
    attrib_return->pixelindex = pixelindex;

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

static int
ParseValues(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];

    /*
     * 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);
    if (l) {
	*extensions = l == 6 && !strncmp("XPMEXT", buf, 6);
	if (*extensions)
	    *hotspot = xpmNextUI(data, x_hotspot)
		&& xpmNextUI(data, y_hotspot);
	else {
	    *hotspot = atoui(buf, l, x_hotspot) && xpmNextUI(data, y_hotspot);
	    l = xpmNextWord(data, buf);
	    *extensions = l == 6 && !strncmp("XPMEXT", buf, 6);
	}
    }
    return (XpmSuccess);
}

static int
ParseColors(data, ncolors, cpp, colorTablePtr, hashtable)
    xpmData *data;
    unsigned int ncolors;
    unsigned int cpp;
    char ****colorTablePtr;		/* Jee, that's something! */
    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 ***ct, **cts, **sptr, *s;
    char ***colorTable;

    colorTable = (char ***) calloc(ncolors, sizeof(char **));
    if (!colorTable)
	return(XpmNoMemory);

    for (a = 0, ct = colorTable; a < ncolors; a++, ct++) {
	xpmNextString(data);		/* skip the line */
	cts = *ct = (char **) calloc((NKEYS + 1), sizeof(char *));
	if (!cts) {
	    xpmFreeColorTable(colorTable, ncolors);
	    return(XpmNoMemory);
	}

	/*
	 * read pixel value 
	 */
	*cts = (char *) malloc((size_t)(cpp + 1)); /* + 1 for null terminated */
	if (!*cts) {
	    xpmFreeColorTable(colorTable, ncolors);
	    return(XpmNoMemory);
	}
	for (b = 0, s = *cts; b < cpp; b++, s++)
	    *s = xpmGetC(data);
	*s = '\0';

	/*
	 * tore the string in the hashtable with its color index number
	 */
	if (USE_HASHTABLE)
	    xpmHashIntern(hashtable, *cts, HashAtomData(a));

	/*
	 * read color keys and values 
	 */
	curkey = 0;
	lastwaskey = 0;
	while ((l = xpmNextWord(data, buf))) {
	    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 = cts[curkey] = (char *) malloc((size_t)(strlen(curbuf) + 1));
		    if (!s) {
			xpmFreeColorTable(colorTable, ncolors);
			return(XpmNoMemory);
		    }
		    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 = cts[curkey] = (char *) malloc((size_t)(strlen(curbuf) + 1));
	if (!s) {
	    xpmFreeColorTable(colorTable, ncolors);
	    return(XpmNoMemory);
	}
	strcpy(s, curbuf);
    }
    *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;
    char ***colorTable;
    xpmHashTable *hashtable;
    unsigned int **pixels;
{
    unsigned int *iptr, *iptr2;
    char *s;
    unsigned int a, x, y;
    char buf[BUFSIZ];

    iptr2 = (unsigned int *) malloc((size_t)(sizeof(unsigned int) * width * height));
    if (!iptr2)
	return(XpmNoMemory);

    buf[cpp] = '\0';
    iptr = iptr2;
    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 */
		    free(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][0], buf))
			break;
		if (a == ncolors) {	/* no color matches */
		    free(iptr2);
		    return(XpmFileInvalid);
		}
		*iptr = a;
	    }
	}
    }
    *pixels = iptr2;
    return (XpmSuccess);
}

static int
ParseExtensions(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;
    int status;
    char *string, *s, *s2, **sp;

    notend = 0;
 
    xpmNextString(data);
    exts = (XpmExtension *) malloc((size_t)(sizeof(XpmExtension)));
    /* get the whole string */
    status = xpmGetString(data, &string, &l);
    if (status != XpmSuccess) {
	free(exts);
	return(status);
    }
    /* look for the key word XPMEXT, skip lines before this */
    while ((notstart = strncmp("XPMEXT", string, 6))
	   && (notend = strncmp("XPMENDEXT", string, 9))) {
	free(string);
	xpmNextString(data);
	status = xpmGetString(data, &string, &l);
	if (status != XpmSuccess) {
	    free(exts);
	    return(status);
	}
    }
    while (!notstart && notend) {
	/* there starts an extension */
	ext = (XpmExtension *) realloc(exts, (num + 1) * sizeof(XpmExtension));
	if (!ext) {
	    free(exts);
	    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 *) malloc((size_t)(l - a - 6));
	if (!ext->name) {
	    free(exts);
	    return(XpmNoMemory);
	}
	strncpy(ext->name, s + a, l - a - 6);
	free(string);
	/* now store the related lines */
	xpmNextString(data);
	status = xpmGetString(data, &string, &l);
	if (status != XpmSuccess) {
	    free(exts);
	    return(status);
	}
	ext->lines = (char **) malloc((size_t)(sizeof(char *)));
	nlines = 0;
	while ((notstart = strncmp("XPMEXT", string, 6))
	       && (notend = strncmp("XPMENDEXT", string, 9))) {
	    sp = (char **) realloc(ext->lines, (nlines + 1) * sizeof(char *));
	    if (!sp) {
		free(exts);
		return(XpmNoMemory);
	    }
	    ext->lines = sp;
	    ext->lines[nlines] = string;
	    nlines++;
	    xpmNextString(data);
	    status = xpmGetString(data, &string, &l);
	    if (status != XpmSuccess) {
		free(exts);
		return(status);
	    }
	}
	if (!nlines) {
	    free(ext->lines);
	    ext->lines = NULL;
	}
	ext->nlines = nlines;
	num++;
    }
    if (!num) {
	free(string);
	free(exts);
	exts = NULL;
    }
    if (!notend)
	free(string);
    *nextensions = num;
    *extensions = exts;
    return(XpmSuccess);
}
