/* * 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. */ /*****************************************************************************\ * parse.c: * * * * XPM library * * Parse an XPM file or array and store the found information * * 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 */ /* October 2004, source code review by Thomas Biege */ #ifdef HAVE_CONFIG_H #include #endif #include "XpmI.h" #include #include /** * like strlcat() but returns true on success and false if the string got * truncated. */ static inline Bool xstrlcat(char *dst, const char *src, size_t dstsize) { #if defined(HAS_STRLCAT) || defined(HAVE_STRLCAT) return strlcat(dst, src, dstsize) < dstsize; #else if ((strlen(dst) + strlen(src)) < dstsize) { strcat(dst, src); return True; } else { return False; } #endif } LFUNC(ParsePixels, int, (xpmData *data, unsigned int width, unsigned int height, unsigned int ncolors, unsigned int cpp, XpmColor *colorTable, xpmHashTable *hashtable, unsigned int **pixels)); const 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 */ }; int xpmParseValues( 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) { unsigned int l; char buf[BUFSIZ + 1]; 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( xpmData *data, unsigned int ncolors, unsigned int cpp, XpmColor **colorTablePtr, xpmHashTable *hashtable) { unsigned int key = 0, l, a, b, len; unsigned int curkey; /* current color key */ unsigned int lastwaskey; /* key read */ char buf[BUFSIZ+1]; char curbuf[BUFSIZ]; /* current buffer */ const char **sptr; char *s; XpmColor *color; XpmColor *colorTable; char **defaults; int ErrorStatus; if (ncolors >= UINT_MAX / sizeof(XpmColor)) return (XpmNoMemory); 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 */ if (cpp >= UINT_MAX - 1) { ErrorStatus = XpmNoMemory; goto error; } color->string = (char *) XpmMalloc(cpp + 1); if (!color->string) { ErrorStatus = XpmNoMemory; goto error; } for (b = 0, s = color->string; b < cpp; b++, s++) { int c = xpmGetC(data); if (c < 0) { ErrorStatus = XpmFileInvalid; goto error; } *s = (char) c; } *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) { goto error; } } /* * 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 */ len = strlen(curbuf) + 1; s = (char *) XpmMalloc(len); if (!s) { ErrorStatus = XpmNoMemory; goto error; } defaults[curkey] = s; memcpy(s, curbuf, len); } curkey = key + 1; /* set new key */ *curbuf = '\0'; /* reset curbuf */ lastwaskey = 1; } else { if (!curkey) { /* key without value */ ErrorStatus = XpmFileInvalid; goto error; } if (!lastwaskey) { if (!xstrlcat(curbuf, " ", sizeof(curbuf))) { /* append space */ ErrorStatus = XpmFileInvalid; goto error; } } buf[l] = '\0'; if (!xstrlcat(curbuf, buf, sizeof(curbuf))) { /* append buf */ ErrorStatus = XpmFileInvalid; goto error; } lastwaskey = 0; } } if (!curkey) { /* key without value */ ErrorStatus = XpmFileInvalid; goto error; } len = strlen(curbuf) + 1; /* integer overflow just theoretically possible */ s = defaults[curkey] = (char *) XpmMalloc(len); if (!s) { ErrorStatus = XpmNoMemory; goto error; } memcpy(s, curbuf, len); } } 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 */ if (cpp >= UINT_MAX - 1) { ErrorStatus = XpmNoMemory; goto error; } color->string = (char *) XpmMalloc(cpp + 1); if (!color->string) { ErrorStatus = XpmNoMemory; goto error; } for (b = 0, s = color->string; b < cpp; b++, s++) { int c = xpmGetC(data); if (c < 0) { ErrorStatus = XpmFileInvalid; goto error; } *s = (char) c; } *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) { goto error; } } /* * read color values */ xpmNextString(data); /* get to the next string */ *curbuf = '\0'; /* init curbuf */ while ((l = xpmNextWord(data, buf, BUFSIZ))) { if (*curbuf != '\0') { if (!xstrlcat(curbuf, " ", sizeof(curbuf))) { /* append space */ ErrorStatus = XpmFileInvalid; goto error; } } buf[l] = '\0'; if (!xstrlcat(curbuf, buf, sizeof(curbuf))) { /* append buf */ ErrorStatus = XpmFileInvalid; goto error; } } len = strlen(curbuf) + 1; s = (char *) XpmMalloc(len); if (!s) { ErrorStatus = XpmNoMemory; goto error; } memcpy(s, curbuf, len); color->c_color = s; *curbuf = '\0'; /* reset curbuf */ if (a < ncolors - 1) /* can we trust ncolors -> leave data's bounds */ xpmNextString(data); /* get to the next string */ } } *colorTablePtr = colorTable; return (XpmSuccess); error: xpmFreeColorTable(colorTable, ncolors); return ErrorStatus; } static int ParsePixels( 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 = NULL; /* found by Egbert Eich */ unsigned int a, x, y; int ErrorStatus; if ((width == 0) && (height != 0)) return (XpmFileInvalid); if ((height == 0) && (width != 0)) return (XpmFileInvalid); if ((height > 0 && width >= UINT_MAX / height) || width * height >= UINT_MAX / sizeof(unsigned int)) return XpmNoMemory; #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]; if (ncolors > 256) { XpmFree(iptr2); /* found by Egbert Eich */ return (XpmFileInvalid); } bzero((char *)colidx, 256 * sizeof(short)); for (a = 0; a < ncolors; a++) colidx[(unsigned char)colorTable[a].string[0]] = a + 1; for (y = 0; y < height; y++) { ErrorStatus = xpmNextString(data); if (ErrorStatus != XpmSuccess) { XpmFree(iptr2); return (ErrorStatus); } 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 \ do \ { \ int f; for (f = 0; f < 256; f++) \ if (cidx[f]) XpmFree(cidx[f]); \ } while(0) /* array of pointers malloced by need */ unsigned short *cidx[256]; unsigned int char1; bzero((char *)cidx, 256 * sizeof(unsigned short *)); /* init */ for (a = 0; a < ncolors; a++) { char1 = (unsigned char) 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++) { ErrorStatus = xpmNextString(data); if (ErrorStatus != XpmSuccess) { FREE_CIDX; XpmFree(iptr2); return (ErrorStatus); } 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] && 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]; if (cpp >= sizeof(buf)) { XpmFree(iptr2); /* found by Egbert Eich */ return (XpmFileInvalid); } buf[cpp] = '\0'; if (USE_HASHTABLE) { xpmHashAtom *slot; for (y = 0; y < height; y++) { ErrorStatus = xpmNextString(data); if (ErrorStatus != XpmSuccess) { XpmFree(iptr2); return (ErrorStatus); } for (x = 0; x < width; x++, iptr++) { for (a = 0, s = buf; a < cpp; a++, s++) { int c = xpmGetC(data); if (c < 0) { XpmFree(iptr2); return (XpmFileInvalid); } *s = (char) c; } slot = xpmHashSlot(hashtable, buf); if (!*slot) { /* no color matches */ XpmFree(iptr2); return (XpmFileInvalid); } *iptr = HashColorIndex(slot); } } } else { for (y = 0; y < height; y++) { ErrorStatus = xpmNextString(data); if (ErrorStatus != XpmSuccess) { XpmFree(iptr2); return (ErrorStatus); } for (x = 0; x < width; x++, iptr++) { for (a = 0, s = buf; a < cpp; a++, s++) { int c = xpmGetC(data); if (c < 0) { XpmFree(iptr2); return (XpmFileInvalid); } *s = (char) c; } 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( 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)); /* can the loop be forced to iterate often enough to make "(num + 1) * sizeof(XpmExtension)" wrapping? */ 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 *)); /* can we iterate enough for a wrapping? */ 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); } /* function call in case of error */ #undef RETURN #define RETURN(status) \ do { \ goto error; \ } while(0) /* * This function parses an Xpm file or data and store the found information * in an an XpmImage structure which is returned. */ int xpmParseData( 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 hashtable */ 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 information 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); }