WINGs: fix possible NULL pointer dereference (Coverity #50197)
[wmaker-crm.git] / wrlib / load.c
blob7c2e6af776023f6eaaa5b7ccb3366c9315815f49
1 /* load.c - load image from file
3 * Raster graphics library
5 * Copyright (c) 1997-2003 Alfredo K. Kojima
6 * Copyright (c) 2014 Window Maker Team
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the Free
20 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
21 * MA 02110-1301, USA.
24 #include <config.h>
26 #include <errno.h>
27 #include <X11/Xlib.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <unistd.h>
31 #include <fcntl.h>
32 #include <sys/stat.h>
33 #include <string.h>
34 #include <time.h>
35 #include <assert.h>
37 #include "wraster.h"
38 #include "imgformat.h"
40 #define RETRY( x ) do { \
41 x; \
42 } while (errno == EINTR);
44 typedef struct RCachedImage {
45 RImage *image;
46 char *file;
47 time_t last_modif; /* last time file was modified */
48 time_t last_use; /* last time image was used */
49 } RCachedImage;
52 * Size of image cache
54 static int RImageCacheSize = -1;
57 * Max. size of image to store in cache
59 static int RImageCacheMaxImage = -1; /* 0 = any size */
61 #define IMAGE_CACHE_SIZE 8
63 #define IMAGE_CACHE_MAX_IMAGE 64*64
65 static RCachedImage *RImageCache;
68 static WRImgFormat identFile(const char *path);
71 char **RSupportedFileFormats(void)
73 static char *tmp[IM_TYPES + 2];
74 int i = 0;
76 /* built-in */
77 tmp[i++] = "XPM";
78 /* built-in PNM here refers to anymap format: PPM, PGM, PBM */
79 tmp[i++] = "PNM";
81 * PPM is a just a sub-type of PNM, but it has to be in the list
82 * for compatibility with legacy programs that may expect it but
83 * not the new PNM type
85 tmp[i++] = "PPM";
86 #ifdef USE_TIFF
87 tmp[i++] = "TIFF";
88 #endif
89 #ifdef USE_PNG
90 tmp[i++] = "PNG";
91 #endif
92 #ifdef USE_JPEG
93 tmp[i++] = "JPEG";
94 #endif
95 #ifdef USE_GIF
96 tmp[i++] = "GIF";
97 #endif
98 #ifdef USE_WEBP
99 tmp[i++] = "WEBP";
100 #endif
101 tmp[i] = NULL;
103 return tmp;
106 static void init_cache(void)
108 char *tmp;
110 tmp = getenv("RIMAGE_CACHE");
111 if (!tmp || sscanf(tmp, "%i", &RImageCacheSize) != 1) {
112 RImageCacheSize = IMAGE_CACHE_SIZE;
114 if (RImageCacheSize < 0)
115 RImageCacheSize = 0;
117 tmp = getenv("RIMAGE_CACHE_SIZE");
118 if (!tmp || sscanf(tmp, "%i", &RImageCacheMaxImage) != 1) {
119 RImageCacheMaxImage = IMAGE_CACHE_MAX_IMAGE;
122 if (RImageCacheSize > 0) {
123 RImageCache = malloc(sizeof(RCachedImage) * RImageCacheSize);
124 if (RImageCache == NULL) {
125 printf("wrlib: out of memory for image cache\n");
126 return;
128 memset(RImageCache, 0, sizeof(RCachedImage) * RImageCacheSize);
132 void RReleaseCache(void)
134 int i;
136 if (RImageCacheSize > 0) {
137 for (i = 0; i < RImageCacheSize; i++) {
138 if (RImageCache[i].file) {
139 RReleaseImage(RImageCache[i].image);
140 free(RImageCache[i].file);
143 free(RImageCache);
144 RImageCache = NULL;
145 RImageCacheSize = -1;
149 RImage *RLoadImage(RContext * context, const char *file, int index)
151 RImage *image = NULL;
152 int i;
153 struct stat st;
155 assert(file != NULL);
157 if (RImageCacheSize < 0) {
158 init_cache();
161 if (RImageCacheSize > 0) {
163 for (i = 0; i < RImageCacheSize; i++) {
164 if (RImageCache[i].file && strcmp(file, RImageCache[i].file) == 0) {
166 if (stat(file, &st) == 0 && st.st_mtime == RImageCache[i].last_modif) {
167 RImageCache[i].last_use = time(NULL);
169 return RCloneImage(RImageCache[i].image);
171 } else {
172 free(RImageCache[i].file);
173 RImageCache[i].file = NULL;
174 RReleaseImage(RImageCache[i].image);
180 switch (identFile(file)) {
181 case IM_ERROR:
182 return NULL;
184 case IM_UNKNOWN:
185 #ifdef USE_MAGICK
186 /* generic file format support using ImageMagick
187 * BMP, PCX, PICT, SVG, ...
189 image = RLoadMagick(file);
190 break;
191 #else
192 RErrorCode = RERR_BADFORMAT;
193 return NULL;
194 #endif
196 case IM_XPM:
197 image = RLoadXPM(context, file);
198 break;
200 #ifdef USE_TIFF
201 case IM_TIFF:
202 image = RLoadTIFF(file, index);
203 break;
204 #endif /* USE_TIFF */
206 #ifdef USE_PNG
207 case IM_PNG:
208 image = RLoadPNG(context, file);
209 break;
210 #endif /* USE_PNG */
212 #ifdef USE_JPEG
213 case IM_JPEG:
214 image = RLoadJPEG(file);
215 break;
216 #endif /* USE_JPEG */
218 #ifdef USE_GIF
219 case IM_GIF:
220 image = RLoadGIF(file, index);
221 break;
222 #endif /* USE_GIF */
224 #ifdef USE_WEBP
225 case IM_WEBP:
226 image = RLoadWEBP(file);
227 break;
228 #endif /* USE_WEBP */
230 case IM_PPM:
231 image = RLoadPPM(file);
232 break;
234 default:
235 RErrorCode = RERR_BADFORMAT;
236 return NULL;
239 /* store image in cache */
240 if (RImageCacheSize > 0 && image &&
241 (RImageCacheMaxImage == 0 || RImageCacheMaxImage >= image->width * image->height)) {
242 time_t oldest = time(NULL);
243 int oldest_idx = 0;
244 int done = 0;
246 for (i = 0; i < RImageCacheSize; i++) {
247 if (!RImageCache[i].file) {
248 RImageCache[i].file = malloc(strlen(file) + 1);
249 strcpy(RImageCache[i].file, file);
250 RImageCache[i].image = RCloneImage(image);
251 RImageCache[i].last_modif = st.st_mtime;
252 RImageCache[i].last_use = time(NULL);
253 done = 1;
254 break;
255 } else {
256 if (oldest > RImageCache[i].last_use) {
257 oldest = RImageCache[i].last_use;
258 oldest_idx = i;
263 /* if no slot available, dump least recently used one */
264 if (!done) {
265 free(RImageCache[oldest_idx].file);
266 RReleaseImage(RImageCache[oldest_idx].image);
267 RImageCache[oldest_idx].file = malloc(strlen(file) + 1);
268 strcpy(RImageCache[oldest_idx].file, file);
269 RImageCache[oldest_idx].image = RCloneImage(image);
270 RImageCache[oldest_idx].last_modif = st.st_mtime;
271 RImageCache[oldest_idx].last_use = time(NULL);
275 return image;
278 char *RGetImageFileFormat(const char *file)
280 switch (identFile(file)) {
281 case IM_XPM:
282 return "XPM";
284 #ifdef USE_TIFF
285 case IM_TIFF:
286 return "TIFF";
287 #endif /* USE_TIFF */
289 #ifdef USE_PNG
290 case IM_PNG:
291 return "PNG";
292 #endif /* USE_PNG */
294 #ifdef USE_JPEG
295 case IM_JPEG:
296 return "JPEG";
297 #endif /* USE_JPEG */
299 #ifdef USE_GIF
300 case IM_GIF:
301 return "GIF";
302 #endif /* USE_GIF */
304 #ifdef USE_WEBP
305 case IM_WEBP:
306 return "WEBP";
307 #endif /* USE_WEBP */
309 case IM_PPM:
310 return "PPM";
312 default:
313 return NULL;
317 static WRImgFormat identFile(const char *path)
319 FILE *file;
320 unsigned char buffer[32];
321 size_t nread;
323 assert(path != NULL);
325 RETRY( file = fopen(path, "rb") )
326 if (file == NULL) {
327 RErrorCode = RERR_OPEN;
328 return IM_ERROR;
331 RETRY( nread = fread(buffer, 1, sizeof(buffer), file) )
332 if (nread < sizeof(buffer) || ferror(file)) {
333 RETRY( fclose(file) )
334 RErrorCode = RERR_READ;
335 return IM_ERROR;
337 RETRY( fclose(file) )
339 /* check for XPM */
340 if (strncmp((char *)buffer, "/* XPM */", 9) == 0)
341 return IM_XPM;
343 /* check for TIFF */
344 if ((buffer[0] == 'I' && buffer[1] == 'I' && buffer[2] == '*' && buffer[3] == 0)
345 || (buffer[0] == 'M' && buffer[1] == 'M' && buffer[2] == 0 && buffer[3] == '*'))
346 return IM_TIFF;
349 * check for PNG
351 * The signature is defined in the PNG specifiation:
352 * http://www.libpng.org/pub/png/spec/1.2/PNG-Structure.html
353 * it is valid for v1.0, v1.1, v1.2 and ISO version
355 if (buffer[0] == 137 && buffer[1] == 80 && buffer[2] == 78 && buffer[3] == 71 &&
356 buffer[4] == 13 && buffer[5] == 10 && buffer[6] == 26 && buffer[7] == 10)
357 return IM_PNG;
359 /* check for PBM or PGM or PPM */
360 if (buffer[0] == 'P' && (buffer[1] > '0' && buffer[1] < '7') && (buffer[2] == 0x0a || buffer[2] == 0x20 || buffer[2] == 0x09 || buffer[2] == 0x0d))
361 return IM_PPM;
363 /* check for JPEG */
364 if (buffer[0] == 0xff && buffer[1] == 0xd8)
365 return IM_JPEG;
367 /* check for GIF */
368 if (buffer[0] == 'G' && buffer[1] == 'I' && buffer[2] == 'F' && buffer[3] == '8' &&
369 (buffer[4] == '7' || buffer[4] == '9') && buffer[5] == 'a')
370 return IM_GIF;
372 /* check for WEBP */
373 if (buffer[ 0] == 'R' && buffer[ 1] == 'I' && buffer[ 2] == 'F' && buffer[ 3] == 'F' &&
374 buffer[ 8] == 'W' && buffer[ 9] == 'E' && buffer[10] == 'B' && buffer[11] == 'P' &&
375 buffer[12] == 'V' && buffer[13] == 'P' && buffer[14] == '8' &&
376 (buffer[15] == ' ' /* Simple File Format (Lossy) */
377 || buffer[15] == 'L' /* Simple File Format (Lossless) */
378 || buffer[15] == 'X')) /* Extended File Format */
379 return IM_WEBP;
381 return IM_UNKNOWN;