Coverage Report

Created: 2026-02-14 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdk-pixbuf/gdk-pixbuf/io-xpm.c
Line
Count
Source
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
138
compare_xcolor_entries (const void *a, const void *b)
139
15.6k
{
140
15.6k
  return g_ascii_strcasecmp ((const char *) a, 
141
15.6k
           color_names + ((const XPMColorEntry *)b)->name_offset);
142
15.6k
}
143
144
static gboolean
145
find_color(const char *name,
146
     XPMColor   *colorPtr)
147
1.57k
{
148
1.57k
  XPMColorEntry *found;
149
150
1.57k
  found = bsearch (name, xColors, G_N_ELEMENTS (xColors), sizeof (XPMColorEntry),
151
1.57k
       compare_xcolor_entries);
152
1.57k
  if (found == NULL)
153
1.57k
    return FALSE;
154
  
155
0
  colorPtr->red = (found->red * 65535) / 255;
156
0
  colorPtr->green = (found->green * 65535) / 255;
157
0
  colorPtr->blue = (found->blue * 65535) / 255;
158
  
159
0
  return TRUE;
160
1.57k
}
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
179
parse_color (const char *spec,
180
       XPMColor   *colorPtr)
181
8.89k
{
182
8.89k
  if (spec[0] == '#') {
183
7.32k
    int i, red, green, blue;
184
185
7.32k
    if ((i = strlen (spec + 1)) % 3) {
186
2.18k
      return FALSE;
187
2.18k
    }
188
5.14k
    i /= 3;
189
190
5.14k
    if (i == 4) {
191
469
      if (sscanf (spec + 1, "%4x%4x%4x", &red, &green, &blue) != 3)
192
345
        return FALSE;
193
124
      colorPtr->red = red;
194
124
      colorPtr->green = green;
195
124
      colorPtr->blue = blue;
196
4.67k
    } else if (i == 1) {
197
914
      if (sscanf (spec + 1, "%1x%1x%1x", &red, &green, &blue) != 3)
198
669
        return FALSE;
199
245
      colorPtr->red = (red * 65535) / 15;
200
245
      colorPtr->green = (green * 65535) / 15;
201
245
      colorPtr->blue = (blue * 65535) / 15;
202
3.76k
    } else if (i == 2) {
203
2.87k
      if (sscanf (spec + 1, "%2x%2x%2x", &red, &green, &blue) != 3)
204
472
        return FALSE;
205
2.40k
      colorPtr->red = (red * 65535) / 255;
206
2.40k
      colorPtr->green = (green * 65535) / 255;
207
2.40k
      colorPtr->blue = (blue * 65535) / 255;
208
2.40k
    } else /* if (i == 3) */ {
209
882
      if (sscanf (spec + 1, "%3x%3x%3x", &red, &green, &blue) != 3)
210
589
        return FALSE;
211
293
      colorPtr->red = (red * 65535) / 4095;
212
293
      colorPtr->green = (green * 65535) / 4095;
213
293
      colorPtr->blue = (blue * 65535) / 4095;
214
293
    }
215
5.14k
  } else {
216
1.57k
    if (!find_color(spec, colorPtr))
217
1.57k
      return FALSE;
218
1.57k
  }
219
3.06k
  return TRUE;
220
8.89k
}
221
222
static gint
223
xpm_seek_string (FILE *infile, const gchar *str)
224
89
{
225
89
  char instr[1024];
226
227
328
  while (!feof (infile)) {
228
328
    if (fscanf (infile, "%1023s", instr) < 0)
229
1
                        return FALSE;
230
327
    if (strcmp (instr, str) == 0)
231
88
      return TRUE;
232
327
  }
233
234
0
  return FALSE;
235
89
}
236
237
static gint
238
xpm_seek_char (FILE *infile, gchar c)
239
23.4k
{
240
23.4k
  gint b, oldb;
241
242
575k
  while ((b = getc (infile)) != EOF) {
243
574k
    if (c != b && b == '/') {
244
2.53k
      b = getc (infile);
245
2.53k
      if (b == EOF)
246
1
        return FALSE;
247
248
2.53k
      else if (b == '*') { /* we have a comment */
249
513
        b = -1;
250
93.1k
        do {
251
93.1k
          oldb = b;
252
93.1k
          b = getc (infile);
253
93.1k
          if (b == EOF)
254
3
            return FALSE;
255
93.1k
        } while (!(oldb == '*' && b == '/'));
256
513
      }
257
572k
    } else if (c == b)
258
23.4k
      return TRUE;
259
574k
  }
260
261
32
  return FALSE;
262
23.4k
}
263
264
static gint
265
xpm_read_string (FILE *infile, gchar **buffer, guint *buffer_size)
266
24.0k
{
267
24.0k
  gint c;
268
24.0k
  guint cnt = 0, bufsiz, ret = FALSE;
269
24.0k
  gchar *buf;
270
271
24.0k
  buf = *buffer;
272
24.0k
  bufsiz = *buffer_size;
273
24.0k
  if (buf == NULL) {
274
85
    bufsiz = 10 * sizeof (gchar);
275
85
    buf = g_new (gchar, bufsiz);
276
85
  }
277
278
40.2k
  do {
279
40.2k
    c = getc (infile);
280
40.2k
  } while (c != EOF && c != '"');
281
282
24.0k
  if (c != '"')
283
37
    goto out;
284
285
3.74M
  while ((c = getc (infile)) != EOF) {
286
3.74M
    if (cnt == bufsiz) {
287
473
      guint new_size = bufsiz * 2;
288
289
473
      if (new_size > bufsiz)
290
473
        bufsiz = new_size;
291
0
      else
292
0
        goto out;
293
294
473
      buf = g_realloc (buf, bufsiz);
295
473
      buf[bufsiz - 1] = '\0';
296
473
    }
297
298
3.74M
    if (c != '"')
299
3.72M
      buf[cnt++] = c;
300
23.9k
    else {
301
23.9k
      buf[cnt] = 0;
302
23.9k
      ret = TRUE;
303
23.9k
      break;
304
23.9k
    }
305
3.74M
  }
306
307
24.0k
 out:
308
24.0k
  buf[bufsiz - 1] = '\0'; /* ensure null termination for errors */
309
24.0k
  *buffer = buf;
310
24.0k
  *buffer_size = bufsiz;
311
24.0k
  return ret;
312
23.9k
}
313
314
static gchar *
315
xpm_extract_color (const gchar *buffer)
316
23.2k
{
317
23.2k
  const gchar *p = &buffer[0];
318
23.2k
  gint new_key = 0;
319
23.2k
  gint key = 0;
320
23.2k
  gint current_key = 1;
321
23.2k
  gint space = 128;
322
23.2k
  gchar word[129], color[129], current_color[129];
323
23.2k
  gchar *r; 
324
  
325
23.2k
  word[0] = '\0';
326
23.2k
  color[0] = '\0';
327
23.2k
  current_color[0] = '\0';
328
49.5k
        while (1) {
329
    /* skip whitespace */
330
80.8k
    for (; *p != '\0' && g_ascii_isspace (*p); p++) {
331
31.2k
    } 
332
    /* copy word */
333
274k
    for (r = word; *p != '\0' && !g_ascii_isspace (*p) && r - word < sizeof (word) - 1; p++, r++) {
334
224k
      *r = *p;
335
224k
    }
336
49.5k
    *r = '\0';
337
49.5k
    if (*word == '\0') {
338
17.9k
      if (color[0] == '\0')  /* incomplete colormap entry */
339
8.34k
        return NULL;        
340
9.65k
      else  /* end of entry, still store the last color */
341
9.65k
        new_key = 1;
342
17.9k
    } 
343
31.5k
    else if (key > 0 && color[0] == '\0')  /* next word must be a color name part */
344
10.4k
      new_key = 0;
345
21.0k
    else {
346
21.0k
      if (strcmp (word, "c") == 0)
347
9.38k
        new_key = 5;
348
11.6k
      else if (strcmp (word, "g") == 0)
349
832
        new_key = 4;
350
10.8k
      else if (strcmp (word, "g4") == 0)
351
0
        new_key = 3;
352
10.8k
      else if (strcmp (word, "m") == 0)
353
830
        new_key = 2;
354
10.0k
      else if (strcmp (word, "s") == 0)
355
0
        new_key = 1;
356
10.0k
      else 
357
10.0k
        new_key = 0;
358
21.0k
    }
359
41.2k
    if (new_key == 0) {  /* word is a color name part */
360
20.5k
      if (key == 0)  /* key expected */
361
5.16k
        return NULL;
362
      /* accumulate color name */
363
15.3k
      if (color[0] != '\0') {
364
4.87k
        strncat (color, " ", space);
365
4.87k
        space -= MIN (space, 1);
366
4.87k
      }
367
15.3k
      strncat (color, word, space);
368
15.3k
      space -= MIN (space, strlen (word));
369
15.3k
    }
370
20.7k
    else {  /* word is a key */
371
20.7k
      if (key > current_key) {
372
9.66k
        current_key = key;
373
9.66k
        strcpy (current_color, color);
374
9.66k
      }
375
20.7k
      space = 128;
376
20.7k
      color[0] = '\0';
377
20.7k
      key = new_key;
378
20.7k
      if (*p == '\0') break;
379
20.7k
    }
380
    
381
41.2k
  }
382
9.71k
  if (current_key > 1)
383
9.65k
    return g_strdup (current_color);
384
57
  else
385
57
    return NULL; 
386
9.71k
}
387
388
/* (almost) direct copy from gdkpixmap.c... loads an XPM from a file */
389
390
static const gchar *
391
file_buffer (enum buf_op op, gpointer handle)
392
24.0k
{
393
24.0k
  struct file_handle *h = handle;
394
395
24.0k
  switch (op) {
396
89
  case op_header:
397
89
    if (xpm_seek_string (h->infile, "XPM") != TRUE)
398
1
      break;
399
400
88
    if (xpm_seek_char (h->infile, '{') != TRUE)
401
3
      break;
402
    /* Fall through to the next xpm_seek_char. */
403
404
23.3k
  case op_cmap:
405
23.3k
    xpm_seek_char (h->infile, '"');
406
23.3k
    if (fseek (h->infile, -1, SEEK_CUR) != 0)
407
0
      return NULL;
408
    /* Fall through to the xpm_read_string. */
409
410
24.0k
  case op_body:
411
24.0k
    if(!xpm_read_string (h->infile, &h->buffer, &h->buffer_size))
412
74
      return NULL;
413
23.9k
    return h->buffer;
414
415
0
  default:
416
0
    g_assert_not_reached ();
417
24.0k
  }
418
419
4
  return NULL;
420
24.0k
}
421
422
/* This reads from memory */
423
static const gchar *
424
mem_buffer (enum buf_op op, gpointer handle)
425
0
{
426
0
  struct mem_handle *h = handle;
427
0
  switch (op) {
428
0
  case op_header:
429
0
  case op_cmap:
430
0
  case op_body:
431
0
                if (h->data[h->offset]) {
432
0
                        const gchar* retval;
433
434
0
                        retval = h->data[h->offset];
435
0
                        h->offset += 1;
436
0
                        return retval;
437
0
                }
438
0
                break;
439
440
0
  default:
441
0
    g_assert_not_reached ();
442
0
                break;
443
0
  }
444
445
0
  return NULL;
446
0
}
447
448
/* This function does all the work. */
449
static GdkPixbuf *
450
pixbuf_create_from_xpm (const gchar * (*get_buf) (enum buf_op op, gpointer handle), gpointer handle,
451
                        GError **error)
452
89
{
453
89
  gint w, h, n_col, cpp, x_hot, y_hot, items;
454
89
  gint cnt, xcnt, ycnt, wbytes, n;
455
89
  gint is_trans = FALSE;
456
89
  const gchar *buffer;
457
89
        gchar *name_buf;
458
89
  gchar pixel_str[32];
459
89
  GHashTable *color_hash;
460
89
  XPMColor *colors, *color, *fallbackcolor;
461
89
  guchar *pixtmp;
462
89
  GdkPixbuf *pixbuf = NULL;
463
89
  gint rowstride;
464
465
89
  fallbackcolor = NULL;
466
467
89
  buffer = (*get_buf) (op_header, handle);
468
89
  if (!buffer) {
469
5
                g_set_error_literal (error,
470
5
                                     GDK_PIXBUF_ERROR,
471
5
                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
472
5
                                     _("No XPM header found"));
473
5
    return NULL;
474
5
  }
475
84
  items = sscanf (buffer, "%d %d %d %d %d %d", &w, &h, &n_col, &cpp, &x_hot, &y_hot);
476
477
84
  if (items != 4 && items != 6) {
478
2
    g_set_error_literal (error,
479
2
                                     GDK_PIXBUF_ERROR,
480
2
                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
481
2
                                     _("Invalid XPM header"));
482
2
    return NULL;
483
2
  }
484
485
82
  if (w <= 0) {
486
0
                g_set_error_literal (error,
487
0
                                     GDK_PIXBUF_ERROR,
488
0
                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
489
0
                                     _("XPM file has image width <= 0"));
490
0
    return NULL;
491
492
0
  }
493
82
  if (h <= 0) {
494
0
                g_set_error_literal (error,
495
0
                                     GDK_PIXBUF_ERROR,
496
0
                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
497
0
                                     _("XPM file has image height <= 0"));
498
0
    return NULL;
499
500
0
  }
501
  /* Check from libXpm's ParsePixels() */
502
82
  if ((h > 0 && w >= UINT_MAX / h) ||
503
82
      w * h >= UINT_MAX / sizeof(unsigned int)) {
504
0
    g_set_error_literal (error,
505
0
                                     GDK_PIXBUF_ERROR,
506
0
                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
507
0
                                     _("Invalid XPM header"));
508
0
    return NULL;
509
0
  }
510
82
  if (cpp <= 0 || cpp >= 32 || w >= G_MAXINT / cpp) {
511
0
                g_set_error_literal (error,
512
0
                                     GDK_PIXBUF_ERROR,
513
0
                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
514
0
                                     _("XPM has invalid number of chars per pixel"));
515
0
    return NULL;
516
0
  }
517
82
  if (n_col <= 0 || 
518
82
      n_col >= G_MAXINT / (cpp + 1) || 
519
82
      n_col >= G_MAXINT / sizeof (XPMColor)) {
520
0
                g_set_error_literal (error,
521
0
                                     GDK_PIXBUF_ERROR,
522
0
                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
523
0
                                     _("XPM file has invalid number of colors"));
524
0
    return NULL;
525
0
  }
526
527
  /* The hash is used for fast lookups of color from chars */
528
82
  color_hash = g_hash_table_new (g_str_hash, g_str_equal);
529
530
82
  name_buf = g_try_malloc (n_col * (cpp + 1));
531
82
  if (!name_buf) {
532
0
    g_set_error_literal (error,
533
0
                                     GDK_PIXBUF_ERROR,
534
0
                                     GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
535
0
                                     _("Cannot allocate memory for loading XPM image"));
536
0
    g_hash_table_destroy (color_hash);
537
0
    return NULL;
538
0
  }
539
82
  colors = (XPMColor *) g_try_malloc (sizeof (XPMColor) * n_col);
540
82
  if (!colors) {
541
0
    g_set_error_literal (error,
542
0
                                     GDK_PIXBUF_ERROR,
543
0
                                     GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
544
0
                                     _("Cannot allocate memory for loading XPM image"));
545
0
    g_hash_table_destroy (color_hash);
546
0
    g_free (name_buf);
547
0
    return NULL;
548
0
  }
549
550
23.2k
  for (cnt = 0; cnt < n_col; cnt++) {
551
23.2k
    gchar *color_name;
552
553
23.2k
    buffer = (*get_buf) (op_cmap, handle);
554
23.2k
    if (!buffer) {
555
67
                        g_set_error_literal (error,
556
67
                                             GDK_PIXBUF_ERROR,
557
67
                                             GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
558
67
                                             _("Cannot read XPM colormap"));
559
67
                        goto out;
560
67
    }
561
562
23.2k
    color = &colors[cnt];
563
23.2k
    color->color_string = &name_buf[cnt * (cpp + 1)];
564
23.2k
    strncpy (color->color_string, buffer, cpp);
565
23.2k
    color->color_string[cpp] = 0;
566
23.2k
    buffer += strlen (color->color_string);
567
23.2k
    color->transparent = FALSE;
568
569
23.2k
    color_name = xpm_extract_color (buffer);
570
571
23.2k
    if ((color_name == NULL) || (g_ascii_strcasecmp (color_name, "None") == 0)
572
20.1k
        || (parse_color (color_name, color) == FALSE)) {
573
20.1k
      color->transparent = TRUE;
574
20.1k
      color->red = 0;
575
20.1k
      color->green = 0;
576
20.1k
      color->blue = 0;
577
20.1k
      is_trans = TRUE;
578
20.1k
    }
579
580
23.2k
    g_free (color_name);
581
23.2k
    g_hash_table_insert (color_hash, color->color_string, color);
582
583
23.2k
    if (cnt == 0)
584
82
      fallbackcolor = color;
585
23.2k
  }
586
587
15
  pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, is_trans, 8, w, h);
588
589
15
  if (!pixbuf) {
590
0
                g_set_error_literal (error,
591
0
                                     GDK_PIXBUF_ERROR,
592
0
                                     GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
593
0
                                     _("Cannot allocate memory for loading XPM image"));
594
0
                goto out;
595
0
  }
596
597
15
  rowstride = gdk_pixbuf_get_rowstride (pixbuf);
598
599
15
  wbytes = w * cpp;
600
601
640
  for (ycnt = 0; ycnt < h; ycnt++) {
602
639
    pixtmp = gdk_pixbuf_get_pixels (pixbuf) + ycnt * rowstride;
603
604
639
    buffer = (*get_buf) (op_body, handle);
605
639
    if ((!buffer) || (strlen (buffer) < wbytes)) {
606
      /* Advertised width doesn't match pixels */
607
14
      g_set_error_literal (error,
608
14
               GDK_PIXBUF_ERROR,
609
14
               GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
610
14
               _("Dimensions do not match data"));
611
14
      goto out;
612
14
    }
613
614
2.42k
    for (n = 0, xcnt = 0; n < wbytes; n += cpp, xcnt++) {
615
1.80k
      strncpy (pixel_str, &buffer[n], cpp);
616
1.80k
      pixel_str[cpp] = 0;
617
618
1.80k
      color = g_hash_table_lookup (color_hash, pixel_str);
619
620
      /* Bad XPM...punt */
621
1.80k
      if (!color)
622
1.23k
        color = fallbackcolor;
623
624
1.80k
      *pixtmp++ = color->red >> 8;
625
1.80k
      *pixtmp++ = color->green >> 8;
626
1.80k
      *pixtmp++ = color->blue >> 8;
627
628
1.80k
      if (is_trans && color->transparent)
629
1.77k
        *pixtmp++ = 0;
630
26
      else if (is_trans)
631
0
        *pixtmp++ = 0xFF;
632
1.80k
    }
633
625
  }
634
635
1
  g_hash_table_destroy (color_hash);
636
1
  g_free (colors);
637
1
  g_free (name_buf);
638
639
1
  if (items == 6) {
640
1
    gchar hot[10];
641
1
    g_snprintf (hot, 10, "%d", x_hot);
642
1
    gdk_pixbuf_set_option (pixbuf, "x_hot", hot);
643
1
    g_snprintf (hot, 10, "%d", y_hot);
644
1
    gdk_pixbuf_set_option (pixbuf, "y_hot", hot);
645
646
1
  }
647
648
1
  return pixbuf;
649
650
81
out:
651
81
  g_hash_table_destroy (color_hash);
652
81
  g_free (colors);
653
81
  g_free (name_buf);
654
655
81
  g_clear_object (&pixbuf);
656
81
  return NULL;
657
15
}
658
659
/* Shared library entry point for file loading */
660
static GdkPixbuf *
661
gdk_pixbuf__xpm_image_load (FILE *f,
662
                            GError **error)
663
89
{
664
89
  GdkPixbuf *pixbuf;
665
89
  struct file_handle h;
666
667
89
  memset (&h, 0, sizeof (h));
668
89
  h.infile = f;
669
89
  pixbuf = pixbuf_create_from_xpm (file_buffer, &h, error);
670
89
  g_free (h.buffer);
671
672
89
  return pixbuf;
673
89
}
674
675
/* Shared library entry point for memory loading */
676
static GdkPixbuf *
677
gdk_pixbuf__xpm_image_load_xpm_data (const gchar **data)
678
0
{
679
0
        GdkPixbuf *pixbuf;
680
0
        struct mem_handle h;
681
0
        GError *error = NULL;
682
        
683
0
        h.data = data;
684
0
        h.offset = 0;
685
        
686
0
  pixbuf = pixbuf_create_from_xpm (mem_buffer, &h, &error);
687
688
0
        if (error) {
689
0
                g_warning ("Inline XPM data is broken: %s", error->message);
690
0
                g_error_free (error);
691
0
                error = NULL;
692
0
        }
693
        
694
0
  return pixbuf;
695
0
}
696
697
/* Progressive loader */
698
typedef struct _XPMContext XPMContext;
699
struct _XPMContext
700
{
701
       GdkPixbufModulePreparedFunc prepared_func;
702
       GdkPixbufModuleUpdatedFunc updated_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
717
gdk_pixbuf__xpm_image_begin_load (GdkPixbufModuleSizeFunc size_func,
718
                                  GdkPixbufModulePreparedFunc prepared_func,
719
                                  GdkPixbufModuleUpdatedFunc updated_func,
720
                                  gpointer user_data,
721
                                  GError **error)
722
89
{
723
89
       XPMContext *context;
724
89
       gint fd;
725
726
89
       g_assert (size_func != NULL);
727
89
       g_assert (prepared_func != NULL);
728
89
       g_assert (updated_func != NULL);
729
730
89
       context = g_new (XPMContext, 1);
731
89
       context->prepared_func = prepared_func;
732
89
       context->updated_func = updated_func;
733
89
       context->user_data = user_data;
734
89
       context->all_okay = TRUE;
735
89
       fd = g_file_open_tmp ("gdkpixbuf-xpm-tmp.XXXXXX", &context->tempname,
736
89
           NULL);
737
89
       if (fd < 0) {
738
0
               g_free (context);
739
0
               return NULL;
740
0
       }
741
742
89
       context->file = fdopen (fd, "w+");
743
89
       if (context->file == NULL) {
744
0
               g_free (context->tempname);
745
0
               g_free (context);
746
0
               return NULL;
747
0
       }
748
749
89
       return context;
750
89
}
751
752
static gboolean
753
gdk_pixbuf__xpm_image_stop_load (gpointer data,
754
                                 GError **error)
755
89
{
756
89
       XPMContext *context = (XPMContext*) data;
757
89
       GdkPixbuf *pixbuf;
758
89
       gboolean retval = FALSE;
759
       
760
89
       g_return_val_if_fail (data != NULL, FALSE);
761
762
89
       fflush (context->file);
763
89
       rewind (context->file);
764
89
       if (context->all_okay) {
765
89
               pixbuf = gdk_pixbuf__xpm_image_load (context->file, error);
766
767
89
               if (pixbuf != NULL) {
768
1
           (* context->prepared_func) (pixbuf,
769
1
               NULL,
770
1
               context->user_data);
771
1
           (* context->updated_func) (pixbuf,
772
1
              0, 0,
773
1
              gdk_pixbuf_get_width (pixbuf),
774
1
              gdk_pixbuf_get_height (pixbuf),
775
1
              context->user_data);
776
1
                       g_object_unref (pixbuf);
777
778
1
                       retval = TRUE;
779
1
               }
780
89
       }
781
782
89
       fclose (context->file);
783
89
       g_unlink (context->tempname);
784
89
       g_free (context->tempname);
785
89
       g_free ((XPMContext *) context);
786
787
89
       return retval;
788
89
}
789
790
static gboolean
791
gdk_pixbuf__xpm_image_load_increment (gpointer data,
792
                                      const guchar *buf,
793
                                      guint    size,
794
                                      GError **error)
795
302
{
796
302
       XPMContext *context = (XPMContext *) data;
797
798
302
       g_return_val_if_fail (data != NULL, FALSE);
799
800
302
       if (fwrite (buf, sizeof (guchar), size, context->file) != size) {
801
0
         gint save_errno = errno;
802
0
               context->all_okay = FALSE;
803
0
               g_set_error_literal (error,
804
0
                                    G_FILE_ERROR,
805
0
                                    g_file_error_from_errno (save_errno),
806
0
                                    _("Failed to write to temporary file when loading XPM image"));
807
0
               return FALSE;
808
0
       }
809
810
302
       return TRUE;
811
302
}
812
813
#ifndef INCLUDE_xpm
814
#define MODULE_ENTRY(function) G_MODULE_EXPORT void function
815
#else
816
#define MODULE_ENTRY(function) void _gdk_pixbuf__xpm_ ## function
817
#endif
818
819
MODULE_ENTRY (fill_vtable) (GdkPixbufModule *module)
820
3
{
821
3
  module->load = gdk_pixbuf__xpm_image_load;
822
3
  module->load_xpm_data = gdk_pixbuf__xpm_image_load_xpm_data;
823
3
  module->begin_load = gdk_pixbuf__xpm_image_begin_load;
824
3
  module->stop_load = gdk_pixbuf__xpm_image_stop_load;
825
3
  module->load_increment = gdk_pixbuf__xpm_image_load_increment;
826
3
}
827
828
MODULE_ENTRY (fill_info) (GdkPixbufFormat *info)
829
3
{
830
3
  static const GdkPixbufModulePattern signature[] = {
831
3
    { "/* XPM */", NULL, 100 },
832
3
    { NULL, NULL, 0 }
833
3
  };
834
3
  static const gchar *mime_types[] = {
835
3
    "image/x-xpixmap",
836
3
    NULL
837
3
  };
838
3
  static const gchar *extensions[] = {
839
3
    "xpm",
840
3
    NULL
841
3
  };
842
843
3
  info->name = "xpm";
844
3
  info->signature = (GdkPixbufModulePattern *) signature;
845
3
  info->description = NC_("image format", "XPM");
846
3
  info->mime_types = (gchar **) mime_types;
847
3
  info->extensions = (gchar **) extensions;
848
3
  info->flags = GDK_PIXBUF_FORMAT_THREADSAFE;
849
3
  info->license = "LGPL";
850
3
}