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 30static _cups_mutex_t sp_mutex = _CUPS_MUTEX_INITIALIZER; 31 /* Mutex to control access to pool */ 32static cups_array_t *stringpool = NULL; 33 /* Global string pool */ 34 35 36/* 37 * Local functions... 38 */ 39 40static 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 47char * /* 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 148char * /* 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 179void 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 206char * /* 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 286void 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 361char * /* 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 398double /* 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 533size_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 585void 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 601char * /* 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 624int /* 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 651int /* 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 684size_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 699 if (size < (dstlen + 1)) 700 return (dstlen); /* No room, return immediately... */ 701 702 size -= dstlen + 1; 703 704 /* 705 * Figure out how much room is needed... 706 */ 707 708 srclen = strlen(src); 709 710 /* 711 * Copy the appropriate amount... 712 */ 713 714 if (srclen > size) 715 srclen = size; 716 717 memmove(dst + dstlen, src, srclen); 718 dst[dstlen + srclen] = '\0'; 719 720 return (dstlen + srclen); 721} 722#endif /* !HAVE_STRLCAT */ 723 724 725#ifndef HAVE_STRLCPY 726/* 727 * '_cups_strlcpy()' - Safely copy two strings. 728 */ 729 730size_t /* O - Length of string */ 731_cups_strlcpy(char *dst, /* O - Destination string */ 732 const char *src, /* I - Source string */ 733 size_t size) /* I - Size of destination string buffer */ 734{ 735 size_t srclen; /* Length of source string */ 736 737 738 /* 739 * Figure out how much room is needed... 740 */ 741 742 size --; 743 744 srclen = strlen(src); 745 746 /* 747 * Copy the appropriate amount... 748 */ 749 750 if (srclen > size) 751 srclen = size; 752 753 memmove(dst, src, srclen); 754 dst[srclen] = '\0'; 755 756 return (srclen); 757} 758#endif /* !HAVE_STRLCPY */ 759 760 761/* 762 * 'compare_sp_items()' - Compare two string pool items... 763 */ 764 765static int /* O - Result of comparison */ 766compare_sp_items(_cups_sp_item_t *a, /* I - First item */ 767 _cups_sp_item_t *b) /* I - Second item */ 768{ 769 return (strcmp(a->str, b->str)); 770} 771