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