/* * Copyright © 2002 Keith Packard * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Keith Packard not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. Keith Packard makes no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #include "xcursorint.h" #include #include XcursorImage * XcursorImageCreate (int width, int height) { XcursorImage *image; if (width < 0 || height < 0) return NULL; if (width > XCURSOR_IMAGE_MAX_SIZE || height > XCURSOR_IMAGE_MAX_SIZE) return NULL; image = malloc (sizeof (XcursorImage) + (size_t) (width * height) * sizeof (XcursorPixel)); if (!image) return NULL; image->version = XCURSOR_IMAGE_VERSION; image->pixels = (XcursorPixel *) (image + 1); image->size = (XcursorDim) (width > height ? width : height); image->width = (XcursorDim) width; image->height = (XcursorDim) height; image->delay = 0; return image; } void XcursorImageDestroy (XcursorImage *image) { free (image); } XcursorImages * XcursorImagesCreate (int size) { XcursorImages *images; images = malloc (sizeof (XcursorImages) + (size_t) size * sizeof (XcursorImage *)); if (!images) return NULL; images->nimage = 0; images->images = (XcursorImage **) (images + 1); images->name = NULL; return images; } void XcursorImagesDestroy (XcursorImages *images) { int n; if (!images) return; for (n = 0; n < images->nimage; n++) XcursorImageDestroy (images->images[n]); if (images->name) free (images->name); free (images); } void XcursorImagesSetName (XcursorImages *images, const char *name) { char *new; if (!images || !name) return; new = strdup (name); if (!new) return; if (images->name) free (images->name); images->name = new; } XcursorComment * XcursorCommentCreate (XcursorUInt comment_type, int length) { XcursorComment *comment; if (length < 0 || length > XCURSOR_COMMENT_MAX_LEN) return NULL; comment = malloc (sizeof (XcursorComment) + (size_t) length + 1); if (!comment) return NULL; comment->version = XCURSOR_COMMENT_VERSION; comment->comment_type = comment_type; comment->comment = (char *) (comment + 1); comment->comment[0] = '\0'; return comment; } void XcursorCommentDestroy (XcursorComment *comment) { free (comment); } XcursorComments * XcursorCommentsCreate (int size) { XcursorComments *comments; comments = malloc (sizeof (XcursorComments) + (size_t) size * sizeof (XcursorComment *)); if (!comments) return NULL; comments->ncomment = 0; comments->comments = (XcursorComment **) (comments + 1); return comments; } void XcursorCommentsDestroy (XcursorComments *comments) { int n; if (!comments) return; for (n = 0; n < comments->ncomment; n++) XcursorCommentDestroy (comments->comments[n]); free (comments); } static XcursorBool _XcursorReadUInt (XcursorFile *file, XcursorUInt *u) { unsigned char bytes[4]; if (!file || !u) return XcursorFalse; if ((*file->read) (file, bytes, 4) != 4) return XcursorFalse; *u = ((XcursorUInt)(bytes[0]) << 0) | ((XcursorUInt)(bytes[1]) << 8) | ((XcursorUInt)(bytes[2]) << 16) | ((XcursorUInt)(bytes[3]) << 24); return XcursorTrue; } static XcursorBool _XcursorReadBytes (XcursorFile *file, char *bytes, int length) { if (!file || !bytes || (*file->read) (file, (unsigned char *) bytes, length) != length) return XcursorFalse; return XcursorTrue; } static XcursorBool _XcursorWriteUInt (XcursorFile *file, XcursorUInt u) { unsigned char bytes[4]; if (!file) return XcursorFalse; bytes[0] = (unsigned char)(u); bytes[1] = (unsigned char)(u >> 8); bytes[2] = (unsigned char)(u >> 16); bytes[3] = (unsigned char)(u >> 24); if ((*file->write) (file, bytes, 4) != 4) return XcursorFalse; return XcursorTrue; } static XcursorBool _XcursorWriteBytes (XcursorFile *file, char *bytes, int length) { if (!file || !bytes || (*file->write) (file, (unsigned char *) bytes, length) != length) return XcursorFalse; return XcursorTrue; } static void _XcursorFileHeaderDestroy (XcursorFileHeader *fileHeader) { free (fileHeader); } static XcursorFileHeader * _XcursorFileHeaderCreate (XcursorUInt ntoc) { XcursorFileHeader *fileHeader; if (ntoc > 0x10000) return NULL; fileHeader = malloc (sizeof (XcursorFileHeader) + ntoc * sizeof (XcursorFileToc)); if (!fileHeader) return NULL; fileHeader->magic = XCURSOR_MAGIC; fileHeader->header = XCURSOR_FILE_HEADER_LEN; fileHeader->version = XCURSOR_FILE_VERSION; fileHeader->ntoc = ntoc; fileHeader->tocs = (XcursorFileToc *) (fileHeader + 1); return fileHeader; } static XcursorFileHeader * _XcursorReadFileHeader (XcursorFile *file) { XcursorFileHeader head, *fileHeader; XcursorUInt skip; XcursorUInt n; if (!file) return NULL; if (!_XcursorReadUInt (file, &head.magic)) return NULL; if (head.magic != XCURSOR_MAGIC) return NULL; if (!_XcursorReadUInt (file, &head.header)) return NULL; if (!_XcursorReadUInt (file, &head.version)) return NULL; if (!_XcursorReadUInt (file, &head.ntoc)) return NULL; skip = head.header - XCURSOR_FILE_HEADER_LEN; if (skip) if ((*file->seek) (file, skip, SEEK_CUR) == EOF) return NULL; fileHeader = _XcursorFileHeaderCreate (head.ntoc); if (!fileHeader) return NULL; fileHeader->magic = head.magic; fileHeader->header = head.header; fileHeader->version = head.version; fileHeader->ntoc = head.ntoc; for (n = 0; n < fileHeader->ntoc; n++) { if (!_XcursorReadUInt (file, &fileHeader->tocs[n].type)) break; if (!_XcursorReadUInt (file, &fileHeader->tocs[n].subtype)) break; if (!_XcursorReadUInt (file, &fileHeader->tocs[n].position)) break; } if (n != fileHeader->ntoc) { _XcursorFileHeaderDestroy (fileHeader); return NULL; } return fileHeader; } static XcursorUInt _XcursorFileHeaderLength (XcursorFileHeader *fileHeader) { return (XCURSOR_FILE_HEADER_LEN + fileHeader->ntoc * XCURSOR_FILE_TOC_LEN); } static XcursorBool _XcursorWriteFileHeader (XcursorFile *file, XcursorFileHeader *fileHeader) { XcursorUInt toc; if (!file || !fileHeader) return XcursorFalse; if (!_XcursorWriteUInt (file, fileHeader->magic)) return XcursorFalse; if (!_XcursorWriteUInt (file, fileHeader->header)) return XcursorFalse; if (!_XcursorWriteUInt (file, fileHeader->version)) return XcursorFalse; if (!_XcursorWriteUInt (file, fileHeader->ntoc)) return XcursorFalse; for (toc = 0; toc < fileHeader->ntoc; toc++) { if (!_XcursorWriteUInt (file, fileHeader->tocs[toc].type)) return XcursorFalse; if (!_XcursorWriteUInt (file, fileHeader->tocs[toc].subtype)) return XcursorFalse; if (!_XcursorWriteUInt (file, fileHeader->tocs[toc].position)) return XcursorFalse; } return XcursorTrue; } static XcursorBool _XcursorSeekToToc (XcursorFile *file, XcursorFileHeader *fileHeader, int toc) { if (!file || !fileHeader || \ (*file->seek) (file, fileHeader->tocs[toc].position, SEEK_SET) == EOF) return XcursorFalse; return XcursorTrue; } static XcursorBool _XcursorFileReadChunkHeader (XcursorFile *file, XcursorFileHeader *fileHeader, int toc, XcursorChunkHeader *chunkHeader) { if (!file || !fileHeader || !chunkHeader) return XcursorFalse; if (!_XcursorSeekToToc (file, fileHeader, toc)) return XcursorFalse; if (!_XcursorReadUInt (file, &chunkHeader->header)) return XcursorFalse; if (!_XcursorReadUInt (file, &chunkHeader->type)) return XcursorFalse; if (!_XcursorReadUInt (file, &chunkHeader->subtype)) return XcursorFalse; if (!_XcursorReadUInt (file, &chunkHeader->version)) return XcursorFalse; /* sanity check */ if (chunkHeader->type != fileHeader->tocs[toc].type || chunkHeader->subtype != fileHeader->tocs[toc].subtype) return XcursorFalse; return XcursorTrue; } static XcursorBool _XcursorFileWriteChunkHeader (XcursorFile *file, XcursorFileHeader *fileHeader, int toc, XcursorChunkHeader *chunkHeader) { if (!file || !fileHeader || !chunkHeader) return XcursorFalse; if (!_XcursorSeekToToc (file, fileHeader, toc)) return XcursorFalse; if (!_XcursorWriteUInt (file, chunkHeader->header)) return XcursorFalse; if (!_XcursorWriteUInt (file, chunkHeader->type)) return XcursorFalse; if (!_XcursorWriteUInt (file, chunkHeader->subtype)) return XcursorFalse; if (!_XcursorWriteUInt (file, chunkHeader->version)) return XcursorFalse; return XcursorTrue; } #define dist(a,b) ((a) > (b) ? (a) - (b) : (b) - (a)) static XcursorDim _XcursorFindBestSize (XcursorFileHeader *fileHeader, XcursorDim size, int *nsizesp) { XcursorUInt n; int nsizes = 0; XcursorDim bestSize = 0; XcursorDim thisSize; if (!fileHeader || !nsizesp) return 0; for (n = 0; n < fileHeader->ntoc; n++) { if (fileHeader->tocs[n].type != XCURSOR_IMAGE_TYPE) continue; thisSize = fileHeader->tocs[n].subtype; if (!bestSize || dist (thisSize, size) < dist (bestSize, size)) { bestSize = thisSize; nsizes = 1; } else if (thisSize == bestSize) nsizes++; } *nsizesp = nsizes; return bestSize; } static int _XcursorFindImageToc (XcursorFileHeader *fileHeader, XcursorDim size, int count) { XcursorUInt toc; XcursorDim thisSize; if (!fileHeader) return 0; for (toc = 0; toc < fileHeader->ntoc; toc++) { if (fileHeader->tocs[toc].type != XCURSOR_IMAGE_TYPE) continue; thisSize = fileHeader->tocs[toc].subtype; if (thisSize != size) continue; if (!count) break; count--; } if (toc == fileHeader->ntoc) return -1; return (int) toc; } static XcursorImage * _XcursorReadImage (XcursorFile *file, XcursorFileHeader *fileHeader, int toc) { XcursorChunkHeader chunkHeader; XcursorImage head; XcursorImage *image; int n; XcursorPixel *p; if (!file || !fileHeader) return NULL; if (!_XcursorFileReadChunkHeader (file, fileHeader, toc, &chunkHeader)) return NULL; if (!_XcursorReadUInt (file, &head.width)) return NULL; if (!_XcursorReadUInt (file, &head.height)) return NULL; if (!_XcursorReadUInt (file, &head.xhot)) return NULL; if (!_XcursorReadUInt (file, &head.yhot)) return NULL; if (!_XcursorReadUInt (file, &head.delay)) return NULL; /* sanity check data */ if (head.width > XCURSOR_IMAGE_MAX_SIZE || head.height > XCURSOR_IMAGE_MAX_SIZE) return NULL; if (head.width == 0 || head.height == 0) return NULL; if (head.xhot > head.width || head.yhot > head.height) return NULL; /* Create the image and initialize it */ image = XcursorImageCreate ((int) head.width, (int) head.height); if (image == NULL) return NULL; if (chunkHeader.version < image->version) image->version = chunkHeader.version; image->size = chunkHeader.subtype; image->xhot = head.xhot; image->yhot = head.yhot; image->delay = head.delay; n = (int) (image->width * image->height); p = image->pixels; while (n--) { if (!_XcursorReadUInt (file, p)) { XcursorImageDestroy (image); return NULL; } p++; } return image; } static XcursorUInt _XcursorImageLength (XcursorImage *image) { if (!image) return 0; return XCURSOR_IMAGE_HEADER_LEN + (image->width * image->height) * 4; } static XcursorBool _XcursorWriteImage (XcursorFile *file, XcursorFileHeader *fileHeader, int toc, XcursorImage *image) { XcursorChunkHeader chunkHeader; int n; XcursorPixel *p; if (!file || !fileHeader || !image) return XcursorFalse; /* sanity check data */ if (image->width > XCURSOR_IMAGE_MAX_SIZE || image->height > XCURSOR_IMAGE_MAX_SIZE) return XcursorFalse; if (image->width == 0 || image->height == 0) return XcursorFalse; if (image->xhot > image->width || image->yhot > image->height) return XcursorFalse; /* write chunk header */ chunkHeader.header = XCURSOR_IMAGE_HEADER_LEN; chunkHeader.type = XCURSOR_IMAGE_TYPE; chunkHeader.subtype = image->size; chunkHeader.version = XCURSOR_IMAGE_VERSION; if (!_XcursorFileWriteChunkHeader (file, fileHeader, toc, &chunkHeader)) return XcursorFalse; /* write extra image header fields */ if (!_XcursorWriteUInt (file, image->width)) return XcursorFalse; if (!_XcursorWriteUInt (file, image->height)) return XcursorFalse; if (!_XcursorWriteUInt (file, image->xhot)) return XcursorFalse; if (!_XcursorWriteUInt (file, image->yhot)) return XcursorFalse; if (!_XcursorWriteUInt (file, image->delay)) return XcursorFalse; /* write the image */ n = (int) (image->width * image->height); p = image->pixels; while (n--) { if (!_XcursorWriteUInt (file, *p)) return XcursorFalse; p++; } return XcursorTrue; } static XcursorComment * _XcursorReadComment (XcursorFile *file, XcursorFileHeader *fileHeader, int toc) { XcursorChunkHeader chunkHeader; XcursorUInt length; XcursorComment *comment; if (!file || !fileHeader) return NULL; /* read chunk header */ if (!_XcursorFileReadChunkHeader (file, fileHeader, toc, &chunkHeader)) return NULL; /* read extra comment header fields */ if (!_XcursorReadUInt (file, &length)) return NULL; comment = XcursorCommentCreate (chunkHeader.subtype, (int) length); if (!comment) return NULL; if (!_XcursorReadBytes (file, comment->comment, (int) length)) { XcursorCommentDestroy (comment); return NULL; } comment->comment[length] = '\0'; return comment; } static XcursorUInt _XcursorCommentLength (XcursorComment *comment) { return XCURSOR_COMMENT_HEADER_LEN + (XcursorUInt) strlen (comment->comment); } static XcursorBool _XcursorWriteComment (XcursorFile *file, XcursorFileHeader *fileHeader, int toc, XcursorComment *comment) { XcursorChunkHeader chunkHeader; XcursorUInt length; if (!file || !fileHeader || !comment || !comment->comment) return XcursorFalse; length = (XcursorUInt) strlen (comment->comment); /* sanity check data */ if (length > XCURSOR_COMMENT_MAX_LEN) return XcursorFalse; /* read chunk header */ chunkHeader.header = XCURSOR_COMMENT_HEADER_LEN; chunkHeader.type = XCURSOR_COMMENT_TYPE; chunkHeader.subtype = comment->comment_type; chunkHeader.version = XCURSOR_COMMENT_VERSION; if (!_XcursorFileWriteChunkHeader (file, fileHeader, toc, &chunkHeader)) return XcursorFalse; /* write extra comment header fields */ if (!_XcursorWriteUInt (file, length)) return XcursorFalse; if (!_XcursorWriteBytes (file, comment->comment, (int) length)) return XcursorFalse; return XcursorTrue; } XcursorImage * XcursorXcFileLoadImage (XcursorFile *file, int size) { XcursorFileHeader *fileHeader; XcursorDim bestSize; int nsize; int toc; XcursorImage *image; if (size < 0) return NULL; fileHeader = _XcursorReadFileHeader (file); if (!fileHeader) return NULL; bestSize = _XcursorFindBestSize (fileHeader, (XcursorDim) size, &nsize); if (!bestSize) return NULL; toc = _XcursorFindImageToc (fileHeader, bestSize, 0); if (toc < 0) return NULL; image = _XcursorReadImage (file, fileHeader, toc); _XcursorFileHeaderDestroy (fileHeader); return image; } XcursorImages * XcursorXcFileLoadImages (XcursorFile *file, int size) { XcursorFileHeader *fileHeader; XcursorDim bestSize; int nsize; XcursorImages *images; int n; if (!file || size < 0) return NULL; fileHeader = _XcursorReadFileHeader (file); if (!fileHeader) return NULL; bestSize = _XcursorFindBestSize (fileHeader, (XcursorDim) size, &nsize); if (!bestSize) { _XcursorFileHeaderDestroy (fileHeader); return NULL; } images = XcursorImagesCreate (nsize); if (!images) { _XcursorFileHeaderDestroy (fileHeader); return NULL; } for (n = 0; n < nsize; n++) { int toc = _XcursorFindImageToc (fileHeader, bestSize, n); if (toc < 0) break; images->images[images->nimage] = _XcursorReadImage (file, fileHeader, toc); if (!images->images[images->nimage]) break; images->nimage++; } _XcursorFileHeaderDestroy (fileHeader); if (images->nimage != nsize) { XcursorImagesDestroy (images); images = NULL; } return images; } XcursorImages * XcursorXcFileLoadAllImages (XcursorFile *file) { XcursorFileHeader *fileHeader; XcursorImage *image; XcursorImages *images; int nimage; XcursorUInt n; XcursorUInt toc; if (!file) return NULL; fileHeader = _XcursorReadFileHeader (file); if (!fileHeader) return NULL; nimage = 0; for (n = 0; n < fileHeader->ntoc; n++) { switch (fileHeader->tocs[n].type) { case XCURSOR_IMAGE_TYPE: nimage++; break; } } images = XcursorImagesCreate (nimage); if (!images) { _XcursorFileHeaderDestroy (fileHeader); return NULL; } for (toc = 0; toc < fileHeader->ntoc; toc++) { switch (fileHeader->tocs[toc].type) { case XCURSOR_IMAGE_TYPE: image = _XcursorReadImage (file, fileHeader, (int) toc); if (image) { images->images[images->nimage] = image; images->nimage++; } break; } } _XcursorFileHeaderDestroy (fileHeader); if (images->nimage != nimage) { XcursorImagesDestroy (images); images = NULL; } return images; } XcursorBool XcursorXcFileLoad (XcursorFile *file, XcursorComments **commentsp, XcursorImages **imagesp) { XcursorFileHeader *fileHeader; int nimage; int ncomment; XcursorImages *images; XcursorImage *image; XcursorComment *comment; XcursorComments *comments; XcursorUInt toc; if (!file) return 0; fileHeader = _XcursorReadFileHeader (file); if (!fileHeader) return 0; nimage = 0; ncomment = 0; for (toc = 0; toc < fileHeader->ntoc; toc++) { switch (fileHeader->tocs[toc].type) { case XCURSOR_COMMENT_TYPE: ncomment++; break; case XCURSOR_IMAGE_TYPE: nimage++; break; } } images = XcursorImagesCreate (nimage); if (!images) return 0; comments = XcursorCommentsCreate (ncomment); if (!comments) { XcursorImagesDestroy (images); return 0; } for (toc = 0; toc < fileHeader->ntoc; toc++) { switch (fileHeader->tocs[toc].type) { case XCURSOR_COMMENT_TYPE: comment = _XcursorReadComment (file, fileHeader, (int) toc); if (comment) { comments->comments[comments->ncomment] = comment; comments->ncomment++; } break; case XCURSOR_IMAGE_TYPE: image = _XcursorReadImage (file, fileHeader, (int) toc); if (image) { images->images[images->nimage] = image; images->nimage++; } break; } } _XcursorFileHeaderDestroy (fileHeader); if (images->nimage != nimage || comments->ncomment != ncomment) { XcursorImagesDestroy (images); XcursorCommentsDestroy (comments); images = NULL; comments = NULL; return XcursorFalse; } *imagesp = images; *commentsp = comments; return XcursorTrue; } XcursorBool XcursorXcFileSave (XcursorFile *file, const XcursorComments *comments, const XcursorImages *images) { XcursorFileHeader *fileHeader; XcursorUInt position; int n; int toc; if (!file || !comments || !images) return XcursorFalse; fileHeader = _XcursorFileHeaderCreate ((XcursorUInt) (comments->ncomment + images->nimage)); if (!fileHeader) return XcursorFalse; position = _XcursorFileHeaderLength (fileHeader); /* * Compute the toc. Place the images before the comments * as they're more often read */ toc = 0; for (n = 0; n < images->nimage; n++) { fileHeader->tocs[toc].type = XCURSOR_IMAGE_TYPE; fileHeader->tocs[toc].subtype = images->images[n]->size; fileHeader->tocs[toc].position = position; position += _XcursorImageLength (images->images[n]); toc++; } for (n = 0; n < comments->ncomment; n++) { fileHeader->tocs[toc].type = XCURSOR_COMMENT_TYPE; fileHeader->tocs[toc].subtype = comments->comments[n]->comment_type; fileHeader->tocs[toc].position = position; position += _XcursorCommentLength (comments->comments[n]); toc++; } /* * Write the header and the toc */ if (!_XcursorWriteFileHeader (file, fileHeader)) goto bail; /* * Write the images */ toc = 0; for (n = 0; n < images->nimage; n++) { if (!_XcursorWriteImage (file, fileHeader, toc, images->images[n])) goto bail; toc++; } /* * Write the comments */ for (n = 0; n < comments->ncomment; n++) { if (!_XcursorWriteComment (file, fileHeader, toc, comments->comments[n])) goto bail; toc++; } _XcursorFileHeaderDestroy (fileHeader); return XcursorTrue; bail: _XcursorFileHeaderDestroy (fileHeader); return XcursorFalse; } static int _XcursorStdioFileRead (XcursorFile *file, unsigned char *buf, int len) { FILE *f = file->closure; return (int) fread (buf, 1, (size_t) len, f); } static int _XcursorStdioFileWrite (XcursorFile *file, unsigned char *buf, int len) { FILE *f = file->closure; return (int) fwrite (buf, 1, (size_t) len, f); } static int _XcursorStdioFileSeek (XcursorFile *file, long offset, int whence) { FILE *f = file->closure; return fseek (f, offset, whence); } static void _XcursorStdioFileInitialize (FILE *stdfile, XcursorFile *file) { file->closure = stdfile; file->read = _XcursorStdioFileRead; file->write = _XcursorStdioFileWrite; file->seek = _XcursorStdioFileSeek; } XcursorImage * XcursorFileLoadImage (FILE *file, int size) { XcursorFile f; if (!file) return NULL; _XcursorStdioFileInitialize (file, &f); return XcursorXcFileLoadImage (&f, size); } XcursorImages * XcursorFileLoadImages (FILE *file, int size) { XcursorFile f; if (!file) return NULL; _XcursorStdioFileInitialize (file, &f); return XcursorXcFileLoadImages (&f, size); } XcursorImages * XcursorFileLoadAllImages (FILE *file) { XcursorFile f; if (!file) return NULL; _XcursorStdioFileInitialize (file, &f); return XcursorXcFileLoadAllImages (&f); } XcursorBool XcursorFileLoad (FILE *file, XcursorComments **commentsp, XcursorImages **imagesp) { XcursorFile f; if (!file || !commentsp || !imagesp) return XcursorFalse; _XcursorStdioFileInitialize (file, &f); return XcursorXcFileLoad (&f, commentsp, imagesp); } XcursorBool XcursorFileSaveImages (FILE *file, const XcursorImages *images) { XcursorComments *comments; XcursorFile f; XcursorBool ret; if (!file || !images) return 0; if ((comments = XcursorCommentsCreate (0)) == NULL) return 0; _XcursorStdioFileInitialize (file, &f); ret = XcursorXcFileSave (&f, comments, images) && fflush (file) != EOF; XcursorCommentsDestroy (comments); return ret; } XcursorBool XcursorFileSave (FILE * file, const XcursorComments *comments, const XcursorImages *images) { XcursorFile f; if (!file || !comments || !images) return XcursorFalse; _XcursorStdioFileInitialize (file, &f); return XcursorXcFileSave (&f, comments, images) && fflush (file) != EOF; } XcursorImage * XcursorFilenameLoadImage (const char *file, int size) { FILE *f; XcursorImage *image; if (!file || size < 0) return NULL; f = fopen (file, "r" FOPEN_CLOEXEC); if (!f) return NULL; image = XcursorFileLoadImage (f, size); fclose (f); return image; } XcursorImages * XcursorFilenameLoadImages (const char *file, int size) { FILE *f; XcursorImages *images; if (!file || size < 0) return NULL; f = fopen (file, "r" FOPEN_CLOEXEC); if (!f) return NULL; images = XcursorFileLoadImages (f, size); fclose (f); return images; } XcursorImages * XcursorFilenameLoadAllImages (const char *file) { FILE *f; XcursorImages *images; if (!file) return NULL; f = fopen (file, "r" FOPEN_CLOEXEC); if (!f) return NULL; images = XcursorFileLoadAllImages (f); fclose (f); return images; } XcursorBool XcursorFilenameLoad (const char *file, XcursorComments **commentsp, XcursorImages **imagesp) { FILE *f; XcursorBool ret; if (!file) return XcursorFalse; f = fopen (file, "r" FOPEN_CLOEXEC); if (!f) return 0; ret = XcursorFileLoad (f, commentsp, imagesp); fclose (f); return ret; } XcursorBool XcursorFilenameSaveImages (const char *file, const XcursorImages *images) { FILE *f; XcursorBool ret; if (!file || !images) return XcursorFalse; f = fopen (file, "w" FOPEN_CLOEXEC); if (!f) return 0; ret = XcursorFileSaveImages (f, images); return fclose (f) != EOF && ret; } XcursorBool XcursorFilenameSave (const char *file, const XcursorComments *comments, const XcursorImages *images) { FILE *f; XcursorBool ret; if (!file || !comments || !images) return XcursorFalse; f = fopen (file, "w" FOPEN_CLOEXEC); if (!f) return 0; ret = XcursorFileSave (f, comments, images); return fclose (f) != EOF && ret; }