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