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 * data.c:                                                                     *
28 *                                                                             *
29 *  XPM library                                                                *
30 *  IO utilities                                                               *
31 *                                                                             *
32 *  Developed by Arnaud Le Hors                                                *
33 \*****************************************************************************/
34 
35 /* October 2004, source code review by Thomas Biege <thomas@suse.de> */
36 
37 #ifndef CXPMPROG
38 #if 0
39 /* Official version number */
40 static char *RCS_Version = "$XpmVersion: 3.4k $";
41 
42 /* Internal version number */
43 static char *RCS_Id = "Id: xpm.shar,v 3.71 1998/03/19 19:47:14 lehors Exp $";
44 #endif
45 #ifdef HAVE_CONFIG_H
46 #include <config.h>
47 #endif
48 #include "XpmI.h"
49 #endif
50 #include <ctype.h>
51 
52 #ifndef CXPMPROG
53 #define Getc(data, file) getc(file)
54 #define Ungetc(data, c, file) ungetc(c, file)
55 #endif
56 
57 static int
ParseComment(xpmData * data)58 ParseComment(xpmData *data)
59 {
60     if (data->type == XPMBUFFER) {
61 	register char c;
62 	register unsigned int n = 0;
63 	unsigned int notend;
64 	char *s;
65 	const char *s2;
66 
67 	s = data->Comment;
68 	*s = data->Bcmt[0];
69 
70 	/* skip the string beginning comment */
71 	s2 = data->Bcmt;
72 	do {
73 	    c = *data->cptr++;
74 	    *++s = c;
75 	    n++;
76 	    s2++;
77 	} while (c == *s2 && *s2 != '\0' && c);
78 
79 	if (*s2 != '\0') {
80 	    /* this wasn't the beginning of a comment */
81 	    data->cptr -= n;
82 	    return 0;
83 	}
84 	/* store comment */
85 	data->Comment[0] = *s;
86 	s = data->Comment;
87 	notend = 1;
88 	n = 0;
89 	while (notend) {
90 	    s2 = data->Ecmt;
91 	    while (*s != *s2 && c) {
92 		c = *data->cptr++;
93 		if (n == XPMMAXCMTLEN - 1)  { /* forget it */
94 		    s = data->Comment;
95 		    n = 0;
96 		}
97 		*++s = c;
98 		n++;
99 	    }
100 	    data->CommentLength = n;
101 	    do {
102 		c = *data->cptr++;
103 		if (n == XPMMAXCMTLEN - 1)  { /* forget it */
104 		    s = data->Comment;
105 		    n = 0;
106 		}
107 		*++s = c;
108 		n++;
109 		s2++;
110 	    } while (c == *s2 && *s2 != '\0' && c);
111 	    if (*s2 == '\0') {
112 		/* this is the end of the comment */
113 		notend = 0;
114 		data->cptr--;
115 	    }
116 	}
117 	return 0;
118     } else {
119 	FILE *file = data->stream.file;
120 	register int c;
121 	register unsigned int n = 0, a;
122 	unsigned int notend;
123 	char *s;
124 	const char *s2;
125 
126 	s = data->Comment;
127 	*s = data->Bcmt[0];
128 
129 	/* skip the string beginning comment */
130 	s2 = data->Bcmt;
131 	do {
132 	    c = Getc(data, file);
133 	    *++s = c;
134 	    n++;
135 	    s2++;
136 	} while (c == *s2 && *s2 != '\0' && c != EOF);
137 
138 	if (*s2 != '\0') {
139 	    /* this wasn't the beginning of a comment */
140 	    /* put characters back in the order that we got them */
141 	    for (a = n; a > 0; a--, s--)
142 		Ungetc(data, *s, file);
143 	    return 0;
144 	}
145 	/* store comment */
146 	data->Comment[0] = *s;
147 	s = data->Comment;
148 	notend = 1;
149 	n = 0;
150 	while (notend) {
151 	    s2 = data->Ecmt;
152 	    while (*s != *s2 && c != EOF) {
153 		c = Getc(data, file);
154 		if (n == XPMMAXCMTLEN - 1)  { /* forget it */
155 		    s = data->Comment;
156 		    n = 0;
157 		}
158 		*++s = c;
159 		n++;
160 	    }
161 	    data->CommentLength = n;
162 	    do {
163 		c = Getc(data, file);
164 		if (n == XPMMAXCMTLEN - 1)  { /* forget it */
165 		    s = data->Comment;
166 		    n = 0;
167 		}
168 		*++s = c;
169 		n++;
170 		s2++;
171 	    } while (c == *s2 && *s2 != '\0' && c != EOF);
172 	    if (*s2 == '\0') {
173 		/* this is the end of the comment */
174 		notend = 0;
175 		Ungetc(data, *s, file);
176 	    }
177 	}
178 	return 0;
179     }
180 }
181 
182 /*
183  * skip to the end of the current string and the beginning of the next one
184  */
185 int
xpmNextString(xpmData * data)186 xpmNextString(xpmData *data)
187 {
188     if (!data->type)
189 	data->cptr = (data->stream.data)[++data->line];
190     else if (data->type == XPMBUFFER) {
191 	register char c;
192 
193 	/* get to the end of the current string */
194 	if (data->Eos)
195 	    while ((c = *data->cptr++) && c != data->Eos);
196 
197 	/*
198 	 * then get to the beginning of the next string looking for possible
199 	 * comment
200 	 */
201 	if (data->Bos) {
202 	    while ((c = *data->cptr++) && c != data->Bos)
203 		if (data->Bcmt && c == data->Bcmt[0])
204 		    ParseComment(data);
205 	} else if (data->Bcmt) {	/* XPM2 natural */
206 	    while ((c = *data->cptr++) == data->Bcmt[0])
207 		ParseComment(data);
208 	    data->cptr--;
209 	}
210     } else {
211 	register int c;
212 	FILE *file = data->stream.file;
213 
214 	/* get to the end of the current string */
215 	if (data->Eos)
216 	    while ((c = Getc(data, file)) != data->Eos && c != EOF);
217 
218 	/*
219 	 * then get to the beginning of the next string looking for possible
220 	 * comment
221 	 */
222 	if (data->Bos) {
223 	    while ((c = Getc(data, file)) != data->Bos && c != EOF)
224 		if (data->Bcmt && c == data->Bcmt[0])
225 		    ParseComment(data);
226 
227 	} else if (data->Bcmt) {	/* XPM2 natural */
228 	    while ((c = Getc(data, file)) == data->Bcmt[0])
229 		ParseComment(data);
230 	    Ungetc(data, c, file);
231 	}
232     }
233     return 0;
234 }
235 
236 
237 /*
238  * skip whitespace and return the following word
239  */
240 unsigned int
xpmNextWord(xpmData * data,char * buf,unsigned int buflen)241 xpmNextWord(
242     xpmData		*data,
243     char		*buf,
244     unsigned int	 buflen)
245 {
246     register unsigned int n = 0;
247     int c;
248 
249     if (!data->type || data->type == XPMBUFFER) {
250 	while (isspace(c = *data->cptr) && c != data->Eos)
251 	    data->cptr++;
252 	do {
253 	    c = *data->cptr++;
254 	    *buf++ = c;
255 	    n++;
256 	} while (!isspace(c) && c != data->Eos && n < buflen);
257 	n--;
258 	data->cptr--;
259     } else {
260 	FILE *file = data->stream.file;
261 
262 	while ((c = Getc(data, file)) != EOF && isspace(c) && c != data->Eos);
263 	while (!isspace(c) && c != data->Eos && c != EOF && n < buflen) {
264 	    *buf++ = c;
265 	    n++;
266 	    c = Getc(data, file);
267 	}
268 	Ungetc(data, c, file);
269     }
270     return (n); /* this returns bytes read + 1 */
271 }
272 
273 /*
274  * skip whitespace and compute the following unsigned int,
275  * returns 1 if one is found and 0 if not
276  */
277 int
xpmNextUI(xpmData * data,unsigned int * ui_return)278 xpmNextUI(
279     xpmData		*data,
280     unsigned int	*ui_return)
281 {
282     char buf[BUFSIZ];
283     int l;
284 
285     l = xpmNextWord(data, buf, BUFSIZ);
286     return xpmatoui(buf, l, ui_return);
287 }
288 
289 /*
290  * return end of string - WARNING: malloc!
291  */
292 int
xpmGetString(xpmData * data,char ** sptr,unsigned int * l)293 xpmGetString(
294     xpmData		 *data,
295     char		**sptr,
296     unsigned int	 *l)
297 {
298     unsigned int i, n = 0;
299     int c;
300     char *p = NULL, *q, buf[BUFSIZ];
301 
302     if (!data->type || data->type == XPMBUFFER) {
303 	if (data->cptr) {
304 	    char *start = data->cptr;
305 	    while ((c = *data->cptr) && c != data->Eos)
306 		data->cptr++;
307 	    n = data->cptr - start + 1;
308 	    p = (char *) XpmMalloc(n);
309 	    if (!p)
310 		return (XpmNoMemory);
311 	    strncpy(p, start, n);
312 	    if (data->type)		/* XPMBUFFER */
313 		p[n - 1] = '\0';
314 	}
315     } else {
316 	FILE *file = data->stream.file;
317 
318 	if ((c = Getc(data, file)) == EOF)
319 	    return (XpmFileInvalid);
320 
321 	i = 0;
322 	q = buf;
323 	p = (char *) XpmMalloc(1);
324 	while (c != data->Eos && c != EOF) {
325 	    if (i == BUFSIZ) {
326 		/* get to the end of the buffer */
327 		/* malloc needed memory */
328 		q = (char *) XpmRealloc(p, n + i);
329 		if (!q) {
330 		    XpmFree(p);
331 		    return (XpmNoMemory);
332 		}
333 		p = q;
334 		q += n;
335 		/* and copy what we already have */
336 		strncpy(q, buf, i);
337 		n += i;
338 		i = 0;
339 		q = buf;
340 	    }
341 	    *q++ = c;
342 	    i++;
343 	    c = Getc(data, file);
344 	}
345 	if (c == EOF) {
346 	    XpmFree(p);
347 	    return (XpmFileInvalid);
348 	}
349 	if (n + i != 0) {
350 	    /* malloc needed memory */
351 	    q = (char *) XpmRealloc(p, n + i + 1);
352 	    if (!q) {
353 		XpmFree(p);
354 		return (XpmNoMemory);
355 	    }
356 	    p = q;
357 	    q += n;
358 	    /* and copy the buffer */
359 	    strncpy(q, buf, i);
360 	    n += i;
361 	    p[n++] = '\0';
362 	} else {
363 	    *p = '\0';
364 	    n = 1;
365 	}
366 	Ungetc(data, c, file);
367     }
368     *sptr = p;
369     *l = n;
370     return (XpmSuccess);
371 }
372 
373 /*
374  * get the current comment line
375  */
376 int
xpmGetCmt(xpmData * data,char ** cmt)377 xpmGetCmt(
378     xpmData	 *data,
379     char	**cmt)
380 {
381     if (!data->type)
382 	*cmt = NULL;
383     else if (data->CommentLength != 0 && data->CommentLength < UINT_MAX - 1) {
384 	if( (*cmt = (char *) XpmMalloc(data->CommentLength + 1)) == NULL)
385 		return XpmNoMemory;
386 	strncpy(*cmt, data->Comment, data->CommentLength);
387 	(*cmt)[data->CommentLength] = '\0';
388 	data->CommentLength = 0;
389     } else
390 	*cmt = NULL;
391     return 0;
392 }
393 
394 xpmDataType xpmDataTypes[] =
395 {
396     {"", "!", "\n", '\0', '\n', "", "", "", ""},	/* Natural type */
397     {"C", "/*", "*/", '"', '"', ",\n", "static char *", "[] = {\n", "};\n"},
398     {"Lisp", ";", "\n", '"', '"', "\n", "(setq ", " '(\n", "))\n"},
399     {NULL, NULL, NULL, 0, 0, NULL, NULL, NULL, NULL}
400 };
401 
402 /*
403  * parse xpm header
404  */
405 int
xpmParseHeader(xpmData * data)406 xpmParseHeader(xpmData *data)
407 {
408     char buf[BUFSIZ+1] = {0};
409     int l, n = 0;
410 
411     if (data->type) {
412 	data->Bos = '\0';
413 	data->Eos = '\n';
414 	data->Bcmt = data->Ecmt = NULL;
415 	l = xpmNextWord(data, buf, BUFSIZ);
416 	if (l == 7 && !strncmp("#define", buf, 7)) {
417 	    /* this maybe an XPM 1 file */
418 	    char *ptr;
419 
420 	    l = xpmNextWord(data, buf, BUFSIZ);
421 	    if (!l)
422 		return (XpmFileInvalid);
423 	    buf[l] = '\0';
424 	    ptr = strrchr(buf, '_');
425 	    if (!ptr || strncmp("_format", ptr, l - (ptr - buf)))
426 		return XpmFileInvalid;
427 	    /* this is definitely an XPM 1 file */
428 	    data->format = 1;
429 	    n = 1;			/* handle XPM1 as mainly XPM2 C */
430 	} else {
431 
432 	    /*
433 	     * skip the first word, get the second one, and see if this is
434 	     * XPM 2 or 3
435 	     */
436 	    l = xpmNextWord(data, buf, BUFSIZ);
437 	    if ((l == 3 && !strncmp("XPM", buf, 3)) ||
438 		(l == 4 && !strncmp("XPM2", buf, 4))) {
439 		if (l == 3)
440 		    n = 1;		/* handle XPM as XPM2 C */
441 		else {
442 		    /* get the type key word */
443 		    l = xpmNextWord(data, buf, BUFSIZ);
444 
445 		    /*
446 		     * get infos about this type
447 		     */
448 		    while (xpmDataTypes[n].type
449 			   && strncmp(xpmDataTypes[n].type, buf, l))
450 			n++;
451 		}
452 		data->format = 0;
453 	    } else
454 		/* nope this is not an XPM file */
455 		return XpmFileInvalid;
456 	}
457 	if (xpmDataTypes[n].type) {
458 	    if (n == 0) {		/* natural type */
459 		data->Bcmt = xpmDataTypes[n].Bcmt;
460 		data->Ecmt = xpmDataTypes[n].Ecmt;
461 		xpmNextString(data);	/* skip the end of the headerline */
462 		data->Bos = xpmDataTypes[n].Bos;
463 		data->Eos = xpmDataTypes[n].Eos;
464 	    } else {
465 		data->Bcmt = xpmDataTypes[n].Bcmt;
466 		data->Ecmt = xpmDataTypes[n].Ecmt;
467 		if (!data->format) {	/* XPM 2 or 3 */
468 		    data->Bos = xpmDataTypes[n].Bos;
469 		    data->Eos = '\0';
470 		    /* get to the beginning of the first string */
471 		    xpmNextString(data);
472 		    data->Eos = xpmDataTypes[n].Eos;
473 		} else			/* XPM 1 skip end of line */
474 		    xpmNextString(data);
475 	    }
476 	} else
477 	    /* we don't know about that type of XPM file... */
478 	    return XpmFileInvalid;
479     }
480     return XpmSuccess;
481 }
482