io-xpm.c 21.6 KB
Newer Older
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 6 7 8
 * Copyright (C) 1999 The Free Software Foundation
 *
 * Authors: Mark Crichton <crichton@gimp.org>
 *          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 13 14 15 16 17
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * 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 28
#include <string.h>
#include <glib.h>
29 30 31
#ifdef HAVE_UNISTD_H
#include <unistd.h> /* for unlink */
#endif
32
#include <errno.h>
33
#include "gdk-pixbuf-private.h"
34
#include <glib/gstdio.h>
35 36


37

38 39 40

/* I have must have done something to deserve this.
 * XPM is such a crappy format to handle.
Matthias Clasen's avatar
Matthias Clasen committed
41
 * This code is an ugly hybrid from gdkpixmap.c
42 43 44 45 46
 * modified to respect transparent colors.
 * It's still a mess, though.
 */

enum buf_op {
47 48 49
	op_header,
	op_cmap,
	op_body
50 51 52
};

typedef struct {
53
	gchar *color_string;
54 55 56
	guint16 red;
	guint16 green;
	guint16 blue;
57
	gint transparent;
58
} XPMColor;
59 60

struct file_handle {
61 62 63
	FILE *infile;
	gchar *buffer;
	guint buffer_size;
64
};
65 66

struct mem_handle {
67
	const gchar **data;
68
	int offset;
69
};
70

71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
/* The following 2 routines (parse_color, find_color) come from Tk, via the Win32
 * port of GDK. The licensing terms on these (longer than the functions) is:
 *
 * This software is copyrighted by the Regents of the University of
 * California, Sun Microsystems, Inc., and other parties.  The following
 * terms apply to all files associated with the software unless explicitly
 * disclaimed in individual files.
 * 
 * The authors hereby grant permission to use, copy, modify, distribute,
 * and license this software and its documentation for any purpose, provided
 * that existing copyright notices are retained in all copies and that this
 * notice is included verbatim in any distributions. No written agreement,
 * license, or royalty fee is required for any of the authorized uses.
 * Modifications to this software may be copyrighted by their authors
 * and need not follow the licensing terms described here, provided that
 * the new terms are clearly indicated on the first page of each file where
 * they apply.
 * 
 * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
 * ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
 * DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 * 
 * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE
 * IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
 * NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
 * MODIFICATIONS.
 * 
 * GOVERNMENT USE: If you are acquiring this software on behalf of the
 * U.S. government, the Government shall have only "Restricted Rights"
 * in the software and related documentation as defined in the Federal 
 * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2).  If you
 * are acquiring the software on behalf of the Department of Defense, the
 * software shall be classified as "Commercial Computer Software" and the
 * Government shall have only "Restricted Rights" as defined in Clause
 * 252.227-7013 (c) (1) of DFARs.  Notwithstanding the foregoing, the
 * authors grant the U.S. Government and others acting in its behalf
 * permission to use and distribute the software in accordance with the
 * terms specified in this license.
 */

115
#include "xpm-color-table.h"
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
 
/*
 *----------------------------------------------------------------------
 *
 * find_color --
 *
 *	This routine finds the color entry that corresponds to the
 *	specified color.
 *
 * Results:
 *	Returns non-zero on success.  The RGB values of the XColor
 *	will be initialized to the proper values on success.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static int
compare_xcolor_entries (const void *a, const void *b)
{
138
  return g_ascii_strcasecmp ((const char *) a, 
139
			     color_names + ((const XPMColorEntry *)b)->name_offset);
140 141 142 143 144 145 146 147
}

static gboolean
find_color(const char *name,
	   XPMColor   *colorPtr)
{
	XPMColorEntry *found;

148
	found = bsearch (name, xColors, G_N_ELEMENTS (xColors), sizeof (XPMColorEntry),
149 150
			 compare_xcolor_entries);
	if (found == NULL)
151
	  return FALSE;
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
	
	colorPtr->red = (found->red * 65535) / 255;
	colorPtr->green = (found->green * 65535) / 255;
	colorPtr->blue = (found->blue * 65535) / 255;
	
	return TRUE;
}

/*
 *----------------------------------------------------------------------
 *
 * parse_color --
 *
 *	Partial implementation of X color name parsing interface.
 *
 * Results:
168
 *	Returns TRUE on success.
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static gboolean
parse_color (const char *spec,
	     XPMColor   *colorPtr)
{
	if (spec[0] == '#') {
		char fmt[16];
		int i, red, green, blue;

184
		if ((i = strlen (spec + 1)) % 3) {
185 186 187 188
			return FALSE;
		}
		i /= 3;

189 190
		g_snprintf (fmt, 16, "%%%dx%%%dx%%%dx", i, i, i);

191
		if (sscanf (spec + 1, fmt, &red, &green, &blue) != 3) {
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
			return FALSE;
		}
		if (i == 4) {
			colorPtr->red = red;
			colorPtr->green = green;
			colorPtr->blue = blue;
		} else if (i == 1) {
			colorPtr->red = (red * 65535) / 15;
			colorPtr->green = (green * 65535) / 15;
			colorPtr->blue = (blue * 65535) / 15;
		} else if (i == 2)
		{
			colorPtr->red = (red * 65535) / 255;
			colorPtr->green = (green * 65535) / 255;
			colorPtr->blue = (blue * 65535) / 255;
		} else /* if (i == 3) */ {
			colorPtr->red = (red * 65535) / 4095;
			colorPtr->green = (green * 65535) / 4095;
			colorPtr->blue = (blue * 65535) / 4095;
		}
	} else {
		if (!find_color(spec, colorPtr))
			return FALSE;
	}
	return TRUE;
}

219
static gint
220
xpm_seek_string (FILE *infile, const gchar *str)
221
{
222
	char instr[1024];
223

224
	while (!feof (infile)) {
Havoc Pennington's avatar
Havoc Pennington committed
225 226
		if (fscanf (infile, "%1023s", instr) < 0)
                        return FALSE;
227
		if (strcmp (instr, str) == 0)
228
			return TRUE;
229 230
	}

231
	return FALSE;
232 233 234
}

static gint
235
xpm_seek_char (FILE *infile, gchar c)
236
{
237 238
	gint b, oldb;

239
	while ((b = getc (infile)) != EOF) {
240
		if (c != b && b == '/') {
241
			b = getc (infile);
242 243
			if (b == EOF)
				return FALSE;
244

245 246 247 248
			else if (b == '*') {	/* we have a comment */
				b = -1;
				do {
					oldb = b;
249
					b = getc (infile);
250 251
					if (b == EOF)
						return FALSE;
252
				} while (!(oldb == '*' && b == '/'));
253 254 255 256
			}
		} else if (c == b)
			return TRUE;
	}
257

258
	return FALSE;
259 260 261
}

static gint
262
xpm_read_string (FILE *infile, gchar **buffer, guint *buffer_size)
263
{
264 265 266 267 268 269 270
	gint c;
	guint cnt = 0, bufsiz, ret = FALSE;
	gchar *buf;

	buf = *buffer;
	bufsiz = *buffer_size;
	if (buf == NULL) {
271 272
		bufsiz = 10 * sizeof (gchar);
		buf = g_new (gchar, bufsiz);
273
	}
274 275

	do {
276
		c = getc (infile);
277 278
	} while (c != EOF && c != '"');

279
	if (c != '"')
280 281
		goto out;

282
	while ((c = getc (infile)) != EOF) {
283 284
		if (cnt == bufsiz) {
			guint new_size = bufsiz * 2;
285

286 287 288 289 290
			if (new_size > bufsiz)
				bufsiz = new_size;
			else
				goto out;

291
			buf = g_realloc (buf, bufsiz);
292 293
			buf[bufsiz - 1] = '\0';
		}
294

295 296 297 298 299 300 301
		if (c != '"')
			buf[cnt++] = c;
		else {
			buf[cnt] = 0;
			ret = TRUE;
			break;
		}
302 303
	}

304 305 306 307 308
 out:
	buf[bufsiz - 1] = '\0';	/* ensure null termination for errors */
	*buffer = buf;
	*buffer_size = bufsiz;
	return ret;
309 310 311
}

static gchar *
312
xpm_extract_color (const gchar *buffer)
313
{
314
	const gchar *p = &buffer[0];
315 316 317 318
	gint new_key = 0;
	gint key = 0;
	gint current_key = 1;
	gint space = 128;
319
	gchar word[129], color[129], current_color[129];
320 321
	gchar *r; 
	
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
	word[0] = '\0';
	color[0] = '\0';
	current_color[0] = '\0';
        while (1) {
		/* skip whitespace */
		for (; *p != '\0' && g_ascii_isspace (*p); p++) {
		} 
		/* copy word */
		for (r = word; *p != '\0' && !g_ascii_isspace (*p) && r - word < sizeof (word) - 1; p++, r++) {
			*r = *p;
		}
		*r = '\0';
		if (*word == '\0') {
			if (color[0] == '\0')  /* incomplete colormap entry */
				return NULL; 				
			else  /* end of entry, still store the last color */
				new_key = 1;
		} 
		else if (key > 0 && color[0] == '\0')  /* next word must be a color name part */
			new_key = 0;
342
		else {
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360
			if (strcmp (word, "c") == 0)
				new_key = 5;
			else if (strcmp (word, "g") == 0)
				new_key = 4;
			else if (strcmp (word, "g4") == 0)
				new_key = 3;
			else if (strcmp (word, "m") == 0)
				new_key = 2;
			else if (strcmp (word, "s") == 0)
				new_key = 1;
			else 
				new_key = 0;
		}
		if (new_key == 0) {  /* word is a color name part */
			if (key == 0)  /* key expected */
				return NULL;
			/* accumulate color name */
			if (color[0] != '\0') {
361 362
				strncat (color, " ", space);
				space -= MIN (space, 1);
363
			}
364 365 366 367 368 369 370 371 372 373 374 375
			strncat (color, word, space);
			space -= MIN (space, strlen (word));
		}
		else {  /* word is a key */
			if (key > current_key) {
				current_key = key;
				strcpy (current_color, color);
			}
			space = 128;
			color[0] = '\0';
			key = new_key;
			if (*p == '\0') break;
376
		}
377
		
378
	}
379 380 381 382
	if (current_key > 1)
		return g_strdup (current_color);
	else
		return NULL; 
383 384 385 386
}

/* (almost) direct copy from gdkpixmap.c... loads an XPM from a file */

387
static const gchar *
388
file_buffer (enum buf_op op, gpointer handle)
389
{
390 391 392 393
	struct file_handle *h = handle;

	switch (op) {
	case op_header:
394
		if (xpm_seek_string (h->infile, "XPM") != TRUE)
395 396
			break;

397
		if (xpm_seek_char (h->infile, '{') != TRUE)
398 399 400 401
			break;
		/* Fall through to the next xpm_seek_char. */

	case op_cmap:
402 403
		xpm_seek_char (h->infile, '"');
		fseek (h->infile, -1, SEEK_CUR);
404 405 406
		/* Fall through to the xpm_read_string. */

	case op_body:
407 408
		if(!xpm_read_string (h->infile, &h->buffer, &h->buffer_size))
			return NULL;
409
		return h->buffer;
410 411 412

	default:
		g_assert_not_reached ();
413
	}
414 415

	return NULL;
416 417 418
}

/* This reads from memory */
419
static const gchar *
420
mem_buffer (enum buf_op op, gpointer handle)
421
{
422 423 424 425 426
	struct mem_handle *h = handle;
	switch (op) {
	case op_header:
	case op_cmap:
	case op_body:
427 428 429 430 431 432 433 434
                if (h->data[h->offset]) {
                        const gchar* retval;

                        retval = h->data[h->offset];
                        h->offset += 1;
                        return retval;
                }
                break;
435 436 437

	default:
		g_assert_not_reached ();
438
                break;
439
	}
440

441
	return NULL;
442 443
}

444
/* This function does all the work. */
445
static GdkPixbuf *
446 447
pixbuf_create_from_xpm (const gchar * (*get_buf) (enum buf_op op, gpointer handle), gpointer handle,
                        GError **error)
448
{
449
	gint w, h, n_col, cpp, x_hot, y_hot, items;
450
	gint cnt, xcnt, ycnt, wbytes, n;
451
	gint is_trans = FALSE;
452 453
	const gchar *buffer;
        gchar *name_buf;
454 455
	gchar pixel_str[32];
	GHashTable *color_hash;
456
	XPMColor *colors, *color, *fallbackcolor;
457 458
	guchar *pixtmp;
	GdkPixbuf *pixbuf;
459

Arturo Espinosa's avatar
Arturo Espinosa committed
460 461
	fallbackcolor = NULL;

462
	buffer = (*get_buf) (op_header, handle);
463
	if (!buffer) {
464 465 466 467
                g_set_error_literal (error,
                                     GDK_PIXBUF_ERROR,
                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
                                     _("No XPM header found"));
468
		return NULL;
469
	}
470
	items = sscanf (buffer, "%d %d %d %d %d %d", &w, &h, &n_col, &cpp, &x_hot, &y_hot);
471 472

	if (items != 4 && items != 6) {
473 474 475 476
		g_set_error_literal (error,
                                     GDK_PIXBUF_ERROR,
                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
                                     _("Invalid XPM header"));
477 478 479
		return NULL;
	}

480
	if (w <= 0) {
481 482 483 484
                g_set_error_literal (error,
                                     GDK_PIXBUF_ERROR,
                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
                                     _("XPM file has image width <= 0"));
485 486 487 488
		return NULL;

	}
	if (h <= 0) {
489 490 491 492
                g_set_error_literal (error,
                                     GDK_PIXBUF_ERROR,
                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
                                     _("XPM file has image height <= 0"));
493 494 495
		return NULL;

	}
496
	if (cpp <= 0 || cpp >= 32) {
497 498 499 500
                g_set_error_literal (error,
                                     GDK_PIXBUF_ERROR,
                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
                                     _("XPM has invalid number of chars per pixel"));
501 502
		return NULL;
	}
503 504 505
	if (n_col <= 0 || 
	    n_col >= G_MAXINT / (cpp + 1) || 
	    n_col >= G_MAXINT / sizeof (XPMColor)) {
506 507 508 509
                g_set_error_literal (error,
                                     GDK_PIXBUF_ERROR,
                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
                                     _("XPM file has invalid number of colors"));
510
		return NULL;
511
	}
512

513
	/* The hash is used for fast lookups of color from chars */
514
	color_hash = g_hash_table_new (g_str_hash, g_str_equal);
515

516 517
	name_buf = g_try_malloc (n_col * (cpp + 1));
	if (!name_buf) {
518 519 520 521
		g_set_error_literal (error,
                                     GDK_PIXBUF_ERROR,
                                     GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
                                     _("Cannot allocate memory for loading XPM image"));
522 523 524 525 526
		g_hash_table_destroy (color_hash);
		return NULL;
	}
	colors = (XPMColor *) g_try_malloc (sizeof (XPMColor) * n_col);
	if (!colors) {
527 528 529 530
		g_set_error_literal (error,
                                     GDK_PIXBUF_ERROR,
                                     GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
                                     _("Cannot allocate memory for loading XPM image"));
531 532 533 534
		g_hash_table_destroy (color_hash);
		g_free (name_buf);
		return NULL;
	}
535 536 537 538 539 540

	for (cnt = 0; cnt < n_col; cnt++) {
		gchar *color_name;

		buffer = (*get_buf) (op_cmap, handle);
		if (!buffer) {
541 542 543 544
                        g_set_error_literal (error,
                                             GDK_PIXBUF_ERROR,
                                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
                                             _("Cannot read XPM colormap"));
545 546 547
			g_hash_table_destroy (color_hash);
			g_free (name_buf);
			g_free (colors);
548 549
			return NULL;
		}
550

551 552
		color = &colors[cnt];
		color->color_string = &name_buf[cnt * (cpp + 1)];
553
		strncpy (color->color_string, buffer, cpp);
554
		color->color_string[cpp] = 0;
555
		buffer += strlen (color->color_string);
556 557
		color->transparent = FALSE;

558
		color_name = xpm_extract_color (buffer);
559

560
		if ((color_name == NULL) || (g_ascii_strcasecmp (color_name, "None") == 0)
561
		    || (parse_color (color_name, color) == FALSE)) {
562
			color->transparent = TRUE;
563 564 565
			color->red = 0;
			color->green = 0;
			color->blue = 0;
566 567
			is_trans = TRUE;
		}
568

569 570
		g_free (color_name);
		g_hash_table_insert (color_hash, color->color_string, color);
571

572 573 574
		if (cnt == 0)
			fallbackcolor = color;
	}
575

576
	pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, is_trans, 8, w, h);
577

578
	if (!pixbuf) {
579 580 581 582
                g_set_error_literal (error,
                                     GDK_PIXBUF_ERROR,
                                     GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
                                     _("Cannot allocate memory for loading XPM image"));
583 584 585
		g_hash_table_destroy (color_hash);
		g_free (colors);
		g_free (name_buf);
586 587
		return NULL;
	}
588

589 590 591
	wbytes = w * cpp;

	for (ycnt = 0; ycnt < h; ycnt++) {
592 593
		pixtmp = pixbuf->pixels + ycnt * pixbuf->rowstride;

594
		buffer = (*get_buf) (op_body, handle);
595
		if ((!buffer) || (strlen (buffer) < wbytes))
596
			continue;
597

Matthias Clasen's avatar
Matthias Clasen committed
598
		for (n = 0, xcnt = 0; n < wbytes; n += cpp, xcnt++) {
599
			strncpy (pixel_str, &buffer[n], cpp);
600 601
			pixel_str[cpp] = 0;

602
			color = g_hash_table_lookup (color_hash, pixel_str);
603 604 605 606 607

			/* Bad XPM...punt */
			if (!color)
				color = fallbackcolor;

608 609 610
			*pixtmp++ = color->red >> 8;
			*pixtmp++ = color->green >> 8;
			*pixtmp++ = color->blue >> 8;
611

612
			if (is_trans && color->transparent)
613
				*pixtmp++ = 0;
614
			else if (is_trans)
615 616 617
				*pixtmp++ = 0xFF;
		}
	}
618

619 620 621
	g_hash_table_destroy (color_hash);
	g_free (colors);
	g_free (name_buf);
622

623 624 625
	if (items == 6) {
		gchar hot[10];
		g_snprintf (hot, 10, "%d", x_hot);
626
		gdk_pixbuf_set_option (pixbuf, "x_hot", hot);
627
		g_snprintf (hot, 10, "%d", y_hot);
628
		gdk_pixbuf_set_option (pixbuf, "y_hot", hot);
629 630 631

	}

632
	return pixbuf;
633 634 635
}

/* Shared library entry point for file loading */
636 637 638
static GdkPixbuf *
gdk_pixbuf__xpm_image_load (FILE *f,
                            GError **error)
639
{
640 641
	GdkPixbuf *pixbuf;
	struct file_handle h;
642

643
	memset (&h, 0, sizeof (h));
644
	h.infile = f;
645
	pixbuf = pixbuf_create_from_xpm (file_buffer, &h, error);
646
	g_free (h.buffer);
647

648
	return pixbuf;
649
}
650 651

/* Shared library entry point for memory loading */
652
static GdkPixbuf *
653
gdk_pixbuf__xpm_image_load_xpm_data (const gchar **data)
654 655 656
{
        GdkPixbuf *pixbuf;
        struct mem_handle h;
657 658
        GError *error = NULL;
        
659 660 661
        h.data = data;
        h.offset = 0;
        
662 663 664 665 666 667 668
	pixbuf = pixbuf_create_from_xpm (mem_buffer, &h, &error);

        if (error) {
                g_warning ("Inline XPM data is broken: %s", error->message);
                g_error_free (error);
                error = NULL;
        }
669 670 671
        
	return pixbuf;
}
Havoc Pennington's avatar
Havoc Pennington committed
672 673 674 675 676

/* Progressive loader */
typedef struct _XPMContext XPMContext;
struct _XPMContext
{
677 678
       GdkPixbufModulePreparedFunc prepare_func;
       GdkPixbufModuleUpdatedFunc update_func;
Havoc Pennington's avatar
Havoc Pennington committed
679 680 681 682 683 684 685 686 687 688
       gpointer user_data;

       gchar *tempname;
       FILE *file;
       gboolean all_okay;
};

/*
 * FIXME xpm loading progressively is not properly implemented.
 * Instead we will buffer to a file then load that file when done.
Matthias Clasen's avatar
Matthias Clasen committed
689
 * This is very broken but it should be relatively simple to fix
Havoc Pennington's avatar
Havoc Pennington committed
690 691
 * in the future.
 */
692
static gpointer
693 694 695
gdk_pixbuf__xpm_image_begin_load (GdkPixbufModuleSizeFunc size_func,
                                  GdkPixbufModulePreparedFunc prepare_func,
                                  GdkPixbufModuleUpdatedFunc update_func,
Havoc Pennington's avatar
Havoc Pennington committed
696 697
                                  gpointer user_data,
                                  GError **error)
Havoc Pennington's avatar
Havoc Pennington committed
698 699 700 701 702 703 704 705 706
{
       XPMContext *context;
       gint fd;

       context = g_new (XPMContext, 1);
       context->prepare_func = prepare_func;
       context->update_func = update_func;
       context->user_data = user_data;
       context->all_okay = TRUE;
707 708
       fd = g_file_open_tmp ("gdkpixbuf-xpm-tmp.XXXXXX", &context->tempname,
			     NULL);
Havoc Pennington's avatar
Havoc Pennington committed
709 710 711 712 713 714 715 716 717 718 719 720 721 722 723
       if (fd < 0) {
               g_free (context);
               return NULL;
       }

       context->file = fdopen (fd, "w+");
       if (context->file == NULL) {
               g_free (context->tempname);
               g_free (context);
               return NULL;
       }

       return context;
}

724 725 726
static gboolean
gdk_pixbuf__xpm_image_stop_load (gpointer data,
                                 GError **error)
Havoc Pennington's avatar
Havoc Pennington committed
727 728 729
{
       XPMContext *context = (XPMContext*) data;
       GdkPixbuf *pixbuf;
730 731 732
       gboolean retval = FALSE;
       
       g_return_val_if_fail (data != NULL, FALSE);
Havoc Pennington's avatar
Havoc Pennington committed
733 734 735 736

       fflush (context->file);
       rewind (context->file);
       if (context->all_okay) {
737 738 739
               pixbuf = gdk_pixbuf__xpm_image_load (context->file, error);

               if (pixbuf != NULL) {
740 741 742 743 744 745
		       if (context->prepare_func)
			       (* context->prepare_func) (pixbuf,
							  NULL,
							  context->user_data);
		       if (context->update_func)
			       (* context->update_func) (pixbuf, 0, 0, pixbuf->width, pixbuf->height, context->user_data);
746
                       g_object_unref (pixbuf);
Havoc Pennington's avatar
Havoc Pennington committed
747

748 749
                       retval = TRUE;
               }
Havoc Pennington's avatar
Havoc Pennington committed
750 751 752
       }

       fclose (context->file);
753
       g_unlink (context->tempname);
Havoc Pennington's avatar
Havoc Pennington committed
754 755
       g_free (context->tempname);
       g_free ((XPMContext *) context);
756 757

       return retval;
Havoc Pennington's avatar
Havoc Pennington committed
758 759
}

760 761 762 763 764
static gboolean
gdk_pixbuf__xpm_image_load_increment (gpointer data,
                                      const guchar *buf,
                                      guint    size,
                                      GError **error)
Havoc Pennington's avatar
Havoc Pennington committed
765 766 767 768 769 770
{
       XPMContext *context = (XPMContext *) data;

       g_return_val_if_fail (data != NULL, FALSE);

       if (fwrite (buf, sizeof (guchar), size, context->file) != size) {
771
	       gint save_errno = errno;
Havoc Pennington's avatar
Havoc Pennington committed
772
               context->all_okay = FALSE;
773 774 775 776
               g_set_error_literal (error,
                                    G_FILE_ERROR,
                                    g_file_error_from_errno (save_errno),
                                    _("Failed to write to temporary file when loading XPM image"));
Havoc Pennington's avatar
Havoc Pennington committed
777 778 779 780 781
               return FALSE;
       }

       return TRUE;
}
782

783
#ifndef INCLUDE_xpm
784
#define MODULE_ENTRY(function) G_MODULE_EXPORT void function
785
#else
786
#define MODULE_ENTRY(function) void _gdk_pixbuf__xpm_ ## function
787 788
#endif

789
MODULE_ENTRY (fill_vtable) (GdkPixbufModule *module)
790
{
791 792 793 794 795 796 797
	module->load = gdk_pixbuf__xpm_image_load;
	module->load_xpm_data = gdk_pixbuf__xpm_image_load_xpm_data;
	module->begin_load = gdk_pixbuf__xpm_image_begin_load;
	module->stop_load = gdk_pixbuf__xpm_image_stop_load;
	module->load_increment = gdk_pixbuf__xpm_image_load_increment;
}

798
MODULE_ENTRY (fill_info) (GdkPixbufFormat *info)
799
{
800
	static const GdkPixbufModulePattern signature[] = {
801 802 803
		{ "/* XPM */", NULL, 100 },
		{ NULL, NULL, 0 }
	};
804
	static const gchar *mime_types[] = {
805 806 807
		"image/x-xpixmap",
		NULL
	};
808
	static const gchar *extensions[] = {
809 810 811 812 813
		"xpm",
		NULL
	};

	info->name = "xpm";
814
	info->signature = (GdkPixbufModulePattern *) signature;
815
	info->description = N_("The XPM image format");
816 817
	info->mime_types = (gchar **) mime_types;
	info->extensions = (gchar **) extensions;
818
	info->flags = GDK_PIXBUF_FORMAT_THREADSAFE;
819
	info->license = "LGPL";
820
}