Home | History | Annotate | Download | only in cups
      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 
    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 
    730 size_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 
    765 static int				/* O - Result of comparison */
    766 compare_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