Wayland: Add missing extern "C"
[profile/ivi/wayland.git] / cursor / xcursor.c
1 /*
2  * Copyright © 2002 Keith Packard
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation, and that the name of Keith Packard not be used in
9  * advertising or publicity pertaining to distribution of the software without
10  * specific, written prior permission.  Keith Packard makes no
11  * representations about the suitability of this software for any purpose.  It
12  * is provided "as is" without express or implied warranty.
13  *
14  * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16  * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
20  * PERFORMANCE OF THIS SOFTWARE.
21  */
22
23 #include "xcursor.h"
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 /*
29  * From libXcursor/include/X11/extensions/Xcursor.h
30  */
31
32 #define XcursorTrue     1
33 #define XcursorFalse    0
34
35 /*
36  * Cursor files start with a header.  The header
37  * contains a magic number, a version number and a
38  * table of contents which has type and offset information
39  * for the remaining tables in the file.
40  *
41  * File minor versions increment for compatible changes
42  * File major versions increment for incompatible changes (never, we hope)
43  *
44  * Chunks of the same type are always upward compatible.  Incompatible
45  * changes are made with new chunk types; the old data can remain under
46  * the old type.  Upward compatible changes can add header data as the
47  * header lengths are specified in the file.
48  *
49  *  File:
50  *      FileHeader
51  *      LISTofChunk
52  *
53  *  FileHeader:
54  *      CARD32          magic       magic number
55  *      CARD32          header      bytes in file header
56  *      CARD32          version     file version
57  *      CARD32          ntoc        number of toc entries
58  *      LISTofFileToc   toc         table of contents
59  *
60  *  FileToc:
61  *      CARD32          type        entry type
62  *      CARD32          subtype     entry subtype (size for images)
63  *      CARD32          position    absolute file position
64  */
65
66 #define XCURSOR_MAGIC   0x72756358  /* "Xcur" LSBFirst */
67
68 /*
69  * Current Xcursor version number.  Will be substituted by configure
70  * from the version in the libXcursor configure.ac file.
71  */
72
73 #define XCURSOR_LIB_MAJOR 1
74 #define XCURSOR_LIB_MINOR 1
75 #define XCURSOR_LIB_REVISION 13
76 #define XCURSOR_LIB_VERSION     ((XCURSOR_LIB_MAJOR * 10000) + \
77                                  (XCURSOR_LIB_MINOR * 100) + \
78                                  (XCURSOR_LIB_REVISION))
79
80 /*
81  * This version number is stored in cursor files; changes to the
82  * file format require updating this version number
83  */
84 #define XCURSOR_FILE_MAJOR      1
85 #define XCURSOR_FILE_MINOR      0
86 #define XCURSOR_FILE_VERSION    ((XCURSOR_FILE_MAJOR << 16) | (XCURSOR_FILE_MINOR))
87 #define XCURSOR_FILE_HEADER_LEN (4 * 4)
88 #define XCURSOR_FILE_TOC_LEN    (3 * 4)
89
90 typedef struct _XcursorFileToc {
91     XcursorUInt     type;       /* chunk type */
92     XcursorUInt     subtype;    /* subtype (size for images) */
93     XcursorUInt     position;   /* absolute position in file */
94 } XcursorFileToc;
95
96 typedef struct _XcursorFileHeader {
97     XcursorUInt     magic;      /* magic number */
98     XcursorUInt     header;     /* byte length of header */
99     XcursorUInt     version;    /* file version number */
100     XcursorUInt     ntoc;       /* number of toc entries */
101     XcursorFileToc  *tocs;      /* table of contents */
102 } XcursorFileHeader;
103
104 /*
105  * The rest of the file is a list of chunks, each tagged by type
106  * and version.
107  *
108  *  Chunk:
109  *      ChunkHeader
110  *      <extra type-specific header fields>
111  *      <type-specific data>
112  *
113  *  ChunkHeader:
114  *      CARD32      header      bytes in chunk header + type header
115  *      CARD32      type        chunk type
116  *      CARD32      subtype     chunk subtype
117  *      CARD32      version     chunk type version
118  */
119
120 #define XCURSOR_CHUNK_HEADER_LEN    (4 * 4)
121
122 typedef struct _XcursorChunkHeader {
123     XcursorUInt     header;     /* bytes in chunk header */
124     XcursorUInt     type;       /* chunk type */
125     XcursorUInt     subtype;    /* chunk subtype (size for images) */
126     XcursorUInt     version;    /* version of this type */
127 } XcursorChunkHeader;
128
129 /*
130  * Here's a list of the known chunk types
131  */
132
133 /*
134  * Comments consist of a 4-byte length field followed by
135  * UTF-8 encoded text
136  *
137  *  Comment:
138  *      ChunkHeader header      chunk header
139  *      CARD32      length      bytes in text
140  *      LISTofCARD8 text        UTF-8 encoded text
141  */
142
143 #define XCURSOR_COMMENT_TYPE        0xfffe0001
144 #define XCURSOR_COMMENT_VERSION     1
145 #define XCURSOR_COMMENT_HEADER_LEN  (XCURSOR_CHUNK_HEADER_LEN + (1 *4))
146 #define XCURSOR_COMMENT_COPYRIGHT   1
147 #define XCURSOR_COMMENT_LICENSE     2
148 #define XCURSOR_COMMENT_OTHER       3
149 #define XCURSOR_COMMENT_MAX_LEN     0x100000
150
151 typedef struct _XcursorComment {
152     XcursorUInt     version;
153     XcursorUInt     comment_type;
154     char            *comment;
155 } XcursorComment;
156
157 /*
158  * Each cursor image occupies a separate image chunk.
159  * The length of the image header follows the chunk header
160  * so that future versions can extend the header without
161  * breaking older applications
162  *
163  *  Image:
164  *      ChunkHeader     header  chunk header
165  *      CARD32          width   actual width
166  *      CARD32          height  actual height
167  *      CARD32          xhot    hot spot x
168  *      CARD32          yhot    hot spot y
169  *      CARD32          delay   animation delay
170  *      LISTofCARD32    pixels  ARGB pixels
171  */
172
173 #define XCURSOR_IMAGE_TYPE          0xfffd0002
174 #define XCURSOR_IMAGE_VERSION       1
175 #define XCURSOR_IMAGE_HEADER_LEN    (XCURSOR_CHUNK_HEADER_LEN + (5*4))
176 #define XCURSOR_IMAGE_MAX_SIZE      0x7fff      /* 32767x32767 max cursor size */
177
178 typedef struct _XcursorFile XcursorFile;
179
180 struct _XcursorFile {
181     void    *closure;
182     int     (*read)  (XcursorFile *file, unsigned char *buf, int len);
183     int     (*write) (XcursorFile *file, unsigned char *buf, int len);
184     int     (*seek)  (XcursorFile *file, long offset, int whence);
185 };
186
187 typedef struct _XcursorComments {
188     int             ncomment;   /* number of comments */
189     XcursorComment  **comments; /* array of XcursorComment pointers */
190 } XcursorComments;
191
192 /*
193  * From libXcursor/src/file.c
194  */
195
196 static XcursorImage *
197 XcursorImageCreate (int width, int height)
198 {
199     XcursorImage    *image;
200
201     image = malloc (sizeof (XcursorImage) +
202                     width * height * sizeof (XcursorPixel));
203     if (!image)
204         return NULL;
205     image->version = XCURSOR_IMAGE_VERSION;
206     image->pixels = (XcursorPixel *) (image + 1);
207     image->size = width > height ? width : height;
208     image->width = width;
209     image->height = height;
210     image->delay = 0;
211     return image;
212 }
213
214 static void
215 XcursorImageDestroy (XcursorImage *image)
216 {
217     free (image);
218 }
219
220 static XcursorImages *
221 XcursorImagesCreate (int size)
222 {
223     XcursorImages   *images;
224
225     images = malloc (sizeof (XcursorImages) +
226                      size * sizeof (XcursorImage *));
227     if (!images)
228         return NULL;
229     images->nimage = 0;
230     images->images = (XcursorImage **) (images + 1);
231     images->name = NULL;
232     return images;
233 }
234
235 void
236 XcursorImagesDestroy (XcursorImages *images)
237 {
238     int n;
239
240     if (!images)
241         return;
242
243     for (n = 0; n < images->nimage; n++)
244         XcursorImageDestroy (images->images[n]);
245     if (images->name)
246         free (images->name);
247     free (images);
248 }
249
250 static void
251 XcursorImagesSetName (XcursorImages *images, const char *name)
252 {
253     char    *new;
254
255     if (!images || !name)
256         return;
257
258     new = malloc (strlen (name) + 1);
259
260     if (!new)
261         return;
262
263     strcpy (new, name);
264     if (images->name)
265         free (images->name);
266     images->name = new;
267 }
268
269 static XcursorBool
270 _XcursorReadUInt (XcursorFile *file, XcursorUInt *u)
271 {
272     unsigned char   bytes[4];
273
274     if (!file || !u)
275         return XcursorFalse;
276
277     if ((*file->read) (file, bytes, 4) != 4)
278         return XcursorFalse;
279     *u = ((bytes[0] << 0) |
280           (bytes[1] << 8) |
281           (bytes[2] << 16) |
282           (bytes[3] << 24));
283     return XcursorTrue;
284 }
285
286 static void
287 _XcursorFileHeaderDestroy (XcursorFileHeader *fileHeader)
288 {
289     free (fileHeader);
290 }
291
292 static XcursorFileHeader *
293 _XcursorFileHeaderCreate (int ntoc)
294 {
295     XcursorFileHeader   *fileHeader;
296
297     if (ntoc > 0x10000)
298         return NULL;
299     fileHeader = malloc (sizeof (XcursorFileHeader) +
300                          ntoc * sizeof (XcursorFileToc));
301     if (!fileHeader)
302         return NULL;
303     fileHeader->magic = XCURSOR_MAGIC;
304     fileHeader->header = XCURSOR_FILE_HEADER_LEN;
305     fileHeader->version = XCURSOR_FILE_VERSION;
306     fileHeader->ntoc = ntoc;
307     fileHeader->tocs = (XcursorFileToc *) (fileHeader + 1);
308     return fileHeader;
309 }
310
311 static XcursorFileHeader *
312 _XcursorReadFileHeader (XcursorFile *file)
313 {
314     XcursorFileHeader   head, *fileHeader;
315     XcursorUInt         skip;
316     unsigned int        n;
317
318     if (!file)
319         return NULL;
320
321     if (!_XcursorReadUInt (file, &head.magic))
322         return NULL;
323     if (head.magic != XCURSOR_MAGIC)
324         return NULL;
325     if (!_XcursorReadUInt (file, &head.header))
326         return NULL;
327     if (!_XcursorReadUInt (file, &head.version))
328         return NULL;
329     if (!_XcursorReadUInt (file, &head.ntoc))
330         return NULL;
331     skip = head.header - XCURSOR_FILE_HEADER_LEN;
332     if (skip)
333         if ((*file->seek) (file, skip, SEEK_CUR) == EOF)
334             return NULL;
335     fileHeader = _XcursorFileHeaderCreate (head.ntoc);
336     if (!fileHeader)
337         return NULL;
338     fileHeader->magic = head.magic;
339     fileHeader->header = head.header;
340     fileHeader->version = head.version;
341     fileHeader->ntoc = head.ntoc;
342     for (n = 0; n < fileHeader->ntoc; n++)
343     {
344         if (!_XcursorReadUInt (file, &fileHeader->tocs[n].type))
345             break;
346         if (!_XcursorReadUInt (file, &fileHeader->tocs[n].subtype))
347             break;
348         if (!_XcursorReadUInt (file, &fileHeader->tocs[n].position))
349             break;
350     }
351     if (n != fileHeader->ntoc)
352     {
353         _XcursorFileHeaderDestroy (fileHeader);
354         return NULL;
355     }
356     return fileHeader;
357 }
358
359 static XcursorBool
360 _XcursorSeekToToc (XcursorFile          *file,
361                    XcursorFileHeader    *fileHeader,
362                    int                  toc)
363 {
364     if (!file || !fileHeader || \
365         (*file->seek) (file, fileHeader->tocs[toc].position, SEEK_SET) == EOF)
366         return XcursorFalse;
367     return XcursorTrue;
368 }
369
370 static XcursorBool
371 _XcursorFileReadChunkHeader (XcursorFile        *file,
372                              XcursorFileHeader  *fileHeader,
373                              int                toc,
374                              XcursorChunkHeader *chunkHeader)
375 {
376     if (!file || !fileHeader || !chunkHeader)
377         return XcursorFalse;
378     if (!_XcursorSeekToToc (file, fileHeader, toc))
379         return XcursorFalse;
380     if (!_XcursorReadUInt (file, &chunkHeader->header))
381         return XcursorFalse;
382     if (!_XcursorReadUInt (file, &chunkHeader->type))
383         return XcursorFalse;
384     if (!_XcursorReadUInt (file, &chunkHeader->subtype))
385         return XcursorFalse;
386     if (!_XcursorReadUInt (file, &chunkHeader->version))
387         return XcursorFalse;
388     /* sanity check */
389     if (chunkHeader->type != fileHeader->tocs[toc].type ||
390         chunkHeader->subtype != fileHeader->tocs[toc].subtype)
391         return XcursorFalse;
392     return XcursorTrue;
393 }
394
395 #define dist(a,b)   ((a) > (b) ? (a) - (b) : (b) - (a))
396
397 static XcursorDim
398 _XcursorFindBestSize (XcursorFileHeader *fileHeader,
399                       XcursorDim        size,
400                       int               *nsizesp)
401 {
402     unsigned int n;
403     int         nsizes = 0;
404     XcursorDim  bestSize = 0;
405     XcursorDim  thisSize;
406
407     if (!fileHeader || !nsizesp)
408         return 0;
409
410     for (n = 0; n < fileHeader->ntoc; n++)
411     {
412         if (fileHeader->tocs[n].type != XCURSOR_IMAGE_TYPE)
413             continue;
414         thisSize = fileHeader->tocs[n].subtype;
415         if (!bestSize || dist (thisSize, size) < dist (bestSize, size))
416         {
417             bestSize = thisSize;
418             nsizes = 1;
419         }
420         else if (thisSize == bestSize)
421             nsizes++;
422     }
423     *nsizesp = nsizes;
424     return bestSize;
425 }
426
427 static int
428 _XcursorFindImageToc (XcursorFileHeader *fileHeader,
429                       XcursorDim        size,
430                       int               count)
431 {
432     unsigned int        toc;
433     XcursorDim          thisSize;
434
435     if (!fileHeader)
436         return 0;
437
438     for (toc = 0; toc < fileHeader->ntoc; toc++)
439     {
440         if (fileHeader->tocs[toc].type != XCURSOR_IMAGE_TYPE)
441             continue;
442         thisSize = fileHeader->tocs[toc].subtype;
443         if (thisSize != size)
444             continue;
445         if (!count)
446             break;
447         count--;
448     }
449     if (toc == fileHeader->ntoc)
450         return -1;
451     return toc;
452 }
453
454 static XcursorImage *
455 _XcursorReadImage (XcursorFile          *file,
456                    XcursorFileHeader    *fileHeader,
457                    int                  toc)
458 {
459     XcursorChunkHeader  chunkHeader;
460     XcursorImage        head;
461     XcursorImage        *image;
462     int                 n;
463     XcursorPixel        *p;
464
465     if (!file || !fileHeader)
466         return NULL;
467
468     if (!_XcursorFileReadChunkHeader (file, fileHeader, toc, &chunkHeader))
469         return NULL;
470     if (!_XcursorReadUInt (file, &head.width))
471         return NULL;
472     if (!_XcursorReadUInt (file, &head.height))
473         return NULL;
474     if (!_XcursorReadUInt (file, &head.xhot))
475         return NULL;
476     if (!_XcursorReadUInt (file, &head.yhot))
477         return NULL;
478     if (!_XcursorReadUInt (file, &head.delay))
479         return NULL;
480     /* sanity check data */
481     if (head.width >= 0x10000 || head.height > 0x10000)
482         return NULL;
483     if (head.width == 0 || head.height == 0)
484         return NULL;
485     if (head.xhot > head.width || head.yhot > head.height)
486         return NULL;
487
488     /* Create the image and initialize it */
489     image = XcursorImageCreate (head.width, head.height);
490     if (chunkHeader.version < image->version)
491         image->version = chunkHeader.version;
492     image->size = chunkHeader.subtype;
493     image->xhot = head.xhot;
494     image->yhot = head.yhot;
495     image->delay = head.delay;
496     n = image->width * image->height;
497     p = image->pixels;
498     while (n--)
499     {
500         if (!_XcursorReadUInt (file, p))
501         {
502             XcursorImageDestroy (image);
503             return NULL;
504         }
505         p++;
506     }
507     return image;
508 }
509
510 static XcursorImages *
511 XcursorXcFileLoadImages (XcursorFile *file, int size)
512 {
513     XcursorFileHeader   *fileHeader;
514     XcursorDim          bestSize;
515     int                 nsize;
516     XcursorImages       *images;
517     int                 n;
518     int                 toc;
519
520     if (!file || size < 0)
521         return NULL;
522     fileHeader = _XcursorReadFileHeader (file);
523     if (!fileHeader)
524         return NULL;
525     bestSize = _XcursorFindBestSize (fileHeader, (XcursorDim) size, &nsize);
526     if (!bestSize)
527     {
528         _XcursorFileHeaderDestroy (fileHeader);
529         return NULL;
530     }
531     images = XcursorImagesCreate (nsize);
532     if (!images)
533     {
534         _XcursorFileHeaderDestroy (fileHeader);
535         return NULL;
536     }
537     for (n = 0; n < nsize; n++)
538     {
539         toc = _XcursorFindImageToc (fileHeader, bestSize, n);
540         if (toc < 0)
541             break;
542         images->images[images->nimage] = _XcursorReadImage (file, fileHeader,
543                                                             toc);
544         if (!images->images[images->nimage])
545             break;
546         images->nimage++;
547     }
548     _XcursorFileHeaderDestroy (fileHeader);
549     if (images->nimage != nsize)
550     {
551         XcursorImagesDestroy (images);
552         images = NULL;
553     }
554     return images;
555 }
556
557 static int
558 _XcursorStdioFileRead (XcursorFile *file, unsigned char *buf, int len)
559 {
560     FILE    *f = file->closure;
561     return fread (buf, 1, len, f);
562 }
563
564 static int
565 _XcursorStdioFileWrite (XcursorFile *file, unsigned char *buf, int len)
566 {
567     FILE    *f = file->closure;
568     return fwrite (buf, 1, len, f);
569 }
570
571 static int
572 _XcursorStdioFileSeek (XcursorFile *file, long offset, int whence)
573 {
574     FILE    *f = file->closure;
575     return fseek (f, offset, whence);
576 }
577
578 static void
579 _XcursorStdioFileInitialize (FILE *stdfile, XcursorFile *file)
580 {
581     file->closure = stdfile;
582     file->read = _XcursorStdioFileRead;
583     file->write = _XcursorStdioFileWrite;
584     file->seek = _XcursorStdioFileSeek;
585 }
586
587 static XcursorImages *
588 XcursorFileLoadImages (FILE *file, int size)
589 {
590     XcursorFile f;
591
592     if (!file)
593         return NULL;
594
595     _XcursorStdioFileInitialize (file, &f);
596     return XcursorXcFileLoadImages (&f, size);
597 }
598
599 /*
600  * From libXcursor/src/library.c
601  */
602
603 #ifndef ICONDIR
604 #define ICONDIR "/usr/X11R6/lib/X11/icons"
605 #endif
606
607 #ifndef XCURSORPATH
608 #define XCURSORPATH "~/.icons:/usr/share/icons:/usr/share/pixmaps:"ICONDIR
609 #endif
610
611 static const char *
612 XcursorLibraryPath (void)
613 {
614     static const char   *path;
615
616     if (!path)
617     {
618         path = getenv ("XCURSOR_PATH");
619         if (!path)
620             path = XCURSORPATH;
621     }
622     return path;
623 }
624
625 static  void
626 _XcursorAddPathElt (char *path, const char *elt, int len)
627 {
628     int     pathlen = strlen (path);
629
630     /* append / if the path doesn't currently have one */
631     if (path[0] == '\0' || path[pathlen - 1] != '/')
632     {
633         strcat (path, "/");
634         pathlen++;
635     }
636     if (len == -1)
637         len = strlen (elt);
638     /* strip leading slashes */
639     while (len && elt[0] == '/')
640     {
641         elt++;
642         len--;
643     }
644     strncpy (path + pathlen, elt, len);
645     path[pathlen + len] = '\0';
646 }
647
648 static char *
649 _XcursorBuildThemeDir (const char *dir, const char *theme)
650 {
651     const char      *colon;
652     const char      *tcolon;
653     char            *full;
654     char            *home;
655     int             dirlen;
656     int             homelen;
657     int             themelen;
658     int             len;
659
660     if (!dir || !theme)
661         return NULL;
662
663     colon = strchr (dir, ':');
664     if (!colon)
665         colon = dir + strlen (dir);
666
667     dirlen = colon - dir;
668
669     tcolon = strchr (theme, ':');
670     if (!tcolon)
671         tcolon = theme + strlen (theme);
672
673     themelen = tcolon - theme;
674
675     home = NULL;
676     homelen = 0;
677     if (*dir == '~')
678     {
679         home = getenv ("HOME");
680         if (!home)
681             return NULL;
682         homelen = strlen (home);
683         dir++;
684         dirlen--;
685     }
686
687     /*
688      * add space for any needed directory separators, one per component,
689      * and one for the trailing null
690      */
691     len = 1 + homelen + 1 + dirlen + 1 + themelen + 1;
692
693     full = malloc (len);
694     if (!full)
695         return NULL;
696     full[0] = '\0';
697
698     if (home)
699         _XcursorAddPathElt (full, home, -1);
700     _XcursorAddPathElt (full, dir, dirlen);
701     _XcursorAddPathElt (full, theme, themelen);
702     return full;
703 }
704
705 static char *
706 _XcursorBuildFullname (const char *dir, const char *subdir, const char *file)
707 {
708     char    *full;
709
710     if (!dir || !subdir || !file)
711         return NULL;
712
713     full = malloc (strlen (dir) + 1 + strlen (subdir) + 1 + strlen (file) + 1);
714     if (!full)
715         return NULL;
716     full[0] = '\0';
717     _XcursorAddPathElt (full, dir, -1);
718     _XcursorAddPathElt (full, subdir, -1);
719     _XcursorAddPathElt (full, file, -1);
720     return full;
721 }
722
723 static const char *
724 _XcursorNextPath (const char *path)
725 {
726     char    *colon = strchr (path, ':');
727
728     if (!colon)
729         return NULL;
730     return colon + 1;
731 }
732
733 #define XcursorWhite(c) ((c) == ' ' || (c) == '\t' || (c) == '\n')
734 #define XcursorSep(c) ((c) == ';' || (c) == ',')
735
736 static char *
737 _XcursorThemeInherits (const char *full)
738 {
739     char    line[8192];
740     char    *result = NULL;
741     FILE    *f;
742
743     if (!full)
744         return NULL;
745
746     f = fopen (full, "r");
747     if (f)
748     {
749         while (fgets (line, sizeof (line), f))
750         {
751             if (!strncmp (line, "Inherits", 8))
752             {
753                 char    *l = line + 8;
754                 char    *r;
755                 while (*l == ' ') l++;
756                 if (*l != '=') continue;
757                 l++;
758                 while (*l == ' ') l++;
759                 result = malloc (strlen (l));
760                 if (result)
761                 {
762                     r = result;
763                     while (*l)
764                     {
765                         while (XcursorSep(*l) || XcursorWhite (*l)) l++;
766                         if (!*l)
767                             break;
768                         if (r != result)
769                             *r++ = ':';
770                         while (*l && !XcursorWhite(*l) &&
771                                !XcursorSep(*l))
772                             *r++ = *l++;
773                     }
774                     *r++ = '\0';
775                 }
776                 break;
777             }
778         }
779         fclose (f);
780     }
781     return result;
782 }
783
784 static FILE *
785 XcursorScanTheme (const char *theme, const char *name)
786 {
787     FILE        *f = NULL;
788     char        *full;
789     char        *dir;
790     const char  *path;
791     char        *inherits = NULL;
792     const char  *i;
793
794     if (!theme || !name)
795         return NULL;
796
797     /*
798      * Scan this theme
799      */
800     for (path = XcursorLibraryPath ();
801          path && f == NULL;
802          path = _XcursorNextPath (path))
803     {
804         dir = _XcursorBuildThemeDir (path, theme);
805         if (dir)
806         {
807             full = _XcursorBuildFullname (dir, "cursors", name);
808             if (full)
809             {
810                 f = fopen (full, "r");
811                 free (full);
812             }
813             if (!f && !inherits)
814             {
815                 full = _XcursorBuildFullname (dir, "", "index.theme");
816                 if (full)
817                 {
818                     inherits = _XcursorThemeInherits (full);
819                     free (full);
820                 }
821             }
822             free (dir);
823         }
824     }
825     /*
826      * Recurse to scan inherited themes
827      */
828     for (i = inherits; i && f == NULL; i = _XcursorNextPath (i))
829         f = XcursorScanTheme (i, name);
830     if (inherits != NULL)
831         free (inherits);
832     return f;
833 }
834
835 XcursorImages *
836 XcursorLibraryLoadImages (const char *file, const char *theme, int size)
837 {
838     FILE            *f = NULL;
839     XcursorImages   *images = NULL;
840
841     if (!file)
842         return NULL;
843
844     if (theme)
845         f = XcursorScanTheme (theme, file);
846     if (!f)
847         f = XcursorScanTheme ("default", file);
848     if (f)
849     {
850         images = XcursorFileLoadImages (f, size);
851         if (images)
852             XcursorImagesSetName (images, file);
853         fclose (f);
854     }
855     return images;
856 }