1 /* 2 * String functions for CUPS. 3 * 4 * Copyright 2007-2014 by Apple Inc. 5 * Copyright 1997-2007 by Easy Software Products. 6 * 7 * These coded instructions, statements, and computer programs are the 8 * property of Apple Inc. and are protected by Federal copyright 9 * law. Distribution and use rights are outlined in the file "LICENSE.txt" 10 * which should have been included with this file. If this file is 11 * missing or damaged, see the license at "http://www.cups.org/". 12 * 13 * This file is subject to the Apple OS-Developed Software exception. 14 */ 15 16 /* 17 * Include necessary headers... 18 */ 19 20 #define _CUPS_STRING_C_ 21 #include "cups-private.h" 22 #include <stddef.h> 23 #include <limits.h> 24 25 26 /* 27 * Local globals... 28 */ 29 30 static _cups_mutex_t sp_mutex = _CUPS_MUTEX_INITIALIZER; 31 /* Mutex to control access to pool */ 32 static cups_array_t *stringpool = NULL; 33 /* Global string pool */ 34 35 36 /* 37 * Local functions... 38 */ 39 40 static int compare_sp_items(_cups_sp_item_t *a, _cups_sp_item_t *b); 41 42 43 /* 44 * '_cupsStrAlloc()' - Allocate/reference a string. 45 */ 46 47 char * /* O - String pointer */ 48 _cupsStrAlloc(const char *s) /* I - String */ 49 { 50 size_t slen; /* Length of string */ 51 _cups_sp_item_t *item, /* String pool item */ 52 *key; /* Search key */ 53 54 55 /* 56 * Range check input... 57 */ 58 59 if (!s) 60 return (NULL); 61 62 /* 63 * Get the string pool... 64 */ 65 66 _cupsMutexLock(&sp_mutex); 67 68 if (!stringpool) 69 stringpool = cupsArrayNew((cups_array_func_t)compare_sp_items, NULL); 70 71 if (!stringpool) 72 { 73 _cupsMutexUnlock(&sp_mutex); 74 75 return (NULL); 76 } 77 78 /* 79 * See if the string is already in the pool... 80 */ 81 82 key = (_cups_sp_item_t *)(s - offsetof(_cups_sp_item_t, str)); 83 84 if ((item = (_cups_sp_item_t *)cupsArrayFind(stringpool, key)) != NULL) 85 { 86 /* 87 * Found it, return the cached string... 88 */ 89 90 item->ref_count ++; 91 92 #ifdef DEBUG_GUARDS 93 DEBUG_printf(("5_cupsStrAlloc: Using string %p(%s) for \"%s\", guard=%08x, " 94 "ref_count=%d", item, item->str, s, item->guard, 95 item->ref_count)); 96 97 if (item->guard != _CUPS_STR_GUARD) 98 abort(); 99 #endif /* DEBUG_GUARDS */ 100 101 _cupsMutexUnlock(&sp_mutex); 102 103 return (item->str); 104 } 105 106 /* 107 * Not found, so allocate a new one... 108 */ 109 110 slen = strlen(s); 111 item = (_cups_sp_item_t *)calloc(1, sizeof(_cups_sp_item_t) + slen); 112 if (!item) 113 { 114 _cupsMutexUnlock(&sp_mutex); 115 116 return (NULL); 117 } 118 119 item->ref_count = 1; 120 memcpy(item->str, s, slen + 1); 121 122 #ifdef DEBUG_GUARDS 123 item->guard = _CUPS_STR_GUARD; 124 125 DEBUG_printf(("5_cupsStrAlloc: Created string %p(%s) for \"%s\", guard=%08x, " 126 "ref_count=%d", item, item->str, s, item->guard, 127 item->ref_count)); 128 #endif /* DEBUG_GUARDS */ 129 130 /* 131 * Add the string to the pool and return it... 132 */ 133 134 cupsArrayAdd(stringpool, item); 135 136 _cupsMutexUnlock(&sp_mutex); 137 138 return (item->str); 139 } 140 141 142 /* 143 * '_cupsStrDate()' - Return a localized date for a given time value. 144 * 145 * This function works around the locale encoding issues of strftime... 146 */ 147 148 char * /* O - Buffer */ 149 _cupsStrDate(char *buf, /* I - Buffer */ 150 size_t bufsize, /* I - Size of buffer */ 151 time_t timeval) /* I - Time value */ 152 { 153 struct tm *dateval; /* Local date/time */ 154 char temp[1024]; /* Temporary buffer */ 155 _cups_globals_t *cg = _cupsGlobals(); /* Per-thread globals */ 156 157 158 if (!cg->lang_default) 159 cg->lang_default = cupsLangDefault(); 160 161 dateval = localtime(&timeval); 162 163 if (cg->lang_default->encoding != CUPS_UTF8) 164 { 165 strftime(temp, sizeof(temp), "%c", dateval); 166 cupsCharsetToUTF8((cups_utf8_t *)buf, temp, (int)bufsize, cg->lang_default->encoding); 167 } 168 else 169 strftime(buf, bufsize, "%c", dateval); 170 171 return (buf); 172 } 173 174 175 /* 176 * '_cupsStrFlush()' - Flush the string pool. 177 */ 178 179 void 180 _cupsStrFlush(void) 181 { 182 _cups_sp_item_t *item; /* Current item */ 183 184 185 DEBUG_printf(("4_cupsStrFlush: %d strings in array", 186 cupsArrayCount(stringpool))); 187 188 _cupsMutexLock(&sp_mutex); 189 190 for (item = (_cups_sp_item_t *)cupsArrayFirst(stringpool); 191 item; 192 item = (_cups_sp_item_t *)cupsArrayNext(stringpool)) 193 free(item); 194 195 cupsArrayDelete(stringpool); 196 stringpool = NULL; 197 198 _cupsMutexUnlock(&sp_mutex); 199 } 200 201 202 /* 203 * '_cupsStrFormatd()' - Format a floating-point number. 204 */ 205 206 char * /* O - Pointer to end of string */ 207 _cupsStrFormatd(char *buf, /* I - String */ 208 char *bufend, /* I - End of string buffer */ 209 double number, /* I - Number to format */ 210 struct lconv *loc) /* I - Locale data */ 211 { 212 char *bufptr, /* Pointer into buffer */ 213 temp[1024], /* Temporary string */ 214 *tempdec, /* Pointer to decimal point */ 215 *tempptr; /* Pointer into temporary string */ 216 const char *dec; /* Decimal point */ 217 int declen; /* Length of decimal point */ 218 219 220 /* 221 * Format the number using the "%.12f" format and then eliminate 222 * unnecessary trailing 0's. 223 */ 224 225 snprintf(temp, sizeof(temp), "%.12f", number); 226 for (tempptr = temp + strlen(temp) - 1; 227 tempptr > temp && *tempptr == '0'; 228 *tempptr-- = '\0'); 229 230 /* 231 * Next, find the decimal point... 232 */ 233 234 if (loc && loc->decimal_point) 235 { 236 dec = loc->decimal_point; 237 declen = (int)strlen(dec); 238 } 239 else 240 { 241 dec = "."; 242 declen = 1; 243 } 244 245 if (declen == 1) 246 tempdec = strchr(temp, *dec); 247 else 248 tempdec = strstr(temp, dec); 249 250 /* 251 * Copy everything up to the decimal point... 252 */ 253 254 if (tempdec) 255 { 256 for (tempptr = temp, bufptr = buf; 257 tempptr < tempdec && bufptr < bufend; 258 *bufptr++ = *tempptr++); 259 260 tempptr += declen; 261 262 if (*tempptr && bufptr < bufend) 263 { 264 *bufptr++ = '.'; 265 266 while (*tempptr && bufptr < bufend) 267 *bufptr++ = *tempptr++; 268 } 269 270 *bufptr = '\0'; 271 } 272 else 273 { 274 strlcpy(buf, temp, (size_t)(bufend - buf + 1)); 275 bufptr = buf + strlen(buf); 276 } 277 278 return (bufptr); 279 } 280 281 282 /* 283 * '_cupsStrFree()' - Free/dereference a string. 284 */ 285 286 void 287 _cupsStrFree(const char *s) /* I - String to free */ 288 { 289 _cups_sp_item_t *item, /* String pool item */ 290 *key; /* Search key */ 291 292 293 /* 294 * Range check input... 295 */ 296 297 if (!s) 298 return; 299 300 /* 301 * Check the string pool... 302 * 303 * We don't need to lock the mutex yet, as we only want to know if 304 * the stringpool is initialized. The rest of the code will still 305 * work if it is initialized before we lock... 306 */ 307 308 if (!stringpool) 309 return; 310 311 /* 312 * See if the string is already in the pool... 313 */ 314 315 _cupsMutexLock(&sp_mutex); 316 317 key = (_cups_sp_item_t *)(s - offsetof(_cups_sp_item_t, str)); 318 319 #ifdef DEBUG_GUARDS 320 if (key->guard != _CUPS_STR_GUARD) 321 { 322 DEBUG_printf(("5_cupsStrFree: Freeing string %p(%s), guard=%08x, " 323 "ref_count=%d", key, key->str, key->guard, key->ref_count)); 324 abort(); 325 } 326 #endif /* DEBUG_GUARDS */ 327 328 if ((item = (_cups_sp_item_t *)cupsArrayFind(stringpool, key)) != NULL && 329 item == key) 330 { 331 /* 332 * Found it, dereference... 333 */ 334 335 item->ref_count --; 336 337 if (!item->ref_count) 338 { 339 /* 340 * Remove and free... 341 */ 342 343 cupsArrayRemove(stringpool, item); 344 345 free(item); 346 } 347 } 348 349 _cupsMutexUnlock(&sp_mutex); 350 } 351 352 353 /* 354 * '_cupsStrRetain()' - Increment the reference count of a string. 355 * 356 * Note: This function does not verify that the passed pointer is in the 357 * string pool, so any calls to it MUST know they are passing in a 358 * good pointer. 359 */ 360 361 char * /* O - Pointer to string */ 362 _cupsStrRetain(const char *s) /* I - String to retain */ 363 { 364 _cups_sp_item_t *item; /* Pointer to string pool item */ 365 366 367 if (s) 368 { 369 item = (_cups_sp_item_t *)(s - offsetof(_cups_sp_item_t, str)); 370 371 #ifdef DEBUG_GUARDS 372 if (item->guard != _CUPS_STR_GUARD) 373 { 374 DEBUG_printf(("5_cupsStrRetain: Retaining string %p(%s), guard=%08x, " 375 "ref_count=%d", item, s, item->guard, item->ref_count)); 376 abort(); 377 } 378 #endif /* DEBUG_GUARDS */ 379 380 _cupsMutexLock(&sp_mutex); 381 382 item->ref_count ++; 383 384 _cupsMutexUnlock(&sp_mutex); 385 } 386 387 return ((char *)s); 388 } 389 390 391 /* 392 * '_cupsStrScand()' - Scan a string for a floating-point number. 393 * 394 * This function handles the locale-specific BS so that a decimal 395 * point is always the period (".")... 396 */ 397 398 double /* O - Number */ 399 _cupsStrScand(const char *buf, /* I - Pointer to number */ 400 char **bufptr, /* O - New pointer or NULL on error */ 401 struct lconv *loc) /* I - Locale data */ 402 { 403 char temp[1024], /* Temporary buffer */ 404 *tempptr; /* Pointer into temporary buffer */ 405 406 407 /* 408 * Range check input... 409 */ 410 411 if (!buf) 412 return (0.0); 413 414 /* 415 * Skip leading whitespace... 416 */ 417 418 while (_cups_isspace(*buf)) 419 buf ++; 420 421 /* 422 * Copy leading sign, numbers, period, and then numbers... 423 */ 424 425 tempptr = temp; 426 if (*buf == '-' || *buf == '+') 427 *tempptr++ = *buf++; 428 429 while (isdigit(*buf & 255)) 430 if (tempptr < (temp + sizeof(temp) - 1)) 431 *tempptr++ = *buf++; 432 else 433 { 434 if (bufptr) 435 *bufptr = NULL; 436 437 return (0.0); 438 } 439 440 if (*buf == '.') 441 { 442 /* 443 * Read fractional portion of number... 444 */ 445 446 buf ++; 447 448 if (loc && loc->decimal_point) 449 { 450 strlcpy(tempptr, loc->decimal_point, sizeof(temp) - (size_t)(tempptr - temp)); 451 tempptr += strlen(tempptr); 452 } 453 else if (tempptr < (temp + sizeof(temp) - 1)) 454 *tempptr++ = '.'; 455 else 456 { 457 if (bufptr) 458 *bufptr = NULL; 459 460 return (0.0); 461 } 462 463 while (isdigit(*buf & 255)) 464 if (tempptr < (temp + sizeof(temp) - 1)) 465 *tempptr++ = *buf++; 466 else 467 { 468 if (bufptr) 469 *bufptr = NULL; 470 471 return (0.0); 472 } 473 } 474 475 if (*buf == 'e' || *buf == 'E') 476 { 477 /* 478 * Read exponent... 479 */ 480 481 if (tempptr < (temp + sizeof(temp) - 1)) 482 *tempptr++ = *buf++; 483 else 484 { 485 if (bufptr) 486 *bufptr = NULL; 487 488 return (0.0); 489 } 490 491 if (*buf == '+' || *buf == '-') 492 { 493 if (tempptr < (temp + sizeof(temp) - 1)) 494 *tempptr++ = *buf++; 495 else 496 { 497 if (bufptr) 498 *bufptr = NULL; 499 500 return (0.0); 501 } 502 } 503 504 while (isdigit(*buf & 255)) 505 if (tempptr < (temp + sizeof(temp) - 1)) 506 *tempptr++ = *buf++; 507 else 508 { 509 if (bufptr) 510 *bufptr = NULL; 511 512 return (0.0); 513 } 514 } 515 516 /* 517 * Nul-terminate the temporary string and return the value... 518 */ 519 520 if (bufptr) 521 *bufptr = (char *)buf; 522 523 *tempptr = '\0'; 524 525 return (strtod(temp, NULL)); 526 } 527 528 529 /* 530 * '_cupsStrStatistics()' - Return allocation statistics for string pool. 531 */ 532 533 size_t /* O - Number of strings */ 534 _cupsStrStatistics(size_t *alloc_bytes, /* O - Allocated bytes */ 535 size_t *total_bytes) /* O - Total string bytes */ 536 { 537 size_t count, /* Number of strings */ 538 abytes, /* Allocated string bytes */ 539 tbytes, /* Total string bytes */ 540 len; /* Length of string */ 541 _cups_sp_item_t *item; /* Current item */ 542 543 544 /* 545 * Loop through strings in pool, counting everything up... 546 */ 547 548 _cupsMutexLock(&sp_mutex); 549 550 for (count = 0, abytes = 0, tbytes = 0, 551 item = (_cups_sp_item_t *)cupsArrayFirst(stringpool); 552 item; 553 item = (_cups_sp_item_t *)cupsArrayNext(stringpool)) 554 { 555 /* 556 * Count allocated memory, using a 64-bit aligned buffer as a basis. 557 */ 558 559 count += item->ref_count; 560 len = (strlen(item->str) + 8) & (size_t)~7; 561 abytes += sizeof(_cups_sp_item_t) + len; 562 tbytes += item->ref_count * len; 563 } 564 565 _cupsMutexUnlock(&sp_mutex); 566 567 /* 568 * Return values... 569 */ 570 571 if (alloc_bytes) 572 *alloc_bytes = abytes; 573 574 if (total_bytes) 575 *total_bytes = tbytes; 576 577 return (count); 578 } 579 580 581 /* 582 * '_cups_strcpy()' - Copy a string allowing for overlapping strings. 583 */ 584 585 void 586 _cups_strcpy(char *dst, /* I - Destination string */ 587 const char *src) /* I - Source string */ 588 { 589 while (*src) 590 *dst++ = *src++; 591 592 *dst = '\0'; 593 } 594 595 596 /* 597 * '_cups_strdup()' - Duplicate a string. 598 */ 599 600 #ifndef HAVE_STRDUP 601 char * /* O - New string pointer */ 602 _cups_strdup(const char *s) /* I - String to duplicate */ 603 { 604 char *t; /* New string pointer */ 605 size_t slen; /* Length of string */ 606 607 608 if (!s) 609 return (NULL); 610 611 slen = strlen(s); 612 if ((t = malloc(slen + 1)) == NULL) 613 return (NULL); 614 615 return (memcpy(t, s, slen + 1)); 616 } 617 #endif /* !HAVE_STRDUP */ 618 619 620 /* 621 * '_cups_strcasecmp()' - Do a case-insensitive comparison. 622 */ 623 624 int /* O - Result of comparison (-1, 0, or 1) */ 625 _cups_strcasecmp(const char *s, /* I - First string */ 626 const char *t) /* I - Second string */ 627 { 628 while (*s != '\0' && *t != '\0') 629 { 630 if (_cups_tolower(*s) < _cups_tolower(*t)) 631 return (-1); 632 else if (_cups_tolower(*s) > _cups_tolower(*t)) 633 return (1); 634 635 s ++; 636 t ++; 637 } 638 639 if (*s == '\0' && *t == '\0') 640 return (0); 641 else if (*s != '\0') 642 return (1); 643 else 644 return (-1); 645 } 646 647 /* 648 * '_cups_strncasecmp()' - Do a case-insensitive comparison on up to N chars. 649 */ 650 651 int /* O - Result of comparison (-1, 0, or 1) */ 652 _cups_strncasecmp(const char *s, /* I - First string */ 653 const char *t, /* I - Second string */ 654 size_t n) /* I - Maximum number of characters to compare */ 655 { 656 while (*s != '\0' && *t != '\0' && n > 0) 657 { 658 if (_cups_tolower(*s) < _cups_tolower(*t)) 659 return (-1); 660 else if (_cups_tolower(*s) > _cups_tolower(*t)) 661 return (1); 662 663 s ++; 664 t ++; 665 n --; 666 } 667 668 if (n == 0) 669 return (0); 670 else if (*s == '\0' && *t == '\0') 671 return (0); 672 else if (*s != '\0') 673 return (1); 674 else 675 return (-1); 676 } 677 678 679 #ifndef HAVE_STRLCAT 680 /* 681 * '_cups_strlcat()' - Safely concatenate two strings. 682 */ 683 684 size_t /* O - Length of string */ 685 _cups_strlcat(char *dst, /* O - Destination string */ 686 const char *src, /* I - Source string */ 687 size_t size) /* I - Size of destination string buffer */ 688 { 689 size_t srclen; /* Length of source string */ 690 size_t dstlen; /* Length of destination string */ 691 692 693 /* 694 * Figure out how much room is left... 695 */ 696 697 dstlen = strlen(dst); 698 size -= dstlen + 1; 699 700 if (!size) 701 return (dstlen); /* No room, return immediately... */ 702 703 /* 704 * Figure out how much room is needed... 705 */ 706 707 srclen = strlen(src); 708 709 /* 710 * Copy the appropriate amount... 711 */ 712 713 if (srclen > size) 714 srclen = size; 715 716 memmove(dst + dstlen, src, srclen); 717 dst[dstlen + srclen] = '\0'; 718 719 return (dstlen + srclen); 720 } 721 #endif /* !HAVE_STRLCAT */ 722 723 724 #ifndef HAVE_STRLCPY 725 /* 726 * '_cups_strlcpy()' - Safely copy two strings. 727 */ 728 729 size_t /* O - Length of string */ 730 _cups_strlcpy(char *dst, /* O - Destination string */ 731 const char *src, /* I - Source string */ 732 size_t size) /* I - Size of destination string buffer */ 733 { 734 size_t srclen; /* Length of source string */ 735 736 737 /* 738 * Figure out how much room is needed... 739 */ 740 741 size --; 742 743 srclen = strlen(src); 744 745 /* 746 * Copy the appropriate amount... 747 */ 748 749 if (srclen > size) 750 srclen = size; 751 752 memmove(dst, src, srclen); 753 dst[srclen] = '\0'; 754 755 return (srclen); 756 } 757 #endif /* !HAVE_STRLCPY */ 758 759 760 /* 761 * 'compare_sp_items()' - Compare two string pool items... 762 */ 763 764 static int /* O - Result of comparison */ 765 compare_sp_items(_cups_sp_item_t *a, /* I - First item */ 766 _cups_sp_item_t *b) /* I - Second item */ 767 { 768 return (strcmp(a->str, b->str)); 769 } 770