1 /*
2  * Copyright (C) 1989-95 GROUPE BULL
3  * Copyright � 2005, The LessTif developers.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy
6  * of this software and associated documentation files (the "Software"), to
7  * deal in the Software without restriction, including without limitation the
8  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9  * sell copies of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18  * GROUPE BULL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
19  * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  *
22  * Except as contained in this notice, the name of GROUPE BULL shall not be
23  * used in advertising or otherwise to promote the sale, use or other dealings
24  * in this Software without prior written authorization from GROUPE BULL.
25  */
26 
27 /*****************************************************************************\
28 * data.c:                                                                     *
29 *                                                                             *
30 *  XPM library                                                                *
31 *  IO utilities                                                               *
32 *                                                                             *
33 *  Developed by Arnaud Le Hors                                                *
34 \*****************************************************************************/
35 /* $XFree86: xc/extras/Xpm/lib/data.c,v 1.3 2001/10/28 03:32:10 tsi Exp $ */
36 /* $Header: /cvsroot/lesstif/lesstif/lib/Xm-2.1/Xpmdata.c,v 1.2 2005/04/13 18:03:26 dannybackx Exp $ */
37 #include <LTconfig.h>
38 
39 /* amai: we can't include DebugUtil.h before system headers.
40    So we assume that system headers are idempotent, #include
41    all of those used below here and then try with our
42    DebugUtil.h! */
43 #include <stdio.h>
44 #include <string.h>
45 
46 #include <ctype.h>
47 #ifdef HAVE_SYS_TYPES_H
48 #include <sys/types.h>
49 #endif
50 #ifdef HAVE_SYS_STAT_H
51 #include <sys/stat.h>
52 #endif
53 #ifdef HAVE_UNISTD_H
54 #include <unistd.h>
55 #endif
56 #ifdef HAVE_FCNTL_H
57 #include <fcntl.h>
58 #endif
59 #if defined(FOR_MSW) || defined(WIN32)
60 #include <io.h>
61 #endif
62 
63 #include <X11/Intrinsic.h> /* Avoid re-definition of Pixel-type */
64 
65 #include <Xm/XpmP.h>
66 #include <XmI/XpmI.h>
67 
68 /* #if !defined(WITH_DBMALLOC) && !defined(WITH_DMALLOC) */
69 #include <XmI/DebugUtil.h>
70 
71 #ifdef HAVE_STRLCAT
72 # define STRLCAT(dst, src, dstsize) { \
73  	if (strlcat(dst, src, dstsize) >= (dstsize)) \
74 	    return (XpmFileInvalid); }
75 # define STRLCPY(dst, src, dstsize) { \
76   	if (strlcpy(dst, src, dstsize) >= (dstsize)) \
77 	    return (XpmFileInvalid); }
78 #else
79 # define STRLCAT(dst, src, dstsize) { \
80 	if ((strlen(dst) + strlen(src)) < (dstsize)) \
81  	    strcat(dst, src); \
82 	else return (XpmFileInvalid); }
83 # define STRLCPY(dst, src, dstsize) { \
84 	if (strlen(src) < (dstsize)) \
85  	    strcpy(dst, src); \
86 	else return (XpmFileInvalid); }
87 #endif
88 
89 /* October 2004, source code review by Thomas Biege <thomas@suse.de> */
90 
91 #ifndef CXPMPROG
92 #include "XmI/XpmI.h"
93 #endif
94 #include <ctype.h>
95 
96 #ifndef CXPMPROG
97 #define Getc(data, file) getc(file)
98 #define Ungetc(data, c, file) ungetc(c, file)
99 #endif
100 
101 static int
ParseComment(xpmData * data)102 ParseComment(xpmData *data)
103 {
104     if (data->type == XPMBUFFER) {
105 	register char c;
106 	register unsigned int n = 0;
107 	unsigned int notend;
108 	char *s, *s2;
109 
110 	s = data->Comment;
111 	*s = data->Bcmt[0];
112 
113 	/* skip the string beginning comment */
114 	s2 = data->Bcmt;
115 	do {
116 	    c = *data->cptr++;
117 	    *++s = c;
118 	    n++;
119 	    s2++;
120 	} while (c == *s2 && *s2 != '\0' && c);
121 
122 	if (*s2 != '\0') {
123 	    /* this wasn't the beginning of a comment */
124 	    data->cptr -= n;
125 	    return 0;
126 	}
127 	/* store comment */
128 	data->Comment[0] = *s;
129 	s = data->Comment;
130 	notend = 1;
131 	n = 0;
132 	while (notend) {
133 	    s2 = data->Ecmt;
134 	    while (*s != *s2 && c) {
135 		c = *data->cptr++;
136 		if (n == XPMMAXCMTLEN - 1)  { /* forget it */
137 		    s = data->Comment;
138 		    n = 0;
139 		}
140 		*++s = c;
141 		n++;
142 	    }
143 	    data->CommentLength = n;
144 	    do {
145 		c = *data->cptr++;
146 		if (n == XPMMAXCMTLEN - 1)  { /* forget it */
147 		    s = data->Comment;
148 		    n = 0;
149 		}
150 		*++s = c;
151 		n++;
152 		s2++;
153 	    } while (c == *s2 && *s2 != '\0' && c);
154 	    if (*s2 == '\0') {
155 		/* this is the end of the comment */
156 		notend = 0;
157 		data->cptr--;
158 	    }
159 	}
160 	return 0;
161     } else {
162 	FILE *file = data->stream.file;
163 	register int c;
164 	register unsigned int n = 0, a;
165 	unsigned int notend;
166 	char *s, *s2;
167 
168 	s = data->Comment;
169 	*s = data->Bcmt[0];
170 
171 	/* skip the string beginning comment */
172 	s2 = data->Bcmt;
173 	do {
174 	    c = Getc(data, file);
175 	    *++s = c;
176 	    n++;
177 	    s2++;
178 	} while (c == *s2 && *s2 != '\0' && c != EOF);
179 
180 	if (*s2 != '\0') {
181 	    /* this wasn't the beginning of a comment */
182 	    /* put characters back in the order that we got them */
183 	    for (a = n; a > 0; a--, s--)
184 		Ungetc(data, *s, file);
185 	    return 0;
186 	}
187 	/* store comment */
188 	data->Comment[0] = *s;
189 	s = data->Comment;
190 	notend = 1;
191 	n = 0;
192 	while (notend) {
193 	    s2 = data->Ecmt;
194 	    while (*s != *s2 && c != EOF) {
195 		c = Getc(data, file);
196 		if (n == XPMMAXCMTLEN - 1)  { /* forget it */
197 		    s = data->Comment;
198 		    n = 0;
199 		}
200 		*++s = c;
201 		n++;
202 	    }
203 	    data->CommentLength = n;
204 	    do {
205 		c = Getc(data, file);
206 		if (n == XPMMAXCMTLEN - 1)  { /* forget it */
207 		    s = data->Comment;
208 		    n = 0;
209 		}
210 		*++s = c;
211 		n++;
212 		s2++;
213 	    } while (c == *s2 && *s2 != '\0' && c != EOF);
214 	    if (*s2 == '\0') {
215 		/* this is the end of the comment */
216 		notend = 0;
217 		Ungetc(data, *s, file);
218 	    }
219 	}
220 	return 0;
221     }
222 }
223 
224 /*
225  * skip to the end of the current string and the beginning of the next one
226  */
227 int
xpmNextString(data)228 xpmNextString(data)
229     xpmData *data;
230 {
231     if (!data->type)
232 	data->cptr = (data->stream.data)[++data->line];
233     else if (data->type == XPMBUFFER) {
234 	register char c;
235 
236 	/* get to the end of the current string */
237 	if (data->Eos)
238 	    while ((c = *data->cptr++) && c != data->Eos);
239 
240 	/*
241 	 * then get to the beginning of the next string looking for possible
242 	 * comment
243 	 */
244 	if (data->Bos) {
245 	    while ((c = *data->cptr++) && c != data->Bos)
246 		if (data->Bcmt && c == data->Bcmt[0])
247 		    ParseComment(data);
248 	} else if (data->Bcmt) {	/* XPM2 natural */
249 	    while ((c = *data->cptr++) == data->Bcmt[0])
250 		ParseComment(data);
251 	    data->cptr--;
252 	}
253     } else {
254 	register int c;
255 	FILE *file = data->stream.file;
256 
257 	/* get to the end of the current string */
258 	if (data->Eos)
259 	    while ((c = Getc(data, file)) != data->Eos && c != EOF);
260 
261 	/*
262 	 * then get to the beginning of the next string looking for possible
263 	 * comment
264 	 */
265 	if (data->Bos) {
266 	    while ((c = Getc(data, file)) != data->Bos && c != EOF)
267 		if (data->Bcmt && c == data->Bcmt[0])
268 		    ParseComment(data);
269 
270 	} else if (data->Bcmt) {	/* XPM2 natural */
271 	    while ((c = Getc(data, file)) == data->Bcmt[0])
272 		ParseComment(data);
273 	    Ungetc(data, c, file);
274 	}
275     }
276     return 0;
277 }
278 
279 
280 /*
281  * skip whitespace and return the following word
282  */
283 unsigned int
xpmNextWord(data,buf,buflen)284 xpmNextWord(data, buf, buflen)
285     xpmData *data;
286     char *buf;
287     unsigned int buflen;
288 {
289     register unsigned int n = 0;
290     int c;
291 
292     if (!data->type || data->type == XPMBUFFER) {
293 	while (isspace(c = *data->cptr) && c != data->Eos)
294 	    data->cptr++;
295 	do {
296 	    c = *data->cptr++;
297 	    *buf++ = c;
298 	    n++;
299 	} while (!isspace(c) && c != data->Eos && n < buflen);
300 	n--;
301 	data->cptr--;
302     } else {
303 	FILE *file = data->stream.file;
304 
305 	while ((c = Getc(data, file)) != EOF && isspace(c) && c != data->Eos);
306 	while (!isspace(c) && c != data->Eos && c != EOF && n < buflen) {
307 	    *buf++ = c;
308 	    n++;
309 	    c = Getc(data, file);
310 	}
311 	Ungetc(data, c, file);
312     }
313     return (n); /* this returns bytes read + 1 */
314 }
315 
316 /*
317  * skip whitespace and compute the following unsigned int,
318  * returns 1 if one is found and 0 if not
319  */
320 int
xpmNextUI(data,ui_return)321 xpmNextUI(data, ui_return)
322     xpmData *data;
323     unsigned int *ui_return;
324 {
325     char buf[BUFSIZ];
326     int l;
327 
328     l = xpmNextWord(data, buf, BUFSIZ);
329     return xpmatoui(buf, l, ui_return);
330 }
331 
332 /*
333  * return end of string - WARNING: malloc!
334  */
335 int
xpmGetString(data,sptr,l)336 xpmGetString(data, sptr, l)
337     xpmData *data;
338     char **sptr;
339     unsigned int *l;
340 {
341     unsigned int i, n = 0;
342     int c;
343     char *p = NULL, *q, buf[BUFSIZ];
344 
345     if (!data->type || data->type == XPMBUFFER) {
346 	if (data->cptr) {
347 	    char *start = data->cptr;
348 	    while ((c = *data->cptr) && c != data->Eos)
349 		data->cptr++;
350 	    n = data->cptr - start + 1;
351 	    p = (char *) XpmMalloc(n);
352 	    if (!p)
353 		return (XpmNoMemory);
354 	    strncpy(p, start, n);
355 	    if (data->type)		/* XPMBUFFER */
356 		p[n - 1] = '\0';
357 	}
358     } else {
359 	FILE *file = data->stream.file;
360 
361 	if ((c = Getc(data, file)) == EOF)
362 	    return (XpmFileInvalid);
363 
364 	i = 0;
365 	q = buf;
366 	p = (char *) XpmMalloc(1);
367 	while (c != data->Eos && c != EOF) {
368 	    if (i == BUFSIZ) {
369 		/* get to the end of the buffer */
370 		/* malloc needed memory */
371 		q = (char *) XpmRealloc(p, n + i);
372 		if (!q) {
373 		    XpmFree(p);
374 		    return (XpmNoMemory);
375 		}
376 		p = q;
377 		q += n;
378 		/* and copy what we already have */
379 		strncpy(q, buf, i);
380 		n += i;
381 		i = 0;
382 		q = buf;
383 	    }
384 	    *q++ = c;
385 	    i++;
386 	    c = Getc(data, file);
387 	}
388 	if (c == EOF) {
389 	    XpmFree(p);
390 	    return (XpmFileInvalid);
391 	}
392 	if (n + i != 0) {
393 	    /* malloc needed memory */
394 	    q = (char *) XpmRealloc(p, n + i + 1);
395 	    if (!q) {
396 		XpmFree(p);
397 		return (XpmNoMemory);
398 	    }
399 	    p = q;
400 	    q += n;
401 	    /* and copy the buffer */
402 	    strncpy(q, buf, i);
403 	    n += i;
404 	    p[n++] = '\0';
405 	} else {
406 	    *p = '\0';
407 	    n = 1;
408 	}
409 	Ungetc(data, c, file);
410     }
411     *sptr = p;
412     *l = n;
413     return (XpmSuccess);
414 }
415 
416 /*
417  * get the current comment line
418  */
419 int
xpmGetCmt(data,cmt)420 xpmGetCmt(data, cmt)
421     xpmData *data;
422     char **cmt;
423 {
424     if (!data->type)
425 	*cmt = NULL;
426     else if (data->CommentLength != 0 && data->CommentLength < UINT_MAX - 1) {
427 	if( (*cmt = (char *) XpmMalloc(data->CommentLength + 1)) == NULL)
428 		return XpmNoMemory;
429 	strncpy(*cmt, data->Comment, data->CommentLength);
430 	(*cmt)[data->CommentLength] = '\0';
431 	data->CommentLength = 0;
432     } else
433 	*cmt = NULL;
434     return 0;
435 }
436 
437 xpmDataType xpmDataTypes[] =
438 {
439     {"", "!", "\n", '\0', '\n', "", "", "", ""},	/* Natural type */
440     {"C", "/*", "*/", '"', '"', ",\n", "static char *", "[] = {\n", "};\n"},
441     {"Lisp", ";", "\n", '"', '"', "\n", "(setq ", " '(\n", "))\n"},
442     {NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, NULL}
443 };
444 
445 /*
446  * parse xpm header
447  */
448 int
xpmParseHeader(data)449 xpmParseHeader(data)
450     xpmData *data;
451 {
452     char buf[BUFSIZ+1] = {0};
453     int l, n = 0;
454 
455     if (data->type) {
456 	data->Bos = '\0';
457 	data->Eos = '\n';
458 	data->Bcmt = data->Ecmt = NULL;
459 	l = xpmNextWord(data, buf, BUFSIZ);
460 	if (l == 7 && !strncmp("#define", buf, 7)) {
461 	    /* this maybe an XPM 1 file */
462 	    char *ptr;
463 
464 	    l = xpmNextWord(data, buf, BUFSIZ);
465 	    if (!l)
466 		return (XpmFileInvalid);
467 	    buf[l] = '\0';
468 	    ptr = strrchr(buf, '_');
469 	    if (!ptr || strncmp("_format", ptr, l - (ptr - buf)))
470 		return XpmFileInvalid;
471 	    /* this is definitely an XPM 1 file */
472 	    data->format = 1;
473 	    n = 1;			/* handle XPM1 as mainly XPM2 C */
474 	} else {
475 
476 	    /*
477 	     * skip the first word, get the second one, and see if this is
478 	     * XPM 2 or 3
479 	     */
480 	    l = xpmNextWord(data, buf, BUFSIZ);
481 	    if ((l == 3 && !strncmp("XPM", buf, 3)) ||
482 		(l == 4 && !strncmp("XPM2", buf, 4))) {
483 		if (l == 3)
484 		    n = 1;		/* handle XPM as XPM2 C */
485 		else {
486 		    /* get the type key word */
487 		    l = xpmNextWord(data, buf, BUFSIZ);
488 
489 		    /*
490 		     * get infos about this type
491 		     */
492 		    while (xpmDataTypes[n].type
493 			   && strncmp(xpmDataTypes[n].type, buf, l))
494 			n++;
495 		}
496 		data->format = 0;
497 	    } else
498 		/* nope this is not an XPM file */
499 		return XpmFileInvalid;
500 	}
501 	if (xpmDataTypes[n].type) {
502 	    if (n == 0) {		/* natural type */
503 		data->Bcmt = xpmDataTypes[n].Bcmt;
504 		data->Ecmt = xpmDataTypes[n].Ecmt;
505 		xpmNextString(data);	/* skip the end of the headerline */
506 		data->Bos = xpmDataTypes[n].Bos;
507 		data->Eos = xpmDataTypes[n].Eos;
508 	    } else {
509 		data->Bcmt = xpmDataTypes[n].Bcmt;
510 		data->Ecmt = xpmDataTypes[n].Ecmt;
511 		if (!data->format) {	/* XPM 2 or 3 */
512 		    data->Bos = xpmDataTypes[n].Bos;
513 		    data->Eos = '\0';
514 		    /* get to the beginning of the first string */
515 		    xpmNextString(data);
516 		    data->Eos = xpmDataTypes[n].Eos;
517 		} else			/* XPM 1 skip end of line */
518 		    xpmNextString(data);
519 	    }
520 	} else
521 	    /* we don't know about that type of XPM file... */
522 	    return XpmFileInvalid;
523     }
524     return XpmSuccess;
525 }
526