file.c revision 6b7436ae
1/* 2 * Copyright © 2024 Thomas E. Dickey 3 * Copyright © 2002 Keith Packard 4 * 5 * Permission to use, copy, modify, distribute, and sell this software and its 6 * documentation for any purpose is hereby granted without fee, provided that 7 * the above copyright notice appear in all copies and that both that 8 * copyright notice and this permission notice appear in supporting 9 * documentation, and that the name of Keith Packard not be used in 10 * advertising or publicity pertaining to distribution of the software without 11 * specific, written prior permission. Keith Packard makes no 12 * representations about the suitability of this software for any purpose. It 13 * is provided "as is" without express or implied warranty. 14 * 15 * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 16 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 17 * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR 18 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 19 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 20 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 21 * PERFORMANCE OF THIS SOFTWARE. 22 */ 23 24#include "xcursorint.h" 25#include <stdlib.h> 26#include <string.h> 27 28#ifdef DEBUG_XCURSOR 29#include <stdarg.h> 30#include <sys/types.h> 31#include <sys/stat.h> 32void _XcursorTrace(const char *fmt, ...) 33{ 34 FILE *fp = fopen("/tmp/xcursor.log", "a"); 35 if (fp != NULL) { 36 unsigned save = umask(0); 37 va_list ap; 38 va_start(ap, fmt); 39 vfprintf(fp, fmt, ap); 40 va_end(ap); 41 fclose(fp); 42 umask(save); 43 } 44} 45 46void *_XcursorReturnAddr(void *addr) 47{ 48 _XcursorTrace(T_RETURN(p), addr); 49 return addr; 50} 51 52int _XcursorReturnCode(int code) 53{ 54 _XcursorTrace(T_RETURN(d), code); 55 return code; 56} 57 58unsigned long _XcursorReturnLong(unsigned long code) 59{ 60 _XcursorTrace(T_RETURN(lu), code); 61 return code; 62} 63 64unsigned _XcursorReturnUint(unsigned code) 65{ 66 _XcursorTrace(T_RETURN(u), code); 67 return code; 68} 69 70void _XcursorReturnVoid(void) 71{ 72 _XcursorTrace(T_RETURN(s), ""); 73 return; 74} 75#endif /* DEBUG_XCURSOR */ 76 77XcursorImage * 78XcursorImageCreate (int width, int height) 79{ 80 XcursorImage *image = NULL; 81 82 enterFunc((T_CALLED(XcursorImageCreate) "(%d, %d)\n", width, height)); 83 84 if (width < 0 || height < 0) { 85 /* EMPTY */; 86 } else if (width > XCURSOR_IMAGE_MAX_SIZE 87 || height > XCURSOR_IMAGE_MAX_SIZE) { 88 /* EMPTY */; 89 } else { 90 image = malloc (sizeof (XcursorImage) + 91 (size_t) (width * height) * sizeof (XcursorPixel)); 92 if (image) { 93 image->version = XCURSOR_IMAGE_VERSION; 94 image->pixels = (XcursorPixel *) (image + 1); 95 image->size = (XcursorDim) (width > height ? width : height); 96 image->width = (XcursorDim) width; 97 image->height = (XcursorDim) height; 98 image->delay = 0; 99 } 100 } 101 returnAddr(image); 102} 103 104void 105XcursorImageDestroy (XcursorImage *image) 106{ 107 enterFunc((T_CALLED(XcursorImageDestroy ) "(%p)\n", (void*)image)); 108 109 free (image); 110 111 returnVoid(); 112} 113 114XcursorImages * 115XcursorImagesCreate (int size) 116{ 117 XcursorImages *images; 118 119 enterFunc((T_CALLED(XcursorImagesCreate) "(%d)\n", size)); 120 121 images = malloc (sizeof (XcursorImages) + 122 (size_t) size * sizeof (XcursorImage *)); 123 if (images) { 124 images->nimage = 0; 125 images->images = (XcursorImage **) (images + 1); 126 images->name = NULL; 127 } 128 returnAddr(images); 129} 130 131void 132XcursorImagesDestroy (XcursorImages *images) 133{ 134 enterFunc((T_CALLED(XcursorImagesDestroy) "(%p)\n", (void*)images)); 135 136 if (images) { 137 int n; 138 139 for (n = 0; n < images->nimage; n++) 140 XcursorImageDestroy (images->images[n]); 141 if (images->name) 142 free (images->name); 143 free (images); 144 } 145 146 returnVoid(); 147} 148 149void 150XcursorImagesSetName (XcursorImages *images, const char *name) 151{ 152 enterFunc((T_CALLED(XcursorImagesSetName) "(%p, \"%s\")\n", 153 (void*)images, 154 NonNull(name))); 155 156 if (images && name) { 157 char *new = strdup (name); 158 159 if (new) { 160 if (images->name) 161 free (images->name); 162 images->name = new; 163 } 164 } 165 166 returnVoid(); 167} 168 169XcursorComment * 170XcursorCommentCreate (XcursorUInt comment_type, int length) 171{ 172 XcursorComment *comment; 173 174 if (length < 0 || length > XCURSOR_COMMENT_MAX_LEN) 175 return NULL; 176 177 comment = malloc (sizeof (XcursorComment) + (size_t) length + 1); 178 if (!comment) 179 return NULL; 180 comment->version = XCURSOR_COMMENT_VERSION; 181 comment->comment_type = comment_type; 182 comment->comment = (char *) (comment + 1); 183 comment->comment[0] = '\0'; 184 return comment; 185} 186 187void 188XcursorCommentDestroy (XcursorComment *comment) 189{ 190 free (comment); 191} 192 193XcursorComments * 194XcursorCommentsCreate (int size) 195{ 196 XcursorComments *comments; 197 198 comments = malloc (sizeof (XcursorComments) + 199 (size_t) size * sizeof (XcursorComment *)); 200 if (!comments) 201 return NULL; 202 comments->ncomment = 0; 203 comments->comments = (XcursorComment **) (comments + 1); 204 return comments; 205} 206 207void 208XcursorCommentsDestroy (XcursorComments *comments) 209{ 210 int n; 211 212 if (!comments) 213 return; 214 215 for (n = 0; n < comments->ncomment; n++) 216 XcursorCommentDestroy (comments->comments[n]); 217 free (comments); 218} 219 220static XcursorBool 221_XcursorReadUInt (XcursorFile *file, XcursorUInt *u) 222{ 223 unsigned char bytes[4]; 224 225 if (!file || !u) 226 return XcursorFalse; 227 228 if ((*file->read) (file, bytes, 4) != 4) 229 return XcursorFalse; 230 231 *u = ((XcursorUInt)(bytes[0]) << 0) | 232 ((XcursorUInt)(bytes[1]) << 8) | 233 ((XcursorUInt)(bytes[2]) << 16) | 234 ((XcursorUInt)(bytes[3]) << 24); 235 return XcursorTrue; 236} 237 238static XcursorBool 239_XcursorReadBytes (XcursorFile *file, char *bytes, int length) 240{ 241 if (!file || !bytes || (*file->read) (file, (unsigned char *) bytes, length) != length) 242 return XcursorFalse; 243 return XcursorTrue; 244} 245 246static XcursorBool 247_XcursorWriteUInt (XcursorFile *file, XcursorUInt u) 248{ 249 unsigned char bytes[4]; 250 251 if (!file) 252 return XcursorFalse; 253 254 bytes[0] = (unsigned char)(u); 255 bytes[1] = (unsigned char)(u >> 8); 256 bytes[2] = (unsigned char)(u >> 16); 257 bytes[3] = (unsigned char)(u >> 24); 258 if ((*file->write) (file, bytes, 4) != 4) 259 return XcursorFalse; 260 return XcursorTrue; 261} 262 263static XcursorBool 264_XcursorWriteBytes (XcursorFile *file, char *bytes, int length) 265{ 266 if (!file || !bytes || (*file->write) (file, (unsigned char *) bytes, length) != length) 267 return XcursorFalse; 268 return XcursorTrue; 269} 270 271static void 272_XcursorFileHeaderDestroy (XcursorFileHeader *fileHeader) 273{ 274 free (fileHeader); 275} 276 277static XcursorFileHeader * 278_XcursorFileHeaderCreate (XcursorUInt ntoc) 279{ 280 XcursorFileHeader *fileHeader; 281 282 if (ntoc > 0x10000) 283 return NULL; 284 fileHeader = malloc (sizeof (XcursorFileHeader) + 285 ntoc * sizeof (XcursorFileToc)); 286 if (!fileHeader) 287 return NULL; 288 fileHeader->magic = XCURSOR_MAGIC; 289 fileHeader->header = XCURSOR_FILE_HEADER_LEN; 290 fileHeader->version = XCURSOR_FILE_VERSION; 291 fileHeader->ntoc = ntoc; 292 fileHeader->tocs = (XcursorFileToc *) (fileHeader + 1); 293 return fileHeader; 294} 295 296static XcursorFileHeader * 297_XcursorReadFileHeader (XcursorFile *file) 298{ 299 XcursorFileHeader head, *fileHeader; 300 XcursorUInt skip; 301 XcursorUInt n; 302 303 if (!file) 304 return NULL; 305 306 if (!_XcursorReadUInt (file, &head.magic)) 307 return NULL; 308 if (head.magic != XCURSOR_MAGIC) 309 return NULL; 310 if (!_XcursorReadUInt (file, &head.header)) 311 return NULL; 312 if (head.header < XCURSOR_FILE_HEADER_LEN) 313 return NULL; 314 if (!_XcursorReadUInt (file, &head.version)) 315 return NULL; 316 if (!_XcursorReadUInt (file, &head.ntoc)) 317 return NULL; 318 skip = head.header - XCURSOR_FILE_HEADER_LEN; 319 if (skip) 320 if ((*file->seek) (file, skip, SEEK_CUR) == EOF) 321 return NULL; 322 fileHeader = _XcursorFileHeaderCreate (head.ntoc); 323 if (!fileHeader) 324 return NULL; 325 fileHeader->magic = head.magic; 326 fileHeader->header = head.header; 327 fileHeader->version = head.version; 328 fileHeader->ntoc = head.ntoc; 329 for (n = 0; n < fileHeader->ntoc; n++) 330 { 331 if (!_XcursorReadUInt (file, &fileHeader->tocs[n].type)) 332 break; 333 if (!_XcursorReadUInt (file, &fileHeader->tocs[n].subtype)) 334 break; 335 if (!_XcursorReadUInt (file, &fileHeader->tocs[n].position)) 336 break; 337 } 338 if (n != fileHeader->ntoc) 339 { 340 _XcursorFileHeaderDestroy (fileHeader); 341 return NULL; 342 } 343 return fileHeader; 344} 345 346static XcursorUInt 347_XcursorFileHeaderLength (XcursorFileHeader *fileHeader) 348{ 349 return (XCURSOR_FILE_HEADER_LEN + 350 fileHeader->ntoc * XCURSOR_FILE_TOC_LEN); 351} 352 353static XcursorBool 354_XcursorWriteFileHeader (XcursorFile *file, XcursorFileHeader *fileHeader) 355{ 356 XcursorUInt toc; 357 358 if (!file || !fileHeader) 359 return XcursorFalse; 360 361 if (!_XcursorWriteUInt (file, fileHeader->magic)) 362 return XcursorFalse; 363 if (!_XcursorWriteUInt (file, fileHeader->header)) 364 return XcursorFalse; 365 if (!_XcursorWriteUInt (file, fileHeader->version)) 366 return XcursorFalse; 367 if (!_XcursorWriteUInt (file, fileHeader->ntoc)) 368 return XcursorFalse; 369 for (toc = 0; toc < fileHeader->ntoc; toc++) 370 { 371 if (!_XcursorWriteUInt (file, fileHeader->tocs[toc].type)) 372 return XcursorFalse; 373 if (!_XcursorWriteUInt (file, fileHeader->tocs[toc].subtype)) 374 return XcursorFalse; 375 if (!_XcursorWriteUInt (file, fileHeader->tocs[toc].position)) 376 return XcursorFalse; 377 } 378 return XcursorTrue; 379} 380 381static XcursorBool 382_XcursorSeekToToc (XcursorFile *file, 383 XcursorFileHeader *fileHeader, 384 int toc) 385{ 386 if (!file || !fileHeader || \ 387 (*file->seek) (file, fileHeader->tocs[toc].position, SEEK_SET) == EOF) 388 return XcursorFalse; 389 return XcursorTrue; 390} 391 392static XcursorBool 393_XcursorFileReadChunkHeader (XcursorFile *file, 394 XcursorFileHeader *fileHeader, 395 int toc, 396 XcursorChunkHeader *chunkHeader) 397{ 398 if (!file || !fileHeader || !chunkHeader) 399 return XcursorFalse; 400 if (!_XcursorSeekToToc (file, fileHeader, toc)) 401 return XcursorFalse; 402 if (!_XcursorReadUInt (file, &chunkHeader->header)) 403 return XcursorFalse; 404 if (!_XcursorReadUInt (file, &chunkHeader->type)) 405 return XcursorFalse; 406 if (!_XcursorReadUInt (file, &chunkHeader->subtype)) 407 return XcursorFalse; 408 if (!_XcursorReadUInt (file, &chunkHeader->version)) 409 return XcursorFalse; 410 /* sanity check */ 411 if (chunkHeader->type != fileHeader->tocs[toc].type || 412 chunkHeader->subtype != fileHeader->tocs[toc].subtype) 413 return XcursorFalse; 414 return XcursorTrue; 415} 416 417static XcursorBool 418_XcursorFileWriteChunkHeader (XcursorFile *file, 419 XcursorFileHeader *fileHeader, 420 int toc, 421 XcursorChunkHeader *chunkHeader) 422{ 423 if (!file || !fileHeader || !chunkHeader) 424 return XcursorFalse; 425 if (!_XcursorSeekToToc (file, fileHeader, toc)) 426 return XcursorFalse; 427 if (!_XcursorWriteUInt (file, chunkHeader->header)) 428 return XcursorFalse; 429 if (!_XcursorWriteUInt (file, chunkHeader->type)) 430 return XcursorFalse; 431 if (!_XcursorWriteUInt (file, chunkHeader->subtype)) 432 return XcursorFalse; 433 if (!_XcursorWriteUInt (file, chunkHeader->version)) 434 return XcursorFalse; 435 return XcursorTrue; 436} 437 438#define dist(a,b) ((a) > (b) ? (a) - (b) : (b) - (a)) 439 440static XcursorDim 441_XcursorFindBestSize (XcursorFileHeader *fileHeader, 442 XcursorDim size, 443 int *nsizesp) 444{ 445 XcursorUInt n; 446 int nsizes = 0; 447 XcursorDim bestSize = 0; 448 XcursorDim thisSize; 449 450 enterFunc((T_CALLED(_XcursorFindBestSize) "(%p, %u, %p)\n", 451 (void*)fileHeader, size, (void*)nsizesp)); 452 453 if (!fileHeader || !nsizesp) 454 returnUint(0); 455 456 for (n = 0; n < fileHeader->ntoc; n++) 457 { 458 if (fileHeader->tocs[n].type != XCURSOR_IMAGE_TYPE) 459 continue; 460 thisSize = fileHeader->tocs[n].subtype; 461 if (!bestSize || dist (thisSize, size) < dist (bestSize, size)) 462 { 463 bestSize = thisSize; 464 nsizes = 1; 465 } 466 else if (thisSize == bestSize) 467 nsizes++; 468 } 469 *nsizesp = nsizes; 470 returnUint(bestSize); 471} 472 473static int 474_XcursorFindImageToc (XcursorFileHeader *fileHeader, 475 XcursorDim size, 476 int count) 477{ 478 XcursorUInt toc; 479 XcursorDim thisSize; 480 481 enterFunc((T_CALLED(_XcursorFindImageToc) "(%p, %u, %d)\n", 482 (void*)fileHeader, size, count)); 483 484 if (!fileHeader) 485 returnCode(0); 486 487 for (toc = 0; toc < fileHeader->ntoc; toc++) 488 { 489 if (fileHeader->tocs[toc].type != XCURSOR_IMAGE_TYPE) 490 continue; 491 thisSize = fileHeader->tocs[toc].subtype; 492 if (thisSize != size) 493 continue; 494 if (!count) 495 break; 496 count--; 497 } 498 if (toc == fileHeader->ntoc) 499 returnCode(-1); 500 returnCode((int) toc); 501} 502 503static XcursorImage * 504_XcursorReadImage (XcursorFile *file, 505 XcursorFileHeader *fileHeader, 506 int toc) 507{ 508 XcursorChunkHeader chunkHeader; 509 XcursorImage head; 510 XcursorImage *image; 511 int n; 512 XcursorPixel *p; 513 514 enterFunc((T_CALLED(_XcursorReadImage) "(%p, %p, %d)\n", 515 (void*)file, (void*)fileHeader, toc)); 516 517 if (!file || !fileHeader) 518 returnAddr(NULL); 519 520 if (!_XcursorFileReadChunkHeader (file, fileHeader, toc, &chunkHeader)) 521 returnAddr(NULL); 522 if (!_XcursorReadUInt (file, &head.width)) 523 returnAddr(NULL); 524 if (!_XcursorReadUInt (file, &head.height)) 525 returnAddr(NULL); 526 if (!_XcursorReadUInt (file, &head.xhot)) 527 returnAddr(NULL); 528 if (!_XcursorReadUInt (file, &head.yhot)) 529 returnAddr(NULL); 530 if (!_XcursorReadUInt (file, &head.delay)) 531 returnAddr(NULL); 532 /* sanity check data */ 533 if (head.width > XCURSOR_IMAGE_MAX_SIZE || 534 head.height > XCURSOR_IMAGE_MAX_SIZE) 535 returnAddr(NULL); 536 if (head.width == 0 || head.height == 0) 537 returnAddr(NULL); 538 if (head.xhot > head.width || head.yhot > head.height) 539 returnAddr(NULL); 540 541 /* Create the image and initialize it */ 542 image = XcursorImageCreate ((int) head.width, (int) head.height); 543 if (image == NULL) 544 returnAddr(NULL); 545 if (chunkHeader.version < image->version) 546 image->version = chunkHeader.version; 547 image->size = chunkHeader.subtype; 548 image->xhot = head.xhot; 549 image->yhot = head.yhot; 550 image->delay = head.delay; 551 n = (int) (image->width * image->height); 552 p = image->pixels; 553 while (n--) 554 { 555 if (!_XcursorReadUInt (file, p)) 556 { 557 XcursorImageDestroy (image); 558 returnAddr(NULL); 559 } 560 p++; 561 } 562 returnAddr(image); 563} 564 565static XcursorUInt 566_XcursorImageLength (XcursorImage *image) 567{ 568 enterFunc((T_CALLED(_XcursorImageLength) "(%p)\n", (void*)image)); 569 570 if (!image) 571 returnUint(0); 572 573 returnUint(XCURSOR_IMAGE_HEADER_LEN + (image->width * image->height) * 4); 574} 575 576static XcursorBool 577_XcursorWriteImage (XcursorFile *file, 578 XcursorFileHeader *fileHeader, 579 int toc, 580 XcursorImage *image) 581{ 582 XcursorChunkHeader chunkHeader; 583 int n; 584 XcursorPixel *p; 585 586 enterFunc((T_CALLED(_XcursorWriteImage) "(%p, %p, %d, %p)\n", 587 (void*)file, (void*)fileHeader, toc, (void*)image)); 588 589 if (!file || !fileHeader || !image) 590 returnCode(XcursorFalse); 591 592 /* sanity check data */ 593 if (image->width > XCURSOR_IMAGE_MAX_SIZE || 594 image->height > XCURSOR_IMAGE_MAX_SIZE) 595 returnCode(XcursorFalse); 596 if (image->width == 0 || image->height == 0) 597 returnCode(XcursorFalse); 598 if (image->xhot > image->width || image->yhot > image->height) 599 returnCode(XcursorFalse); 600 601 /* write chunk header */ 602 chunkHeader.header = XCURSOR_IMAGE_HEADER_LEN; 603 chunkHeader.type = XCURSOR_IMAGE_TYPE; 604 chunkHeader.subtype = image->size; 605 chunkHeader.version = XCURSOR_IMAGE_VERSION; 606 607 if (!_XcursorFileWriteChunkHeader (file, fileHeader, toc, &chunkHeader)) 608 returnCode(XcursorFalse); 609 610 /* write extra image header fields */ 611 if (!_XcursorWriteUInt (file, image->width)) 612 returnCode(XcursorFalse); 613 if (!_XcursorWriteUInt (file, image->height)) 614 returnCode(XcursorFalse); 615 if (!_XcursorWriteUInt (file, image->xhot)) 616 returnCode(XcursorFalse); 617 if (!_XcursorWriteUInt (file, image->yhot)) 618 returnCode(XcursorFalse); 619 if (!_XcursorWriteUInt (file, image->delay)) 620 returnCode(XcursorFalse); 621 622 /* write the image */ 623 n = (int) (image->width * image->height); 624 p = image->pixels; 625 while (n--) 626 { 627 if (!_XcursorWriteUInt (file, *p)) 628 returnCode(XcursorFalse); 629 p++; 630 } 631 returnCode(XcursorTrue); 632} 633 634static XcursorComment * 635_XcursorReadComment (XcursorFile *file, 636 XcursorFileHeader *fileHeader, 637 int toc) 638{ 639 XcursorChunkHeader chunkHeader; 640 XcursorUInt length; 641 XcursorComment *comment; 642 643 enterFunc((T_CALLED(_XcursorReadComment) "(%p, %p, %d)\n", 644 (void*)file, (void*)fileHeader, toc)); 645 646 if (!file || !fileHeader) 647 returnAddr(NULL); 648 649 /* read chunk header */ 650 if (!_XcursorFileReadChunkHeader (file, fileHeader, toc, &chunkHeader)) 651 returnAddr(NULL); 652 /* read extra comment header fields */ 653 if (!_XcursorReadUInt (file, &length)) 654 returnAddr(NULL); 655 comment = XcursorCommentCreate (chunkHeader.subtype, (int) length); 656 if (!comment) 657 returnAddr(NULL); 658 if (!_XcursorReadBytes (file, comment->comment, (int) length)) 659 { 660 XcursorCommentDestroy (comment); 661 returnAddr(NULL); 662 } 663 comment->comment[length] = '\0'; 664 returnAddr(comment); 665} 666 667static XcursorUInt 668_XcursorCommentLength (XcursorComment *comment) 669{ 670 return XCURSOR_COMMENT_HEADER_LEN + (XcursorUInt) strlen (comment->comment); 671} 672 673static XcursorBool 674_XcursorWriteComment (XcursorFile *file, 675 XcursorFileHeader *fileHeader, 676 int toc, 677 XcursorComment *comment) 678{ 679 XcursorChunkHeader chunkHeader; 680 XcursorUInt length; 681 682 if (!file || !fileHeader || !comment || !comment->comment) 683 return XcursorFalse; 684 685 length = (XcursorUInt) strlen (comment->comment); 686 687 /* sanity check data */ 688 if (length > XCURSOR_COMMENT_MAX_LEN) 689 return XcursorFalse; 690 691 /* read chunk header */ 692 chunkHeader.header = XCURSOR_COMMENT_HEADER_LEN; 693 chunkHeader.type = XCURSOR_COMMENT_TYPE; 694 chunkHeader.subtype = comment->comment_type; 695 chunkHeader.version = XCURSOR_COMMENT_VERSION; 696 697 if (!_XcursorFileWriteChunkHeader (file, fileHeader, toc, &chunkHeader)) 698 return XcursorFalse; 699 700 /* write extra comment header fields */ 701 if (!_XcursorWriteUInt (file, length)) 702 return XcursorFalse; 703 704 if (!_XcursorWriteBytes (file, comment->comment, (int) length)) 705 return XcursorFalse; 706 return XcursorTrue; 707} 708 709static XcursorImage * 710_XcursorResizeImage (XcursorImage *src, int size) 711{ 712 XcursorDim dest_y, dest_x; 713 double scale = (double) size / src->size; 714 XcursorImage *dest; 715 716 enterFunc((T_CALLED(_XcursorResizeImage) "(%p, %d)\n", (void*)src, size)); 717 718 dest = XcursorImageCreate ((int) (src->width * scale), 719 (int) (src->height * scale)); 720 if (!dest) 721 returnAddr(NULL); 722 723 dest->size = (XcursorDim) size; 724 dest->xhot = (XcursorDim) (src->xhot * scale); 725 dest->yhot = (XcursorDim) (src->yhot * scale); 726 dest->delay = src->delay; 727 728 for (dest_y = 0; dest_y < dest->height; dest_y++) 729 { 730 XcursorDim src_y = (XcursorDim) (dest_y / scale); 731 XcursorPixel *src_row = src->pixels + (src_y * src->width); 732 XcursorPixel *dest_row = dest->pixels + (dest_y * dest->width); 733 for (dest_x = 0; dest_x < dest->width; dest_x++) 734 { 735 XcursorDim src_x = (XcursorDim) (dest_x / scale); 736 dest_row[dest_x] = src_row[src_x]; 737 } 738 } 739 740 returnAddr(dest); 741} 742 743static XcursorImage * 744_XcursorXcFileLoadImage (XcursorFile *file, int size, XcursorBool resize) 745{ 746 XcursorFileHeader *fileHeader; 747 XcursorDim bestSize; 748 int nsize; 749 int toc; 750 XcursorImage *image; 751 752 enterFunc((T_CALLED(_XcursorXcFileLoadImage) "(%p, %d, %d)\n", 753 (void*)file, size, resize)); 754 755 if (size < 0) 756 returnAddr(NULL); 757 fileHeader = _XcursorReadFileHeader (file); 758 if (!fileHeader) 759 returnAddr(NULL); 760 bestSize = _XcursorFindBestSize (fileHeader, (XcursorDim) size, &nsize); 761 if (!bestSize) 762 returnAddr(NULL); 763 toc = _XcursorFindImageToc (fileHeader, bestSize, 0); 764 if (toc < 0) 765 returnAddr(NULL); 766 image = _XcursorReadImage (file, fileHeader, toc); 767 _XcursorFileHeaderDestroy (fileHeader); 768 769 if (resize && image != NULL && (image->size != (XcursorDim) size)) 770 { 771 XcursorImage *resized_image = _XcursorResizeImage (image, size); 772 XcursorImageDestroy (image); 773 image = resized_image; 774 } 775 776 returnAddr(image); 777} 778 779XcursorImage * 780XcursorXcFileLoadImage (XcursorFile *file, int size) 781{ 782 enterFunc((T_CALLED(XcursorXcFileLoadImage) "(%p, %d)\n", (void*)file, size)); 783 784 returnAddr(_XcursorXcFileLoadImage (file, size, XcursorFalse)); 785} 786 787XcursorImages * 788_XcursorXcFileLoadImages (XcursorFile *file, int size, XcursorBool resize) 789{ 790 XcursorFileHeader *fileHeader; 791 XcursorDim bestSize; 792 int nsize; 793 XcursorImages *images; 794 int n; 795 XcursorImage *image; 796 797 enterFunc((T_CALLED(_XcursorXcFileLoadImages) "(%p, %d, %d)\n", 798 (void*)file, size, resize)); 799 800 if (!file || size < 0) 801 returnAddr(NULL); 802 fileHeader = _XcursorReadFileHeader (file); 803 if (!fileHeader) 804 returnAddr(NULL); 805 bestSize = _XcursorFindBestSize (fileHeader, (XcursorDim) size, &nsize); 806 if (!bestSize) 807 { 808 _XcursorFileHeaderDestroy (fileHeader); 809 returnAddr(NULL); 810 } 811 images = XcursorImagesCreate (nsize); 812 if (!images) 813 { 814 _XcursorFileHeaderDestroy (fileHeader); 815 returnAddr(NULL); 816 } 817 for (n = 0; n < nsize; n++) 818 { 819 int toc = _XcursorFindImageToc (fileHeader, bestSize, n); 820 if (toc < 0) 821 break; 822 image = _XcursorReadImage (file, fileHeader, toc); 823 if (!image) 824 break; 825 if (resize && (image->size != (XcursorDim) size)) 826 { 827 XcursorImage *resized_image = _XcursorResizeImage (image, size); 828 XcursorImageDestroy (image); 829 image = resized_image; 830 if (image == NULL) 831 break; 832 } 833 images->images[images->nimage] = image; 834 images->nimage++; 835 } 836 _XcursorFileHeaderDestroy (fileHeader); 837 if (images != NULL && images->nimage != nsize) 838 { 839 XcursorImagesDestroy (images); 840 images = NULL; 841 } 842 returnAddr(images); 843} 844 845XcursorImages * 846XcursorXcFileLoadImages (XcursorFile *file, int size) 847{ 848 enterFunc((T_CALLED(XcursorXcFileLoadImages) "(%p, %d)\n", (void*)file, size)); 849 850 returnAddr(_XcursorXcFileLoadImages (file, size, XcursorFalse)); 851} 852 853XcursorImages * 854XcursorXcFileLoadAllImages (XcursorFile *file) 855{ 856 XcursorFileHeader *fileHeader; 857 XcursorImage *image; 858 XcursorImages *images; 859 int nimage; 860 XcursorUInt n; 861 XcursorUInt toc; 862 863 enterFunc((T_CALLED(XcursorXcFileLoadAllImages) "(%p)\n", (void*)file)); 864 865 if (!file) 866 returnAddr(NULL); 867 868 fileHeader = _XcursorReadFileHeader (file); 869 if (!fileHeader) 870 returnAddr(NULL); 871 nimage = 0; 872 for (n = 0; n < fileHeader->ntoc; n++) 873 { 874 switch (fileHeader->tocs[n].type) { 875 case XCURSOR_IMAGE_TYPE: 876 nimage++; 877 break; 878 } 879 } 880 images = XcursorImagesCreate (nimage); 881 if (!images) 882 { 883 _XcursorFileHeaderDestroy (fileHeader); 884 returnAddr(NULL); 885 } 886 for (toc = 0; toc < fileHeader->ntoc; toc++) 887 { 888 switch (fileHeader->tocs[toc].type) { 889 case XCURSOR_IMAGE_TYPE: 890 image = _XcursorReadImage (file, fileHeader, (int) toc); 891 if (image) 892 { 893 images->images[images->nimage] = image; 894 images->nimage++; 895 } 896 break; 897 } 898 } 899 _XcursorFileHeaderDestroy (fileHeader); 900 if (images->nimage != nimage) 901 { 902 XcursorImagesDestroy (images); 903 images = NULL; 904 } 905 returnAddr(images); 906} 907 908XcursorBool 909XcursorXcFileLoad (XcursorFile *file, 910 XcursorComments **commentsp, 911 XcursorImages **imagesp) 912{ 913 XcursorFileHeader *fileHeader; 914 int nimage; 915 int ncomment; 916 XcursorImages *images; 917 XcursorImage *image; 918 XcursorComment *comment; 919 XcursorComments *comments; 920 XcursorUInt toc; 921 922 enterFunc((T_CALLED(XcursorXcFileLoad) "(%p, %p, %p)\n", 923 (void*)file, (void*)commentsp, (void*)imagesp)); 924 925 if (!file) 926 returnCode(0); 927 fileHeader = _XcursorReadFileHeader (file); 928 if (!fileHeader) 929 returnCode(0); 930 nimage = 0; 931 ncomment = 0; 932 for (toc = 0; toc < fileHeader->ntoc; toc++) 933 { 934 switch (fileHeader->tocs[toc].type) { 935 case XCURSOR_COMMENT_TYPE: 936 ncomment++; 937 break; 938 case XCURSOR_IMAGE_TYPE: 939 nimage++; 940 break; 941 } 942 } 943 images = XcursorImagesCreate (nimage); 944 if (!images) 945 { 946 _XcursorFileHeaderDestroy (fileHeader); 947 returnCode(0); 948 } 949 comments = XcursorCommentsCreate (ncomment); 950 if (!comments) 951 { 952 _XcursorFileHeaderDestroy (fileHeader); 953 XcursorImagesDestroy (images); 954 returnCode(0); 955 } 956 for (toc = 0; toc < fileHeader->ntoc; toc++) 957 { 958 switch (fileHeader->tocs[toc].type) { 959 case XCURSOR_COMMENT_TYPE: 960 comment = _XcursorReadComment (file, fileHeader, (int) toc); 961 if (comment) 962 { 963 comments->comments[comments->ncomment] = comment; 964 comments->ncomment++; 965 } 966 break; 967 case XCURSOR_IMAGE_TYPE: 968 image = _XcursorReadImage (file, fileHeader, (int) toc); 969 if (image) 970 { 971 images->images[images->nimage] = image; 972 images->nimage++; 973 } 974 break; 975 } 976 } 977 _XcursorFileHeaderDestroy (fileHeader); 978 if (images->nimage != nimage || comments->ncomment != ncomment) 979 { 980 XcursorImagesDestroy (images); 981 XcursorCommentsDestroy (comments); 982 images = NULL; 983 comments = NULL; 984 returnCode(XcursorFalse); 985 } 986 *imagesp = images; 987 *commentsp = comments; 988 returnCode(XcursorTrue); 989} 990 991XcursorBool 992XcursorXcFileSave (XcursorFile *file, 993 const XcursorComments *comments, 994 const XcursorImages *images) 995{ 996 XcursorFileHeader *fileHeader; 997 XcursorUInt position; 998 XcursorUInt n; 999 int toc; 1000 XcursorUInt ncomment; 1001 XcursorUInt nimage; 1002 1003 enterFunc((T_CALLED(XcursorXcFileSave) "(%p, %p, %p)\n", 1004 (void*)file, (const void*)comments, (const void*)images)); 1005 1006 if (!file || !comments || !images) 1007 returnCode(XcursorFalse); 1008 1009 /* 1010 * Caller may have tainted the counts. 1011 */ 1012 ncomment = (XcursorUInt)(comments->ncomment > 0 ? comments->ncomment : 0); 1013 nimage = (XcursorUInt)(images->nimage > 0 ? images->nimage : 0); 1014 1015 fileHeader = _XcursorFileHeaderCreate (ncomment + nimage); 1016 if (!fileHeader) 1017 returnCode(XcursorFalse); 1018 1019 position = _XcursorFileHeaderLength (fileHeader); 1020 1021 /* 1022 * Compute the toc. Place the images before the comments 1023 * as they're more often read 1024 */ 1025 1026 toc = 0; 1027 for (n = 0; n < nimage; n++) 1028 { 1029 fileHeader->tocs[toc].type = XCURSOR_IMAGE_TYPE; 1030 fileHeader->tocs[toc].subtype = images->images[n]->size; 1031 fileHeader->tocs[toc].position = position; 1032 position += _XcursorImageLength (images->images[n]); 1033 toc++; 1034 } 1035 1036 for (n = 0; n < ncomment; n++) 1037 { 1038 fileHeader->tocs[toc].type = XCURSOR_COMMENT_TYPE; 1039 fileHeader->tocs[toc].subtype = comments->comments[n]->comment_type; 1040 fileHeader->tocs[toc].position = position; 1041 position += _XcursorCommentLength (comments->comments[n]); 1042 toc++; 1043 } 1044 1045 /* 1046 * Write the header and the toc 1047 */ 1048 if (!_XcursorWriteFileHeader (file, fileHeader)) 1049 goto bail; 1050 1051 /* 1052 * Write the images 1053 */ 1054 toc = 0; 1055 for (n = 0; n < nimage; n++) 1056 { 1057 if (!_XcursorWriteImage (file, fileHeader, toc, images->images[n])) 1058 goto bail; 1059 toc++; 1060 } 1061 1062 /* 1063 * Write the comments 1064 */ 1065 for (n = 0; n < ncomment; n++) 1066 { 1067 if (!_XcursorWriteComment (file, fileHeader, toc, comments->comments[n])) 1068 goto bail; 1069 toc++; 1070 } 1071 1072 _XcursorFileHeaderDestroy (fileHeader); 1073 returnCode(XcursorTrue); 1074 1075bail: 1076 _XcursorFileHeaderDestroy (fileHeader); 1077 returnCode(XcursorFalse); 1078} 1079 1080static int 1081_XcursorStdioFileRead (XcursorFile *file, unsigned char *buf, int len) 1082{ 1083 FILE *f = file->closure; 1084 return (int) fread (buf, 1, (size_t) len, f); 1085} 1086 1087static int 1088_XcursorStdioFileWrite (XcursorFile *file, unsigned char *buf, int len) 1089{ 1090 FILE *f = file->closure; 1091 return (int) fwrite (buf, 1, (size_t) len, f); 1092} 1093 1094static int 1095_XcursorStdioFileSeek (XcursorFile *file, long offset, int whence) 1096{ 1097 FILE *f = file->closure; 1098 return fseek (f, offset, whence); 1099} 1100 1101static void 1102_XcursorStdioFileInitialize (FILE *stdfile, XcursorFile *file) 1103{ 1104 file->closure = stdfile; 1105 file->read = _XcursorStdioFileRead; 1106 file->write = _XcursorStdioFileWrite; 1107 file->seek = _XcursorStdioFileSeek; 1108} 1109 1110XcursorImage * 1111_XcursorFileLoadImage (FILE *file, int size, XcursorBool resize) 1112{ 1113 XcursorFile f; 1114 1115 enterFunc((T_CALLED(_XcursorFileLoadImage) "(%p, %d, %d)\n", (void*)file, size, resize)); 1116 1117 if (!file) 1118 returnAddr(NULL); 1119 1120 _XcursorStdioFileInitialize (file, &f); 1121 returnAddr(_XcursorXcFileLoadImage (&f, size, resize)); 1122} 1123 1124XcursorImages * 1125_XcursorFileLoadImages (FILE *file, int size, XcursorBool resize) 1126{ 1127 XcursorFile f; 1128 1129 enterFunc((T_CALLED(_XcursorFileLoadImages) "(%p, %d, %d)\n", (void*)file, size, resize)); 1130 1131 if (!file) 1132 returnAddr(NULL); 1133 1134 _XcursorStdioFileInitialize (file, &f); 1135 returnAddr(_XcursorXcFileLoadImages (&f, size, resize)); 1136} 1137 1138XcursorImage * 1139XcursorFileLoadImage (FILE *file, int size) 1140{ 1141 XcursorFile f; 1142 1143 enterFunc((T_CALLED(XcursorFileLoadImage) "(%p, %d)\n", (void*)file, size)); 1144 1145 if (!file) 1146 returnAddr(NULL); 1147 1148 _XcursorStdioFileInitialize (file, &f); 1149 returnAddr(XcursorXcFileLoadImage (&f, size)); 1150} 1151 1152XcursorImages * 1153XcursorFileLoadImages (FILE *file, int size) 1154{ 1155 XcursorFile f; 1156 1157 enterFunc((T_CALLED(XcursorFileLoadImages) "(%p, %d)\n", (void*)file, size)); 1158 1159 if (!file) 1160 returnAddr(NULL); 1161 1162 _XcursorStdioFileInitialize (file, &f); 1163 returnAddr(XcursorXcFileLoadImages (&f, size)); 1164} 1165 1166XcursorImages * 1167XcursorFileLoadAllImages (FILE *file) 1168{ 1169 XcursorFile f; 1170 1171 enterFunc((T_CALLED(XcursorFileLoadAllImages) "(%p)\n", (void*)file)); 1172 1173 if (!file) 1174 returnAddr(NULL); 1175 1176 _XcursorStdioFileInitialize (file, &f); 1177 returnAddr(XcursorXcFileLoadAllImages (&f)); 1178} 1179 1180XcursorBool 1181XcursorFileLoad (FILE *file, 1182 XcursorComments **commentsp, 1183 XcursorImages **imagesp) 1184{ 1185 XcursorFile f; 1186 1187 enterFunc((T_CALLED(XcursorFileLoad) "(%p, %p, %p)\n", 1188 (void*)file, (void*)commentsp, (void*)imagesp)); 1189 1190 if (!file || !commentsp || !imagesp) 1191 returnCode(XcursorFalse); 1192 1193 _XcursorStdioFileInitialize (file, &f); 1194 returnCode(XcursorXcFileLoad (&f, commentsp, imagesp)); 1195} 1196 1197XcursorBool 1198XcursorFileSaveImages (FILE *file, const XcursorImages *images) 1199{ 1200 XcursorComments *comments; 1201 XcursorFile f; 1202 XcursorBool ret; 1203 1204 enterFunc((T_CALLED(XcursorFileSaveImages) "(%p, %p)\n", 1205 (void*)file, (const void*)images)); 1206 1207 if (!file || !images) 1208 returnCode(0); 1209 if ((comments = XcursorCommentsCreate (0)) == NULL) 1210 returnCode(0); 1211 _XcursorStdioFileInitialize (file, &f); 1212 ret = XcursorXcFileSave (&f, comments, images) && fflush (file) != EOF; 1213 XcursorCommentsDestroy (comments); 1214 returnCode(ret); 1215} 1216 1217XcursorBool 1218XcursorFileSave (FILE * file, 1219 const XcursorComments *comments, 1220 const XcursorImages *images) 1221{ 1222 XcursorFile f; 1223 1224 enterFunc((T_CALLED(_XcursorFileSave) "(%p, %p, %p)\n", 1225 (void*)file, (const void*)comments, (const void*)images)); 1226 1227 if (!file || !comments || !images) 1228 returnCode(XcursorFalse); 1229 1230 _XcursorStdioFileInitialize (file, &f); 1231 returnCode(XcursorXcFileSave (&f, comments, images) && fflush (file) != EOF); 1232} 1233 1234XcursorImage * 1235XcursorFilenameLoadImage (const char *file, int size) 1236{ 1237 FILE *f; 1238 XcursorImage *image; 1239 1240 enterFunc((T_CALLED(XcursorFilenameLoadImage) "(\"%s\", %d)\n", 1241 NonNull(file), size)); 1242 1243 if (!file || size < 0) 1244 returnAddr(NULL); 1245 1246 f = fopen (file, "r" FOPEN_CLOEXEC); 1247 if (!f) 1248 returnAddr(NULL); 1249 image = XcursorFileLoadImage (f, size); 1250 fclose (f); 1251 returnAddr(image); 1252} 1253 1254XcursorImages * 1255_XcursorFilenameLoadImages (const char *file, int size, XcursorBool resize) 1256{ 1257 FILE *f; 1258 XcursorImages *images; 1259 1260 enterFunc((T_CALLED(_XcursorFilenameLoadImages) "(\"%s\", %d, %d)\n", 1261 NonNull(file), size, resize)); 1262 1263 if (!file || size < 0) 1264 returnAddr(NULL); 1265 1266 f = fopen (file, "r" FOPEN_CLOEXEC); 1267 if (!f) 1268 returnAddr(NULL); 1269 images = _XcursorFileLoadImages (f, size, resize); 1270 fclose (f); 1271 returnAddr(images); 1272} 1273 1274XcursorImages * 1275XcursorFilenameLoadImages (const char *file, int size) 1276{ 1277 FILE *f; 1278 XcursorImages *images; 1279 1280 enterFunc((T_CALLED(XcursorFilenameLoadImages) "(\"%s\", %d)\n", 1281 NonNull(file), size)); 1282 1283 if (!file || size < 0) 1284 returnAddr(NULL); 1285 1286 f = fopen (file, "r" FOPEN_CLOEXEC); 1287 if (!f) 1288 returnAddr(NULL); 1289 images = XcursorFileLoadImages (f, size); 1290 fclose (f); 1291 returnAddr(images); 1292} 1293 1294XcursorImages * 1295XcursorFilenameLoadAllImages (const char *file) 1296{ 1297 FILE *f; 1298 XcursorImages *images; 1299 1300 enterFunc((T_CALLED(XcursorFilenameLoadAllImages) "(\"%s\")\n", 1301 NonNull(file))); 1302 1303 if (!file) 1304 returnAddr(NULL); 1305 1306 f = fopen (file, "r" FOPEN_CLOEXEC); 1307 if (!f) 1308 returnAddr(NULL); 1309 images = XcursorFileLoadAllImages (f); 1310 fclose (f); 1311 returnAddr(images); 1312} 1313 1314XcursorBool 1315XcursorFilenameLoad (const char *file, 1316 XcursorComments **commentsp, 1317 XcursorImages **imagesp) 1318{ 1319 FILE *f; 1320 XcursorBool ret; 1321 1322 enterFunc((T_CALLED() "(\"%s\", %p, %p)\n", 1323 NonNull(file), (void*)commentsp, (void*)imagesp)); 1324 1325 if (!file) 1326 returnCode(XcursorFalse); 1327 1328 f = fopen (file, "r" FOPEN_CLOEXEC); 1329 if (!f) 1330 returnCode(0); 1331 ret = XcursorFileLoad (f, commentsp, imagesp); 1332 fclose (f); 1333 returnCode(ret); 1334} 1335 1336XcursorBool 1337XcursorFilenameSaveImages (const char *file, const XcursorImages *images) 1338{ 1339 FILE *f; 1340 XcursorBool ret; 1341 1342 enterFunc((T_CALLED(XcursorFilenameSaveImages) "(\"%s\", %p)\n", 1343 NonNull(file), (const void*)images)); 1344 1345 if (!file || !images) 1346 returnCode(XcursorFalse); 1347 1348 f = fopen (file, "w" FOPEN_CLOEXEC); 1349 if (!f) 1350 returnCode(0); 1351 ret = XcursorFileSaveImages (f, images); 1352 returnCode(fclose (f) != EOF && ret); 1353} 1354 1355XcursorBool 1356XcursorFilenameSave (const char *file, 1357 const XcursorComments *comments, 1358 const XcursorImages *images) 1359{ 1360 FILE *f; 1361 XcursorBool ret; 1362 1363 enterFunc((T_CALLED(XcursorFilenameSave ) "(\"%s\", %p, %p)\n", 1364 NonNull(file), 1365 (const void *) comments, 1366 (const void *) images)); 1367 1368 if (!file || !comments || !images) { 1369 ret = XcursorFalse; 1370 } else { 1371 f = fopen (file, "w" FOPEN_CLOEXEC); 1372 if (!f) { 1373 ret = 0; 1374 } else { 1375 ret = XcursorFileSave (f, comments, images); 1376 ret = fclose (f) != EOF && ret; 1377 } 1378 } 1379 returnCode(ret); 1380} 1381