1/*
2 * Copyright (C) 1989-95 GROUPE BULL
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to
6 * deal in the Software without restriction, including without limitation the
7 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 * sell copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * GROUPE BULL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 *
21 * Except as contained in this notice, the name of GROUPE BULL shall not be
22 * used in advertising or otherwise to promote the sale, use or other dealings
23 * in this Software without prior written authorization from GROUPE BULL.
24 */
25
26/*****************************************************************************\
27* parse.c:                                                                    *
28*                                                                             *
29*  XPM library                                                                *
30*  Parse an XPM file or array and store the found information                *
31*  in the given XpmImage structure.                                           *
32*                                                                             *
33*  Developed by Arnaud Le Hors                                                *
34\*****************************************************************************/
35
36/*
37 * The code related to FOR_MSW has been added by
38 * HeDu (hedu@cul-ipn.uni-kiel.de) 4/94
39 */
40
41/* October 2004, source code review by Thomas Biege <thomas@suse.de> */
42
43#ifdef HAVE_CONFIG_H
44#include <config.h>
45#endif
46#include "XpmI.h"
47#include <ctype.h>
48#include <string.h>
49
50/**
51 * like strlcat() but returns true on success and false if the string got
52 * truncated.
53 */
54static inline Bool
55xstrlcat(char *dst, const char *src, size_t dstsize)
56{
57#if defined(HAS_STRLCAT) || defined(HAVE_STRLCAT)
58    return strlcat(dst, src, dstsize) < dstsize;
59#else
60    if ((strlen(dst) + strlen(src)) < dstsize) {
61        strcat(dst, src);
62        return True;
63    } else {
64        return False;
65    }
66#endif
67}
68
69LFUNC(ParsePixels, int, (xpmData *data, unsigned int width,
70			 unsigned int height, unsigned int ncolors,
71			 unsigned int cpp, XpmColor *colorTable,
72			 xpmHashTable *hashtable, unsigned int **pixels));
73
74const char *xpmColorKeys[] = {
75    "s",				/* key #1: symbol */
76    "m",				/* key #2: mono visual */
77    "g4",				/* key #3: 4 grays visual */
78    "g",				/* key #4: gray visual */
79    "c",				/* key #5: color visual */
80};
81
82int
83xpmParseValues(
84    xpmData		*data,
85    unsigned int	*width,
86    unsigned int	*height,
87    unsigned int	*ncolors,
88    unsigned int	*cpp,
89    unsigned int	*x_hotspot,
90    unsigned int	*y_hotspot,
91    unsigned int	*hotspot,
92    unsigned int	*extensions)
93{
94    unsigned int l;
95    char buf[BUFSIZ + 1];
96
97    if (!data->format) {		/* XPM 2 or 3 */
98
99	/*
100	 * read values: width, height, ncolors, chars_per_pixel
101	 */
102	if (!(xpmNextUI(data, width) && xpmNextUI(data, height)
103	      && xpmNextUI(data, ncolors) && xpmNextUI(data, cpp)))
104	    return (XpmFileInvalid);
105
106	/*
107	 * read optional information (hotspot and/or XPMEXT) if any
108	 */
109	l = xpmNextWord(data, buf, BUFSIZ);
110	if (l) {
111	    *extensions = (l == 6 && !strncmp("XPMEXT", buf, 6));
112	    if (*extensions)
113		*hotspot = (xpmNextUI(data, x_hotspot)
114			    && xpmNextUI(data, y_hotspot));
115	    else {
116		*hotspot = (xpmatoui(buf, l, x_hotspot)
117			    && xpmNextUI(data, y_hotspot));
118		l = xpmNextWord(data, buf, BUFSIZ);
119		*extensions = (l == 6 && !strncmp("XPMEXT", buf, 6));
120	    }
121	}
122    } else {
123
124	/*
125	 * XPM 1 file read values: width, height, ncolors, chars_per_pixel
126	 */
127	int i;
128	char *ptr;
129	Bool got_one, saw_width = False, saw_height = False;
130	Bool saw_ncolors = False, saw_chars_per_pixel = False;
131
132	for (i = 0; i < 4; i++) {
133	    l = xpmNextWord(data, buf, BUFSIZ);
134	    if (l != 7 || strncmp("#define", buf, 7))
135		return (XpmFileInvalid);
136	    l = xpmNextWord(data, buf, BUFSIZ);
137	    if (!l)
138		return (XpmFileInvalid);
139	    buf[l] = '\0';
140	    ptr = buf;
141	    got_one = False;
142	    while (!got_one) {
143		ptr = strchr(ptr, '_');
144		if (!ptr)
145		    return (XpmFileInvalid);
146		switch (l - (ptr - buf)) {
147		case 6:
148		    if (saw_width || strncmp("_width", ptr, 6)
149			|| !xpmNextUI(data, width))
150			return (XpmFileInvalid);
151		    else
152			saw_width = True;
153		    got_one = True;
154		    break;
155		case 7:
156		    if (saw_height || strncmp("_height", ptr, 7)
157			|| !xpmNextUI(data, height))
158			return (XpmFileInvalid);
159		    else
160			saw_height = True;
161		    got_one = True;
162		    break;
163		case 8:
164		    if (saw_ncolors || strncmp("_ncolors", ptr, 8)
165			|| !xpmNextUI(data, ncolors))
166			return (XpmFileInvalid);
167		    else
168			saw_ncolors = True;
169		    got_one = True;
170		    break;
171		case 16:
172		    if (saw_chars_per_pixel
173			|| strncmp("_chars_per_pixel", ptr, 16)
174			|| !xpmNextUI(data, cpp))
175			return (XpmFileInvalid);
176		    else
177			saw_chars_per_pixel = True;
178		    got_one = True;
179		    break;
180		default:
181		    ptr++;
182		}
183	    }
184	    /* skip the end of line */
185	    xpmNextString(data);
186	}
187	if (!saw_width || !saw_height || !saw_ncolors || !saw_chars_per_pixel)
188	  return (XpmFileInvalid);
189
190	*hotspot = 0;
191	*extensions = 0;
192    }
193    return (XpmSuccess);
194}
195
196int
197xpmParseColors(
198    xpmData		 *data,
199    unsigned int	  ncolors,
200    unsigned int	  cpp,
201    XpmColor		**colorTablePtr,
202    xpmHashTable	 *hashtable)
203{
204    unsigned int key = 0, l, a, b, len;
205    unsigned int curkey;		/* current color key */
206    unsigned int lastwaskey;		/* key read */
207    char buf[BUFSIZ+1];
208    char curbuf[BUFSIZ];		/* current buffer */
209    const char **sptr;
210    char *s;
211    XpmColor *color;
212    XpmColor *colorTable;
213    char **defaults;
214    int ErrorStatus;
215
216    if (ncolors >= UINT_MAX / sizeof(XpmColor))
217	return (XpmNoMemory);
218    colorTable = (XpmColor *) XpmCalloc(ncolors, sizeof(XpmColor));
219    if (!colorTable)
220	return (XpmNoMemory);
221
222    if (!data->format) {		/* XPM 2 or 3 */
223	for (a = 0, color = colorTable; a < ncolors; a++, color++) {
224	    xpmNextString(data);	/* skip the line */
225
226	    /*
227	     * read pixel value
228	     */
229	    if (cpp >= UINT_MAX - 1) {
230		ErrorStatus = XpmNoMemory;
231		goto error;
232	    }
233	    color->string = (char *) XpmMalloc(cpp + 1);
234	    if (!color->string) {
235		ErrorStatus = XpmNoMemory;
236		goto error;
237	    }
238	    for (b = 0, s = color->string; b < cpp; b++, s++) {
239		int c = xpmGetC(data);
240		if (c < 0) {
241		    ErrorStatus = XpmFileInvalid;
242		    goto error;
243		}
244		*s = (char) c;
245	    }
246	    *s = '\0';
247
248	    /*
249	     * store the string in the hashtable with its color index number
250	     */
251	    if (USE_HASHTABLE) {
252		ErrorStatus =
253		    xpmHashIntern(hashtable, color->string, HashAtomData(a));
254		if (ErrorStatus != XpmSuccess) {
255		    goto error;
256		}
257	    }
258
259	    /*
260	     * read color keys and values
261	     */
262	    defaults = (char **) color;
263	    curkey = 0;
264	    lastwaskey = 0;
265	    *curbuf = '\0';		/* init curbuf */
266	    while ((l = xpmNextWord(data, buf, BUFSIZ))) {
267		if (!lastwaskey) {
268		    for (key = 0, sptr = xpmColorKeys; key < NKEYS; key++,
269			 sptr++)
270			if ((strlen(*sptr) == l) && (!strncmp(*sptr, buf, l)))
271			    break;
272		}
273		if (!lastwaskey && key < NKEYS) {	/* open new key */
274		    if (curkey) {	/* flush string */
275			len = strlen(curbuf) + 1;
276			s = (char *) XpmMalloc(len);
277			if (!s) {
278			    ErrorStatus = XpmNoMemory;
279			    goto error;
280			}
281			defaults[curkey] = s;
282			memcpy(s, curbuf, len);
283		    }
284		    curkey = key + 1;	/* set new key  */
285		    *curbuf = '\0';	/* reset curbuf */
286		    lastwaskey = 1;
287		} else {
288		    if (!curkey) {	/* key without value */
289			ErrorStatus = XpmFileInvalid;
290			goto error;
291		    }
292		    if (!lastwaskey) {
293                        if (!xstrlcat(curbuf, " ", sizeof(curbuf))) { /* append space */
294                            ErrorStatus = XpmFileInvalid;
295                            goto error;
296                        }
297                    }
298		    buf[l] = '\0';
299		    if (!xstrlcat(curbuf, buf, sizeof(curbuf))) { /* append buf */
300                        ErrorStatus = XpmFileInvalid;
301                        goto error;
302                    }
303		    lastwaskey = 0;
304		}
305	    }
306	    if (!curkey) {		/* key without value */
307		ErrorStatus = XpmFileInvalid;
308		goto error;
309	    }
310	    len = strlen(curbuf) + 1; /* integer overflow just theoretically possible */
311	    s = defaults[curkey] = (char *) XpmMalloc(len);
312	    if (!s) {
313		ErrorStatus = XpmNoMemory;
314		goto error;
315	    }
316	    memcpy(s, curbuf, len);
317	}
318    } else {				/* XPM 1 */
319	/* get to the beginning of the first string */
320	data->Bos = '"';
321	data->Eos = '\0';
322	xpmNextString(data);
323	data->Eos = '"';
324	for (a = 0, color = colorTable; a < ncolors; a++, color++) {
325
326	    /*
327	     * read pixel value
328	     */
329	    if (cpp >= UINT_MAX - 1) {
330		ErrorStatus = XpmNoMemory;
331		goto error;
332	    }
333	    color->string = (char *) XpmMalloc(cpp + 1);
334	    if (!color->string) {
335		ErrorStatus = XpmNoMemory;
336		goto error;
337	    }
338	    for (b = 0, s = color->string; b < cpp; b++, s++) {
339		int c = xpmGetC(data);
340		if (c < 0) {
341		    ErrorStatus = XpmFileInvalid;
342		    goto error;
343		}
344		*s = (char) c;
345	    }
346	    *s = '\0';
347
348	    /*
349	     * store the string in the hashtable with its color index number
350	     */
351	    if (USE_HASHTABLE) {
352		ErrorStatus =
353		    xpmHashIntern(hashtable, color->string, HashAtomData(a));
354		if (ErrorStatus != XpmSuccess) {
355		    goto error;
356		}
357	    }
358
359	    /*
360	     * read color values
361	     */
362	    xpmNextString(data);	/* get to the next string */
363	    *curbuf = '\0';		/* init curbuf */
364	    while ((l = xpmNextWord(data, buf, BUFSIZ))) {
365		if (*curbuf != '\0') {
366		    if (!xstrlcat(curbuf, " ", sizeof(curbuf))) { /* append space */
367                        ErrorStatus = XpmFileInvalid;
368                        goto error;
369                    }
370                }
371		buf[l] = '\0';
372		if (!xstrlcat(curbuf, buf, sizeof(curbuf))) {	/* append buf */
373                    ErrorStatus = XpmFileInvalid;
374                    goto error;
375                }
376	    }
377	    len = strlen(curbuf) + 1;
378	    s = (char *) XpmMalloc(len);
379	    if (!s) {
380		ErrorStatus = XpmNoMemory;
381		goto error;
382	    }
383	    memcpy(s, curbuf, len);
384	    color->c_color = s;
385	    *curbuf = '\0';		/* reset curbuf */
386	    if (a < ncolors - 1)	/* can we trust ncolors -> leave data's bounds */
387		xpmNextString(data);	/* get to the next string */
388	}
389    }
390    *colorTablePtr = colorTable;
391    return (XpmSuccess);
392
393error:
394    xpmFreeColorTable(colorTable, ncolors);
395    return ErrorStatus;
396}
397
398static int
399ParsePixels(
400    xpmData		 *data,
401    unsigned int	  width,
402    unsigned int	  height,
403    unsigned int	  ncolors,
404    unsigned int	  cpp,
405    XpmColor		 *colorTable,
406    xpmHashTable	 *hashtable,
407    unsigned int	**pixels)
408{
409    unsigned int *iptr, *iptr2 = NULL; /* found by Egbert Eich */
410    unsigned int a, x, y;
411    int ErrorStatus;
412
413    if ((width == 0) && (height != 0))
414	return (XpmFileInvalid);
415
416    if ((height == 0) && (width != 0))
417	return (XpmFileInvalid);
418
419    if ((height > 0 && width >= UINT_MAX / height) ||
420	width * height >= UINT_MAX / sizeof(unsigned int))
421	return XpmNoMemory;
422#ifndef FOR_MSW
423    iptr2 = (unsigned int *) XpmMalloc(sizeof(unsigned int) * width * height);
424#else
425
426    /*
427     * special treatment to trick DOS malloc(size_t) where size_t is 16 bit!!
428     * XpmMalloc is defined to longMalloc(long) and checks the 16 bit boundary
429     */
430    iptr2 = (unsigned int *)
431	XpmMalloc((long) sizeof(unsigned int) * (long) width * (long) height);
432#endif
433    if (!iptr2)
434	return (XpmNoMemory);
435
436    iptr = iptr2;
437
438    switch (cpp) {
439
440    case (1):				/* Optimize for single character
441					 * colors */
442	{
443	    unsigned short colidx[256];
444
445	    if (ncolors > 256) {
446		XpmFree(iptr2); /* found by Egbert Eich */
447		return (XpmFileInvalid);
448	    }
449
450	    bzero((char *)colidx, 256 * sizeof(short));
451	    for (a = 0; a < ncolors; a++)
452		colidx[(unsigned char)colorTable[a].string[0]] = a + 1;
453
454	    for (y = 0; y < height; y++) {
455		ErrorStatus = xpmNextString(data);
456		if (ErrorStatus != XpmSuccess) {
457		    XpmFree(iptr2);
458		    return (ErrorStatus);
459		}
460		for (x = 0; x < width; x++, iptr++) {
461		    int c = xpmGetC(data);
462
463		    if (c > 0 && c < 256 && colidx[c] != 0)
464			*iptr = colidx[c] - 1;
465		    else {
466			XpmFree(iptr2);
467			return (XpmFileInvalid);
468		    }
469		}
470	    }
471	}
472	break;
473
474    case (2):				/* Optimize for double character
475					 * colors */
476	{
477
478/* free all allocated pointers at all exits */
479#define FREE_CIDX \
480do \
481{ \
482	int f; for (f = 0; f < 256; f++) \
483	if (cidx[f]) XpmFree(cidx[f]); \
484} while(0)
485
486	    /* array of pointers malloced by need */
487	    unsigned short *cidx[256];
488	    unsigned int char1;
489
490	    bzero((char *)cidx, 256 * sizeof(unsigned short *)); /* init */
491	    for (a = 0; a < ncolors; a++) {
492		char1 = (unsigned char) colorTable[a].string[0];
493		if (cidx[char1] == NULL) { /* get new memory */
494		    cidx[char1] = (unsigned short *)
495			XpmCalloc(256, sizeof(unsigned short));
496		    if (cidx[char1] == NULL) { /* new block failed */
497			FREE_CIDX;
498			XpmFree(iptr2);
499			return (XpmNoMemory);
500		    }
501		}
502		cidx[char1][(unsigned char)colorTable[a].string[1]] = a + 1;
503	    }
504
505	    for (y = 0; y < height; y++) {
506		ErrorStatus = xpmNextString(data);
507		if (ErrorStatus != XpmSuccess) {
508		    FREE_CIDX;
509		    XpmFree(iptr2);
510		    return (ErrorStatus);
511		}
512		for (x = 0; x < width; x++, iptr++) {
513		    int cc1 = xpmGetC(data);
514		    if (cc1 > 0 && cc1 < 256) {
515			int cc2 = xpmGetC(data);
516			if (cc2 > 0 && cc2 < 256 &&
517			    cidx[cc1] && cidx[cc1][cc2] != 0)
518			    *iptr = cidx[cc1][cc2] - 1;
519			else {
520			    FREE_CIDX;
521			    XpmFree(iptr2);
522			    return (XpmFileInvalid);
523			}
524		    } else {
525			FREE_CIDX;
526			XpmFree(iptr2);
527			return (XpmFileInvalid);
528		    }
529		}
530	    }
531	    FREE_CIDX;
532	}
533	break;
534
535    default:				/* Non-optimized case of long color
536					 * names */
537	{
538	    char *s;
539	    char buf[BUFSIZ];
540
541	    if (cpp >= sizeof(buf)) {
542		XpmFree(iptr2); /* found by Egbert Eich */
543		return (XpmFileInvalid);
544	    }
545
546	    buf[cpp] = '\0';
547	    if (USE_HASHTABLE) {
548		xpmHashAtom *slot;
549
550		for (y = 0; y < height; y++) {
551		    ErrorStatus = xpmNextString(data);
552		    if (ErrorStatus != XpmSuccess) {
553			XpmFree(iptr2);
554			return (ErrorStatus);
555		    }
556		    for (x = 0; x < width; x++, iptr++) {
557			for (a = 0, s = buf; a < cpp; a++, s++) {
558			    int c = xpmGetC(data);
559			    if (c < 0) {
560				XpmFree(iptr2);
561				return (XpmFileInvalid);
562			    }
563			    *s = (char) c;
564			}
565			slot = xpmHashSlot(hashtable, buf);
566			if (!*slot) {	/* no color matches */
567			    XpmFree(iptr2);
568			    return (XpmFileInvalid);
569			}
570			*iptr = HashColorIndex(slot);
571		    }
572		}
573	    } else {
574		for (y = 0; y < height; y++) {
575		    ErrorStatus = xpmNextString(data);
576		    if (ErrorStatus != XpmSuccess) {
577			XpmFree(iptr2);
578			return (ErrorStatus);
579		    }
580		    for (x = 0; x < width; x++, iptr++) {
581			for (a = 0, s = buf; a < cpp; a++, s++) {
582			    int c = xpmGetC(data);
583			    if (c < 0) {
584				XpmFree(iptr2);
585				return (XpmFileInvalid);
586			    }
587			    *s = (char) c;
588			}
589			for (a = 0; a < ncolors; a++)
590			    if (!strcmp(colorTable[a].string, buf))
591				break;
592			if (a == ncolors) {	/* no color matches */
593			    XpmFree(iptr2);
594			    return (XpmFileInvalid);
595			}
596			*iptr = a;
597		    }
598		}
599	    }
600	}
601	break;
602    }
603    *pixels = iptr2;
604    return (XpmSuccess);
605}
606
607int
608xpmParseExtensions(
609    xpmData		 *data,
610    XpmExtension	**extensions,
611    unsigned int	 *nextensions)
612{
613    XpmExtension *exts = NULL, *ext;
614    unsigned int num = 0;
615    unsigned int nlines, a, l, notstart, notend = 0;
616    int status;
617    char *string, *s, *s2, **sp;
618
619    xpmNextString(data);
620    exts = (XpmExtension *) XpmMalloc(sizeof(XpmExtension));
621    /* get the whole string */
622    status = xpmGetString(data, &string, &l);
623    if (status != XpmSuccess) {
624	XpmFree(exts);
625	return (status);
626    }
627    /* look for the key word XPMEXT, skip lines before this */
628    while ((notstart = strncmp("XPMEXT", string, 6))
629	   && (notend = strncmp("XPMENDEXT", string, 9))) {
630	XpmFree(string);
631	xpmNextString(data);
632	status = xpmGetString(data, &string, &l);
633	if (status != XpmSuccess) {
634	    XpmFree(exts);
635	    return (status);
636	}
637    }
638    if (!notstart)
639	notend = strncmp("XPMENDEXT", string, 9);
640    while (!notstart && notend) {
641	/* there starts an extension */
642	ext = (XpmExtension *)
643	    XpmRealloc(exts, (num + 1) * sizeof(XpmExtension)); /* can the loop be forced to iterate often enough to make "(num + 1) * sizeof(XpmExtension)" wrapping? */
644	if (!ext) {
645	    XpmFree(string);
646	    XpmFreeExtensions(exts, num);
647	    return (XpmNoMemory);
648	}
649	exts = ext;
650	ext += num;
651	/* skip whitespace and store its name */
652	s2 = s = string + 6;
653	while (isspace(*s2))
654	    s2++;
655	a = s2 - s;
656	ext->name = (char *) XpmMalloc(l - a - 6);
657	if (!ext->name) {
658	    XpmFree(string);
659	    ext->lines = NULL;
660	    ext->nlines = 0;
661	    XpmFreeExtensions(exts, num + 1);
662	    return (XpmNoMemory);
663	}
664	strncpy(ext->name, s + a, l - a - 6);
665	XpmFree(string);
666	/* now store the related lines */
667	xpmNextString(data);
668	status = xpmGetString(data, &string, &l);
669	if (status != XpmSuccess) {
670	    ext->lines = NULL;
671	    ext->nlines = 0;
672	    XpmFreeExtensions(exts, num + 1);
673	    return (status);
674	}
675	ext->lines = (char **) XpmMalloc(sizeof(char *));
676	nlines = 0;
677	while ((notstart = strncmp("XPMEXT", string, 6))
678	       && (notend = strncmp("XPMENDEXT", string, 9))) {
679	    sp = (char **)
680		XpmRealloc(ext->lines, (nlines + 1) * sizeof(char *)); /* can we iterate enough for a wrapping? */
681	    if (!sp) {
682		XpmFree(string);
683		ext->nlines = nlines;
684		XpmFreeExtensions(exts, num + 1);
685		return (XpmNoMemory);
686	    }
687	    ext->lines = sp;
688	    ext->lines[nlines] = string;
689	    nlines++;
690	    xpmNextString(data);
691	    status = xpmGetString(data, &string, &l);
692	    if (status != XpmSuccess) {
693		ext->nlines = nlines;
694		XpmFreeExtensions(exts, num + 1);
695		return (status);
696	    }
697	}
698	if (!nlines) {
699	    XpmFree(ext->lines);
700	    ext->lines = NULL;
701	}
702	ext->nlines = nlines;
703	num++;
704    }
705    if (!num) {
706	XpmFree(string);
707	XpmFree(exts);
708	exts = NULL;
709    } else if (!notend)
710	XpmFree(string);
711    *nextensions = num;
712    *extensions = exts;
713    return (XpmSuccess);
714}
715
716
717/* function call in case of error */
718#undef RETURN
719#define RETURN(status) \
720do { \
721      goto error; \
722} while(0)
723
724/*
725 * This function parses an Xpm file or data and store the found information
726 * in an an XpmImage structure which is returned.
727 */
728int
729xpmParseData(
730    xpmData	*data,
731    XpmImage	*image,
732    XpmInfo	*info)
733{
734    /* variables to return */
735    unsigned int width, height, ncolors, cpp;
736    unsigned int x_hotspot, y_hotspot, hotspot = 0, extensions = 0;
737    XpmColor *colorTable = NULL;
738    unsigned int *pixelindex = NULL;
739    char *hints_cmt = NULL;
740    char *colors_cmt = NULL;
741    char *pixels_cmt = NULL;
742
743    unsigned int cmts;
744    int ErrorStatus;
745    xpmHashTable hashtable;
746
747    cmts = info && (info->valuemask & XpmReturnComments);
748
749    /*
750     * parse the header
751     */
752    ErrorStatus = xpmParseHeader(data);
753    if (ErrorStatus != XpmSuccess)
754	return (ErrorStatus);
755
756    /*
757     * read values
758     */
759    ErrorStatus = xpmParseValues(data, &width, &height, &ncolors, &cpp,
760				 &x_hotspot, &y_hotspot, &hotspot,
761				 &extensions);
762    if (ErrorStatus != XpmSuccess)
763	return (ErrorStatus);
764
765    /*
766     * store the hints comment line
767     */
768    if (cmts)
769	xpmGetCmt(data, &hints_cmt);
770
771    /*
772     * init the hashtable
773     */
774    if (USE_HASHTABLE) {
775	ErrorStatus = xpmHashTableInit(&hashtable);
776	if (ErrorStatus != XpmSuccess)
777	    RETURN(ErrorStatus);
778    }
779
780    /*
781     * read colors
782     */
783    ErrorStatus = xpmParseColors(data, ncolors, cpp, &colorTable, &hashtable);
784    if (ErrorStatus != XpmSuccess) {
785	if (USE_HASHTABLE)
786	    xpmHashTableFree(&hashtable);
787	RETURN(ErrorStatus);
788    }
789
790    /*
791     * store the colors comment line
792     */
793    if (cmts)
794	xpmGetCmt(data, &colors_cmt);
795
796    /*
797     * read pixels and index them on color number
798     */
799    ErrorStatus = ParsePixels(data, width, height, ncolors, cpp, colorTable,
800			      &hashtable, &pixelindex);
801
802    /*
803     * free the hastable
804     */
805    if (USE_HASHTABLE)
806	xpmHashTableFree(&hashtable);
807
808    if (ErrorStatus != XpmSuccess)
809	RETURN(ErrorStatus);
810
811    /*
812     * store the pixels comment line
813     */
814    if (cmts)
815	xpmGetCmt(data, &pixels_cmt);
816
817    /*
818     * parse extensions
819     */
820    if (info && (info->valuemask & XpmReturnExtensions)) {
821	if (extensions) {
822	    ErrorStatus = xpmParseExtensions(data, &info->extensions,
823					     &info->nextensions);
824	    if (ErrorStatus != XpmSuccess)
825		RETURN(ErrorStatus);
826	} else {
827	    info->extensions = NULL;
828	    info->nextensions = 0;
829	}
830    }
831
832    /*
833     * store found information in the XpmImage structure
834     */
835    image->width = width;
836    image->height = height;
837    image->cpp = cpp;
838    image->ncolors = ncolors;
839    image->colorTable = colorTable;
840    image->data = pixelindex;
841
842    if (info) {
843	if (cmts) {
844	    info->hints_cmt = hints_cmt;
845	    info->colors_cmt = colors_cmt;
846	    info->pixels_cmt = pixels_cmt;
847	}
848	if (hotspot) {
849	    info->x_hotspot = x_hotspot;
850	    info->y_hotspot = y_hotspot;
851	    info->valuemask |= XpmHotspot;
852	}
853    }
854    return (XpmSuccess);
855
856/* exit point in case of error, free only locally allocated variables */
857error:
858    if (colorTable)
859	xpmFreeColorTable(colorTable, ncolors);
860    if (pixelindex)
861	XpmFree(pixelindex);
862    if (hints_cmt)
863	XpmFree(hints_cmt);
864    if (colors_cmt)
865	XpmFree(colors_cmt);
866    if (pixels_cmt)
867	XpmFree(pixels_cmt);
868
869    return(ErrorStatus);
870}
871