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