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