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 */
_cupsStrAlloc(const char * s)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 */
_cupsStrDate(char * buf,size_t bufsize,time_t timeval)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
_cupsStrFlush(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 */
_cupsStrFormatd(char * buf,char * bufend,double number,struct lconv * loc)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
_cupsStrFree(const char * s)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 */
_cupsStrRetain(const char * s)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 */
_cupsStrScand(const char * buf,char ** bufptr,struct lconv * loc)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 */
_cupsStrStatistics(size_t * alloc_bytes,size_t * total_bytes)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
_cups_strcpy(char * dst,const char * src)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 */
_cups_strdup(const char * s)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) */
_cups_strcasecmp(const char * s,const char * t)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) */
_cups_strncasecmp(const char * s,const char * t,size_t n)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 */
_cups_strlcat(char * dst,const char * src,size_t size)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 */
_cups_strlcpy(char * dst,const char * src,size_t size)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 */
compare_sp_items(_cups_sp_item_t * a,_cups_sp_item_t * b)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