1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* GdkPixbuf library - XPM image loader
3  *
4  * Copyright (C) 1999 Mark Crichton
5  * Copyright (C) 1999 The Free Software Foundation
6  *
7  * Authors: Mark Crichton <crichton@gimp.org>
8  *          Federico Mena-Quintero <federico@gimp.org>
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
22  */
23 
24 #include "config.h"
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <glib.h>
29 #ifdef HAVE_UNISTD_H
30 #include <unistd.h> /* for unlink */
31 #endif
32 #include <errno.h>
33 #include <glib/gstdio.h>
34 #include <glib/gi18n-lib.h>
35 #include "gdk-pixbuf-core.h"
36 #include "gdk-pixbuf-io.h"
37 
38 
39 
40 
41 /* I have must have done something to deserve this.
42  * XPM is such a crappy format to handle.
43  * This code is an ugly hybrid from gdkpixmap.c
44  * modified to respect transparent colors.
45  * It's still a mess, though.
46  */
47 
48 enum buf_op {
49 	op_header,
50 	op_cmap,
51 	op_body
52 };
53 
54 typedef struct {
55 	gchar *color_string;
56 	guint16 red;
57 	guint16 green;
58 	guint16 blue;
59 	gint transparent;
60 } XPMColor;
61 
62 struct file_handle {
63 	FILE *infile;
64 	gchar *buffer;
65 	guint buffer_size;
66 };
67 
68 struct mem_handle {
69 	const gchar **data;
70 	int offset;
71 };
72 
73 /* The following 2 routines (parse_color, find_color) come from Tk, via the Win32
74  * port of GDK. The licensing terms on these (longer than the functions) is:
75  *
76  * This software is copyrighted by the Regents of the University of
77  * California, Sun Microsystems, Inc., and other parties.  The following
78  * terms apply to all files associated with the software unless explicitly
79  * disclaimed in individual files.
80  *
81  * The authors hereby grant permission to use, copy, modify, distribute,
82  * and license this software and its documentation for any purpose, provided
83  * that existing copyright notices are retained in all copies and that this
84  * notice is included verbatim in any distributions. No written agreement,
85  * license, or royalty fee is required for any of the authorized uses.
86  * Modifications to this software may be copyrighted by their authors
87  * and need not follow the licensing terms described here, provided that
88  * the new terms are clearly indicated on the first page of each file where
89  * they apply.
90  *
91  * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
92  * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
93  * ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
94  * DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
95  * POSSIBILITY OF SUCH DAMAGE.
96  *
97  * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
98  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
99  * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE
100  * IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
101  * NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
102  * MODIFICATIONS.
103  *
104  * GOVERNMENT USE: If you are acquiring this software on behalf of the
105  * U.S. government, the Government shall have only "Restricted Rights"
106  * in the software and related documentation as defined in the Federal
107  * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2).  If you
108  * are acquiring the software on behalf of the Department of Defense, the
109  * software shall be classified as "Commercial Computer Software" and the
110  * Government shall have only "Restricted Rights" as defined in Clause
111  * 252.227-7013 (c) (1) of DFARs.  Notwithstanding the foregoing, the
112  * authors grant the U.S. Government and others acting in its behalf
113  * permission to use and distribute the software in accordance with the
114  * terms specified in this license.
115  */
116 
117 #include "xpm-color-table.h"
118 
119 /*
120  *----------------------------------------------------------------------
121  *
122  * find_color --
123  *
124  *	This routine finds the color entry that corresponds to the
125  *	specified color.
126  *
127  * Results:
128  *	Returns non-zero on success.  The RGB values of the XColor
129  *	will be initialized to the proper values on success.
130  *
131  * Side effects:
132  *	None.
133  *
134  *----------------------------------------------------------------------
135  */
136 
137 static int
compare_xcolor_entries(const void * a,const void * b)138 compare_xcolor_entries (const void *a, const void *b)
139 {
140   return g_ascii_strcasecmp ((const char *) a,
141 			     color_names + ((const XPMColorEntry *)b)->name_offset);
142 }
143 
144 static gboolean
find_color(const char * name,XPMColor * colorPtr)145 find_color(const char *name,
146 	   XPMColor   *colorPtr)
147 {
148 	XPMColorEntry *found;
149 
150 	found = bsearch (name, xColors, G_N_ELEMENTS (xColors), sizeof (XPMColorEntry),
151 			 compare_xcolor_entries);
152 	if (found == NULL)
153 	  return FALSE;
154 
155 	colorPtr->red = (found->red * 65535) / 255;
156 	colorPtr->green = (found->green * 65535) / 255;
157 	colorPtr->blue = (found->blue * 65535) / 255;
158 
159 	return TRUE;
160 }
161 
162 /*
163  *----------------------------------------------------------------------
164  *
165  * parse_color --
166  *
167  *	Partial implementation of X color name parsing interface.
168  *
169  * Results:
170  *	Returns TRUE on success.
171  *
172  * Side effects:
173  *	None.
174  *
175  *----------------------------------------------------------------------
176  */
177 
178 static gboolean
parse_color(const char * spec,XPMColor * colorPtr)179 parse_color (const char *spec,
180 	     XPMColor   *colorPtr)
181 {
182 	if (spec[0] == '#') {
183 		int i, red, green, blue;
184 
185 		if ((i = strlen (spec + 1)) % 3) {
186 			return FALSE;
187 		}
188 		i /= 3;
189 
190 		if (i == 4) {
191 			if (sscanf (spec + 1, "%4x%4x%4x", &red, &green, &blue) != 3)
192 				return FALSE;
193 			colorPtr->red = red;
194 			colorPtr->green = green;
195 			colorPtr->blue = blue;
196 		} else if (i == 1) {
197 			if (sscanf (spec + 1, "%1x%1x%1x", &red, &green, &blue) != 3)
198 				return FALSE;
199 			colorPtr->red = (red * 65535) / 15;
200 			colorPtr->green = (green * 65535) / 15;
201 			colorPtr->blue = (blue * 65535) / 15;
202 		} else if (i == 2) {
203 			if (sscanf (spec + 1, "%2x%2x%2x", &red, &green, &blue) != 3)
204 				return FALSE;
205 			colorPtr->red = (red * 65535) / 255;
206 			colorPtr->green = (green * 65535) / 255;
207 			colorPtr->blue = (blue * 65535) / 255;
208 		} else /* if (i == 3) */ {
209 			if (sscanf (spec + 1, "%3x%3x%3x", &red, &green, &blue) != 3)
210 				return FALSE;
211 			colorPtr->red = (red * 65535) / 4095;
212 			colorPtr->green = (green * 65535) / 4095;
213 			colorPtr->blue = (blue * 65535) / 4095;
214 		}
215 	} else {
216 		if (!find_color(spec, colorPtr))
217 			return FALSE;
218 	}
219 	return TRUE;
220 }
221 
222 static gint
xpm_seek_string(FILE * infile,const gchar * str)223 xpm_seek_string (FILE *infile, const gchar *str)
224 {
225 	char instr[1024];
226 
227 	while (!feof (infile)) {
228 		if (fscanf (infile, "%1023s", instr) < 0)
229                         return FALSE;
230 		if (strcmp (instr, str) == 0)
231 			return TRUE;
232 	}
233 
234 	return FALSE;
235 }
236 
237 static gint
xpm_seek_char(FILE * infile,gchar c)238 xpm_seek_char (FILE *infile, gchar c)
239 {
240 	gint b, oldb;
241 
242 	while ((b = getc (infile)) != EOF) {
243 		if (c != b && b == '/') {
244 			b = getc (infile);
245 			if (b == EOF)
246 				return FALSE;
247 
248 			else if (b == '*') {	/* we have a comment */
249 				b = -1;
250 				do {
251 					oldb = b;
252 					b = getc (infile);
253 					if (b == EOF)
254 						return FALSE;
255 				} while (!(oldb == '*' && b == '/'));
256 			}
257 		} else if (c == b)
258 			return TRUE;
259 	}
260 
261 	return FALSE;
262 }
263 
264 static gint
xpm_read_string(FILE * infile,gchar ** buffer,guint * buffer_size)265 xpm_read_string (FILE *infile, gchar **buffer, guint *buffer_size)
266 {
267 	gint c;
268 	guint cnt = 0, bufsiz, ret = FALSE;
269 	gchar *buf;
270 
271 	buf = *buffer;
272 	bufsiz = *buffer_size;
273 	if (buf == NULL) {
274 		bufsiz = 10 * sizeof (gchar);
275 		buf = g_new (gchar, bufsiz);
276 	}
277 
278 	do {
279 		c = getc (infile);
280 	} while (c != EOF && c != '"');
281 
282 	if (c != '"')
283 		goto out;
284 
285 	while ((c = getc (infile)) != EOF) {
286 		if (cnt == bufsiz) {
287 			guint new_size = bufsiz * 2;
288 
289 			if (new_size > bufsiz)
290 				bufsiz = new_size;
291 			else
292 				goto out;
293 
294 			buf = g_realloc (buf, bufsiz);
295 			buf[bufsiz - 1] = '\0';
296 		}
297 
298 		if (c != '"')
299 			buf[cnt++] = c;
300 		else {
301 			buf[cnt] = 0;
302 			ret = TRUE;
303 			break;
304 		}
305 	}
306 
307  out:
308 	buf[bufsiz - 1] = '\0';	/* ensure null termination for errors */
309 	*buffer = buf;
310 	*buffer_size = bufsiz;
311 	return ret;
312 }
313 
314 static gchar *
xpm_extract_color(const gchar * buffer)315 xpm_extract_color (const gchar *buffer)
316 {
317 	const gchar *p = &buffer[0];
318 	gint new_key = 0;
319 	gint key = 0;
320 	gint current_key = 1;
321 	gint space = 128;
322 	gchar word[129], color[129], current_color[129];
323 	gchar *r;
324 
325 	word[0] = '\0';
326 	color[0] = '\0';
327 	current_color[0] = '\0';
328         while (1) {
329 		/* skip whitespace */
330 		for (; *p != '\0' && g_ascii_isspace (*p); p++) {
331 		}
332 		/* copy word */
333 		for (r = word; *p != '\0' && !g_ascii_isspace (*p) && r - word < sizeof (word) - 1; p++, r++) {
334 			*r = *p;
335 		}
336 		*r = '\0';
337 		if (*word == '\0') {
338 			if (color[0] == '\0')  /* incomplete colormap entry */
339 				return NULL;
340 			else  /* end of entry, still store the last color */
341 				new_key = 1;
342 		}
343 		else if (key > 0 && color[0] == '\0')  /* next word must be a color name part */
344 			new_key = 0;
345 		else {
346 			if (strcmp (word, "c") == 0)
347 				new_key = 5;
348 			else if (strcmp (word, "g") == 0)
349 				new_key = 4;
350 			else if (strcmp (word, "g4") == 0)
351 				new_key = 3;
352 			else if (strcmp (word, "m") == 0)
353 				new_key = 2;
354 			else if (strcmp (word, "s") == 0)
355 				new_key = 1;
356 			else
357 				new_key = 0;
358 		}
359 		if (new_key == 0) {  /* word is a color name part */
360 			if (key == 0)  /* key expected */
361 				return NULL;
362 			/* accumulate color name */
363 			if (color[0] != '\0') {
364 				strncat (color, " ", space);
365 				space -= MIN (space, 1);
366 			}
367 			strncat (color, word, space);
368 			space -= MIN (space, strlen (word));
369 		}
370 		else {  /* word is a key */
371 			if (key > current_key) {
372 				current_key = key;
373 				strcpy (current_color, color);
374 			}
375 			space = 128;
376 			color[0] = '\0';
377 			key = new_key;
378 			if (*p == '\0') break;
379 		}
380 
381 	}
382 	if (current_key > 1)
383 		return g_strdup (current_color);
384 	else
385 		return NULL;
386 }
387 
388 /* (almost) direct copy from gdkpixmap.c... loads an XPM from a file */
389 
390 static const gchar *
file_buffer(enum buf_op op,gpointer handle)391 file_buffer (enum buf_op op, gpointer handle)
392 {
393 	struct file_handle *h = handle;
394 
395 	switch (op) {
396 	case op_header:
397 		if (xpm_seek_string (h->infile, "XPM") != TRUE)
398 			break;
399 
400 		if (xpm_seek_char (h->infile, '{') != TRUE)
401 			break;
402 		/* Fall through to the next xpm_seek_char. */
403 
404 	case op_cmap:
405 		xpm_seek_char (h->infile, '"');
406 		if (fseek (h->infile, -1, SEEK_CUR) != 0)
407 			return NULL;
408 		/* Fall through to the xpm_read_string. */
409 
410 	case op_body:
411 		if(!xpm_read_string (h->infile, &h->buffer, &h->buffer_size))
412 			return NULL;
413 		return h->buffer;
414 
415 	default:
416 		g_assert_not_reached ();
417 	}
418 
419 	return NULL;
420 }
421 
422 /* This reads from memory */
423 static const gchar *
mem_buffer(enum buf_op op,gpointer handle)424 mem_buffer (enum buf_op op, gpointer handle)
425 {
426 	struct mem_handle *h = handle;
427 	switch (op) {
428 	case op_header:
429 	case op_cmap:
430 	case op_body:
431                 if (h->data[h->offset]) {
432                         const gchar* retval;
433 
434                         retval = h->data[h->offset];
435                         h->offset += 1;
436                         return retval;
437                 }
438                 break;
439 
440 	default:
441 		g_assert_not_reached ();
442                 break;
443 	}
444 
445 	return NULL;
446 }
447 
448 /* This function does all the work. */
449 static GdkPixbuf *
pixbuf_create_from_xpm(const gchar * (* get_buf)(enum buf_op op,gpointer handle),gpointer handle,GError ** error)450 pixbuf_create_from_xpm (const gchar * (*get_buf) (enum buf_op op, gpointer handle), gpointer handle,
451                         GError **error)
452 {
453 	gint w, h, n_col, cpp, x_hot, y_hot, items;
454 	gint cnt, xcnt, ycnt, wbytes, n;
455 	gint is_trans = FALSE;
456 	const gchar *buffer;
457         gchar *name_buf;
458 	gchar pixel_str[32];
459 	GHashTable *color_hash;
460 	XPMColor *colors, *color, *fallbackcolor;
461 	guchar *pixtmp;
462 	GdkPixbuf *pixbuf = NULL;
463 	gint rowstride;
464 
465 	fallbackcolor = NULL;
466 
467 	buffer = (*get_buf) (op_header, handle);
468 	if (!buffer) {
469                 g_set_error_literal (error,
470                                      GDK_PIXBUF_ERROR,
471                                      GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
472                                      _("No XPM header found"));
473 		return NULL;
474 	}
475 	items = sscanf (buffer, "%d %d %d %d %d %d", &w, &h, &n_col, &cpp, &x_hot, &y_hot);
476 
477 	if (items != 4 && items != 6) {
478 		g_set_error_literal (error,
479                                      GDK_PIXBUF_ERROR,
480                                      GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
481                                      _("Invalid XPM header"));
482 		return NULL;
483 	}
484 
485 	if (w <= 0) {
486                 g_set_error_literal (error,
487                                      GDK_PIXBUF_ERROR,
488                                      GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
489                                      _("XPM file has image width <= 0"));
490 		return NULL;
491 
492 	}
493 	if (h <= 0) {
494                 g_set_error_literal (error,
495                                      GDK_PIXBUF_ERROR,
496                                      GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
497                                      _("XPM file has image height <= 0"));
498 		return NULL;
499 
500 	}
501 	/* Check from libXpm's ParsePixels() */
502 	if ((h > 0 && w >= UINT_MAX / h) ||
503 	    w * h >= UINT_MAX / sizeof(unsigned int)) {
504 		g_set_error_literal (error,
505                                      GDK_PIXBUF_ERROR,
506                                      GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
507                                      _("Invalid XPM header"));
508 		return NULL;
509 	}
510 	if (cpp <= 0 || cpp >= 32) {
511                 g_set_error_literal (error,
512                                      GDK_PIXBUF_ERROR,
513                                      GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
514                                      _("XPM has invalid number of chars per pixel"));
515 		return NULL;
516 	}
517 	if (n_col <= 0 ||
518 	    n_col >= G_MAXINT / (cpp + 1) ||
519 	    n_col >= G_MAXINT / sizeof (XPMColor)) {
520                 g_set_error_literal (error,
521                                      GDK_PIXBUF_ERROR,
522                                      GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
523                                      _("XPM file has invalid number of colors"));
524 		return NULL;
525 	}
526 
527 	/* The hash is used for fast lookups of color from chars */
528 	color_hash = g_hash_table_new (g_str_hash, g_str_equal);
529 
530 	name_buf = g_try_malloc (n_col * (cpp + 1));
531 	if (!name_buf) {
532 		g_set_error_literal (error,
533                                      GDK_PIXBUF_ERROR,
534                                      GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
535                                      _("Cannot allocate memory for loading XPM image"));
536 		g_hash_table_destroy (color_hash);
537 		return NULL;
538 	}
539 	colors = (XPMColor *) g_try_malloc (sizeof (XPMColor) * n_col);
540 	if (!colors) {
541 		g_set_error_literal (error,
542                                      GDK_PIXBUF_ERROR,
543                                      GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
544                                      _("Cannot allocate memory for loading XPM image"));
545 		g_hash_table_destroy (color_hash);
546 		g_free (name_buf);
547 		return NULL;
548 	}
549 
550 	for (cnt = 0; cnt < n_col; cnt++) {
551 		gchar *color_name;
552 
553 		buffer = (*get_buf) (op_cmap, handle);
554 		if (!buffer) {
555                         g_set_error_literal (error,
556                                              GDK_PIXBUF_ERROR,
557                                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
558                                              _("Cannot read XPM colormap"));
559                         goto out;
560 		}
561 
562 		color = &colors[cnt];
563 		color->color_string = &name_buf[cnt * (cpp + 1)];
564 		strncpy (color->color_string, buffer, cpp);
565 		color->color_string[cpp] = 0;
566 		buffer += strlen (color->color_string);
567 		color->transparent = FALSE;
568 
569 		color_name = xpm_extract_color (buffer);
570 
571 		if ((color_name == NULL) || (g_ascii_strcasecmp (color_name, "None") == 0)
572 		    || (parse_color (color_name, color) == FALSE)) {
573 			color->transparent = TRUE;
574 			color->red = 0;
575 			color->green = 0;
576 			color->blue = 0;
577 			is_trans = TRUE;
578 		}
579 
580 		g_free (color_name);
581 		g_hash_table_insert (color_hash, color->color_string, color);
582 
583 		if (cnt == 0)
584 			fallbackcolor = color;
585 	}
586 
587 	pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, is_trans, 8, w, h);
588 
589 	if (!pixbuf) {
590                 g_set_error_literal (error,
591                                      GDK_PIXBUF_ERROR,
592                                      GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
593                                      _("Cannot allocate memory for loading XPM image"));
594                 goto out;
595 	}
596 
597 	rowstride = gdk_pixbuf_get_rowstride (pixbuf);
598 
599 	wbytes = w * cpp;
600 
601 	for (ycnt = 0; ycnt < h; ycnt++) {
602 		pixtmp = gdk_pixbuf_get_pixels (pixbuf) + ycnt * rowstride;
603 
604 		buffer = (*get_buf) (op_body, handle);
605 		if ((!buffer) || (strlen (buffer) < wbytes)) {
606 			/* Advertised width doesn't match pixels */
607 			g_set_error_literal (error,
608 					     GDK_PIXBUF_ERROR,
609 					     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
610 					     _("Dimensions do not match data"));
611 			goto out;
612 		}
613 
614 		for (n = 0, xcnt = 0; n < wbytes; n += cpp, xcnt++) {
615 			strncpy (pixel_str, &buffer[n], cpp);
616 			pixel_str[cpp] = 0;
617 
618 			color = g_hash_table_lookup (color_hash, pixel_str);
619 
620 			/* Bad XPM...punt */
621 			if (!color)
622 				color = fallbackcolor;
623 
624 			*pixtmp++ = color->red >> 8;
625 			*pixtmp++ = color->green >> 8;
626 			*pixtmp++ = color->blue >> 8;
627 
628 			if (is_trans && color->transparent)
629 				*pixtmp++ = 0;
630 			else if (is_trans)
631 				*pixtmp++ = 0xFF;
632 		}
633 	}
634 
635 	g_hash_table_destroy (color_hash);
636 	g_free (colors);
637 	g_free (name_buf);
638 
639 	if (items == 6) {
640 		gchar hot[10];
641 		g_snprintf (hot, 10, "%d", x_hot);
642 		gdk_pixbuf_set_option (pixbuf, "x_hot", hot);
643 		g_snprintf (hot, 10, "%d", y_hot);
644 		gdk_pixbuf_set_option (pixbuf, "y_hot", hot);
645 
646 	}
647 
648 	return pixbuf;
649 
650 out:
651 	g_hash_table_destroy (color_hash);
652 	g_free (colors);
653 	g_free (name_buf);
654 
655 	g_clear_object (&pixbuf);
656 	return NULL;
657 }
658 
659 /* Shared library entry point for file loading */
660 static GdkPixbuf *
gdk_pixbuf__xpm_image_load(FILE * f,GError ** error)661 gdk_pixbuf__xpm_image_load (FILE *f,
662                             GError **error)
663 {
664 	GdkPixbuf *pixbuf;
665 	struct file_handle h;
666 
667 	memset (&h, 0, sizeof (h));
668 	h.infile = f;
669 	pixbuf = pixbuf_create_from_xpm (file_buffer, &h, error);
670 	g_free (h.buffer);
671 
672 	return pixbuf;
673 }
674 
675 /* Shared library entry point for memory loading */
676 static GdkPixbuf *
gdk_pixbuf__xpm_image_load_xpm_data(const gchar ** data)677 gdk_pixbuf__xpm_image_load_xpm_data (const gchar **data)
678 {
679         GdkPixbuf *pixbuf;
680         struct mem_handle h;
681         GError *error = NULL;
682 
683         h.data = data;
684         h.offset = 0;
685 
686 	pixbuf = pixbuf_create_from_xpm (mem_buffer, &h, &error);
687 
688         if (error) {
689                 g_warning ("Inline XPM data is broken: %s", error->message);
690                 g_error_free (error);
691                 error = NULL;
692         }
693 
694 	return pixbuf;
695 }
696 
697 /* Progressive loader */
698 typedef struct _XPMContext XPMContext;
699 struct _XPMContext
700 {
701        GdkPixbufModulePreparedFunc prepare_func;
702        GdkPixbufModuleUpdatedFunc update_func;
703        gpointer user_data;
704 
705        gchar *tempname;
706        FILE *file;
707        gboolean all_okay;
708 };
709 
710 /*
711  * FIXME xpm loading progressively is not properly implemented.
712  * Instead we will buffer to a file then load that file when done.
713  * This is very broken but it should be relatively simple to fix
714  * in the future.
715  */
716 static gpointer
gdk_pixbuf__xpm_image_begin_load(GdkPixbufModuleSizeFunc size_func,GdkPixbufModulePreparedFunc prepare_func,GdkPixbufModuleUpdatedFunc update_func,gpointer user_data,GError ** error)717 gdk_pixbuf__xpm_image_begin_load (GdkPixbufModuleSizeFunc size_func,
718                                   GdkPixbufModulePreparedFunc prepare_func,
719                                   GdkPixbufModuleUpdatedFunc update_func,
720                                   gpointer user_data,
721                                   GError **error)
722 {
723        XPMContext *context;
724        gint fd;
725 
726        context = g_new (XPMContext, 1);
727        context->prepare_func = prepare_func;
728        context->update_func = update_func;
729        context->user_data = user_data;
730        context->all_okay = TRUE;
731        fd = g_file_open_tmp ("gdkpixbuf-xpm-tmp.XXXXXX", &context->tempname,
732 			     NULL);
733        if (fd < 0) {
734                g_free (context);
735                return NULL;
736        }
737 
738        context->file = fdopen (fd, "w+");
739        if (context->file == NULL) {
740                g_free (context->tempname);
741                g_free (context);
742                return NULL;
743        }
744 
745        return context;
746 }
747 
748 static gboolean
gdk_pixbuf__xpm_image_stop_load(gpointer data,GError ** error)749 gdk_pixbuf__xpm_image_stop_load (gpointer data,
750                                  GError **error)
751 {
752        XPMContext *context = (XPMContext*) data;
753        GdkPixbuf *pixbuf;
754        gboolean retval = FALSE;
755 
756        g_return_val_if_fail (data != NULL, FALSE);
757 
758        fflush (context->file);
759        rewind (context->file);
760        if (context->all_okay) {
761                pixbuf = gdk_pixbuf__xpm_image_load (context->file, error);
762 
763                if (pixbuf != NULL) {
764 		       if (context->prepare_func)
765 			       (* context->prepare_func) (pixbuf,
766 							  NULL,
767 							  context->user_data);
768 		       if (context->update_func)
769 			       (* context->update_func) (pixbuf,
770 							 0, 0,
771 							 gdk_pixbuf_get_width (pixbuf),
772 							 gdk_pixbuf_get_height (pixbuf),
773 							 context->user_data);
774                        g_object_unref (pixbuf);
775 
776                        retval = TRUE;
777                }
778        }
779 
780        fclose (context->file);
781        g_unlink (context->tempname);
782        g_free (context->tempname);
783        g_free ((XPMContext *) context);
784 
785        return retval;
786 }
787 
788 static gboolean
gdk_pixbuf__xpm_image_load_increment(gpointer data,const guchar * buf,guint size,GError ** error)789 gdk_pixbuf__xpm_image_load_increment (gpointer data,
790                                       const guchar *buf,
791                                       guint    size,
792                                       GError **error)
793 {
794        XPMContext *context = (XPMContext *) data;
795 
796        g_return_val_if_fail (data != NULL, FALSE);
797 
798        if (fwrite (buf, sizeof (guchar), size, context->file) != size) {
799 	       gint save_errno = errno;
800                context->all_okay = FALSE;
801                g_set_error_literal (error,
802                                     G_FILE_ERROR,
803                                     g_file_error_from_errno (save_errno),
804                                     _("Failed to write to temporary file when loading XPM image"));
805                return FALSE;
806        }
807 
808        return TRUE;
809 }
810 
811 #ifndef INCLUDE_xpm
812 #define MODULE_ENTRY(function) G_MODULE_EXPORT void function
813 #else
814 #define MODULE_ENTRY(function) void _gdk_pixbuf__xpm_ ## function
815 #endif
816 
MODULE_ENTRY(fill_vtable)817 MODULE_ENTRY (fill_vtable) (GdkPixbufModule *module)
818 {
819 	module->load = gdk_pixbuf__xpm_image_load;
820 	module->load_xpm_data = gdk_pixbuf__xpm_image_load_xpm_data;
821 	module->begin_load = gdk_pixbuf__xpm_image_begin_load;
822 	module->stop_load = gdk_pixbuf__xpm_image_stop_load;
823 	module->load_increment = gdk_pixbuf__xpm_image_load_increment;
824 }
825 
MODULE_ENTRY(fill_info)826 MODULE_ENTRY (fill_info) (GdkPixbufFormat *info)
827 {
828 	static const GdkPixbufModulePattern signature[] = {
829 		{ "/* XPM */", NULL, 100 },
830 		{ NULL, NULL, 0 }
831 	};
832 	static const gchar *mime_types[] = {
833 		"image/x-xpixmap",
834 		NULL
835 	};
836 	static const gchar *extensions[] = {
837 		"xpm",
838 		NULL
839 	};
840 
841 	info->name = "xpm";
842 	info->signature = (GdkPixbufModulePattern *) signature;
843 	info->description = NC_("image format", "XPM");
844 	info->mime_types = (gchar **) mime_types;
845 	info->extensions = (gchar **) extensions;
846 	info->flags = GDK_PIXBUF_FORMAT_THREADSAFE;
847 	info->license = "LGPL";
848 }
849